Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebCore / platform / ios / PlatformPasteboardIOS.mm
1 /*
2  * Copyright (C) 2013-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 "PlatformPasteboard.h"
28
29 #if PLATFORM(IOS_FAMILY)
30
31 #import "Color.h"
32 #import "Image.h"
33 #import "Pasteboard.h"
34 #import "SharedBuffer.h"
35 #import "UTIUtilities.h"
36 #import "WebItemProviderPasteboard.h"
37 #import <MobileCoreServices/MobileCoreServices.h>
38 #import <UIKit/UIColor.h>
39 #import <UIKit/UIImage.h>
40 #import <UIKit/UIPasteboard.h>
41 #import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
42 #import <pal/spi/ios/UIKitSPI.h>
43 #import <wtf/ListHashSet.h>
44 #import <wtf/SoftLinking.h>
45 #import <wtf/URL.h>
46 #import <wtf/cocoa/NSURLExtras.h>
47 #import <wtf/text/StringHash.h>
48
49 #define PASTEBOARD_SUPPORTS_ITEM_PROVIDERS (PLATFORM(IOS_FAMILY) && !(PLATFORM(WATCHOS) || PLATFORM(APPLETV)))
50 #define PASTEBOARD_SUPPORTS_PRESENTATION_STYLE_AND_TEAM_DATA (PASTEBOARD_SUPPORTS_ITEM_PROVIDERS && !PLATFORM(IOSMAC))
51 #define NSURL_SUPPORTS_TITLE (!PLATFORM(IOSMAC))
52
53 SOFT_LINK_FRAMEWORK(UIKit)
54 SOFT_LINK_CLASS(UIKit, UIColor)
55 SOFT_LINK_CLASS(UIKit, UIImage)
56 SOFT_LINK_CLASS(UIKit, UIPasteboard)
57
58 namespace WebCore {
59
60 PlatformPasteboard::PlatformPasteboard()
61     : m_pasteboard([getUIPasteboardClass() generalPasteboard])
62 {
63 }
64
65 #if PASTEBOARD_SUPPORTS_ITEM_PROVIDERS
66 PlatformPasteboard::PlatformPasteboard(const String& name)
67 {
68     if (name == "data interaction pasteboard")
69         m_pasteboard = [WebItemProviderPasteboard sharedInstance];
70     else
71         m_pasteboard = [getUIPasteboardClass() generalPasteboard];
72 }
73 #else
74 PlatformPasteboard::PlatformPasteboard(const String&)
75     : m_pasteboard([getUIPasteboardClass() generalPasteboard])
76 {
77 }
78 #endif
79
80 void PlatformPasteboard::getTypes(Vector<String>& types)
81 {
82     for (NSString *pasteboardType in [m_pasteboard pasteboardTypes])
83         types.append(pasteboardType);
84 }
85
86 void PlatformPasteboard::getTypesByFidelityForItemAtIndex(Vector<String>& types, int index)
87 {
88     if (index >= [m_pasteboard numberOfItems] || ![m_pasteboard respondsToSelector:@selector(pasteboardTypesByFidelityForItemAtIndex:)])
89         return;
90
91     NSArray *pasteboardTypesByFidelity = [m_pasteboard pasteboardTypesByFidelityForItemAtIndex:index];
92     for (NSString *typeIdentifier in pasteboardTypesByFidelity)
93         types.append(typeIdentifier);
94 }
95
96 RefPtr<SharedBuffer> PlatformPasteboard::bufferForType(const String& type)
97 {
98     if (NSData *data = [m_pasteboard dataForPasteboardType:type])
99         return SharedBuffer::create(data);
100     return nullptr;
101 }
102
103 void PlatformPasteboard::getPathnamesForType(Vector<String>&, const String&) const
104 {
105 }
106
107 int PlatformPasteboard::numberOfFiles() const
108 {
109     return [m_pasteboard respondsToSelector:@selector(numberOfFiles)] ? [m_pasteboard numberOfFiles] : 0;
110 }
111
112 #if PASTEBOARD_SUPPORTS_ITEM_PROVIDERS
113
114 #if PASTEBOARD_SUPPORTS_PRESENTATION_STYLE_AND_TEAM_DATA
115
116 static PasteboardItemPresentationStyle pasteboardItemPresentationStyle(UIPreferredPresentationStyle style)
117 {
118     switch (style) {
119     case UIPreferredPresentationStyleUnspecified:
120         return PasteboardItemPresentationStyle::Unspecified;
121     case UIPreferredPresentationStyleInline:
122         return PasteboardItemPresentationStyle::Inline;
123     case UIPreferredPresentationStyleAttachment:
124         return PasteboardItemPresentationStyle::Attachment;
125     default:
126         ASSERT_NOT_REACHED();
127         return PasteboardItemPresentationStyle::Unspecified;
128     }
129 }
130
131 #endif // PASTEBOARD_SUPPORTS_PRESENTATION_STYLE_AND_TEAM_DATA
132
133 Vector<PasteboardItemInfo> PlatformPasteboard::allPasteboardItemInfo()
134 {
135     Vector<PasteboardItemInfo> itemInfo;
136     for (NSInteger itemIndex = 0; itemIndex < [m_pasteboard numberOfItems]; ++itemIndex)
137         itemInfo.append(informationForItemAtIndex(itemIndex));
138     return itemInfo;
139 }
140
141 PasteboardItemInfo PlatformPasteboard::informationForItemAtIndex(int index)
142 {
143     if (index >= [m_pasteboard numberOfItems])
144         return { };
145
146     PasteboardItemInfo info;
147     if ([m_pasteboard respondsToSelector:@selector(preferredFileUploadURLAtIndex:fileType:)]) {
148         NSString *fileType = nil;
149         info.pathForFileUpload = [m_pasteboard preferredFileUploadURLAtIndex:index fileType:&fileType].path;
150         info.contentTypeForFileUpload = fileType;
151     }
152
153     NSItemProvider *itemProvider = [[m_pasteboard itemProviders] objectAtIndex:index];
154 #if PASTEBOARD_SUPPORTS_PRESENTATION_STYLE_AND_TEAM_DATA
155     info.preferredPresentationStyle = pasteboardItemPresentationStyle(itemProvider.preferredPresentationStyle);
156 #endif
157     info.suggestedFileName = itemProvider.suggestedName;
158     for (NSString *typeIdentifier in itemProvider.registeredTypeIdentifiers) {
159         CFStringRef cfTypeIdentifier = (CFStringRef)typeIdentifier;
160         if (!UTTypeIsDeclared(cfTypeIdentifier))
161             continue;
162
163         if (UTTypeConformsTo(cfTypeIdentifier, kUTTypeText))
164             continue;
165
166         if (UTTypeConformsTo(cfTypeIdentifier, kUTTypeURL))
167             continue;
168
169         if (UTTypeConformsTo(cfTypeIdentifier, kUTTypeRTFD))
170             continue;
171
172         if (UTTypeConformsTo(cfTypeIdentifier, kUTTypeFlatRTFD))
173             continue;
174
175         info.isNonTextType = true;
176         break;
177     }
178
179     return info;
180 }
181
182 #else
183
184 PasteboardItemInfo PlatformPasteboard::informationForItemAtIndex(int)
185 {
186     return { };
187 }
188
189 Vector<PasteboardItemInfo> PlatformPasteboard::allPasteboardItemInfo()
190 {
191     return { };
192 }
193
194 #endif
195
196 static bool pasteboardMayContainFilePaths(id<AbstractPasteboard> pasteboard)
197 {
198 #if PASTEBOARD_SUPPORTS_ITEM_PROVIDERS
199     if ([pasteboard isKindOfClass:[WebItemProviderPasteboard class]])
200         return false;
201 #endif
202
203     for (NSString *type in pasteboard.pasteboardTypes) {
204         if (Pasteboard::shouldTreatCocoaTypeAsFile(type))
205             return true;
206     }
207     return false;
208 }
209
210 String PlatformPasteboard::stringForType(const String& type) const
211 {
212     auto result = readString(0, type);
213
214     if (pasteboardMayContainFilePaths(m_pasteboard.get()) && type == String { kUTTypeURL }) {
215         if (!Pasteboard::canExposeURLToDOMWhenPasteboardContainsFiles(result))
216             result = { };
217     }
218
219     return result;
220 }
221
222 Color PlatformPasteboard::color()
223 {
224     NSData *data = [m_pasteboard dataForPasteboardType:UIColorPboardType];
225     UIColor *uiColor = [NSKeyedUnarchiver unarchivedObjectOfClass:getUIColorClass() fromData:data error:nil];
226     return Color(uiColor.CGColor);
227 }
228
229 URL PlatformPasteboard::url()
230 {
231     return URL();
232 }
233
234 long PlatformPasteboard::copy(const String&)
235 {
236     return 0;
237 }
238
239 long PlatformPasteboard::addTypes(const Vector<String>&)
240 {
241     return 0;
242 }
243
244 long PlatformPasteboard::setTypes(const Vector<String>&)
245 {
246     return 0;
247 }
248
249 long PlatformPasteboard::setBufferForType(SharedBuffer*, const String&)
250 {
251     return 0;
252 }
253
254 long PlatformPasteboard::setURL(const PasteboardURL&)
255 {
256     return 0;
257 }
258
259 long PlatformPasteboard::setStringForType(const String&, const String&)
260 {
261     return 0;
262 }
263
264 long PlatformPasteboard::changeCount() const
265 {
266     return [m_pasteboard changeCount];
267 }
268
269 String PlatformPasteboard::uniqueName()
270 {
271     return String();
272 }
273
274 String PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(const String& domType)
275 {
276     if (domType == "text/plain")
277         return kUTTypePlainText;
278
279     if (domType == "text/html")
280         return kUTTypeHTML;
281
282     if (domType == "text/uri-list")
283         return kUTTypeURL;
284
285     return { };
286 }
287
288 #if PASTEBOARD_SUPPORTS_ITEM_PROVIDERS
289
290 static NSString *webIOSPastePboardType = @"iOS rich content paste pasteboard type";
291
292 static void registerItemToPasteboard(WebItemProviderRegistrationInfoList *representationsToRegister, id <AbstractPasteboard> pasteboard)
293 {
294 #if PLATFORM(IOSMAC)
295     // In iOSMac, -[UIPasteboard setItemProviders:] is not yet supported, so we fall back to setting an item dictionary when
296     // populating the pasteboard upon copy.
297     if ([pasteboard isKindOfClass:getUIPasteboardClass()]) {
298         auto itemDictionary = adoptNS([[NSMutableDictionary alloc] init]);
299         [representationsToRegister enumerateItems:[itemDictionary] (id <WebItemProviderRegistrar> item, NSUInteger) {
300             if ([item respondsToSelector:@selector(typeIdentifierForClient)] && [item respondsToSelector:@selector(dataForClient)])
301                 [itemDictionary setObject:item.dataForClient forKey:item.typeIdentifierForClient];
302         }];
303         [pasteboard setItems:@[ itemDictionary.get() ]];
304         return;
305     }
306 #endif // PLATFORM(IOSMAC)
307
308     if (NSItemProvider *itemProvider = representationsToRegister.itemProvider)
309         [pasteboard setItemProviders:@[ itemProvider ]];
310     else
311         [pasteboard setItemProviders:@[ ]];
312
313     if ([pasteboard respondsToSelector:@selector(stageRegistrationList:)])
314         [pasteboard stageRegistrationList:representationsToRegister];
315 }
316
317 long PlatformPasteboard::setColor(const Color& color)
318 {
319     auto representationsToRegister = adoptNS([[WebItemProviderRegistrationInfoList alloc] init]);
320     UIColor *uiColor = [getUIColorClass() colorWithCGColor:cachedCGColor(color)];
321     [representationsToRegister addData:[NSKeyedArchiver archivedDataWithRootObject:uiColor requiringSecureCoding:NO error:nil] forType:UIColorPboardType];
322     registerItemToPasteboard(representationsToRegister.get(), m_pasteboard.get());
323     return 0;
324 }
325
326 static void addRepresentationsForPlainText(WebItemProviderRegistrationInfoList *itemsToRegister, const String& plainText)
327 {
328     if (plainText.isEmpty())
329         return;
330
331     NSURL *platformURL = [NSURL URLWithString:plainText];
332     if (URL(platformURL).isValid())
333         [itemsToRegister addRepresentingObject:platformURL];
334
335     [itemsToRegister addData:[(NSString *)plainText dataUsingEncoding:NSUTF8StringEncoding] forType:(NSString *)kUTTypeUTF8PlainText];
336 }
337
338 bool PlatformPasteboard::allowReadingURLAtIndex(const URL& url, int index) const
339 {
340     NSItemProvider *itemProvider = (NSUInteger)index < [m_pasteboard itemProviders].count ? [[m_pasteboard itemProviders] objectAtIndex:index] : nil;
341     for (NSString *type in itemProvider.registeredTypeIdentifiers) {
342         if (UTTypeConformsTo((CFStringRef)type, kUTTypeURL))
343             return true;
344     }
345
346     return url.isValid();
347 }
348
349 void PlatformPasteboard::write(const PasteboardWebContent& content)
350 {
351     auto representationsToRegister = adoptNS([[WebItemProviderRegistrationInfoList alloc] init]);
352
353 #if !PLATFORM(IOSMAC)
354     [representationsToRegister addData:[webIOSPastePboardType dataUsingEncoding:NSUTF8StringEncoding] forType:webIOSPastePboardType];
355 #endif
356
357     ASSERT(content.clientTypes.size() == content.clientData.size());
358     for (size_t i = 0, size = content.clientTypes.size(); i < size; ++i)
359         [representationsToRegister addData:content.clientData[i]->createNSData().get() forType:content.clientTypes[i]];
360
361     if (content.dataInWebArchiveFormat) {
362         auto webArchiveData = content.dataInWebArchiveFormat->createNSData();
363 #if !PLATFORM(IOSMAC)
364         [representationsToRegister addData:webArchiveData.get() forType:WebArchivePboardType];
365 #endif
366         [representationsToRegister addData:webArchiveData.get() forType:(__bridge NSString *)kUTTypeWebArchive];
367     }
368
369     if (content.dataInAttributedStringFormat) {
370         NSAttributedString *attributedString = unarchivedObjectOfClassesFromData([NSSet setWithObject:[NSAttributedString class]], content.dataInAttributedStringFormat->createNSData().get());
371         if (attributedString)
372             [representationsToRegister addRepresentingObject:attributedString];
373     }
374
375     if (content.dataInRTFDFormat)
376         [representationsToRegister addData:content.dataInRTFDFormat->createNSData().get() forType:(NSString *)kUTTypeFlatRTFD];
377
378     if (content.dataInRTFFormat)
379         [representationsToRegister addData:content.dataInRTFFormat->createNSData().get() forType:(NSString *)kUTTypeRTF];
380
381     if (!content.dataInHTMLFormat.isEmpty()) {
382         NSData *htmlAsData = [(NSString *)content.dataInHTMLFormat dataUsingEncoding:NSUTF8StringEncoding];
383         [representationsToRegister addData:htmlAsData forType:(NSString *)kUTTypeHTML];
384     }
385
386     if (!content.dataInStringFormat.isEmpty())
387         addRepresentationsForPlainText(representationsToRegister.get(), content.dataInStringFormat);
388
389     PasteboardCustomData customData;
390     customData.origin = content.contentOrigin;
391     [representationsToRegister addData:customData.createSharedBuffer()->createNSData().get() forType:@(PasteboardCustomData::cocoaType())];
392
393     registerItemToPasteboard(representationsToRegister.get(), m_pasteboard.get());
394 }
395
396 void PlatformPasteboard::write(const PasteboardImage& pasteboardImage)
397 {
398     auto representationsToRegister = adoptNS([[WebItemProviderRegistrationInfoList alloc] init]);
399
400     auto& types = pasteboardImage.clientTypes;
401     auto& data = pasteboardImage.clientData;
402     ASSERT(types.size() == data.size());
403     for (size_t i = 0, size = types.size(); i < size; ++i)
404         [representationsToRegister addData:data[i]->createNSData().get() forType:types[i]];
405
406     if (pasteboardImage.resourceData && !pasteboardImage.resourceMIMEType.isEmpty()) {
407         auto utiOrMIMEType = pasteboardImage.resourceMIMEType;
408         if (!isDeclaredUTI(utiOrMIMEType))
409             utiOrMIMEType = UTIFromMIMEType(utiOrMIMEType);
410
411         auto imageData = pasteboardImage.resourceData->createNSData();
412         [representationsToRegister addData:imageData.get() forType:(NSString *)utiOrMIMEType];
413         [representationsToRegister setPreferredPresentationSize:pasteboardImage.imageSize];
414         [representationsToRegister setSuggestedName:pasteboardImage.suggestedName];
415     }
416
417     // FIXME: When writing a PasteboardImage, we currently always place the image data at a higer fidelity than the
418     // associated image URL. However, in the case of an image enclosed by an anchor, we might want to consider the
419     // the URL (i.e. the anchor's href attribute) to be a higher fidelity representation.
420     auto& pasteboardURL = pasteboardImage.url;
421     if (NSURL *nsURL = pasteboardURL.url) {
422 #if NSURL_SUPPORTS_TITLE
423         nsURL._title = pasteboardURL.title.isEmpty() ? WTF::userVisibleString(pasteboardURL.url) : (NSString *)pasteboardURL.title;
424 #endif
425         [representationsToRegister addRepresentingObject:nsURL];
426     }
427
428     registerItemToPasteboard(representationsToRegister.get(), m_pasteboard.get());
429 }
430
431 void PlatformPasteboard::write(const String& pasteboardType, const String& text)
432 {
433     auto representationsToRegister = adoptNS([[WebItemProviderRegistrationInfoList alloc] init]);
434     [representationsToRegister setPreferredPresentationStyle:WebPreferredPresentationStyleInline];
435
436     NSString *pasteboardTypeAsNSString = pasteboardType;
437     if (!text.isEmpty() && pasteboardTypeAsNSString.length) {
438         auto pasteboardTypeAsCFString = (CFStringRef)pasteboardTypeAsNSString;
439         if (UTTypeConformsTo(pasteboardTypeAsCFString, kUTTypeURL) || UTTypeConformsTo(pasteboardTypeAsCFString, kUTTypeText))
440             addRepresentationsForPlainText(representationsToRegister.get(), text);
441         else
442             [representationsToRegister addData:[pasteboardTypeAsNSString dataUsingEncoding:NSUTF8StringEncoding] forType:pasteboardType];
443     }
444
445     registerItemToPasteboard(representationsToRegister.get(), m_pasteboard.get());
446 }
447
448 void PlatformPasteboard::write(const PasteboardURL& url)
449 {
450     auto representationsToRegister = adoptNS([[WebItemProviderRegistrationInfoList alloc] init]);
451     [representationsToRegister setPreferredPresentationStyle:WebPreferredPresentationStyleInline];
452
453     if (NSURL *nsURL = url.url) {
454 #if NSURL_SUPPORTS_TITLE
455         if (!url.title.isEmpty())
456             nsURL._title = url.title;
457 #endif
458         [representationsToRegister addRepresentingObject:nsURL];
459         [representationsToRegister addRepresentingObject:(NSString *)url.url.string()];
460     }
461
462     registerItemToPasteboard(representationsToRegister.get(), m_pasteboard.get());
463 }
464
465 static const char *safeTypeForDOMToReadAndWriteForPlatformType(const String& platformType)
466 {
467     auto cfType = platformType.createCFString();
468     if (UTTypeConformsTo(cfType.get(), kUTTypePlainText))
469         return "text/plain"_s;
470
471     if (UTTypeConformsTo(cfType.get(), kUTTypeHTML) || UTTypeConformsTo(cfType.get(), (CFStringRef)WebArchivePboardType)
472         || UTTypeConformsTo(cfType.get(), kUTTypeRTF) || UTTypeConformsTo(cfType.get(), kUTTypeFlatRTFD))
473         return "text/html"_s;
474
475     if (UTTypeConformsTo(cfType.get(), kUTTypeURL))
476         return "text/uri-list"_s;
477
478     return nullptr;
479 }
480
481 static const char originKeyForTeamData[] = "com.apple.WebKit.drag-and-drop-team-data.origin";
482 static const char customTypesKeyForTeamData[] = "com.apple.WebKit.drag-and-drop-team-data.custom-types";
483
484 Vector<String> PlatformPasteboard::typesSafeForDOMToReadAndWrite(const String& origin) const
485 {
486     ListHashSet<String> domPasteboardTypes;
487 #if PASTEBOARD_SUPPORTS_PRESENTATION_STYLE_AND_TEAM_DATA
488     for (NSItemProvider *provider in [m_pasteboard itemProviders]) {
489         if (!provider.teamData.length)
490             continue;
491
492         NSDictionary *teamDataObject = unarchivedObjectOfClassesFromData([NSSet setWithObjects:[NSDictionary class], [NSString class], [NSArray class], nil], provider.teamData);
493         if (!teamDataObject)
494             continue;
495
496         id originInTeamData = [teamDataObject objectForKey:@(originKeyForTeamData)];
497         if (![originInTeamData isKindOfClass:[NSString class]])
498             continue;
499         if (String((NSString *)originInTeamData) != origin)
500             continue;
501
502         id customTypes = [(NSDictionary *)teamDataObject objectForKey:@(customTypesKeyForTeamData)];
503         if (![customTypes isKindOfClass:[NSArray class]])
504             continue;
505
506         for (NSString *type in customTypes)
507             domPasteboardTypes.add(type);
508     }
509 #endif // PASTEBOARD_SUPPORTS_PRESENTATION_STYLE_AND_TEAM_DATA
510
511     if (NSData *serializedCustomData = [m_pasteboard dataForPasteboardType:@(PasteboardCustomData::cocoaType())]) {
512         auto data = PasteboardCustomData::fromSharedBuffer(SharedBuffer::create(serializedCustomData).get());
513         if (data.origin == origin) {
514             for (auto& type : data.orderedTypes)
515                 domPasteboardTypes.add(type);
516         }
517     }
518
519     for (NSString *type in [m_pasteboard pasteboardTypes]) {
520         if ([type isEqualToString:@(PasteboardCustomData::cocoaType())])
521             continue;
522
523         if (Pasteboard::isSafeTypeForDOMToReadAndWrite(type)) {
524             domPasteboardTypes.add(type);
525             continue;
526         }
527
528         if (auto* coercedType = safeTypeForDOMToReadAndWriteForPlatformType(type)) {
529             auto domTypeAsString = String::fromUTF8(coercedType);
530             if (domTypeAsString == "text/uri-list") {
531                 BOOL ableToDetermineProtocolOfPasteboardURL = ![m_pasteboard isKindOfClass:[WebItemProviderPasteboard class]];
532                 if (ableToDetermineProtocolOfPasteboardURL && stringForType(kUTTypeURL).isEmpty())
533                     continue;
534             }
535             domPasteboardTypes.add(WTFMove(domTypeAsString));
536         }
537     }
538
539     return copyToVector(domPasteboardTypes);
540 }
541
542 long PlatformPasteboard::write(const PasteboardCustomData& data)
543 {
544     auto representationsToRegister = adoptNS([[WebItemProviderRegistrationInfoList alloc] init]);
545     [representationsToRegister setPreferredPresentationStyle:WebPreferredPresentationStyleInline];
546
547     if (data.sameOriginCustomData.size()) {
548         if (auto serializedSharedBuffer = data.createSharedBuffer()->createNSData()) {
549             // We stash the list of supplied pasteboard types in teamData here for compatibility with drag and drop.
550             // Since the contents of item providers cannot be loaded prior to drop, but the pasteboard types are
551             // contained within the custom data blob and we need to vend them to the page when firing `dragover`
552             // events, we need an additional in-memory representation of the pasteboard types array that contains
553             // all of the custom types. We use the teamData property, available on NSItemProvider on iOS, to store
554             // this information, since the contents of teamData are immediately available prior to the drop.
555             NSMutableArray<NSString *> *typesAsNSArray = [NSMutableArray array];
556             for (auto& type : data.orderedTypes)
557                 [typesAsNSArray addObject:type];
558             [representationsToRegister setTeamData:securelyArchivedDataWithRootObject(@{ @(originKeyForTeamData) : data.origin, @(customTypesKeyForTeamData) : typesAsNSArray })];
559             [representationsToRegister addData:serializedSharedBuffer.get() forType:@(PasteboardCustomData::cocoaType())];
560         }
561     }
562
563     for (auto& type : data.orderedTypes) {
564         NSString *stringValue = data.platformData.get(type);
565         if (!stringValue.length)
566             continue;
567
568         auto cocoaType = platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(type).createCFString();
569         if (UTTypeConformsTo(cocoaType.get(), kUTTypeURL))
570             [representationsToRegister addRepresentingObject:[NSURL URLWithString:stringValue]];
571         else if (UTTypeConformsTo(cocoaType.get(), kUTTypePlainText))
572             [representationsToRegister addRepresentingObject:stringValue];
573         else
574             [representationsToRegister addData:[stringValue dataUsingEncoding:NSUTF8StringEncoding] forType:(NSString *)cocoaType.get()];
575     }
576
577     registerItemToPasteboard(representationsToRegister.get(), m_pasteboard.get());
578     return [m_pasteboard changeCount];
579 }
580
581 #else
582
583 long PlatformPasteboard::setColor(const Color&)
584 {
585     return 0;
586 }
587
588 bool PlatformPasteboard::allowReadingURLAtIndex(const URL&, int) const
589 {
590     return false;
591 }
592
593 void PlatformPasteboard::write(const PasteboardWebContent&)
594 {
595 }
596
597 void PlatformPasteboard::write(const PasteboardImage&)
598 {
599 }
600
601 void PlatformPasteboard::write(const String&, const String&)
602 {
603 }
604
605 void PlatformPasteboard::write(const PasteboardURL&)
606 {
607 }
608
609 Vector<String> PlatformPasteboard::typesSafeForDOMToReadAndWrite(const String&) const
610 {
611     return { };
612 }
613
614 long PlatformPasteboard::write(const PasteboardCustomData&)
615 {
616     return 0;
617 }
618
619 #endif
620
621 int PlatformPasteboard::count() const
622 {
623     return [m_pasteboard numberOfItems];
624 }
625
626 Vector<String> PlatformPasteboard::allStringsForType(const String& type) const
627 {
628     auto numberOfItems = count();
629     Vector<String> strings;
630     strings.reserveInitialCapacity(numberOfItems);
631     for (int index = 0; index < numberOfItems; ++index) {
632         String value = readString(index, type);
633         if (!value.isEmpty())
634             strings.uncheckedAppend(WTFMove(value));
635     }
636     return strings;
637 }
638
639 RefPtr<SharedBuffer> PlatformPasteboard::readBuffer(int index, const String& type) const
640 {
641     NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:index];
642
643     RetainPtr<NSArray> pasteboardItem = [m_pasteboard dataForPasteboardType:type inItemSet:indexSet];
644
645     if (![pasteboardItem count])
646         return nullptr;
647     return SharedBuffer::create([pasteboardItem.get() objectAtIndex:0]);
648 }
649
650 String PlatformPasteboard::readString(int index, const String& type) const
651 {
652     if (type == String(kUTTypeURL)) {
653         String title;
654         return [(NSURL *)readURL(index, title) absoluteString];
655     }
656
657     NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:index];
658     auto value = retainPtr([m_pasteboard valuesForPasteboardType:type inItemSet:indexSet].firstObject ?: [m_pasteboard dataForPasteboardType:type inItemSet:indexSet].firstObject);
659     if (!value)
660         return { };
661
662     if ([value isKindOfClass:[NSData class]])
663         value = adoptNS([[NSString alloc] initWithData:(NSData *)value.get() encoding:NSUTF8StringEncoding]);
664     
665     if (type == String(kUTTypePlainText) || type == String(kUTTypeHTML)) {
666         ASSERT([value isKindOfClass:[NSString class]]);
667         return [value isKindOfClass:[NSString class]] ? value.get() : nil;
668     }
669     if (type == String(kUTTypeText)) {
670         ASSERT([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSAttributedString class]]);
671         if ([value isKindOfClass:[NSString class]])
672             return value.get();
673         if ([value isKindOfClass:[NSAttributedString class]])
674             return [(NSAttributedString *)value string];
675     }
676
677     return String();
678 }
679
680 URL PlatformPasteboard::readURL(int index, String& title) const
681 {
682     id value = [m_pasteboard valuesForPasteboardType:(__bridge NSString *)kUTTypeURL inItemSet:[NSIndexSet indexSetWithIndex:index]].firstObject;
683     if (!value)
684         return { };
685
686     ASSERT([value isKindOfClass:[NSURL class]]);
687     if (![value isKindOfClass:[NSURL class]])
688         return { };
689
690     NSURL *url = (NSURL *)value;
691     if (!allowReadingURLAtIndex(url, index))
692         return { };
693
694 #if PASTEBOARD_SUPPORTS_ITEM_PROVIDERS && NSURL_SUPPORTS_TITLE
695     title = [url _title];
696 #else
697     UNUSED_PARAM(title);
698 #endif
699
700     return url;
701 }
702
703 void PlatformPasteboard::updateSupportedTypeIdentifiers(const Vector<String>& types)
704 {
705     if (![m_pasteboard respondsToSelector:@selector(updateSupportedTypeIdentifiers:)])
706         return;
707
708     NSMutableArray *typesArray = [NSMutableArray arrayWithCapacity:types.size()];
709     for (auto type : types)
710         [typesArray addObject:(NSString *)type];
711
712     [m_pasteboard updateSupportedTypeIdentifiers:typesArray];
713 }
714
715 }
716
717 #endif // PLATFORM(IOS_FAMILY)