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