[Attachment Support] Create attachment elements when dropping files on iOS
[WebKit-https.git] / Source / WebCore / platform / mac / PasteboardMac.mm
1 /*
2  * Copyright (C) 2006-2017 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 #import "DragData.h"
30 #import "Image.h"
31 #import "LegacyNSPasteboardTypes.h"
32 #import "LoaderNSURLExtras.h"
33 #import "MIMETypeRegistry.h"
34 #import "PasteboardStrategy.h"
35 #import "PlatformPasteboard.h"
36 #import "PlatformStrategies.h"
37 #import "SharedBuffer.h"
38 #import "URL.h"
39 #import "UTIUtilities.h"
40 #import "WebNSAttributedStringExtras.h"
41 #import <pal/spi/cg/CoreGraphicsSPI.h>
42 #import <pal/spi/mac/HIServicesSPI.h>
43 #import <wtf/RetainPtr.h>
44 #import <wtf/StdLibExtras.h>
45 #import <wtf/text/StringBuilder.h>
46 #import <wtf/unicode/CharacterNames.h>
47
48 namespace WebCore {
49
50 const char* const WebArchivePboardType = "Apple Web Archive pasteboard type";
51 const char* const WebURLNamePboardType = "public.url-name";
52
53 const char WebSmartPastePboardType[] = "NeXT smart paste pasteboard type";
54 const char WebURLPboardType[] = "public.url";
55 const char WebURLsWithTitlesPboardType[] = "WebURLsWithTitlesPboardType";
56
57 static const Vector<String> writableTypesForURL()
58 {
59     Vector<String> types;
60     
61     types.append(WebURLsWithTitlesPboardType);
62     types.append(String(legacyURLPasteboardType()));
63     types.append(WebURLPboardType);
64     types.append(WebURLNamePboardType);
65     types.append(String(legacyStringPasteboardType()));
66     return types;
67 }
68
69 static Vector<String> writableTypesForImage()
70 {
71     Vector<String> types;
72     types.append(String(legacyTIFFPasteboardType()));
73     types.appendVector(writableTypesForURL());
74     types.append(String(legacyRTFDPasteboardType()));
75     return types;
76 }
77
78 NSArray *Pasteboard::supportedFileUploadPasteboardTypes()
79 {
80     return @[ (NSString *)legacyFilesPromisePasteboardType(), (NSString *)legacyFilenamesPasteboardType() ];
81 }
82
83 Pasteboard::Pasteboard()
84     : m_pasteboardName(emptyString())
85     , m_changeCount(0)
86 {
87 }
88
89 Pasteboard::Pasteboard(const String& pasteboardName)
90     : m_pasteboardName(pasteboardName)
91     , m_changeCount(platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName))
92 {
93     ASSERT(pasteboardName);
94 }
95
96 std::unique_ptr<Pasteboard> Pasteboard::createForCopyAndPaste()
97 {
98 #pragma clang diagnostic push
99 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
100     return std::make_unique<Pasteboard>(NSGeneralPboard);
101 #pragma clang diagnostic pop
102 }
103
104 #if ENABLE(DRAG_SUPPORT)
105 std::unique_ptr<Pasteboard> Pasteboard::createForDragAndDrop()
106 {
107 #pragma clang diagnostic push
108 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
109     return std::make_unique<Pasteboard>(NSDragPboard);
110 #pragma clang diagnostic pop
111 }
112
113 std::unique_ptr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData)
114 {
115     return std::make_unique<Pasteboard>(dragData.pasteboardName());
116 }
117 #endif
118
119 void Pasteboard::clear()
120 {
121     m_changeCount = platformStrategies()->pasteboardStrategy()->setTypes(Vector<String>(), m_pasteboardName);
122 }
123
124 void Pasteboard::write(const PasteboardWebContent& content)
125 {
126     Vector<String> types;
127
128     if (content.canSmartCopyOrDelete)
129         types.append(WebSmartPastePboardType);
130     if (content.dataInWebArchiveFormat)
131         types.append(WebArchivePboardType);
132     if (content.dataInRTFDFormat)
133         types.append(String(legacyRTFDPasteboardType()));
134     if (content.dataInRTFFormat)
135         types.append(String(legacyRTFPasteboardType()));
136     if (!content.dataInHTMLFormat.isNull())
137         types.append(String(legacyHTMLPasteboardType()));
138     if (!content.dataInStringFormat.isNull())
139         types.append(String(legacyStringPasteboardType()));
140     types.appendVector(content.clientTypes);
141     types.append(PasteboardCustomData::cocoaType());
142
143     m_changeCount = platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
144
145     ASSERT(content.clientTypes.size() == content.clientData.size());
146     for (size_t i = 0, size = content.clientTypes.size(); i < size; ++i)
147         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.clientData[i].get(), content.clientTypes[i], m_pasteboardName);
148     if (content.canSmartCopyOrDelete)
149         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(nullptr, WebSmartPastePboardType, m_pasteboardName);
150     if (content.dataInWebArchiveFormat)
151         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInWebArchiveFormat.get(), WebArchivePboardType, m_pasteboardName);
152     if (content.dataInRTFDFormat)
153         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInRTFDFormat.get(), legacyRTFDPasteboardType(), m_pasteboardName);
154     if (content.dataInRTFFormat)
155         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInRTFFormat.get(), legacyRTFPasteboardType(), m_pasteboardName);
156     if (!content.dataInHTMLFormat.isNull())
157         m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(content.dataInHTMLFormat, legacyHTMLPasteboardType(), m_pasteboardName);
158     if (!content.dataInStringFormat.isNull())
159         m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(content.dataInStringFormat, legacyStringPasteboardType(), m_pasteboardName);
160
161     PasteboardCustomData data;
162     data.origin = content.contentOrigin;
163     m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(data.createSharedBuffer().ptr(), PasteboardCustomData::cocoaType(), m_pasteboardName);
164
165 }
166
167 void Pasteboard::writePlainText(const String& text, SmartReplaceOption smartReplaceOption)
168 {
169     Vector<String> types;
170     types.append(legacyStringPasteboardType());
171     if (smartReplaceOption == CanSmartReplace)
172         types.append(WebSmartPastePboardType);
173
174     platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
175     m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(text, legacyStringPasteboardType(), m_pasteboardName);
176     if (smartReplaceOption == CanSmartReplace)
177         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(0, WebSmartPastePboardType, m_pasteboardName);
178 }
179
180 static long writeURLForTypes(const Vector<String>& types, const String& pasteboardName, const PasteboardURL& pasteboardURL)
181 {
182     long newChangeCount = platformStrategies()->pasteboardStrategy()->setTypes(types, pasteboardName);
183     
184     ASSERT(!pasteboardURL.url.isEmpty());
185     
186     NSURL *cocoaURL = pasteboardURL.url;
187     NSString *userVisibleString = pasteboardURL.userVisibleForm;
188     NSString *title = (NSString *)pasteboardURL.title;
189     if (![title length]) {
190         title = [[cocoaURL path] lastPathComponent];
191         if (![title length])
192             title = userVisibleString;
193     }
194
195     if (types.contains(WebURLsWithTitlesPboardType)) {
196         Vector<String> paths;
197         paths.append([cocoaURL absoluteString]);
198         paths.append(pasteboardURL.title.stripWhiteSpace());
199         newChangeCount = platformStrategies()->pasteboardStrategy()->setPathnamesForType(paths, WebURLsWithTitlesPboardType, 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     NSURL *cocoaURL = pasteboardURL.url;
221     Vector<String> paths = { [cocoaURL absoluteString], pasteboardURL.title.stripWhiteSpace() };
222     m_changeCount = platformStrategies()->pasteboardStrategy()->setPathnamesForType(paths, WebURLsWithTitlesPboardType, m_pasteboardName);
223 }
224
225 static NSFileWrapper* fileWrapper(const PasteboardImage& pasteboardImage)
226 {
227     NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:pasteboardImage.resourceData->createNSData().get()] autorelease];
228     [wrapper setPreferredFilename:suggestedFilenameWithMIMEType(pasteboardImage.url.url, pasteboardImage.resourceMIMEType)];
229     return wrapper;
230 }
231
232 static void writeFileWrapperAsRTFDAttachment(NSFileWrapper *wrapper, const String& pasteboardName, long& newChangeCount)
233 {
234     NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:wrapper];
235     NSAttributedString *string = [NSAttributedString attributedStringWithAttachment:attachment];
236     [attachment release];
237
238     NSData *RTFDData = [string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:@{ }];
239     if (!RTFDData)
240         return;
241
242     newChangeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::create(RTFDData).ptr(), legacyRTFDPasteboardType(), pasteboardName);
243 }
244
245 void Pasteboard::write(const PasteboardImage& pasteboardImage)
246 {
247     CFDataRef imageData = pasteboardImage.image->tiffRepresentation();
248     if (!imageData)
249         return;
250
251     // FIXME: Why can we assert this? It doesn't seem like it's guaranteed.
252     ASSERT(MIMETypeRegistry::isSupportedImageResourceMIMEType(pasteboardImage.resourceMIMEType));
253
254     auto types = writableTypesForImage();
255     if (pasteboardImage.dataInWebArchiveFormat)
256         types.append(WebArchivePboardType);
257
258     m_changeCount = writeURLForTypes(types, m_pasteboardName, pasteboardImage.url);
259     m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::create(imageData).ptr(), legacyTIFFPasteboardType(), m_pasteboardName);
260     if (pasteboardImage.dataInWebArchiveFormat)
261         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(pasteboardImage.dataInWebArchiveFormat.get(), WebArchivePboardType, m_pasteboardName);
262     writeFileWrapperAsRTFDAttachment(fileWrapper(pasteboardImage), m_pasteboardName, m_changeCount);
263 }
264
265 bool Pasteboard::canSmartReplace()
266 {
267     Vector<String> types;
268     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
269     return types.contains(WebSmartPastePboardType);
270 }
271
272 void Pasteboard::writeMarkup(const String&)
273 {
274 }
275
276 void Pasteboard::read(PasteboardPlainText& text)
277 {
278     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
279
280     Vector<String> types;
281     strategy.getTypes(types, m_pasteboardName);
282
283     if (types.contains(String(NSPasteboardTypeString))) {
284         text.text = strategy.stringForType(NSPasteboardTypeString, m_pasteboardName);
285         text.isURL = false;
286         return;
287     }
288
289     if (types.contains(String(legacyStringPasteboardType()))) {
290         text.text = strategy.stringForType(legacyStringPasteboardType(), m_pasteboardName);
291         text.isURL = false;
292         return;
293     }
294     
295     if (types.contains(String(legacyRTFDPasteboardType()))) {
296         if (RefPtr<SharedBuffer> data = strategy.bufferForType(legacyRTFDPasteboardType(), m_pasteboardName)) {
297             if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTFD:data->createNSData().get() documentAttributes:NULL])) {
298                 text.text = [attributedString string];
299                 text.isURL = false;
300                 return;
301             }
302         }
303     }
304
305     if (types.contains(String(legacyRTFPasteboardType()))) {
306         if (RefPtr<SharedBuffer> data = strategy.bufferForType(legacyRTFPasteboardType(), m_pasteboardName)) {
307             if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTF:data->createNSData().get() documentAttributes:NULL])) {
308                 text.text = [attributedString string];
309                 text.isURL = false;
310                 return;
311             }
312         }
313     }
314
315     if (types.contains(String(legacyFilenamesPasteboardType()))) {
316         Vector<String> pathnames;
317         strategy.getPathnamesForType(pathnames, legacyFilenamesPasteboardType(), m_pasteboardName);
318         StringBuilder builder;
319         for (size_t i = 0, size = pathnames.size(); i < size; i++) {
320             if (i)
321                 builder.append('\n');
322             builder.append(pathnames[i]);
323         }
324         text.text = builder.toString();
325         text.isURL = false;
326         return;
327     }
328
329     // FIXME: The code above looks at the types vector first, but this just gets the string without checking. Why the difference?
330     text.text = strategy.stringForType(legacyURLPasteboardType(), m_pasteboardName);
331     text.isURL = !text.text.isNull();
332 }
333
334 void Pasteboard::read(PasteboardWebContentReader& reader)
335 {
336     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
337
338     Vector<String> types;
339     strategy.getTypes(types, m_pasteboardName);
340
341     reader.contentOrigin = readOrigin();
342
343     if (types.contains(WebArchivePboardType)) {
344         if (auto buffer = strategy.bufferForType(WebArchivePboardType, m_pasteboardName)) {
345             if (m_changeCount != changeCount() || reader.readWebArchive(*buffer))
346                 return;
347         }
348     }
349
350     if (types.contains(String(legacyFilenamesPasteboardType()))) {
351         Vector<String> paths;
352         strategy.getPathnamesForType(paths, legacyFilenamesPasteboardType(), m_pasteboardName);
353         if (m_changeCount != changeCount() || reader.readFilePaths(paths))
354             return;
355     }
356
357     if (types.contains(String(legacyHTMLPasteboardType()))) {
358         String string = strategy.stringForType(legacyHTMLPasteboardType(), m_pasteboardName);
359         if (m_changeCount != changeCount() || (!string.isNull() && reader.readHTML(string)))
360             return;
361     }
362
363     if (types.contains(String(legacyRTFDPasteboardType()))) {
364         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyRTFDPasteboardType(), m_pasteboardName)) {
365             if (m_changeCount != changeCount() || reader.readRTFD(*buffer))
366                 return;
367         }
368     }
369
370     if (types.contains(String(legacyRTFPasteboardType()))) {
371         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyRTFPasteboardType(), m_pasteboardName)) {
372             if (m_changeCount != changeCount() || reader.readRTF(*buffer))
373                 return;
374         }
375     }
376
377     if (types.contains(String(legacyTIFFPasteboardType()))) {
378         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyTIFFPasteboardType(), m_pasteboardName)) {
379             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/tiff")))
380                 return;
381         }
382     }
383
384     if (types.contains(String(legacyPDFPasteboardType()))) {
385         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyPDFPasteboardType(), m_pasteboardName)) {
386             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("application/pdf")))
387                 return;
388         }
389     }
390
391     if (types.contains(String(kUTTypePNG))) {
392         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypePNG, m_pasteboardName)) {
393             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/png")))
394                 return;
395         }
396     }
397
398     if (types.contains(String(kUTTypeJPEG))) {
399         if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypeJPEG, m_pasteboardName)) {
400             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/jpeg")))
401                 return;
402         }
403     }
404
405     if (types.contains(String(legacyURLPasteboardType()))) {
406         URL url = strategy.url(m_pasteboardName);
407         String title = strategy.stringForType(WebURLNamePboardType, m_pasteboardName);
408         if (m_changeCount != changeCount() || (!url.isNull() && reader.readURL(url, title)))
409             return;
410     }
411
412     if (types.contains(String(legacyStringPasteboardType()))) {
413         String string = strategy.stringForType(legacyStringPasteboardType(), m_pasteboardName);
414         if (m_changeCount != changeCount() || (!string.isNull() && reader.readPlainText(string)))
415             return;
416     }
417
418     if (types.contains(String(kUTTypeUTF8PlainText))) {
419         String string = strategy.stringForType(kUTTypeUTF8PlainText, m_pasteboardName);
420         if (m_changeCount != changeCount() || (!string.isNull() && reader.readPlainText(string)))
421             return;
422     }
423 }
424
425 bool Pasteboard::hasData()
426 {
427     Vector<String> types;
428     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
429     return !types.isEmpty();
430 }
431
432 static String cocoaTypeFromHTMLClipboardType(const String& type)
433 {
434     if (NSString *platformType = PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(type)) {
435         if (platformType.length)
436             return platformType;
437     }
438
439     // Blacklist types that might contain subframe information.
440     if (type == "text/rtf" || type == "public.rtf" || type == "com.apple.traditional-mac-plain-text")
441         return String();
442
443     auto utiType = UTIFromMIMEType(type);
444     if (!utiType.isEmpty()) {
445         if (auto pbType = adoptCF(UTTypeCopyPreferredTagWithClass(utiType.createCFString().get(), kUTTagClassNSPboardType)))
446             return pbType.get();
447     }
448
449     // No mapping, just pass the whole string though
450     return type;
451 }
452
453 void Pasteboard::clear(const String& type)
454 {
455     String cocoaType = cocoaTypeFromHTMLClipboardType(type);
456     if (cocoaType.isEmpty())
457         return;
458     m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(emptyString(), cocoaType, m_pasteboardName);
459 }
460
461 static Vector<String> absoluteURLsFromPasteboardFilenames(const String& pasteboardName, bool onlyFirstURL = false)
462 {
463     Vector<String> fileList;
464     platformStrategies()->pasteboardStrategy()->getPathnamesForType(fileList, String(legacyFilenamesPasteboardType()), pasteboardName);
465
466     if (fileList.isEmpty())
467         return fileList;
468
469     size_t count = onlyFirstURL ? 1 : fileList.size();
470     Vector<String> urls;
471     for (size_t i = 0; i < count; i++) {
472         NSURL *url = [NSURL fileURLWithPath:fileList[i]];
473         urls.append(String([url absoluteString]));
474     }
475     return urls;
476 }
477
478 String Pasteboard::readPlatformValueAsString(const String& domType, long changeCount, const String& pasteboardName)
479 {
480     const String& cocoaType = cocoaTypeFromHTMLClipboardType(domType);
481     String cocoaValue;
482
483     if (cocoaType == String(legacyStringPasteboardType()))
484         cocoaValue = [platformStrategies()->pasteboardStrategy()->stringForType(cocoaType, pasteboardName) precomposedStringWithCanonicalMapping];
485     else if (!cocoaType.isEmpty())
486         cocoaValue = platformStrategies()->pasteboardStrategy()->stringForType(cocoaType, pasteboardName);
487
488     // Enforce changeCount ourselves for security.  We check after reading instead of before to be
489     // sure it doesn't change between our testing the change count and accessing the data.
490     if (!cocoaValue.isEmpty() && changeCount == platformStrategies()->pasteboardStrategy()->changeCount(pasteboardName))
491         return cocoaValue;
492
493     return String();
494 }
495
496 static String utiTypeFromCocoaType(const String& type)
497 {
498     if (RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, type.createCFString().get(), 0))) {
499         if (RetainPtr<CFStringRef> mimeType = adoptCF(UTTypeCopyPreferredTagWithClass(utiType.get(), kUTTagClassMIMEType)))
500             return String(mimeType.get());
501     }
502     return String();
503 }
504
505 void Pasteboard::addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes, const String& cocoaType)
506 {
507     if (cocoaType == "NeXT plain ascii pasteboard type")
508         return; // Skip this ancient type that gets auto-supplied by some system conversion.
509
510     // UTI may not do these right, so make sure we get the right, predictable result
511     if (cocoaType == String(legacyStringPasteboardType()) || cocoaType == String(NSPasteboardTypeString)) {
512         resultTypes.add(ASCIILiteral("text/plain"));
513         return;
514     }
515     if (cocoaType == String(legacyURLPasteboardType())) {
516         resultTypes.add(ASCIILiteral("text/uri-list"));
517         return;
518     }
519     if (cocoaType == String(legacyFilenamesPasteboardType()) || Pasteboard::shouldTreatCocoaTypeAsFile(cocoaType))
520         return;
521     String utiType = utiTypeFromCocoaType(cocoaType);
522     if (!utiType.isEmpty()) {
523         resultTypes.add(utiType);
524         return;
525     }
526     // No mapping, just pass the whole string through.
527     resultTypes.add(cocoaType);
528 }
529
530 void Pasteboard::writeString(const String& type, const String& data)
531 {
532     const String& cocoaType = cocoaTypeFromHTMLClipboardType(type);
533     String cocoaData = data;
534
535     if (cocoaType == String(legacyURLPasteboardType()) || cocoaType == String(kUTTypeFileURL)) {
536         NSURL *url = [NSURL URLWithString:cocoaData];
537         if ([url isFileURL])
538             return;
539
540         Vector<String> types;
541         types.append(cocoaType);
542         platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
543         m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(cocoaData, cocoaType, m_pasteboardName);
544
545         return;
546     }
547
548     if (!cocoaType.isEmpty()) {
549         // everything else we know of goes on the pboard as a string
550         Vector<String> types;
551         types.append(cocoaType);
552         platformStrategies()->pasteboardStrategy()->addTypes(types, m_pasteboardName);
553         m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(cocoaData, cocoaType, m_pasteboardName);
554     }
555 }
556
557 Vector<String> Pasteboard::readFilePaths()
558 {
559     // FIXME: Seems silly to convert paths to URLs and then back to paths. Does that do anything helpful?
560     Vector<String> absoluteURLs = absoluteURLsFromPasteboardFilenames(m_pasteboardName);
561     Vector<String> paths;
562     paths.reserveCapacity(absoluteURLs.size());
563     for (size_t i = 0; i < absoluteURLs.size(); i++) {
564         NSURL *absoluteURL = [NSURL URLWithString:absoluteURLs[i]];
565         ASSERT([absoluteURL isFileURL]);
566         paths.uncheckedAppend([absoluteURL path]);
567     }
568     return paths;
569 }
570
571 #if ENABLE(DRAG_SUPPORT)
572 static void flipImageSpec(CoreDragImageSpec* imageSpec)
573 {
574     unsigned char* tempRow = (unsigned char*)fastMalloc(imageSpec->bytesPerRow);
575     int planes = imageSpec->isPlanar ? imageSpec->samplesPerPixel : 1;
576
577     for (int p = 0; p < planes; ++p) {
578         unsigned char* topRow = (unsigned char*)imageSpec->data[p];
579         unsigned char* botRow = topRow + (imageSpec->pixelsHigh - 1) * imageSpec->bytesPerRow;
580         for (int i = 0; i < imageSpec->pixelsHigh / 2; ++i, topRow += imageSpec->bytesPerRow, botRow -= imageSpec->bytesPerRow) {
581             bcopy(topRow, tempRow, imageSpec->bytesPerRow);
582             bcopy(botRow, topRow, imageSpec->bytesPerRow);
583             bcopy(tempRow, botRow, imageSpec->bytesPerRow);
584         }
585     }
586
587     fastFree(tempRow);
588 }
589
590 static void setDragImageImpl(NSImage *image, NSPoint offset)
591 {
592     bool flipImage;
593     NSSize imageSize = image.size;
594     CGRect imageRect = CGRectMake(0, 0, imageSize.width, imageSize.height);
595     NSImageRep *imageRep = [image bestRepresentationForRect:NSRectFromCGRect(imageRect) context:nil hints:nil];
596     RetainPtr<NSBitmapImageRep> bitmapImage;
597     if (!imageRep || ![imageRep isKindOfClass:[NSBitmapImageRep class]] || !NSEqualSizes(imageRep.size, imageSize)) {
598         [image lockFocus];
599         bitmapImage = adoptNS([[NSBitmapImageRep alloc] initWithFocusedViewRect:*(NSRect*)&imageRect]);
600         [image unlockFocus];
601         
602         // we may have to flip the bits we just read if the image was flipped since it means the cache was also
603         // and CoreDragSetImage can't take a transform for rendering.
604 #pragma clang diagnostic push
605 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
606         flipImage = image.isFlipped;
607 #pragma clang diagnostic pop
608     } else {
609         flipImage = false;
610         bitmapImage = (NSBitmapImageRep *)imageRep;
611     }
612     ASSERT(bitmapImage);
613
614     CoreDragImageSpec imageSpec;
615     imageSpec.version = kCoreDragImageSpecVersionOne;
616     imageSpec.pixelsWide = [bitmapImage pixelsWide];
617     imageSpec.pixelsHigh = [bitmapImage pixelsHigh];
618     imageSpec.bitsPerSample = [bitmapImage bitsPerSample];
619     imageSpec.samplesPerPixel = [bitmapImage samplesPerPixel];
620     imageSpec.bitsPerPixel = [bitmapImage bitsPerPixel];
621     imageSpec.bytesPerRow = [bitmapImage bytesPerRow];
622     imageSpec.isPlanar = [bitmapImage isPlanar];
623     imageSpec.hasAlpha = [bitmapImage hasAlpha];
624     [bitmapImage getBitmapDataPlanes:(unsigned char**)imageSpec.data];
625
626     // if image was flipped, we have an upside down bitmap since the cache is rendered flipped
627     if (flipImage)
628         flipImageSpec(&imageSpec);
629
630     CGSRegionObj imageShape;
631     OSStatus error = CGSNewRegionWithRect(&imageRect, &imageShape);
632     ASSERT(error == kCGErrorSuccess);
633     if (error != kCGErrorSuccess)
634         return;
635
636     // make sure image has integer offset
637     CGPoint imageOffset = { -offset.x, -(imageSize.height - offset.y) };
638     imageOffset.x = floor(imageOffset.x + 0.5);
639     imageOffset.y = floor(imageOffset.y + 0.5);
640
641     error = CoreDragSetImage(CoreDragGetCurrentDrag(), imageOffset, &imageSpec, imageShape, 1.0);
642     CGSReleaseRegion(imageShape);
643     ASSERT(error == kCGErrorSuccess);
644 }
645
646 void Pasteboard::setDragImage(DragImage image, const IntPoint& location)
647 {
648     // Don't allow setting the drag image if someone kept a pasteboard and is trying to set the image too late.
649     if (m_changeCount != platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName))
650         return;
651
652     // Dashboard wants to be able to set the drag image during dragging, but Cocoa does not allow this.
653     // Instead we must drop down to the CoreGraphics API.
654     setDragImageImpl(image.get().get(), location);
655
656     // Hack: We must post an event to wake up the NSDragManager, which is sitting in a nextEvent call
657     // up the stack from us because the CoreFoundation drag manager does not use the run loop by itself.
658     // This is the most innocuous event to use, per Kristen Forster.
659     NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved location:NSZeroPoint
660         modifierFlags:0 timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:0 pressure:0];
661     [NSApp postEvent:event atStart:YES];
662 }
663 #endif
664
665 }