b343168ef2442e5e7d3f7b5d53d0929afe86e7d9
[WebKit-https.git] / Source / WebCore / platform / ios / WebItemProviderPasteboard.mm
1 /*
2  * Copyright (C) 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #import "WebItemProviderPasteboard.h"
28
29 #if ENABLE(DATA_INTERACTION)
30
31 #import <Foundation/NSItemProvider.h>
32 #import <Foundation/NSProgress.h>
33 #import <MobileCoreServices/MobileCoreServices.h>
34 #import <UIKit/NSItemProvider+UIKitAdditions.h>
35 #import <UIKit/UIColor.h>
36 #import <UIKit/UIImage.h>
37 #import <WebCore/FileSystem.h>
38 #import <WebCore/Pasteboard.h>
39 #import <pal/spi/ios/UIKitSPI.h>
40 #import <wtf/BlockPtr.h>
41 #import <wtf/OSObjectPtr.h>
42 #import <wtf/RetainPtr.h>
43 #import <wtf/SoftLinking.h>
44
45 SOFT_LINK_FRAMEWORK(UIKit)
46 SOFT_LINK_CLASS(UIKit, UIColor)
47 SOFT_LINK_CLASS(UIKit, UIImage)
48 SOFT_LINK_CLASS(UIKit, UIItemProvider)
49
50 typedef void(^ItemProviderDataLoadCompletionHandler)(NSData *, NSError *);
51 typedef NSMutableDictionary<NSString *, NSURL *> TypeToFileURLMap;
52
53 using WebCore::Pasteboard;
54 using WebCore::PasteboardCustomData;
55 @interface WebItemProviderRegistrationInfo ()
56 {
57     RetainPtr<id <UIItemProviderWriting>> _representingObject;
58     RetainPtr<NSString> _typeIdentifier;
59     RetainPtr<NSData> _data;
60 }
61 @end
62
63 @implementation WebItemProviderRegistrationInfo
64
65 - (instancetype)initWithRepresentingObject:(id <UIItemProviderWriting>)representingObject typeIdentifier:(NSString *)typeIdentifier data:(NSData *)data
66 {
67     if (representingObject)
68         ASSERT(!typeIdentifier && !data);
69     else
70         ASSERT(typeIdentifier && data);
71
72     if (self = [super init]) {
73         _representingObject = representingObject;
74         _typeIdentifier = typeIdentifier;
75         _data = data;
76     }
77     return self;
78 }
79
80 - (id <UIItemProviderWriting>)representingObject
81 {
82     return _representingObject.get();
83 }
84
85 - (NSString *)typeIdentifier
86 {
87     return _typeIdentifier.get();
88 }
89
90 - (NSData *)data
91 {
92     return _data.get();
93 }
94
95 @end
96
97 @interface WebItemProviderRegistrationInfoList ()
98 {
99     RetainPtr<NSMutableArray> _representations;
100 }
101 @end
102
103 @implementation WebItemProviderRegistrationInfoList
104
105 - (instancetype)init
106 {
107     if (self = [super init]) {
108         _representations = adoptNS([[NSMutableArray alloc] init]);
109         _preferredPresentationSize = CGSizeZero;
110         _preferredPresentationStyle = WebPreferredPresentationStyleUnspecified;
111     }
112
113     return self;
114 }
115
116 - (void)dealloc
117 {
118     [_suggestedName release];
119     [super dealloc];
120 }
121
122 - (void)addData:(NSData *)data forType:(NSString *)typeIdentifier
123 {
124     [_representations addObject:[[[WebItemProviderRegistrationInfo alloc] initWithRepresentingObject:nil typeIdentifier:typeIdentifier data:data] autorelease]];
125 }
126
127 - (void)addRepresentingObject:(id <UIItemProviderWriting>)object
128 {
129     ASSERT([object conformsToProtocol:@protocol(UIItemProviderWriting)]);
130     [_representations addObject:[[[WebItemProviderRegistrationInfo alloc] initWithRepresentingObject:object typeIdentifier:nil data:nil] autorelease]];
131 }
132
133 - (NSUInteger)numberOfItems
134 {
135     return [_representations count];
136 }
137
138 - (WebItemProviderRegistrationInfo *)itemAtIndex:(NSUInteger)index
139 {
140     if (index >= self.numberOfItems)
141         return nil;
142
143     return [_representations objectAtIndex:index];
144 }
145
146 - (void)enumerateItems:(void (^)(WebItemProviderRegistrationInfo *, NSUInteger))block
147 {
148     for (NSUInteger index = 0; index < self.numberOfItems; ++index)
149         block([self itemAtIndex:index], index);
150 }
151
152 static UIPreferredPresentationStyle uiPreferredPresentationStyle(WebPreferredPresentationStyle style)
153 {
154     switch (style) {
155     case WebPreferredPresentationStyleUnspecified:
156         return UIPreferredPresentationStyleUnspecified;
157     case WebPreferredPresentationStyleInline:
158         return UIPreferredPresentationStyleInline;
159     case WebPreferredPresentationStyleAttachment:
160         return UIPreferredPresentationStyleAttachment;
161     default:
162         ASSERT_NOT_REACHED();
163         return UIPreferredPresentationStyleUnspecified;
164     }
165 }
166
167 - (UIItemProvider *)itemProvider
168 {
169     if (!self.numberOfItems)
170         return nil;
171
172     auto itemProvider = adoptNS([allocUIItemProviderInstance() init]);
173     for (WebItemProviderRegistrationInfo *representation in _representations.get()) {
174         if (representation.representingObject) {
175             [itemProvider registerObject:representation.representingObject visibility:UIItemProviderRepresentationOptionsVisibilityAll];
176             continue;
177         }
178
179         if (!representation.typeIdentifier.length || !representation.data.length)
180             continue;
181
182         RetainPtr<NSData> itemData = representation.data;
183         [itemProvider registerDataRepresentationForTypeIdentifier:representation.typeIdentifier visibility:UIItemProviderRepresentationOptionsVisibilityAll loadHandler:[itemData] (ItemProviderDataLoadCompletionHandler completionHandler) -> NSProgress * {
184             completionHandler(itemData.get(), nil);
185             return nil;
186         }];
187     }
188     [itemProvider setPreferredPresentationSize:self.preferredPresentationSize];
189     [itemProvider setSuggestedName:self.suggestedName];
190     [itemProvider setPreferredPresentationStyle:uiPreferredPresentationStyle(self.preferredPresentationStyle)];
191     [itemProvider setTeamData:self.teamData];
192     return itemProvider.autorelease();
193 }
194
195 - (NSString *)description
196 {
197     __block NSMutableString *description = [NSMutableString string];
198     [description appendFormat:@"<%@: %p", [self class], self];
199     [self enumerateItems:^(WebItemProviderRegistrationInfo *item, NSUInteger index) {
200         if (index)
201             [description appendString:@","];
202
203         if (item.representingObject)
204             [description appendFormat:@" (%@: %p)", [item.representingObject class], item.representingObject];
205         else
206             [description appendFormat:@" ('%@' => %tu bytes)", item.typeIdentifier, item.data.length];
207     }];
208     [description appendString:@">"];
209     return description;
210 }
211
212 @end
213
214 @interface WebItemProviderLoadResult : NSObject
215
216 - (instancetype)initWithItemProvider:(NSItemProvider *)itemProvider typesToLoad:(NSArray<NSString *> *)typesToLoad;
217 - (NSURL *)fileURLForType:(NSString *)type;
218 - (void)setFileURL:(NSURL *)url forType:(NSString *)type;
219 @property (nonatomic, readonly) NSArray<NSURL *> *loadedFileURLs;
220 @property (nonatomic, readonly) NSArray<NSString *> *loadedTypeIdentifiers;
221 @property (nonatomic, readonly) BOOL canBeRepresentedAsFileUpload;
222 @property (nonatomic, readonly) NSItemProvider *itemProvider;
223 @property (nonatomic, readonly) NSArray<NSString *> *typesToLoad;
224
225 @end
226
227 @implementation WebItemProviderLoadResult {
228     RetainPtr<TypeToFileURLMap> _fileURLs;
229     RetainPtr<NSItemProvider> _itemProvider;
230     RetainPtr<NSArray<NSString *>> _typesToLoad;
231 }
232
233 + (instancetype)loadResultWithItemProvider:(NSItemProvider *)itemProvider typesToLoad:(NSArray<NSString *> *)typesToLoad
234 {
235     return [[[self alloc] initWithItemProvider:itemProvider typesToLoad:typesToLoad] autorelease];
236 }
237
238 - (instancetype)initWithItemProvider:(NSItemProvider *)itemProvider typesToLoad:(NSArray<NSString *> *)typesToLoad
239 {
240     if (!(self = [super init]))
241         return nil;
242
243     _fileURLs = adoptNS([[NSMutableDictionary alloc] init]);
244     _itemProvider = itemProvider;
245     _typesToLoad = typesToLoad;
246     _canBeRepresentedAsFileUpload = itemProvider.preferredPresentationStyle != UIPreferredPresentationStyleInline;
247
248     return self;
249 }
250
251 - (NSArray<NSString *> *)typesToLoad
252 {
253     return _typesToLoad.get();
254 }
255
256 - (NSURL *)fileURLForType:(NSString *)type
257 {
258     return [_fileURLs objectForKey:type];
259 }
260
261 - (void)setFileURL:(NSURL *)url forType:(NSString *)type
262 {
263     [_fileURLs setObject:url forKey:type];
264 }
265
266 - (NSArray<NSURL *> *)loadedFileURLs
267 {
268     return [_fileURLs allValues];
269 }
270
271 - (NSArray<NSString *> *)loadedTypeIdentifiers
272 {
273     return [_fileURLs allKeys];
274 }
275
276 - (NSItemProvider *)itemProvider
277 {
278     return _itemProvider.get();
279 }
280
281 @end
282
283 @interface WebItemProviderPasteboard ()
284
285 @property (nonatomic) NSInteger numberOfItems;
286 @property (nonatomic) NSInteger changeCount;
287 @property (nonatomic) NSInteger pendingOperationCount;
288
289 @end
290
291 @implementation WebItemProviderPasteboard {
292     // FIXME: These ivars should be refactored to be Vector<RetainPtr<Type>> instead of generic NSArrays.
293     RetainPtr<NSArray> _itemProviders;
294     RetainPtr<NSArray> _supportedTypeIdentifiers;
295     RetainPtr<WebItemProviderRegistrationInfoList> _stagedRegistrationInfoList;
296
297     Vector<RetainPtr<WebItemProviderLoadResult>> _loadResults;
298 }
299
300 + (instancetype)sharedInstance
301 {
302     static WebItemProviderPasteboard *sharedPasteboard = nil;
303     static dispatch_once_t onceToken;
304     dispatch_once(&onceToken, ^() {
305         sharedPasteboard = [[WebItemProviderPasteboard alloc] init];
306     });
307     return sharedPasteboard;
308 }
309
310 - (instancetype)init
311 {
312     if (self = [super init]) {
313         _itemProviders = adoptNS([[NSArray alloc] init]);
314         _changeCount = 0;
315         _pendingOperationCount = 0;
316         _supportedTypeIdentifiers = nil;
317         _stagedRegistrationInfoList = nil;
318         _loadResults = { };
319     }
320     return self;
321 }
322
323 - (void)updateSupportedTypeIdentifiers:(NSArray<NSString *> *)types
324 {
325     _supportedTypeIdentifiers = types;
326 }
327
328 - (NSArray<NSString *> *)pasteboardTypesByFidelityForItemAtIndex:(NSUInteger)index
329 {
330     return [self itemProviderAtIndex:index].registeredTypeIdentifiers ?: @[ ];
331 }
332
333 - (NSArray<NSString *> *)pasteboardTypes
334 {
335     NSMutableSet<NSString *> *allTypes = [NSMutableSet set];
336     NSMutableArray<NSString *> *allTypesInOrder = [NSMutableArray array];
337     for (UIItemProvider *provider in _itemProviders.get()) {
338         for (NSString *typeIdentifier in provider.registeredTypeIdentifiers) {
339             if ([allTypes containsObject:typeIdentifier])
340                 continue;
341
342             [allTypes addObject:typeIdentifier];
343             [allTypesInOrder addObject:typeIdentifier];
344         }
345     }
346     return allTypesInOrder;
347 }
348
349 - (NSArray<__kindof NSItemProvider *> *)itemProviders
350 {
351     return _itemProviders.get();
352 }
353
354 - (void)setItemProviders:(NSArray<__kindof NSItemProvider *> *)itemProviders
355 {
356     itemProviders = itemProviders ?: [NSArray array];
357     if (_itemProviders == itemProviders || [_itemProviders isEqualToArray:itemProviders])
358         return;
359
360     _itemProviders = itemProviders;
361     _changeCount++;
362
363     if (!itemProviders.count)
364         _loadResults = { };
365 }
366
367 - (NSInteger)numberOfItems
368 {
369     return [_itemProviders count];
370 }
371
372 - (NSData *)_preLoadedDataConformingToType:(NSString *)typeIdentifier forItemProviderAtIndex:(NSUInteger)index
373 {
374     if (_loadResults.size() != [_itemProviders count]) {
375         ASSERT_NOT_REACHED();
376         return nil;
377     }
378
379     WebItemProviderLoadResult *loadResult = _loadResults[index].get();
380     for (NSString *loadedType in loadResult.loadedTypeIdentifiers) {
381         if (!UTTypeConformsTo((CFStringRef)loadedType, (CFStringRef)typeIdentifier))
382             continue;
383
384         // We've already loaded data relevant for this UTI type onto disk, so there's no need to ask the UIItemProvider for the same data again.
385         if (NSData *result = [NSData dataWithContentsOfURL:[loadResult fileURLForType:loadedType] options:NSDataReadingMappedIfSafe error:nil])
386             return result;
387     }
388     return nil;
389 }
390
391 - (NSData *)dataForPasteboardType:(NSString *)pasteboardType
392 {
393     return [self dataForPasteboardType:pasteboardType inItemSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.numberOfItems)]].firstObject;
394 }
395
396 - (NSArray *)dataForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet
397 {
398     if (_loadResults.isEmpty())
399         return @[ ];
400
401     auto values = adoptNS([[NSMutableArray alloc] init]);
402     RetainPtr<WebItemProviderPasteboard> retainedSelf = self;
403     [itemSet enumerateIndexesUsingBlock:[retainedSelf, pasteboardType, values] (NSUInteger index, BOOL *) {
404         UIItemProvider *provider = [retainedSelf itemProviderAtIndex:index];
405         if (!provider)
406             return;
407
408         if (NSData *loadedData = [retainedSelf _preLoadedDataConformingToType:pasteboardType forItemProviderAtIndex:index])
409             [values addObject:loadedData];
410     }];
411     return values.autorelease();
412 }
413
414 static NSArray<Class<UIItemProviderReading>> *allLoadableClasses()
415 {
416     return @[ [getUIColorClass() class], [getUIImageClass() class], [NSURL class], [NSString class], [NSAttributedString class] ];
417 }
418
419 static Class classForTypeIdentifier(NSString *typeIdentifier, NSString *&outTypeIdentifierToLoad)
420 {
421     outTypeIdentifierToLoad = typeIdentifier;
422
423     // First, try to load a platform UIItemProviderReading-conformant object as-is.
424     for (Class<UIItemProviderReading> loadableClass in allLoadableClasses()) {
425         if ([[loadableClass readableTypeIdentifiersForItemProvider] containsObject:(NSString *)typeIdentifier])
426             return loadableClass;
427     }
428
429     // If we were unable to load any object, check if the given type identifier is still something
430     // WebKit knows how to handle.
431     if ([typeIdentifier isEqualToString:(NSString *)kUTTypeHTML]) {
432         // Load kUTTypeHTML as a plain text HTML string.
433         outTypeIdentifierToLoad = (NSString *)kUTTypePlainText;
434         return [NSString class];
435     }
436
437     return nil;
438 }
439
440 - (NSArray *)valuesForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet
441 {
442     if (_loadResults.isEmpty())
443         return @[ ];
444
445     auto values = adoptNS([[NSMutableArray alloc] init]);
446     RetainPtr<WebItemProviderPasteboard> retainedSelf = self;
447     [itemSet enumerateIndexesUsingBlock:[retainedSelf, pasteboardType, values] (NSUInteger index, BOOL *) {
448         UIItemProvider *provider = [retainedSelf itemProviderAtIndex:index];
449         if (!provider)
450             return;
451
452         NSString *typeIdentifierToLoad;
453         Class readableClass = classForTypeIdentifier(pasteboardType, typeIdentifierToLoad);
454         if (!readableClass)
455             return;
456
457         NSData *preloadedData = [retainedSelf _preLoadedDataConformingToType:pasteboardType forItemProviderAtIndex:index];
458         if (!preloadedData)
459             return;
460
461         if (id <NSItemProviderReading> readObject = [readableClass objectWithItemProviderData:preloadedData typeIdentifier:(NSString *)typeIdentifierToLoad error:nil])
462             [values addObject:readObject];
463     }];
464
465     return values.autorelease();
466 }
467
468 - (NSInteger)changeCount
469 {
470     return _changeCount;
471 }
472
473 - (NSArray<NSURL *> *)droppedFileURLs
474 {
475     NSMutableArray<NSURL *> *fileURLs = [NSMutableArray array];
476     for (auto loadResult : _loadResults) {
477         if ([loadResult canBeRepresentedAsFileUpload])
478             [fileURLs addObjectsFromArray:[loadResult loadedFileURLs]];
479     }
480     return fileURLs;
481 }
482
483 static BOOL typeConformsToTypes(NSString *type, NSArray *conformsToTypes)
484 {
485     // A type is considered appropriate to load if it conforms to one or more supported types.
486     for (NSString *conformsToType in conformsToTypes) {
487         if (UTTypeConformsTo((CFStringRef)type, (CFStringRef)conformsToType))
488             return YES;
489     }
490     return NO;
491 }
492
493 - (NSInteger)numberOfFiles
494 {
495     NSArray *supportedFileTypes = Pasteboard::supportedFileUploadPasteboardTypes();
496     NSInteger numberOfFiles = 0;
497     for (UIItemProvider *itemProvider in _itemProviders.get()) {
498         if (itemProvider.preferredPresentationStyle == UIPreferredPresentationStyleInline)
499             continue;
500
501         for (NSString *identifier in itemProvider.registeredTypeIdentifiers) {
502             if (!typeConformsToTypes(identifier, supportedFileTypes))
503                 continue;
504             ++numberOfFiles;
505             break;
506         }
507     }
508     return numberOfFiles;
509 }
510
511 static NSURL *linkTemporaryItemProviderFilesToDropStagingDirectory(NSURL *url, NSString *suggestedName, NSString *typeIdentifier)
512 {
513     static NSString *defaultDropFolderName = @"folder";
514     static NSString *defaultDropFileName = @"file";
515     static NSString *dataInteractionDirectoryPrefix = @"data-interaction";
516     if (!url)
517         return nil;
518
519     NSString *temporaryDataInteractionDirectory = WebCore::FileSystem::createTemporaryDirectory(dataInteractionDirectoryPrefix);
520     if (!temporaryDataInteractionDirectory)
521         return nil;
522
523     NSURL *destination = nil;
524     BOOL isFolder = UTTypeConformsTo((CFStringRef)typeIdentifier, kUTTypeFolder);
525     NSFileManager *fileManager = [NSFileManager defaultManager];
526
527     if (!suggestedName)
528         suggestedName = url.lastPathComponent ?: (isFolder ? defaultDropFolderName : defaultDropFileName);
529
530     if (![suggestedName containsString:@"."] && !isFolder)
531         suggestedName = [suggestedName stringByAppendingPathExtension:url.pathExtension];
532
533     destination = [NSURL fileURLWithPath:[temporaryDataInteractionDirectory stringByAppendingPathComponent:suggestedName]];
534     return [fileManager linkItemAtURL:url toURL:destination error:nil] ? destination : nil;
535 }
536
537 - (NSArray<NSString *> *)typeIdentifiersToLoadForRegisteredTypeIdentfiers:(NSArray<NSString *> *)registeredTypeIdentifiers
538 {
539     NSMutableSet *typesToLoad = [NSMutableSet set];
540     NSString *highestFidelityContentType = nil;
541
542     // First, we want to either load the highest fidelity supported type or the highest fidelity generic content type.
543     for (NSString *registeredTypeIdentifier in registeredTypeIdentifiers) {
544         if (typeConformsToTypes(registeredTypeIdentifier, _supportedTypeIdentifiers.get())) {
545             [typesToLoad addObject:registeredTypeIdentifier];
546             break;
547         }
548
549         if (!highestFidelityContentType && UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypeContent))
550             highestFidelityContentType = registeredTypeIdentifier;
551     }
552     if (!typesToLoad.count && highestFidelityContentType)
553         [typesToLoad addObject:highestFidelityContentType];
554
555     // For compatibility with DataTransfer APIs, additionally load web-exposed types. Since this is the only chance to
556     // fault in any data at all, we need to load up front any information that the page may ask for later down the line.
557     NSString *customPasteboardDataUTI = @(PasteboardCustomData::cocoaType());
558     for (NSString *registeredTypeIdentifier in registeredTypeIdentifiers) {
559         if ([typesToLoad containsObject:registeredTypeIdentifier])
560             continue;
561         if ([registeredTypeIdentifier isEqualToString:customPasteboardDataUTI])
562             [typesToLoad addObject:customPasteboardDataUTI];
563         if (UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypeURL))
564             [typesToLoad addObject:(NSString *)kUTTypeURL];
565         if (UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypeHTML))
566             [typesToLoad addObject:(NSString *)kUTTypeHTML];
567         if (UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypePlainText))
568             [typesToLoad addObject:(NSString *)kUTTypePlainText];
569     }
570
571     return typesToLoad.allObjects;
572 }
573
574 - (void)doAfterLoadingProvidedContentIntoFileURLs:(WebItemProviderFileLoadBlock)action
575 {
576     [self doAfterLoadingProvidedContentIntoFileURLs:action synchronousTimeout:0];
577 }
578
579 - (void)doAfterLoadingProvidedContentIntoFileURLs:(WebItemProviderFileLoadBlock)action synchronousTimeout:(NSTimeInterval)synchronousTimeout
580 {
581     _loadResults.clear();
582
583     auto changeCountBeforeLoading = _changeCount;
584     auto loadResults = adoptNS([[NSMutableArray alloc] initWithCapacity:[_itemProviders count]]);
585
586     // First, figure out which item providers we want to try and load files from.
587     BOOL foundAnyDataToLoad = NO;
588     RetainPtr<WebItemProviderPasteboard> protectedSelf = self;
589     for (NSItemProvider *itemProvider in _itemProviders.get()) {
590         NSArray<NSString *> *typeIdentifiersToLoad = [protectedSelf typeIdentifiersToLoadForRegisteredTypeIdentfiers:itemProvider.registeredTypeIdentifiers];
591         foundAnyDataToLoad |= typeIdentifiersToLoad.count;
592         [loadResults addObject:[WebItemProviderLoadResult loadResultWithItemProvider:itemProvider typesToLoad:typeIdentifiersToLoad]];
593     }
594
595     if (!foundAnyDataToLoad) {
596         action(@[ ]);
597         return;
598     }
599
600     auto setFileURLsLock = adoptNS([[NSLock alloc] init]);
601     auto synchronousFileLoadingGroup = adoptOSObject(dispatch_group_create());
602     auto fileLoadingGroup = adoptOSObject(dispatch_group_create());
603     for (WebItemProviderLoadResult *loadResult in loadResults.get()) {
604         for (NSString *typeToLoad in loadResult.typesToLoad) {
605             dispatch_group_enter(fileLoadingGroup.get());
606             dispatch_group_enter(synchronousFileLoadingGroup.get());
607             [loadResult.itemProvider loadFileRepresentationForTypeIdentifier:typeToLoad completionHandler:[protectedLoadResult = retainPtr(loadResult), synchronousFileLoadingGroup, setFileURLsLock, protectedTypeToLoad = retainPtr(typeToLoad), fileLoadingGroup] (NSURL *url, NSError *) {
608                 // After executing this completion block, UIKit removes the file at the given URL. However, we need this data to persist longer for the web content process.
609                 // To address this, we hard link the given URL to a new temporary file in the temporary directory. This follows the same flow as regular file upload, in
610                 // WKFileUploadPanel.mm. The temporary files are cleaned up by the system at a later time.
611                 if (NSURL *destination = linkTemporaryItemProviderFilesToDropStagingDirectory(url, [protectedLoadResult itemProvider].suggestedName, protectedTypeToLoad.get())) {
612                     [setFileURLsLock lock];
613                     [protectedLoadResult setFileURL:destination forType:protectedTypeToLoad.get()];
614                     [setFileURLsLock unlock];
615                 }
616                 dispatch_group_leave(fileLoadingGroup.get());
617                 dispatch_group_leave(synchronousFileLoadingGroup.get());
618             }];
619         }
620     }
621
622     RetainPtr<WebItemProviderPasteboard> retainedSelf = self;
623     auto itemLoadCompletion = [retainedSelf, synchronousFileLoadingGroup, fileLoadingGroup, loadResults, completionBlock = makeBlockPtr(action), changeCountBeforeLoading] {
624         if (changeCountBeforeLoading == retainedSelf->_changeCount) {
625             for (WebItemProviderLoadResult *loadResult in loadResults.get())
626                 retainedSelf->_loadResults.append(loadResult);
627         }
628
629         completionBlock([retainedSelf droppedFileURLs]);
630     };
631
632     if (synchronousTimeout > 0 && !dispatch_group_wait(synchronousFileLoadingGroup.get(), dispatch_time(DISPATCH_TIME_NOW, synchronousTimeout * NSEC_PER_SEC))) {
633         itemLoadCompletion();
634         return;
635     }
636
637     dispatch_group_notify(fileLoadingGroup.get(), dispatch_get_main_queue(), itemLoadCompletion);
638 }
639
640 - (UIItemProvider *)itemProviderAtIndex:(NSUInteger)index
641 {
642     return index < [_itemProviders count] ? [_itemProviders objectAtIndex:index] : nil;
643 }
644
645 - (BOOL)hasPendingOperation
646 {
647     return _pendingOperationCount;
648 }
649
650 - (void)incrementPendingOperationCount
651 {
652     _pendingOperationCount++;
653 }
654
655 - (void)decrementPendingOperationCount
656 {
657     _pendingOperationCount--;
658 }
659
660 - (void)enumerateItemProvidersWithBlock:(void (^)(UIItemProvider *itemProvider, NSUInteger index, BOOL *stop))block
661 {
662     [_itemProviders enumerateObjectsUsingBlock:block];
663 }
664
665 - (void)stageRegistrationList:(nullable WebItemProviderRegistrationInfoList *)info
666 {
667     _stagedRegistrationInfoList = info.numberOfItems ? info : nil;
668 }
669
670 - (WebItemProviderRegistrationInfoList *)takeRegistrationList
671 {
672     auto stagedRegistrationInfoList = _stagedRegistrationInfoList;
673     _stagedRegistrationInfoList = nil;
674     return stagedRegistrationInfoList.autorelease();
675 }
676
677 @end
678
679 #endif // ENABLE(DATA_INTERACTION)