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