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