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