[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebCore / platform / mac / PasteboardMac.mm
1 /*
2  * Copyright (C) 2006-2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "Pasteboard.h"
28
29 #if PLATFORM(MAC)
30
31 #import "DragData.h"
32 #import "Image.h"
33 #import "LegacyNSPasteboardTypes.h"
34 #import "LoaderNSURLExtras.h"
35 #import "MIMETypeRegistry.h"
36 #import "PasteboardStrategy.h"
37 #import "PlatformPasteboard.h"
38 #import "PlatformStrategies.h"
39 #import "SharedBuffer.h"
40 #import "UTIUtilities.h"
41 #import "WebNSAttributedStringExtras.h"
42 #import <pal/spi/cg/CoreGraphicsSPI.h>
43 #import <pal/spi/mac/HIServicesSPI.h>
44 #import <wtf/ProcessPrivilege.h>
45 #import <wtf/RetainPtr.h>
46 #import <wtf/StdLibExtras.h>
47 #import <wtf/URL.h>
48 #import <wtf/text/StringBuilder.h>
49 #import <wtf/unicode/CharacterNames.h>
50
51 namespace WebCore {
52
53 const char* const WebArchivePboardType = "Apple Web Archive pasteboard type";
54 const char* const WebURLNamePboardType = "public.url-name";
55 const char* const WebURLsWithTitlesPboardType = "WebURLsWithTitlesPboardType";
56
57 const char WebSmartPastePboardType[] = "NeXT smart paste pasteboard type";
58 const char WebURLPboardType[] = "public.url";
59
60 static const Vector<String> writableTypesForURL()
61 {
62     Vector<String> types;
63     
64     types.append(WebURLsWithTitlesPboardType);
65     types.append(String(legacyURLPasteboardType()));
66     types.append(WebURLPboardType);
67     types.append(WebURLNamePboardType);
68     types.append(String(legacyStringPasteboardType()));
69     return types;
70 }
71
72 static Vector<String> writableTypesForImage()
73 {
74     Vector<String> types;
75     types.append(String(legacyTIFFPasteboardType()));
76     types.appendVector(writableTypesForURL());
77     types.append(String(legacyRTFDPasteboardType()));
78     return types;
79 }
80
81 NSArray *Pasteboard::supportedFileUploadPasteboardTypes()
82 {
83     return @[ (NSString *)legacyFilesPromisePasteboardType(), (NSString *)legacyFilenamesPasteboardType() ];
84 }
85
86 Pasteboard::Pasteboard()
87     : m_pasteboardName(emptyString())
88     , m_changeCount(0)
89 {
90 }
91
92 Pasteboard::Pasteboard(const String& pasteboardName, const Vector<String>& promisedFilePaths)
93     : m_pasteboardName(pasteboardName)
94     , m_changeCount(platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName))
95     , m_promisedFilePaths(promisedFilePaths)
96 {
97     ASSERT(pasteboardName);
98 }
99
100 std::unique_ptr<Pasteboard> Pasteboard::createForCopyAndPaste()
101 {
102     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
103     return makeUnique<Pasteboard>(NSGeneralPboard);
104     ALLOW_DEPRECATED_DECLARATIONS_END
105 }
106
107 #if ENABLE(DRAG_SUPPORT)
108 std::unique_ptr<Pasteboard> Pasteboard::createForDragAndDrop()
109 {
110     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
111     return makeUnique<Pasteboard>(NSDragPboard);
112     ALLOW_DEPRECATED_DECLARATIONS_END
113 }
114
115 std::unique_ptr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData)
116 {
117     return makeUnique<Pasteboard>(dragData.pasteboardName(), dragData.fileNames());
118 }
119 #endif
120
121 void Pasteboard::clear()
122 {
123     m_changeCount = platformStrategies()->pasteboardStrategy()->setTypes(Vector<String>(), m_pasteboardName);
124 }
125
126 void Pasteboard::write(const PasteboardWebContent& content)
127 {
128     Vector<String> types;
129
130     if (content.canSmartCopyOrDelete)
131         types.append(WebSmartPastePboardType);
132     if (content.dataInWebArchiveFormat)
133         types.append(WebArchivePboardType);
134     if (content.dataInRTFDFormat)
135         types.append(String(legacyRTFDPasteboardType()));
136     if (content.dataInRTFFormat)
137         types.append(String(legacyRTFPasteboardType()));
138     if (!content.dataInHTMLFormat.isNull())
139         types.append(String(legacyHTMLPasteboardType()));
140     if (!content.dataInStringFormat.isNull())
141         types.append(String(legacyStringPasteboardType()));
142     types.appendVector(content.clientTypes);
143     types.append(PasteboardCustomData::cocoaType());
144
145     m_changeCount = platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
146
147     ASSERT(content.clientTypes.size() == content.clientData.size());
148     for (size_t i = 0, size = content.clientTypes.size(); i < size; ++i)
149         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.clientData[i].get(), content.clientTypes[i], m_pasteboardName);
150     if (content.canSmartCopyOrDelete)
151         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(nullptr, WebSmartPastePboardType, m_pasteboardName);
152     if (content.dataInWebArchiveFormat)
153         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInWebArchiveFormat.get(), WebArchivePboardType, m_pasteboardName);
154     if (content.dataInRTFDFormat)
155         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInRTFDFormat.get(), legacyRTFDPasteboardType(), m_pasteboardName);
156     if (content.dataInRTFFormat)
157         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInRTFFormat.get(), legacyRTFPasteboardType(), m_pasteboardName);
158     if (!content.dataInHTMLFormat.isNull())
159         m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(content.dataInHTMLFormat, legacyHTMLPasteboardType(), m_pasteboardName);
160     if (!content.dataInStringFormat.isNull())
161         m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(content.dataInStringFormat, legacyStringPasteboardType(), m_pasteboardName);
162
163     PasteboardCustomData data;
164     data.origin = content.contentOrigin;
165     m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(data.createSharedBuffer().ptr(), PasteboardCustomData::cocoaType(), m_pasteboardName);
166
167 }
168
169 void Pasteboard::writePlainText(const String& text, SmartReplaceOption smartReplaceOption)
170 {
171     Vector<String> types;
172     types.append(legacyStringPasteboardType());
173     if (smartReplaceOption == CanSmartReplace)
174         types.append(WebSmartPastePboardType);
175
176     platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
177     m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(text, legacyStringPasteboardType(), m_pasteboardName);
178     if (smartReplaceOption == CanSmartReplace)
179         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(0, WebSmartPastePboardType, m_pasteboardName);
180 }
181
182 static long writeURLForTypes(const Vector<String>& types, const String& pasteboardName, const PasteboardURL& pasteboardURL)
183 {
184     long newChangeCount = platformStrategies()->pasteboardStrategy()->setTypes(types, pasteboardName);
185     
186     ASSERT(!pasteboardURL.url.isEmpty());
187     
188     NSURL *cocoaURL = pasteboardURL.url;
189     NSString *userVisibleString = pasteboardURL.userVisibleForm;
190     NSString *title = (NSString *)pasteboardURL.title;
191     if (![title length]) {
192         title = [[cocoaURL path] lastPathComponent];
193         if (![title length])
194             title = userVisibleString;
195     }
196
197     if (types.contains(WebURLsWithTitlesPboardType)) {
198         PasteboardURL url = { pasteboardURL.url, String(title).stripWhiteSpace(), emptyString() };
199         newChangeCount = platformStrategies()->pasteboardStrategy()->setURL(url, pasteboardName);
200     }
201     if (types.contains(String(legacyURLPasteboardType())))
202         newChangeCount = platformStrategies()->pasteboardStrategy()->setStringForType([cocoaURL absoluteString], legacyURLPasteboardType(), pasteboardName);
203     if (types.contains(WebURLPboardType))
204         newChangeCount = platformStrategies()->pasteboardStrategy()->setStringForType(userVisibleString, WebURLPboardType, pasteboardName);
205     if (types.contains(WebURLNamePboardType))
206         newChangeCount = platformStrategies()->pasteboardStrategy()->setStringForType(title, WebURLNamePboardType, pasteboardName);
207     if (types.contains(String(legacyStringPasteboardType())))
208         newChangeCount = platformStrategies()->pasteboardStrategy()->setStringForType(userVisibleString, legacyStringPasteboardType(), pasteboardName);
209
210     return newChangeCount;
211 }
212     
213 void Pasteboard::write(const PasteboardURL& pasteboardURL)
214 {
215     m_changeCount = writeURLForTypes(writableTypesForURL(), m_pasteboardName, pasteboardURL);
216 }
217
218 void Pasteboard::writeTrustworthyWebURLsPboardType(const PasteboardURL& pasteboardURL)
219 {
220     PasteboardURL url = { pasteboardURL.url, pasteboardURL.title.stripWhiteSpace(), emptyString() };
221     m_changeCount = platformStrategies()->pasteboardStrategy()->setURL(url, m_pasteboardName);
222 }
223
224 void Pasteboard::write(const Color& color)
225 {
226     Vector<String> types = { legacyColorPasteboardType() };
227     platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
228     m_changeCount = platformStrategies()->pasteboardStrategy()->setColor(color, m_pasteboardName);
229 }
230
231 static NSFileWrapper* fileWrapper(const PasteboardImage& pasteboardImage)
232 {
233     NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:pasteboardImage.resourceData->createNSData().get()] autorelease];
234     [wrapper setPreferredFilename:suggestedFilenameWithMIMEType(pasteboardImage.url.url, pasteboardImage.resourceMIMEType)];
235     return wrapper;
236 }
237
238 static void writeFileWrapperAsRTFDAttachment(NSFileWrapper *wrapper, const String& pasteboardName, long& newChangeCount)
239 {
240     NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:wrapper];
241     NSAttributedString *string = [NSAttributedString attributedStringWithAttachment:attachment];
242     [attachment release];
243
244     NSData *RTFDData = [string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:@{ }];
245     if (!RTFDData)
246         return;
247
248     newChangeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::create(RTFDData).ptr(), legacyRTFDPasteboardType(), pasteboardName);
249 }
250
251 void Pasteboard::write(const PasteboardImage& pasteboardImage)
252 {
253     CFDataRef imageData = pasteboardImage.image->tiffRepresentation();
254     if (!imageData)
255         return;
256
257     // FIXME: Why can we assert this? It doesn't seem like it's guaranteed.
258     ASSERT(MIMETypeRegistry::isSupportedImageMIMEType(pasteboardImage.resourceMIMEType));
259
260     auto types = writableTypesForImage();
261     if (pasteboardImage.dataInWebArchiveFormat)
262         types.append(WebArchivePboardType);
263
264     m_changeCount = writeURLForTypes(types, m_pasteboardName, pasteboardImage.url);
265     m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::create(imageData).ptr(), legacyTIFFPasteboardType(), m_pasteboardName);
266     if (pasteboardImage.dataInWebArchiveFormat)
267         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(pasteboardImage.dataInWebArchiveFormat.get(), WebArchivePboardType, m_pasteboardName);
268     writeFileWrapperAsRTFDAttachment(fileWrapper(pasteboardImage), m_pasteboardName, m_changeCount);
269 }
270
271 bool Pasteboard::canSmartReplace()
272 {
273     Vector<String> types;
274     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
275     return types.contains(WebSmartPastePboardType);
276 }
277
278 void Pasteboard::writeMarkup(const String&)
279 {
280 }
281
282 // FIXME: This should be a general utility function for Vectors of Strings (or things that can be
283 // converted to Strings). It could also be faster by computing the total length and reserving that
284 // capacity in the StringBuilder.
285 static String joinPathnames(const Vector<String>& pathnames)
286 {
287     StringBuilder builder;
288     for (auto& path : pathnames) {
289         if (!builder.isEmpty())
290             builder.append('\n');
291         builder.append(path);
292     }
293     return builder.toString();
294 }
295
296 void Pasteboard::read(PasteboardPlainText& text)
297 {
298     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
299
300     Vector<String> types;
301     strategy.getTypes(types, m_pasteboardName);
302
303     if (types.contains(String(NSPasteboardTypeString))) {
304         text.text = strategy.stringForType(NSPasteboardTypeString, m_pasteboardName);
305         text.isURL = false;
306         return;
307     }
308
309     if (types.contains(String(legacyStringPasteboardType()))) {
310         text.text = strategy.stringForType(legacyStringPasteboardType(), m_pasteboardName);
311         text.isURL = false;
312         return;
313     }
314     
315     if (types.contains(String(legacyRTFDPasteboardType()))) {
316         if (RefPtr<SharedBuffer> data = strategy.bufferForType(legacyRTFDPasteboardType(), m_pasteboardName)) {
317             if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTFD:data->createNSData().get() documentAttributes:NULL])) {
318                 text.text = [attributedString string];
319                 text.isURL = false;
320                 return;
321             }
322         }
323     }
324
325     if (types.contains(String(legacyRTFPasteboardType()))) {
326         if (RefPtr<SharedBuffer> data = strategy.bufferForType(legacyRTFPasteboardType(), m_pasteboardName)) {
327             if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTF:data->createNSData().get() documentAttributes:NULL])) {
328                 text.text = [attributedString string];
329                 text.isURL = false;
330                 return;
331             }
332         }
333     }
334
335     if (types.contains(String(legacyFilesPromisePasteboardType()))) {
336         text.text = joinPathnames(m_promisedFilePaths);
337         text.isURL = false;
338         return;
339     }
340
341     if (types.contains(String(legacyFilenamesPasteboardType()))) {
342         Vector<String> pathnames;
343         strategy.getPathnamesForType(pathnames, legacyFilenamesPasteboardType(), m_pasteboardName);
344         text.text = joinPathnames(pathnames);
345         text.isURL = false;
346         return;
347     }
348
349     // FIXME: The code above looks at the types vector first, but this just gets the string without checking. Why the difference?
350     text.text = strategy.stringForType(legacyURLPasteboardType(), m_pasteboardName);
351     text.isURL = !text.text.isNull();
352 }
353
354 void Pasteboard::read(PasteboardWebContentReader& reader, WebContentReadingPolicy policy)
355 {
356     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
357
358     Vector<String> types;
359     strategy.getTypes(types, m_pasteboardName);
360
361     reader.contentOrigin = readOrigin();
362
363     if (types.contains(WebArchivePboardType)) {
364         if (auto buffer = strategy.bufferForType(WebArchivePboardType, m_pasteboardName)) {
365             if (m_changeCount != changeCount() || reader.readWebArchive(*buffer))
366                 return;
367         }
368     }
369
370     if (policy == WebContentReadingPolicy::AnyType && types.contains(String(legacyFilesPromisePasteboardType()))) {
371         if (m_changeCount != changeCount() || reader.readFilePaths(m_promisedFilePaths))
372             return;
373     }
374
375     if (policy == WebContentReadingPolicy::AnyType && types.contains(String(legacyFilenamesPasteboardType()))) {
376         Vector<String> paths;
377         strategy.getPathnamesForType(paths, legacyFilenamesPasteboardType(), m_pasteboardName);
378         if (m_changeCount != changeCount() || reader.readFilePaths(paths))
379             return;
380     }
381
382     if (types.contains(String(legacyHTMLPasteboardType()))) {
383         String string = strategy.stringForType(legacyHTMLPasteboardType(), m_pasteboardName);
384         if (m_changeCount != changeCount() || (!string.isNull() && reader.readHTML(string)))
385             return;
386     }
387
388     if (types.contains(String(legacyRTFDPasteboardType()))) {
389         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyRTFDPasteboardType(), m_pasteboardName)) {
390             if (m_changeCount != changeCount() || reader.readRTFD(*buffer))
391                 return;
392         }
393     }
394
395     if (types.contains(String(legacyRTFPasteboardType()))) {
396         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyRTFPasteboardType(), m_pasteboardName)) {
397             if (m_changeCount != changeCount() || reader.readRTF(*buffer))
398                 return;
399         }
400     }
401
402     if (policy == WebContentReadingPolicy::OnlyRichTextTypes)
403         return;
404
405     if (types.contains(String(legacyTIFFPasteboardType()))) {
406         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyTIFFPasteboardType(), m_pasteboardName)) {
407             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), "image/tiff"_s))
408                 return;
409         }
410     }
411
412     if (types.contains(String(legacyPDFPasteboardType()))) {
413         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyPDFPasteboardType(), m_pasteboardName)) {
414             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), "application/pdf"_s))
415                 return;
416         }
417     }
418
419     if (types.contains(String(kUTTypePNG))) {
420         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypePNG, m_pasteboardName)) {
421             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), "image/png"_s))
422                 return;
423         }
424     }
425
426     if (types.contains(String(kUTTypeJPEG))) {
427         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypeJPEG, m_pasteboardName)) {
428             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), "image/jpeg"_s))
429                 return;
430         }
431     }
432
433     if (types.contains(String(legacyURLPasteboardType()))) {
434         URL url = strategy.url(m_pasteboardName);
435         String title = strategy.stringForType(WebURLNamePboardType, m_pasteboardName);
436         if (m_changeCount != changeCount() || (!url.isNull() && reader.readURL(url, title)))
437             return;
438     }
439
440     if (types.contains(String(legacyStringPasteboardType()))) {
441         String string = strategy.stringForType(legacyStringPasteboardType(), m_pasteboardName);
442         if (m_changeCount != changeCount() || (!string.isNull() && reader.readPlainText(string)))
443             return;
444     }
445
446     if (types.contains(String(kUTTypeUTF8PlainText))) {
447         String string = strategy.stringForType(kUTTypeUTF8PlainText, m_pasteboardName);
448         if (m_changeCount != changeCount() || (!string.isNull() && reader.readPlainText(string)))
449             return;
450     }
451 }
452
453 bool Pasteboard::hasData()
454 {
455     Vector<String> types;
456     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
457     return !types.isEmpty();
458 }
459
460 static String cocoaTypeFromHTMLClipboardType(const String& type)
461 {
462     if (NSString *platformType = PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(type)) {
463         if (platformType.length)
464             return platformType;
465     }
466
467     // Blacklist types that might contain subframe information.
468     if (type == "text/rtf" || type == "public.rtf" || type == "com.apple.traditional-mac-plain-text")
469         return String();
470
471     auto utiType = UTIFromMIMEType(type);
472     if (!utiType.isEmpty()) {
473         if (auto pbType = adoptCF(UTTypeCopyPreferredTagWithClass(utiType.createCFString().get(), kUTTagClassNSPboardType)))
474             return pbType.get();
475     }
476
477     // No mapping, just pass the whole string though
478     return type;
479 }
480
481 void Pasteboard::clear(const String& type)
482 {
483     String cocoaType = cocoaTypeFromHTMLClipboardType(type);
484     if (cocoaType.isEmpty())
485         return;
486     m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(emptyString(), cocoaType, m_pasteboardName);
487 }
488
489 Vector<String> Pasteboard::readPlatformValuesAsStrings(const String& domType, long changeCount, const String& pasteboardName)
490 {
491     auto& strategy = *platformStrategies()->pasteboardStrategy();
492     auto cocoaType = cocoaTypeFromHTMLClipboardType(domType);
493     if (cocoaType.isEmpty())
494         return { };
495
496     auto values = strategy.allStringsForType(cocoaType, pasteboardName);
497     if (cocoaType == String(legacyStringPasteboardType())) {
498         values = values.map([&] (auto& value) -> String {
499             return [value precomposedStringWithCanonicalMapping];
500         });
501     }
502
503     // Enforce changeCount ourselves for security.  We check after reading instead of before to be
504     // sure it doesn't change between our testing the change count and accessing the data.
505     if (changeCount != platformStrategies()->pasteboardStrategy()->changeCount(pasteboardName))
506         return { };
507
508     return values;
509 }
510
511 static String utiTypeFromCocoaType(const String& type)
512 {
513     if (RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, type.createCFString().get(), 0))) {
514         if (RetainPtr<CFStringRef> mimeType = adoptCF(UTTypeCopyPreferredTagWithClass(utiType.get(), kUTTagClassMIMEType)))
515             return String(mimeType.get());
516     }
517     return String();
518 }
519
520 void Pasteboard::addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes, const String& cocoaType)
521 {
522     if (cocoaType == "NeXT plain ascii pasteboard type")
523         return; // Skip this ancient type that gets auto-supplied by some system conversion.
524
525     // UTI may not do these right, so make sure we get the right, predictable result
526     if (cocoaType == String(legacyStringPasteboardType()) || cocoaType == String(NSPasteboardTypeString)) {
527         resultTypes.add("text/plain"_s);
528         return;
529     }
530     if (cocoaType == String(legacyURLPasteboardType())) {
531         resultTypes.add("text/uri-list"_s);
532         return;
533     }
534     if (cocoaType == String(legacyFilenamesPasteboardType()) || Pasteboard::shouldTreatCocoaTypeAsFile(cocoaType))
535         return;
536     String utiType = utiTypeFromCocoaType(cocoaType);
537     if (!utiType.isEmpty()) {
538         resultTypes.add(utiType);
539         return;
540     }
541     // No mapping, just pass the whole string through.
542     resultTypes.add(cocoaType);
543 }
544
545 void Pasteboard::writeString(const String& type, const String& data)
546 {
547     const String& cocoaType = cocoaTypeFromHTMLClipboardType(type);
548     String cocoaData = data;
549
550     if (cocoaType == String(legacyURLPasteboardType()) || cocoaType == String(kUTTypeFileURL)) {
551         NSURL *url = [NSURL URLWithString:cocoaData];
552         if ([url isFileURL])
553             return;
554
555         Vector<String> types;
556         types.append(cocoaType);
557         platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
558         m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(cocoaData, cocoaType, m_pasteboardName);
559
560         return;
561     }
562
563     if (!cocoaType.isEmpty()) {
564         // everything else we know of goes on the pboard as a string
565         Vector<String> types;
566         types.append(cocoaType);
567         platformStrategies()->pasteboardStrategy()->addTypes(types, m_pasteboardName);
568         m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(cocoaData, cocoaType, m_pasteboardName);
569     }
570 }
571
572 Vector<String> Pasteboard::readFilePaths()
573 {
574     auto& strategy = *platformStrategies()->pasteboardStrategy();
575
576     Vector<String> types;
577     strategy.getTypes(types, m_pasteboardName);
578
579     if (types.contains(String(legacyFilesPromisePasteboardType())))
580         return m_promisedFilePaths;
581
582     if (types.contains(String(legacyFilenamesPasteboardType()))) {
583         Vector<String> filePaths;
584         strategy.getPathnamesForType(filePaths, legacyFilenamesPasteboardType(), m_pasteboardName);
585         return filePaths;
586     }
587
588     return { };
589 }
590
591 #if ENABLE(DRAG_SUPPORT)
592 static void flipImageSpec(CoreDragImageSpec* imageSpec)
593 {
594     unsigned char* tempRow = (unsigned char*)fastMalloc(imageSpec->bytesPerRow);
595     int planes = imageSpec->isPlanar ? imageSpec->samplesPerPixel : 1;
596
597     for (int p = 0; p < planes; ++p) {
598         unsigned char* topRow = const_cast<unsigned char*>(imageSpec->data[p]);
599         unsigned char* botRow = topRow + (imageSpec->pixelsHigh - 1) * imageSpec->bytesPerRow;
600         for (int i = 0; i < imageSpec->pixelsHigh / 2; ++i, topRow += imageSpec->bytesPerRow, botRow -= imageSpec->bytesPerRow) {
601             bcopy(topRow, tempRow, imageSpec->bytesPerRow);
602             bcopy(botRow, topRow, imageSpec->bytesPerRow);
603             bcopy(tempRow, botRow, imageSpec->bytesPerRow);
604         }
605     }
606
607     fastFree(tempRow);
608 }
609
610 static void setDragImageImpl(NSImage *image, NSPoint offset)
611 {
612     bool flipImage;
613     NSSize imageSize = image.size;
614     CGRect imageRect = CGRectMake(0, 0, imageSize.width, imageSize.height);
615     NSImageRep *imageRep = [image bestRepresentationForRect:NSRectFromCGRect(imageRect) context:nil hints:nil];
616     RetainPtr<NSBitmapImageRep> bitmapImage;
617     if (!imageRep || ![imageRep isKindOfClass:[NSBitmapImageRep class]] || !NSEqualSizes(imageRep.size, imageSize)) {
618         [image lockFocus];
619         ALLOW_DEPRECATED_DECLARATIONS_BEGIN
620         bitmapImage = adoptNS([[NSBitmapImageRep alloc] initWithFocusedViewRect:*(NSRect*)&imageRect]);
621         ALLOW_DEPRECATED_DECLARATIONS_END
622         [image unlockFocus];
623         
624         // we may have to flip the bits we just read if the image was flipped since it means the cache was also
625         // and CoreDragSetImage can't take a transform for rendering.
626         ALLOW_DEPRECATED_DECLARATIONS_BEGIN
627         flipImage = image.isFlipped;
628         ALLOW_DEPRECATED_DECLARATIONS_END
629     } else {
630         flipImage = false;
631         bitmapImage = (NSBitmapImageRep *)imageRep;
632     }
633     ASSERT(bitmapImage);
634
635     CoreDragImageSpec imageSpec;
636     imageSpec.version = kCoreDragImageSpecVersionOne;
637     imageSpec.pixelsWide = [bitmapImage pixelsWide];
638     imageSpec.pixelsHigh = [bitmapImage pixelsHigh];
639     imageSpec.bitsPerSample = [bitmapImage bitsPerSample];
640     imageSpec.samplesPerPixel = [bitmapImage samplesPerPixel];
641     imageSpec.bitsPerPixel = [bitmapImage bitsPerPixel];
642     imageSpec.bytesPerRow = [bitmapImage bytesPerRow];
643     imageSpec.isPlanar = [bitmapImage isPlanar];
644     imageSpec.hasAlpha = [bitmapImage hasAlpha];
645     [bitmapImage getBitmapDataPlanes:const_cast<unsigned char**>(imageSpec.data)];
646
647     // if image was flipped, we have an upside down bitmap since the cache is rendered flipped
648     if (flipImage)
649         flipImageSpec(&imageSpec);
650
651     CGSRegionObj imageShape;
652     OSStatus error = CGSNewRegionWithRect(&imageRect, &imageShape);
653     ASSERT(error == kCGErrorSuccess);
654     if (error != kCGErrorSuccess)
655         return;
656
657     // make sure image has integer offset
658     CGPoint imageOffset = { -offset.x, -(imageSize.height - offset.y) };
659     imageOffset.x = floor(imageOffset.x + 0.5);
660     imageOffset.y = floor(imageOffset.y + 0.5);
661
662     error = CoreDragSetImage(CoreDragGetCurrentDrag(), imageOffset, &imageSpec, imageShape, 1.0);
663     CGSReleaseRegion(imageShape);
664     ASSERT(error == kCGErrorSuccess);
665 }
666
667 void Pasteboard::setDragImage(DragImage image, const IntPoint& location)
668 {
669     // Don't allow setting the drag image if someone kept a pasteboard and is trying to set the image too late.
670     if (m_changeCount != platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName))
671         return;
672
673     // Dashboard wants to be able to set the drag image during dragging, but Cocoa does not allow this.
674     // Instead we must drop down to the CoreGraphics API.
675     setDragImageImpl(image.get().get(), location);
676
677     // Hack: We must post an event to wake up the NSDragManager, which is sitting in a nextEvent call
678     // up the stack from us because the CoreFoundation drag manager does not use the run loop by itself.
679     // This is the most innocuous event to use, per Kristin Forster.
680     // This is only relevant in WK1. Do not execute in the WebContent process, since it is now using
681     // NSRunLoop, and not the NSApplication run loop.
682     if ([NSApp isRunning]) {
683         ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
684         NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved location:NSZeroPoint
685             modifierFlags:0 timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:0 pressure:0];
686         [NSApp postEvent:event atStart:YES];
687     }
688 }
689 #endif
690
691 }
692
693 #endif // PLATFORM(MAC)