[iOS] Respect type fidelities when copying image elements to the pasteboard
[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/FileSystemIOS.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 using namespace WebCore;
51
52 typedef void(^ItemProviderDataLoadCompletionHandler)(NSData *, NSError *);
53 typedef NSDictionary<NSString *, NSURL *> TypeToFileURLMap;
54
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     }
111
112     return self;
113 }
114
115 - (void)dealloc
116 {
117     [_suggestedName release];
118     [super dealloc];
119 }
120
121 - (void)addData:(NSData *)data forType:(NSString *)typeIdentifier
122 {
123     [_representations addObject:[[[WebItemProviderRegistrationInfo alloc] initWithRepresentingObject:nil typeIdentifier:typeIdentifier data:data] autorelease]];
124 }
125
126 - (void)addRepresentingObject:(id <UIItemProviderWriting>)object
127 {
128     ASSERT([object conformsToProtocol:@protocol(UIItemProviderWriting)]);
129     [_representations addObject:[[[WebItemProviderRegistrationInfo alloc] initWithRepresentingObject:object typeIdentifier:nil data:nil] autorelease]];
130 }
131
132 - (NSUInteger)numberOfItems
133 {
134     return [_representations count];
135 }
136
137 - (WebItemProviderRegistrationInfo *)itemAtIndex:(NSUInteger)index
138 {
139     if (index >= self.numberOfItems)
140         return nil;
141
142     return [_representations objectAtIndex:index];
143 }
144
145 - (void)enumerateItems:(void (^)(WebItemProviderRegistrationInfo *, NSUInteger))block
146 {
147     for (NSUInteger index = 0; index < self.numberOfItems; ++index)
148         block([self itemAtIndex:index], index);
149 }
150
151 - (UIItemProvider *)itemProvider
152 {
153     if (!self.numberOfItems)
154         return nil;
155
156     auto itemProvider = adoptNS([allocUIItemProviderInstance() init]);
157     for (WebItemProviderRegistrationInfo *representation in _representations.get()) {
158         if (representation.representingObject) {
159             [itemProvider registerObject:representation.representingObject visibility:UIItemProviderRepresentationOptionsVisibilityAll];
160             continue;
161         }
162
163         if (!representation.typeIdentifier.length || !representation.data.length)
164             continue;
165
166         RetainPtr<NSData> itemData = representation.data;
167         [itemProvider registerDataRepresentationForTypeIdentifier:representation.typeIdentifier visibility:UIItemProviderRepresentationOptionsVisibilityAll loadHandler:[itemData] (ItemProviderDataLoadCompletionHandler completionHandler) -> NSProgress * {
168             completionHandler(itemData.get(), nil);
169             return nil;
170         }];
171     }
172     [itemProvider setPreferredPresentationSize:self.preferredPresentationSize];
173     [itemProvider setSuggestedName:self.suggestedName];
174     return itemProvider.autorelease();
175 }
176
177 - (NSString *)description
178 {
179     __block NSMutableString *description = [NSMutableString string];
180     [description appendFormat:@"<%@: %p", [self class], self];
181     [self enumerateItems:^(WebItemProviderRegistrationInfo *item, NSUInteger index) {
182         if (index)
183             [description appendString:@","];
184
185         if (item.representingObject)
186             [description appendFormat:@" (%@: %p)", [item.representingObject class], item.representingObject];
187         else
188             [description appendFormat:@" ('%@' => %tu bytes)", item.typeIdentifier, item.data.length];
189     }];
190     [description appendString:@">"];
191     return description;
192 }
193
194 @end
195
196 @interface WebItemProviderPasteboard ()
197
198 @property (nonatomic) NSInteger numberOfItems;
199 @property (nonatomic) NSInteger changeCount;
200 @property (nonatomic) NSInteger pendingOperationCount;
201
202 @end
203
204 @implementation WebItemProviderPasteboard {
205     // FIXME: These ivars should be refactored to be Vector<RetainPtr<Type>> instead of generic NSArrays.
206     RetainPtr<NSArray> _itemProviders;
207     RetainPtr<NSArray> _cachedTypeIdentifiers;
208     RetainPtr<NSArray> _typeToFileURLMaps;
209     RetainPtr<NSArray> _supportedTypeIdentifiers;
210     RetainPtr<NSArray> _registrationInfoLists;
211 }
212
213 + (instancetype)sharedInstance
214 {
215     static WebItemProviderPasteboard *sharedPasteboard = nil;
216     static dispatch_once_t onceToken;
217     dispatch_once(&onceToken, ^() {
218         sharedPasteboard = [[WebItemProviderPasteboard alloc] init];
219     });
220     return sharedPasteboard;
221 }
222
223 - (instancetype)init
224 {
225     if (self = [super init]) {
226         _itemProviders = adoptNS([[NSArray alloc] init]);
227         _changeCount = 0;
228         _pendingOperationCount = 0;
229         _typeToFileURLMaps = adoptNS([[NSArray alloc] init]);
230         _supportedTypeIdentifiers = nil;
231         _registrationInfoLists = nil;
232     }
233     return self;
234 }
235
236 - (void)updateSupportedTypeIdentifiers:(NSArray<NSString *> *)types
237 {
238     _supportedTypeIdentifiers = types;
239 }
240
241 - (NSArray<NSString *> *)pasteboardTypesByFidelityForItemAtIndex:(NSUInteger)index
242 {
243     return [self itemProviderAtIndex:index].registeredTypeIdentifiers ?: @[ ];
244 }
245
246 - (NSArray<NSString *> *)pasteboardTypes
247 {
248     if (_cachedTypeIdentifiers)
249         return _cachedTypeIdentifiers.get();
250
251     NSMutableSet<NSString *> *allTypes = [NSMutableSet set];
252     NSMutableArray<NSString *> *allTypesInOrder = [NSMutableArray array];
253     for (UIItemProvider *provider in _itemProviders.get()) {
254         for (NSString *typeIdentifier in provider.registeredTypeIdentifiers) {
255             if ([allTypes containsObject:typeIdentifier])
256                 continue;
257
258             [allTypes addObject:typeIdentifier];
259             [allTypesInOrder addObject:typeIdentifier];
260         }
261     }
262     _cachedTypeIdentifiers = allTypesInOrder;
263     return _cachedTypeIdentifiers.get();
264 }
265
266 - (NSArray<__kindof NSItemProvider *> *)itemProviders
267 {
268     return _itemProviders.get();
269 }
270
271 - (void)setItemProviders:(NSArray<__kindof NSItemProvider *> *)itemProviders
272 {
273     itemProviders = itemProviders ?: [NSArray array];
274     if (_itemProviders == itemProviders || [_itemProviders isEqualToArray:itemProviders])
275         return;
276
277     _itemProviders = itemProviders;
278     _changeCount++;
279     _cachedTypeIdentifiers = nil;
280     _registrationInfoLists = nil;
281
282     NSMutableArray *typeToFileURLMaps = [NSMutableArray arrayWithCapacity:itemProviders.count];
283     [itemProviders enumerateObjectsUsingBlock:[typeToFileURLMaps] (UIItemProvider *, NSUInteger, BOOL *) {
284         [typeToFileURLMaps addObject:@{ }];
285     }];
286 }
287
288 - (NSInteger)numberOfItems
289 {
290     return [_itemProviders count];
291 }
292
293 - (NSData *)_preLoadedDataConformingToType:(NSString *)typeIdentifier forItemProviderAtIndex:(NSUInteger)index
294 {
295     if ([_typeToFileURLMaps count] != [_itemProviders count]) {
296         ASSERT_NOT_REACHED();
297         return nil;
298     }
299
300     TypeToFileURLMap *typeToFileURLMap = [_typeToFileURLMaps objectAtIndex:index];
301     for (NSString *loadedType in typeToFileURLMap) {
302         if (!UTTypeConformsTo((CFStringRef)loadedType, (CFStringRef)typeIdentifier))
303             continue;
304
305         // 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.
306         if (NSData *result = [NSData dataWithContentsOfURL:typeToFileURLMap[loadedType] options:NSDataReadingMappedIfSafe error:nil])
307             return result;
308     }
309     return nil;
310 }
311
312 - (NSArray *)dataForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet
313 {
314     auto values = adoptNS([[NSMutableArray alloc] init]);
315     RetainPtr<WebItemProviderPasteboard> retainedSelf = self;
316     [itemSet enumerateIndexesUsingBlock:[retainedSelf, pasteboardType, values] (NSUInteger index, BOOL *) {
317         UIItemProvider *provider = [retainedSelf itemProviderAtIndex:index];
318         if (!provider)
319             return;
320
321         if (NSData *loadedData = [retainedSelf _preLoadedDataConformingToType:pasteboardType forItemProviderAtIndex:index])
322             [values addObject:loadedData];
323     }];
324     return values.autorelease();
325 }
326
327 static NSArray<Class<UIItemProviderReading>> *allLoadableClasses()
328 {
329     return @[ [getUIColorClass() class], [getUIImageClass() class], [NSURL class], [NSString class], [NSAttributedString class] ];
330 }
331
332 static Class classForTypeIdentifier(NSString *typeIdentifier, NSString *&outTypeIdentifierToLoad)
333 {
334     outTypeIdentifierToLoad = typeIdentifier;
335
336     // First, try to load a platform UIItemProviderReading-conformant object as-is.
337     for (Class<UIItemProviderReading> loadableClass in allLoadableClasses()) {
338         if ([[loadableClass readableTypeIdentifiersForItemProvider] containsObject:(NSString *)typeIdentifier])
339             return loadableClass;
340     }
341
342     // If we were unable to load any object, check if the given type identifier is still something
343     // WebKit knows how to handle.
344     if ([typeIdentifier isEqualToString:(NSString *)kUTTypeHTML]) {
345         // Load kUTTypeHTML as a plain text HTML string.
346         outTypeIdentifierToLoad = (NSString *)kUTTypePlainText;
347         return [NSString class];
348     }
349
350     return nil;
351 }
352
353 - (NSArray *)valuesForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet
354 {
355     auto values = adoptNS([[NSMutableArray alloc] init]);
356     RetainPtr<WebItemProviderPasteboard> retainedSelf = self;
357     [itemSet enumerateIndexesUsingBlock:[retainedSelf, pasteboardType, values] (NSUInteger index, BOOL *) {
358         UIItemProvider *provider = [retainedSelf itemProviderAtIndex:index];
359         if (!provider)
360             return;
361
362         NSString *typeIdentifierToLoad;
363         Class readableClass = classForTypeIdentifier(pasteboardType, typeIdentifierToLoad);
364         if (!readableClass)
365             return;
366
367         NSData *preloadedData = [retainedSelf _preLoadedDataConformingToType:pasteboardType forItemProviderAtIndex:index];
368         if (!preloadedData)
369             return;
370
371         if (id <NSItemProviderReading> readObject = [readableClass objectWithItemProviderData:preloadedData typeIdentifier:(NSString *)typeIdentifierToLoad error:nil])
372             [values addObject:readObject];
373     }];
374
375     return values.autorelease();
376 }
377
378 - (NSInteger)changeCount
379 {
380     return _changeCount;
381 }
382
383 - (NSArray<NSURL *> *)fileURLsForDataInteraction
384 {
385     NSMutableArray<NSURL *> *fileURLs = [NSMutableArray array];
386     for (TypeToFileURLMap *typeToFileURLMap in _typeToFileURLMaps.get())
387         [fileURLs addObjectsFromArray:[typeToFileURLMap allValues]];
388     return fileURLs;
389 }
390
391 static BOOL typeConformsToTypes(NSString *type, NSArray *conformsToTypes)
392 {
393     // A type is considered appropriate to load if it conforms to one or more supported types.
394     for (NSString *conformsToType in conformsToTypes) {
395         if (UTTypeConformsTo((CFStringRef)type, (CFStringRef)conformsToType))
396             return YES;
397     }
398     return NO;
399 }
400
401 - (NSInteger)numberOfFiles
402 {
403     NSArray *supportedFileTypes = Pasteboard::supportedFileUploadPasteboardTypes();
404     NSInteger numberOfFiles = 0;
405     for (UIItemProvider *itemProvider in _itemProviders.get()) {
406         for (NSString *identifier in itemProvider.registeredTypeIdentifiers) {
407             if (!typeConformsToTypes(identifier, supportedFileTypes))
408                 continue;
409             ++numberOfFiles;
410             break;
411         }
412     }
413     return numberOfFiles;
414 }
415
416 static NSURL *temporaryFileURLForDataInteractionContent(NSURL *url, NSString *suggestedName)
417 {
418     static NSString *defaultDataInteractionFileName = @"file";
419     static NSString *dataInteractionDirectoryPrefix = @"data-interaction";
420     if (!url)
421         return nil;
422
423     NSString *temporaryDataInteractionDirectory = WebCore::createTemporaryDirectory(dataInteractionDirectoryPrefix);
424     if (!temporaryDataInteractionDirectory)
425         return nil;
426
427     suggestedName = suggestedName ?: defaultDataInteractionFileName;
428     if (![suggestedName containsString:@"."])
429         suggestedName = [suggestedName stringByAppendingPathExtension:url.pathExtension];
430
431     return [NSURL fileURLWithPath:[temporaryDataInteractionDirectory stringByAppendingPathComponent:suggestedName ?: url.lastPathComponent]];
432 }
433
434 - (NSString *)typeIdentifierToLoadForRegisteredTypeIdentfiers:(NSArray<NSString *> *)registeredTypeIdentifiers
435 {
436     NSString *highestFidelityContentType = nil;
437     for (NSString *registeredTypeIdentifier in registeredTypeIdentifiers) {
438         if (typeConformsToTypes(registeredTypeIdentifier, _supportedTypeIdentifiers.get()))
439             return registeredTypeIdentifier;
440
441         if (!highestFidelityContentType && UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypeContent))
442             highestFidelityContentType = registeredTypeIdentifier;
443     }
444     return highestFidelityContentType;
445 }
446
447 - (void)doAfterLoadingProvidedContentIntoFileURLs:(WebItemProviderFileLoadBlock)action
448 {
449     [self doAfterLoadingProvidedContentIntoFileURLs:action synchronousTimeout:0];
450 }
451
452 - (void)doAfterLoadingProvidedContentIntoFileURLs:(WebItemProviderFileLoadBlock)action synchronousTimeout:(NSTimeInterval)synchronousTimeout
453 {
454     auto changeCountBeforeLoading = _changeCount;
455     auto typeToFileURLMaps = adoptNS([[NSMutableArray alloc] initWithCapacity:[_itemProviders count]]);
456
457     // First, figure out which item providers we want to try and load files from.
458     auto itemProvidersToLoad = adoptNS([[NSMutableArray alloc] init]);
459     auto typeIdentifiersToLoad = adoptNS([[NSMutableArray alloc] init]);
460     auto indicesOfitemProvidersToLoad = adoptNS([[NSMutableArray alloc] init]);
461     RetainPtr<WebItemProviderPasteboard> protectedSelf = self;
462     [_itemProviders enumerateObjectsUsingBlock:[protectedSelf, itemProvidersToLoad, typeIdentifiersToLoad, indicesOfitemProvidersToLoad, typeToFileURLMaps] (UIItemProvider *itemProvider, NSUInteger index, BOOL *) {
463         NSString *typeIdentifierToLoad = [protectedSelf typeIdentifierToLoadForRegisteredTypeIdentfiers:itemProvider.registeredTypeIdentifiers];
464         if (typeIdentifierToLoad) {
465             [itemProvidersToLoad addObject:itemProvider];
466             [typeIdentifiersToLoad addObject:typeIdentifierToLoad];
467             [indicesOfitemProvidersToLoad addObject:@(index)];
468         }
469         [typeToFileURLMaps addObject:@{ }];
470     }];
471
472     if (![itemProvidersToLoad count]) {
473         action(@[ ]);
474         return;
475     }
476
477     auto setFileURLsLock = adoptNS([[NSLock alloc] init]);
478     auto synchronousFileLoadingGroup = adoptOSObject(dispatch_group_create());
479     auto fileLoadingGroup = adoptOSObject(dispatch_group_create());
480     for (NSUInteger index = 0; index < [itemProvidersToLoad count]; ++index) {
481         RetainPtr<UIItemProvider> itemProvider = [itemProvidersToLoad objectAtIndex:index];
482         RetainPtr<NSString> typeIdentifier = [typeIdentifiersToLoad objectAtIndex:index];
483         NSUInteger indexInItemProviderArray = [[indicesOfitemProvidersToLoad objectAtIndex:index] unsignedIntegerValue];
484         RetainPtr<NSString> suggestedName = [itemProvider suggestedName];
485         dispatch_group_enter(fileLoadingGroup.get());
486         dispatch_group_enter(synchronousFileLoadingGroup.get());
487         [itemProvider loadFileRepresentationForTypeIdentifier:typeIdentifier.get() completionHandler:[synchronousFileLoadingGroup, setFileURLsLock, indexInItemProviderArray, suggestedName, typeIdentifier, typeToFileURLMaps, fileLoadingGroup] (NSURL *url, NSError *error) {
488             // 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.
489             // 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
490             // WKFileUploadPanel.mm. The temporary files are cleaned up by the system at a later time.
491             RetainPtr<NSURL> destinationURL = temporaryFileURLForDataInteractionContent(url, suggestedName.get());
492             if (destinationURL && !error && [[NSFileManager defaultManager] linkItemAtURL:url toURL:destinationURL.get() error:nil]) {
493                 [setFileURLsLock lock];
494                 [typeToFileURLMaps setObject:[NSDictionary dictionaryWithObject:destinationURL.get() forKey:typeIdentifier.get()] atIndexedSubscript:indexInItemProviderArray];
495                 [setFileURLsLock unlock];
496             }
497             dispatch_group_leave(fileLoadingGroup.get());
498             dispatch_group_leave(synchronousFileLoadingGroup.get());
499         }];
500     }
501
502     RetainPtr<WebItemProviderPasteboard> retainedSelf = self;
503     auto itemLoadCompletion = [retainedSelf, synchronousFileLoadingGroup, fileLoadingGroup, typeToFileURLMaps, completionBlock = makeBlockPtr(action), changeCountBeforeLoading] {
504         if (changeCountBeforeLoading == retainedSelf->_changeCount)
505             retainedSelf->_typeToFileURLMaps = typeToFileURLMaps;
506
507         completionBlock([retainedSelf fileURLsForDataInteraction]);
508     };
509
510     if (synchronousTimeout > 0 && !dispatch_group_wait(synchronousFileLoadingGroup.get(), dispatch_time(DISPATCH_TIME_NOW, synchronousTimeout * NSEC_PER_SEC))) {
511         itemLoadCompletion();
512         return;
513     }
514
515     dispatch_group_notify(fileLoadingGroup.get(), dispatch_get_main_queue(), itemLoadCompletion);
516 }
517
518 - (WebItemProviderRegistrationInfoList *)registrationInfoAtIndex:(NSUInteger)index
519 {
520     return index < [_registrationInfoLists count] ? [_registrationInfoLists objectAtIndex:index] : nil;
521 }
522
523 - (UIItemProvider *)itemProviderAtIndex:(NSUInteger)index
524 {
525     return index < [_itemProviders count] ? [_itemProviders objectAtIndex:index] : nil;
526 }
527
528 - (BOOL)hasPendingOperation
529 {
530     return _pendingOperationCount;
531 }
532
533 - (void)incrementPendingOperationCount
534 {
535     _pendingOperationCount++;
536 }
537
538 - (void)decrementPendingOperationCount
539 {
540     _pendingOperationCount--;
541 }
542
543 - (void)enumerateItemProvidersWithBlock:(void (^)(UIItemProvider *itemProvider, NSUInteger index, BOOL *stop))block
544 {
545     [_itemProviders enumerateObjectsUsingBlock:block];
546 }
547
548 - (void)setRegistrationInfoLists:(NSArray <WebItemProviderRegistrationInfoList *> *)infoLists
549 {
550     _registrationInfoLists = infoLists;
551 }
552
553 @end
554
555 #endif // ENABLE(DATA_INTERACTION)