Cannot access images included in the content pasted from Microsoft Word
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / ios / DataInteractionTests.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
28 #if ENABLE(DATA_INTERACTION)
29
30 #import "DataInteractionSimulator.h"
31 #import "PlatformUtilities.h"
32 #import "TestWKWebView.h"
33 #import "UIKitSPI.h"
34 #import "WKWebViewConfigurationExtras.h"
35 #import <MobileCoreServices/MobileCoreServices.h>
36 #import <UIKit/NSItemProvider+UIKitAdditions.h>
37 #import <WebKit/WKPreferencesPrivate.h>
38 #import <WebKit/WKPreferencesRefPrivate.h>
39 #import <WebKit/WKProcessPoolPrivate.h>
40 #import <WebKit/WKWebViewConfigurationPrivate.h>
41 #import <WebKit/WebItemProviderPasteboard.h>
42 #import <WebKit/_WKProcessPoolConfiguration.h>
43
44 typedef void (^FileLoadCompletionBlock)(NSURL *, BOOL, NSError *);
45 typedef void (^DataLoadCompletionBlock)(NSData *, NSError *);
46 typedef void (^UIItemProviderDataLoadCompletionBlock)(NSData *, NSError *);
47
48 #if !USE(APPLE_INTERNAL_SDK)
49
50 @interface UIItemProviderRepresentationOptions : NSObject
51 @end
52
53 #endif
54
55 @interface UIItemProvider()
56 + (UIItemProvider *)itemProviderWithURL:(NSURL *)url title:(NSString *)title;
57 - (void) registerDataRepresentationForTypeIdentifier:(NSString *)typeIdentifier options:(UIItemProviderRepresentationOptions*)options loadHandler:(NSProgress * (^)(void (^UIItemProviderDataLoadCompletionBlock)(NSData *item, NSError *error))) loadHandler;
58 @end
59
60 static NSString *InjectedBundlePasteboardDataType = @"org.webkit.data";
61
62 static UIImage *testIconImage()
63 {
64     return [UIImage imageNamed:@"TestWebKitAPI.resources/icon.png"];
65 }
66
67 static NSData *testZIPArchive()
68 {
69     NSURL *zipFileURL = [[NSBundle mainBundle] URLForResource:@"compressed-files" withExtension:@"zip" subdirectory:@"TestWebKitAPI.resources"];
70     return [NSData dataWithContentsOfURL:zipFileURL];
71 }
72
73 @implementation UIItemProvider (DataInteractionTests)
74
75 - (void)registerDataRepresentationForTypeIdentifier:(NSString *)typeIdentifier withData:(NSData *)data
76 {
77     RetainPtr<NSData> retainedData = data;
78     [self registerDataRepresentationForTypeIdentifier:typeIdentifier visibility:UIItemProviderRepresentationOptionsVisibilityAll loadHandler: [retainedData] (DataLoadCompletionBlock block) -> NSProgress * {
79         block(retainedData.get(), nil);
80         return [NSProgress discreteProgressWithTotalUnitCount:100];
81     }];
82 }
83
84 @end
85
86 @implementation TestWKWebView (DataInteractionTests)
87
88 - (BOOL)editorContainsImageElement
89 {
90     return [self stringByEvaluatingJavaScript:@"!!editor.querySelector('img')"].boolValue;
91 }
92
93 - (NSString *)editorValue
94 {
95     return [self stringByEvaluatingJavaScript:@"editor.value"];
96 }
97
98 @end
99
100 static NSValue *makeCGRectValue(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
101 {
102     return [NSValue valueWithCGRect:CGRectMake(x, y, width, height)];
103 }
104
105 static void checkCGRectIsEqualToCGRectWithLogging(CGRect expected, CGRect observed)
106 {
107     BOOL isEqual = CGRectEqualToRect(expected, observed);
108     EXPECT_TRUE(isEqual);
109     if (!isEqual)
110         NSLog(@"Expected: %@ but observed: %@", NSStringFromCGRect(expected), NSStringFromCGRect(observed));
111 }
112
113 static void checkSelectionRectsWithLogging(NSArray *expected, NSArray *observed)
114 {
115     if (![expected isEqualToArray:observed])
116         NSLog(@"Expected selection rects: %@ but observed: %@", expected, observed);
117     EXPECT_TRUE([expected isEqualToArray:observed]);
118 }
119
120 static void checkTypeIdentifierPrecedesOtherTypeIdentifier(DataInteractionSimulator *simulator, NSString *firstType, NSString *secondType)
121 {
122     NSArray *registeredTypes = [simulator.sourceItemProviders.firstObject registeredTypeIdentifiers];
123     EXPECT_TRUE([registeredTypes containsObject:firstType]);
124     EXPECT_TRUE([registeredTypes containsObject:secondType]);
125     EXPECT_TRUE([registeredTypes indexOfObject:firstType] < [registeredTypes indexOfObject:secondType]);
126 }
127
128 static void checkFirstTypeIsPresentAndSecondTypeIsMissing(DataInteractionSimulator *simulator, CFStringRef firstType, CFStringRef secondType)
129 {
130     NSArray *registeredTypes = [simulator.sourceItemProviders.firstObject registeredTypeIdentifiers];
131     EXPECT_TRUE([registeredTypes containsObject:(NSString *)firstType]);
132     EXPECT_FALSE([registeredTypes containsObject:(NSString *)secondType]);
133 }
134
135 static void checkTypeIdentifierIsRegisteredAtIndex(DataInteractionSimulator *simulator, NSString *type, NSUInteger index)
136 {
137     NSArray *registeredTypes = [simulator.sourceItemProviders.firstObject registeredTypeIdentifiers];
138     EXPECT_GT(registeredTypes.count, index);
139     EXPECT_WK_STREQ(type.UTF8String, [registeredTypes[index] UTF8String]);
140 }
141
142 static void checkEstimatedSize(DataInteractionSimulator *simulator, CGSize estimatedSize)
143 {
144     UIItemProvider *sourceItemProvider = [simulator sourceItemProviders].firstObject;
145     EXPECT_EQ(estimatedSize.width, sourceItemProvider.preferredPresentationSize.width);
146     EXPECT_EQ(estimatedSize.height, sourceItemProvider.preferredPresentationSize.height);
147 }
148
149 static void checkSuggestedNameAndEstimatedSize(DataInteractionSimulator *simulator, NSString *suggestedName, CGSize estimatedSize)
150 {
151     UIItemProvider *sourceItemProvider = [simulator sourceItemProviders].firstObject;
152     EXPECT_WK_STREQ(suggestedName.UTF8String, sourceItemProvider.suggestedName.UTF8String);
153     EXPECT_EQ(estimatedSize.width, sourceItemProvider.preferredPresentationSize.width);
154     EXPECT_EQ(estimatedSize.height, sourceItemProvider.preferredPresentationSize.height);
155 }
156
157 static void checkStringArraysAreEqual(NSArray<NSString *> *expected, NSArray<NSString *> *observed)
158 {
159     EXPECT_EQ(expected.count, observed.count);
160     for (NSUInteger index = 0; index < expected.count; ++index) {
161         NSString *expectedString = [expected objectAtIndex:index];
162         NSString *observedString = [observed objectAtIndex:index];
163         EXPECT_WK_STREQ(expectedString, observedString);
164         if (![expectedString isEqualToString:observedString])
165             NSLog(@"Expected observed string: %@ to match expected string: %@ at index: %tu", observedString, expectedString, index);
166     }
167 }
168
169 static void checkDragCaretRectIsContainedInRect(CGRect caretRect, CGRect containerRect)
170 {
171     BOOL contained = CGRectContainsRect(containerRect, caretRect);
172     EXPECT_TRUE(contained);
173     if (!contained)
174         NSLog(@"Expected caret rect: %@ to fit within container rect: %@", NSStringFromCGRect(caretRect), NSStringFromCGRect(containerRect));
175 }
176
177 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
178
179 static void checkJSONWithLogging(NSString *jsonString, NSDictionary *expected)
180 {
181     BOOL success = TestWebKitAPI::Util::jsonMatchesExpectedValues(jsonString, expected);
182     EXPECT_TRUE(success);
183     if (!success)
184         NSLog(@"Expected JSON: %@ to match values: %@", jsonString, expected);
185 }
186
187 static void runTestWithTemporaryTextFile(void(^runTest)(NSURL *fileURL))
188 {
189     NSString *fileName = [NSString stringWithFormat:@"drag-drop-text-file-%@.txt", [NSUUID UUID].UUIDString];
190     RetainPtr<NSURL> temporaryFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName] isDirectory:NO];
191     [[NSFileManager defaultManager] removeItemAtURL:temporaryFile.get() error:nil];
192
193     NSError *error = nil;
194     [@"This is a tiny blob of text." writeToURL:temporaryFile.get() atomically:YES encoding:NSUTF8StringEncoding error:&error];
195
196     if (error)
197         NSLog(@"Error writing temporary file: %@", error);
198
199     @try {
200         runTest(temporaryFile.get());
201     } @finally {
202         [[NSFileManager defaultManager] removeItemAtURL:temporaryFile.get() error:nil];
203     }
204 }
205
206 static void runTestWithTemporaryFolder(void(^runTest)(NSURL *folderURL))
207 {
208     NSString *folderName = [NSString stringWithFormat:@"some.directory-%@", [NSUUID UUID].UUIDString];
209     RetainPtr<NSURL> temporaryFolder = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:folderName] isDirectory:YES];
210     [[NSFileManager defaultManager] removeItemAtURL:temporaryFolder.get() error:nil];
211
212     NSError *error = nil;
213     NSFileManager *defaultManager = [NSFileManager defaultManager];
214     [defaultManager createDirectoryAtURL:temporaryFolder.get() withIntermediateDirectories:NO attributes:nil error:&error];
215     [UIImagePNGRepresentation(testIconImage()) writeToURL:[temporaryFolder.get() URLByAppendingPathComponent:@"icon.png" isDirectory:NO] atomically:YES];
216     [testZIPArchive() writeToURL:[temporaryFolder.get() URLByAppendingPathComponent:@"archive.zip" isDirectory:NO] atomically:YES];
217
218     NSURL *firstSubdirectory = [temporaryFolder.get() URLByAppendingPathComponent:@"subdirectory1" isDirectory:YES];
219     [defaultManager createDirectoryAtURL:firstSubdirectory withIntermediateDirectories:NO attributes:nil error:&error];
220     [@"I am a text file in the first subdirectory." writeToURL:[firstSubdirectory URLByAppendingPathComponent:@"text-file-1.txt" isDirectory:NO] atomically:YES encoding:NSUTF8StringEncoding error:&error];
221
222     NSURL *secondSubdirectory = [temporaryFolder.get() URLByAppendingPathComponent:@"subdirectory2" isDirectory:YES];
223     [defaultManager createDirectoryAtURL:secondSubdirectory withIntermediateDirectories:NO attributes:nil error:&error];
224     [@"I am a text file in the second subdirectory." writeToURL:[secondSubdirectory URLByAppendingPathComponent:@"text-file-2.txt" isDirectory:NO] atomically:YES encoding:NSUTF8StringEncoding error:&error];
225
226     if (error)
227         NSLog(@"Error writing temporary file: %@", error);
228
229     @try {
230         runTest(temporaryFolder.get());
231     } @finally {
232         [[NSFileManager defaultManager] removeItemAtURL:temporaryFolder.get() error:nil];
233     }
234 }
235
236 #endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
237
238 namespace TestWebKitAPI {
239
240 TEST(DataInteractionTests, ImageToContentEditable)
241 {
242     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
243     [webView synchronouslyLoadTestPageNamed:@"image-and-contenteditable"];
244
245     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
246     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
247
248     EXPECT_TRUE([webView editorContainsImageElement]);
249
250     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
251     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
252     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
253     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
254     checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 215, 174) ], [dataInteractionSimulator finalSelectionRects]);
255     checkFirstTypeIsPresentAndSecondTypeIsMissing(dataInteractionSimulator.get(), kUTTypePNG, kUTTypeFileURL);
256     checkEstimatedSize(dataInteractionSimulator.get(), { 215, 174 });
257 }
258
259 TEST(DataInteractionTests, CanStartDragOnEnormousImage)
260 {
261     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
262     [webView synchronouslyLoadHTMLString:@"<img src='enormous.svg'></img>"];
263
264     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
265     [dataInteractionSimulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 100)];
266
267     NSArray *registeredTypes = [[dataInteractionSimulator sourceItemProviders].firstObject registeredTypeIdentifiers];
268     EXPECT_WK_STREQ((NSString *)kUTTypeScalableVectorGraphics, [registeredTypes firstObject]);
269 }
270
271 TEST(DataInteractionTests, ImageToTextarea)
272 {
273     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
274     [webView synchronouslyLoadTestPageNamed:@"image-and-textarea"];
275
276     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
277     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
278
279     EXPECT_WK_STREQ("", [webView editorValue]);
280
281     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
282     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
283     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
284     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
285     checkFirstTypeIsPresentAndSecondTypeIsMissing(dataInteractionSimulator.get(), kUTTypePNG, kUTTypeFileURL);
286     checkEstimatedSize(dataInteractionSimulator.get(), { 215, 174 });
287 }
288
289 TEST(DataInteractionTests, ImageInLinkToInput)
290 {
291     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
292     [webView synchronouslyLoadTestPageNamed:@"image-in-link-and-input"];
293
294     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
295     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
296
297     EXPECT_WK_STREQ("https://www.apple.com/", [webView editorValue].UTF8String);
298     checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 241, 2057, 232) ], [dataInteractionSimulator finalSelectionRects]);
299     checkSuggestedNameAndEstimatedSize(dataInteractionSimulator.get(), @"icon.png", { 215, 174 });
300     checkTypeIdentifierIsRegisteredAtIndex(dataInteractionSimulator.get(), (NSString *)kUTTypePNG, 0);
301 }
302
303 TEST(DataInteractionTests, ImageInLinkWithoutHREFToInput)
304 {
305     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
306     [webView synchronouslyLoadTestPageNamed:@"image-in-link-and-input"];
307     [webView stringByEvaluatingJavaScript:@"link.href = ''"];
308
309     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
310     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
311
312     EXPECT_WK_STREQ("", [webView editorValue]);
313     checkEstimatedSize(dataInteractionSimulator.get(), { 215, 174 });
314     checkTypeIdentifierIsRegisteredAtIndex(dataInteractionSimulator.get(), (NSString *)kUTTypePNG, 0);
315 }
316
317 TEST(DataInteractionTests, ImageDoesNotUseElementSizeAsEstimatedSize)
318 {
319     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
320     [webView synchronouslyLoadTestPageNamed:@"gif-and-file-input"];
321
322     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
323     [dataInteractionSimulator runFrom: { 100, 100 } to: { 100, 300 }];
324
325     checkTypeIdentifierIsRegisteredAtIndex(dataInteractionSimulator.get(), (NSString *)kUTTypeGIF, 0);
326     checkSuggestedNameAndEstimatedSize(dataInteractionSimulator.get(), @"apple.gif", { 52, 64 });
327     EXPECT_WK_STREQ("apple.gif (image/gif)", [webView stringByEvaluatingJavaScript:@"output.textContent"]);
328 }
329
330 TEST(DataInteractionTests, ContentEditableToContentEditable)
331 {
332     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
333     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
334
335     [webView loadTestPageNamed:@"autofocus-contenteditable"];
336     [dataInteractionSimulator waitForInputSession];
337     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
338
339     EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.textContent"].length, 0UL);
340     EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String);
341
342     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
343     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
344     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
345     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
346     checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 961, 227) ], [dataInteractionSimulator finalSelectionRects]);
347     checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypeRTFD, (NSString *)kUTTypeUTF8PlainText);
348 }
349
350 TEST(DataInteractionTests, ContentEditableToTextarea)
351 {
352     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
353     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
354
355     [webView loadTestPageNamed:@"contenteditable-and-textarea"];
356     [dataInteractionSimulator waitForInputSession];
357     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
358
359     EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.textContent"].length, 0UL);
360     EXPECT_WK_STREQ("Hello world", [webView editorValue].UTF8String);
361
362     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
363     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
364     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
365     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
366     checkSelectionRectsWithLogging(@[ makeCGRectValue(6, 203, 990, 232) ], [dataInteractionSimulator finalSelectionRects]);
367     checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypeRTFD, (NSString *)kUTTypeUTF8PlainText);
368 }
369
370 TEST(DataInteractionTests, ContentEditableMoveParagraphs)
371 {
372     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
373     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
374
375     [webView loadTestPageNamed:@"two-paragraph-contenteditable"];
376     [dataInteractionSimulator waitForInputSession];
377     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(250, 450)];
378
379     NSString *finalTextContent = [webView stringByEvaluatingJavaScript:@"editor.textContent"];
380     NSUInteger firstParagraphOffset = [finalTextContent rangeOfString:@"This is the first paragraph"].location;
381     NSUInteger secondParagraphOffset = [finalTextContent rangeOfString:@"This is the second paragraph"].location;
382
383     EXPECT_FALSE(firstParagraphOffset == NSNotFound);
384     EXPECT_FALSE(secondParagraphOffset == NSNotFound);
385     EXPECT_GT(firstParagraphOffset, secondParagraphOffset);
386     checkSelectionRectsWithLogging(@[ makeCGRectValue(190, 100, 130, 20), makeCGRectValue(0, 120, 320, 100), makeCGRectValue(0, 220, 252, 20) ], [dataInteractionSimulator finalSelectionRects]);
387 }
388
389 TEST(DataInteractionTests, DragImageFromContentEditable)
390 {
391     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
392     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
393
394     [webView synchronouslyLoadTestPageNamed:@"contenteditable-and-target"];
395     [dataInteractionSimulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 300)];
396
397     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"]);
398 }
399
400 TEST(DataInteractionTests, TextAreaToInput)
401 {
402     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
403     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
404
405     [webView loadTestPageNamed:@"textarea-to-input"];
406     [dataInteractionSimulator waitForInputSession];
407     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
408
409     EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.value"].length, 0UL);
410     EXPECT_WK_STREQ("Hello world", [webView editorValue].UTF8String);
411     checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 241, 990, 232) ], [dataInteractionSimulator finalSelectionRects]);
412 }
413
414 TEST(DataInteractionTests, SinglePlainTextWordTypeIdentifiers)
415 {
416     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
417     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
418
419     [webView loadTestPageNamed:@"textarea-to-input"];
420     [dataInteractionSimulator waitForInputSession];
421     [webView stringByEvaluatingJavaScript:@"source.value = 'pneumonoultramicroscopicsilicovolcanoconiosis'"];
422     [webView stringByEvaluatingJavaScript:@"source.selectionStart = 0"];
423     [webView stringByEvaluatingJavaScript:@"source.selectionEnd = source.value.length"];
424     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
425
426     NSItemProvider *itemProvider = [dataInteractionSimulator sourceItemProviders].firstObject;
427     NSArray *registeredTypes = [itemProvider registeredTypeIdentifiers];
428     EXPECT_EQ(1UL, registeredTypes.count);
429     EXPECT_WK_STREQ([(NSString *)kUTTypeUTF8PlainText UTF8String], [registeredTypes.firstObject UTF8String]);
430     EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.value"].length, 0UL);
431     EXPECT_EQ(UIPreferredPresentationStyleInline, itemProvider.preferredPresentationStyle);
432     EXPECT_WK_STREQ("pneumonoultramicroscopicsilicovolcanoconiosis", [webView editorValue].UTF8String);
433 }
434
435 TEST(DataInteractionTests, SinglePlainTextURLTypeIdentifiers)
436 {
437     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
438     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
439
440     [webView loadTestPageNamed:@"textarea-to-input"];
441     [dataInteractionSimulator waitForInputSession];
442     [webView stringByEvaluatingJavaScript:@"source.value = 'https://webkit.org/'"];
443     [webView stringByEvaluatingJavaScript:@"source.selectionStart = 0"];
444     [webView stringByEvaluatingJavaScript:@"source.selectionEnd = source.value.length"];
445     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
446
447     NSItemProvider *itemProvider = [dataInteractionSimulator sourceItemProviders].firstObject;
448     NSArray *registeredTypes = [itemProvider registeredTypeIdentifiers];
449     EXPECT_EQ(2UL, registeredTypes.count);
450     EXPECT_WK_STREQ([(NSString *)kUTTypeURL UTF8String], [registeredTypes.firstObject UTF8String]);
451     EXPECT_WK_STREQ([(NSString *)kUTTypeUTF8PlainText UTF8String], [registeredTypes.lastObject UTF8String]);
452     EXPECT_EQ(0UL, [webView stringByEvaluatingJavaScript:@"source.value"].length);
453     EXPECT_EQ(UIPreferredPresentationStyleInline, itemProvider.preferredPresentationStyle);
454     EXPECT_WK_STREQ("https://webkit.org/", [webView editorValue].UTF8String);
455 }
456
457 TEST(DataInteractionTests, LinkToInput)
458 {
459     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
460     [webView synchronouslyLoadTestPageNamed:@"link-and-input"];
461
462     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
463     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
464
465     EXPECT_WK_STREQ("https://www.apple.com/", [webView editorValue].UTF8String);
466
467     __block bool doneLoadingURL = false;
468     UIItemProvider *sourceItemProvider = [dataInteractionSimulator sourceItemProviders].firstObject;
469     [sourceItemProvider loadObjectOfClass:[NSURL class] completionHandler:^(id object, NSError *error) {
470         NSURL *url = object;
471         EXPECT_WK_STREQ("Hello world", url._title.UTF8String ?: "");
472         doneLoadingURL = true;
473     }];
474     TestWebKitAPI::Util::run(&doneLoadingURL);
475
476     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
477     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
478     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
479     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
480     checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 273, 2057, 232) ], [dataInteractionSimulator finalSelectionRects]);
481     checkTypeIdentifierIsRegisteredAtIndex(dataInteractionSimulator.get(), (NSString *)kUTTypeURL, 0);
482 }
483
484 TEST(DataInteractionTests, BackgroundImageLinkToInput)
485 {
486     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
487     [webView synchronouslyLoadTestPageNamed:@"background-image-link-and-input"];
488
489     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
490     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
491
492     EXPECT_WK_STREQ("https://www.apple.com/", [webView editorValue].UTF8String);
493
494     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
495     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
496     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
497     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
498     checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 241, 2057, 232) ], [dataInteractionSimulator finalSelectionRects]);
499     checkTypeIdentifierIsRegisteredAtIndex(dataInteractionSimulator.get(), (NSString *)kUTTypeURL, 0);
500 }
501
502 TEST(DataInteractionTests, CanPreventStart)
503 {
504     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
505     [webView synchronouslyLoadTestPageNamed:@"prevent-start"];
506
507     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
508     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
509
510     EXPECT_EQ(DataInteractionCancelled, [dataInteractionSimulator phase]);
511     EXPECT_FALSE([webView editorContainsImageElement]);
512
513     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
514     EXPECT_FALSE([observedEventNames containsObject:DataInteractionEnterEventName]);
515     EXPECT_FALSE([observedEventNames containsObject:DataInteractionOverEventName]);
516     checkSelectionRectsWithLogging(@[ ], [dataInteractionSimulator finalSelectionRects]);
517 }
518
519 TEST(DataInteractionTests, CanPreventOperation)
520 {
521     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
522     [webView synchronouslyLoadTestPageNamed:@"prevent-operation"];
523
524     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
525     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
526
527     EXPECT_FALSE([webView editorContainsImageElement]);
528
529     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
530     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
531     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
532     checkSelectionRectsWithLogging(@[ ], [dataInteractionSimulator finalSelectionRects]);
533 }
534
535 TEST(DataInteractionTests, EnterAndLeaveEvents)
536 {
537     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
538     [webView synchronouslyLoadTestPageNamed:@"link-and-input"];
539
540     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
541     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 450)];
542
543     EXPECT_WK_STREQ("", [webView editorValue].UTF8String);
544
545     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
546     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
547     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
548     EXPECT_TRUE([observedEventNames containsObject:DataInteractionLeaveEventName]);
549     EXPECT_FALSE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
550     checkSelectionRectsWithLogging(@[ ], [dataInteractionSimulator finalSelectionRects]);
551 }
552
553 TEST(DataInteractionTests, CanStartDragOnDivWithDraggableAttribute)
554 {
555     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
556     [webView synchronouslyLoadTestPageNamed:@"custom-draggable-div"];
557
558     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
559     [dataInteractionSimulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 250)];
560
561     EXPECT_GT([dataInteractionSimulator sourceItemProviders].count, 0UL);
562     NSItemProvider *itemProvider = [dataInteractionSimulator sourceItemProviders].firstObject;
563     EXPECT_EQ(UIPreferredPresentationStyleInline, itemProvider.preferredPresentationStyle);
564     EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!!destination.querySelector('#item')"]);
565     EXPECT_WK_STREQ(@"PASS", [webView stringByEvaluatingJavaScript:@"item.textContent"]);
566 }
567
568 TEST(DataInteractionTests, ExternalSourcePlainTextToIFrame)
569 {
570     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
571     [webView synchronouslyLoadTestPageNamed:@"contenteditable-in-iframe"];
572
573     auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
574     [itemProvider registerObject:@"Hello world" visibility:UIItemProviderRepresentationOptionsVisibilityAll];
575
576     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
577     [simulator setExternalItemProviders:@[ itemProvider.get() ]];
578     [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(160, 250)];
579
580     auto containerLeft = [webView stringByEvaluatingJavaScript:@"container.getBoundingClientRect().left"].floatValue;
581     auto containerTop = [webView stringByEvaluatingJavaScript:@"container.getBoundingClientRect().top"].floatValue;
582     auto containerWidth = [webView stringByEvaluatingJavaScript:@"container.getBoundingClientRect().width"].floatValue;
583     auto containerHeight = [webView stringByEvaluatingJavaScript:@"container.getBoundingClientRect().height"].floatValue;
584     checkDragCaretRectIsContainedInRect([simulator lastKnownDragCaretRect], CGRectMake(containerLeft, containerTop, containerWidth, containerHeight));
585 }
586
587 TEST(DataInteractionTests, ExternalSourceInlineTextToFileInput)
588 {
589     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
590     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
591
592     auto simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
593     [simulatedItemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleInline];
594     [simulatedItemProvider registerObject:@"This item provider requested inline presentation style." visibility:NSItemProviderRepresentationVisibilityAll];
595
596     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
597     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider.get() ]];
598     [dataInteractionSimulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)];
599
600     EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"output.value"]);
601 }
602
603 TEST(DataInteractionTests, ExternalSourceJSONToFileInput)
604 {
605     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
606     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
607
608     auto simulatedJSONItemProvider = adoptNS([[UIItemProvider alloc] init]);
609     NSData *jsonData = [@"{ \"foo\": \"bar\",  \"bar\": \"baz\" }" dataUsingEncoding:NSUTF8StringEncoding];
610     [simulatedJSONItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeJSON withData:jsonData];
611
612     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
613     [dataInteractionSimulator setExternalItemProviders:@[ simulatedJSONItemProvider.get() ]];
614     [dataInteractionSimulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)];
615
616     EXPECT_WK_STREQ("application/json", [webView stringByEvaluatingJavaScript:@"output.value"]);
617 }
618
619 TEST(DataInteractionTests, ExternalSourceImageToFileInput)
620 {
621     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
622     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
623
624     auto simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
625     NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5);
626     [simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG withData:imageData];
627
628     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
629     [dataInteractionSimulator setExternalItemProviders:@[ simulatedImageItemProvider.get() ]];
630     [dataInteractionSimulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)];
631
632     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
633     EXPECT_WK_STREQ("image/jpeg", outputValue.UTF8String);
634 }
635
636 TEST(DataInteractionTests, ExternalSourceHTMLToUploadArea)
637 {
638     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
639     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
640
641     auto simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
642     NSData *htmlData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
643     [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:htmlData];
644
645     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
646     [dataInteractionSimulator setShouldAllowMoveOperation:NO];
647     [dataInteractionSimulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get() ]];
648     [dataInteractionSimulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)];
649
650     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
651     EXPECT_WK_STREQ("text/html", outputValue.UTF8String);
652 }
653
654 TEST(DataInteractionTests, ExternalSourceMoveOperationNotAllowed)
655 {
656     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
657     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
658     [webView stringByEvaluatingJavaScript:@"upload.dropEffect = 'move'"];
659
660     auto simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
661     NSData *htmlData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
662     [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:htmlData];
663
664     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
665     [dataInteractionSimulator setShouldAllowMoveOperation:NO];
666     [dataInteractionSimulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get() ]];
667     [dataInteractionSimulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)];
668
669     EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"output.value"]);
670 }
671
672 TEST(DataInteractionTests, ExternalSourceZIPArchiveAndURLToSingleFileInput)
673 {
674     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
675     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
676
677     auto archiveProvider = adoptNS([[UIItemProvider alloc] init]);
678     [archiveProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeZipArchive withData:testZIPArchive()];
679
680     auto urlProvider = adoptNS([[UIItemProvider alloc] init]);
681     [urlProvider registerObject:[NSURL URLWithString:@"https://webkit.org"] visibility:UIItemProviderRepresentationOptionsVisibilityAll];
682
683     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
684     [dataInteractionSimulator setExternalItemProviders:@[ archiveProvider.get(), urlProvider.get() ]];
685     [dataInteractionSimulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)];
686
687     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
688     EXPECT_WK_STREQ("application/zip", outputValue.UTF8String);
689 }
690
691 TEST(DataInteractionTests, ExternalSourceZIPArchiveToUploadArea)
692 {
693     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
694     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
695
696     auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
697     [itemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeZipArchive withData:testZIPArchive()];
698
699     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
700     [dataInteractionSimulator setExternalItemProviders:@[ itemProvider.get() ]];
701     [dataInteractionSimulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)];
702
703     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
704     EXPECT_WK_STREQ("application/zip", outputValue.UTF8String);
705 }
706
707 TEST(DataInteractionTests, ExternalSourceImageAndHTMLToSingleFileInput)
708 {
709     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
710     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
711
712     auto simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
713     NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5);
714     [simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG withData:imageData];
715
716     auto simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
717     NSData *htmlData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
718     [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:htmlData];
719
720     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
721     [dataInteractionSimulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get(), simulatedImageItemProvider.get() ]];
722     [dataInteractionSimulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)];
723
724     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
725     EXPECT_WK_STREQ("", outputValue.UTF8String);
726 }
727
728 TEST(DataInteractionTests, ExternalSourceImageAndHTMLToMultipleFileInput)
729 {
730     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
731     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
732     [webView stringByEvaluatingJavaScript:@"input.setAttribute('multiple', '')"];
733
734     auto simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
735     NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5);
736     [simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG withData:imageData];
737
738     auto simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
739     NSData *htmlData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
740     [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:htmlData];
741
742     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
743     [dataInteractionSimulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get(), simulatedImageItemProvider.get() ]];
744     [dataInteractionSimulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)];
745
746     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
747     EXPECT_WK_STREQ("image/jpeg, text/html", outputValue.UTF8String);
748 }
749
750 TEST(DataInteractionTests, ExternalSourceImageAndHTMLToUploadArea)
751 {
752     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
753     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
754
755     auto simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
756     NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5);
757     [simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG withData:imageData];
758
759     auto firstSimulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
760     NSData *firstHTMLData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
761     [firstSimulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:firstHTMLData];
762
763     auto secondSimulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
764     NSData *secondHTMLData = [@"<html><body>hello world</body></html>" dataUsingEncoding:NSUTF8StringEncoding];
765     [secondSimulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:secondHTMLData];
766
767     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
768     [dataInteractionSimulator setShouldAllowMoveOperation:NO];
769     [dataInteractionSimulator setExternalItemProviders:@[ simulatedImageItemProvider.get(), firstSimulatedHTMLItemProvider.get(), secondSimulatedHTMLItemProvider.get() ]];
770     [dataInteractionSimulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)];
771
772     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
773     EXPECT_WK_STREQ("image/jpeg, text/html, text/html", outputValue.UTF8String);
774 }
775
776 TEST(DataInteractionTests, ExternalSourceHTMLToContentEditable)
777 {
778     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
779     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
780     [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
781
782     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
783     auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
784     NSData *htmlData = [@"<h1>This is a test</h1>" dataUsingEncoding:NSUTF8StringEncoding];
785     [itemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:htmlData];
786     [dataInteractionSimulator setExternalItemProviders:@[ itemProvider.get() ]];
787     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
788
789     NSString *textContent = [webView stringByEvaluatingJavaScript:@"editor.textContent"];
790     EXPECT_WK_STREQ("This is a test", textContent.UTF8String);
791     EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!!editor.querySelector('h1')"].boolValue);
792 }
793
794 TEST(DataInteractionTests, ExternalSourceBoldSystemAttributedStringToContentEditable)
795 {
796     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
797     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
798     [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
799
800     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
801     NSDictionary *textAttributes = @{ NSFontAttributeName: [UIFont boldSystemFontOfSize:20] };
802     NSAttributedString *richText = [[NSAttributedString alloc] initWithString:@"This is a test" attributes:textAttributes];
803     auto itemProvider = adoptNS([[UIItemProvider alloc] initWithObject:richText]);
804     [dataInteractionSimulator setExternalItemProviders:@[ itemProvider.get() ]];
805     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
806
807     EXPECT_WK_STREQ("This is a test", [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String);
808 }
809
810 TEST(DataInteractionTests, ExternalSourceColoredAttributedStringToContentEditable)
811 {
812     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
813     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
814     [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
815
816     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
817     NSDictionary *textAttributes = @{ NSForegroundColorAttributeName: [UIColor redColor] };
818     NSAttributedString *richText = [[NSAttributedString alloc] initWithString:@"This is a test" attributes:textAttributes];
819     auto itemProvider = adoptNS([[UIItemProvider alloc] initWithObject:richText]);
820     [dataInteractionSimulator setExternalItemProviders:@[ itemProvider.get() ]];
821     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
822
823     EXPECT_WK_STREQ("rgb(255, 0, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(document.querySelector('p')).color"]);
824     EXPECT_WK_STREQ("This is a test", [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String);
825 }
826
827 TEST(DataInteractionTests, ExternalSourceMultipleURLsToContentEditable)
828 {
829     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
830     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
831     [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
832
833     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
834     auto firstItem = adoptNS([[UIItemProvider alloc] init]);
835     [firstItem registerObject:[NSURL URLWithString:@"https://www.apple.com/iphone/"] visibility:UIItemProviderRepresentationOptionsVisibilityAll];
836     auto secondItem = adoptNS([[UIItemProvider alloc] init]);
837     [secondItem registerObject:[NSURL URLWithString:@"https://www.apple.com/mac/"] visibility:UIItemProviderRepresentationOptionsVisibilityAll];
838     auto thirdItem = adoptNS([[UIItemProvider alloc] init]);
839     [thirdItem registerObject:[NSURL URLWithString:@"https://webkit.org/"] visibility:UIItemProviderRepresentationOptionsVisibilityAll];
840     [dataInteractionSimulator setExternalItemProviders:@[ firstItem.get(), secondItem.get(), thirdItem.get() ]];
841     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
842
843     NSArray *separatedLinks = [[webView stringByEvaluatingJavaScript:@"editor.textContent"] componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
844     EXPECT_EQ(3UL, separatedLinks.count);
845     EXPECT_WK_STREQ("https://www.apple.com/iphone/", separatedLinks[0]);
846     EXPECT_WK_STREQ("https://www.apple.com/mac/", separatedLinks[1]);
847     EXPECT_WK_STREQ("https://webkit.org/", separatedLinks[2]);
848 }
849
850 TEST(DataInteractionTests, RespectsExternalSourceFidelityRankings)
851 {
852     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
853     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
854     [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
855
856     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
857
858     // Here, our source item provider vends two representations: plain text, and then an image. If we don't respect the
859     // fidelity order requested by the source, we'll end up assuming that the image is a higher fidelity representation
860     // than the plain text, and erroneously insert the image. If we respect source fidelities, we'll insert text rather
861     // than an image.
862     auto simulatedItemProviderWithTextFirst = adoptNS([[UIItemProvider alloc] init]);
863     [simulatedItemProviderWithTextFirst registerObject:@"Hello world" visibility:UIItemProviderRepresentationOptionsVisibilityAll];
864     [simulatedItemProviderWithTextFirst registerObject:testIconImage() visibility:UIItemProviderRepresentationOptionsVisibilityAll];
865     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProviderWithTextFirst.get() ]];
866
867     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
868     EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
869     EXPECT_FALSE([webView editorContainsImageElement]);
870     [webView stringByEvaluatingJavaScript:@"editor.innerHTML = ''"];
871
872     // Now we register the item providers in reverse, and expect the image to be inserted instead of text.
873     auto simulatedItemProviderWithImageFirst = adoptNS([[UIItemProvider alloc] init]);
874     [simulatedItemProviderWithImageFirst registerObject:testIconImage() visibility:UIItemProviderRepresentationOptionsVisibilityAll];
875     [simulatedItemProviderWithImageFirst registerObject:@"Hello world" visibility:UIItemProviderRepresentationOptionsVisibilityAll];
876     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProviderWithImageFirst.get() ]];
877
878     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
879     EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
880     EXPECT_TRUE([webView editorContainsImageElement]);
881 }
882
883 TEST(DataInteractionTests, ExternalSourceUTF8PlainTextOnly)
884 {
885     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
886     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
887
888     NSString *textPayload = @"Ceci n'est pas une string";
889     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
890     auto simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
891     [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeUTF8PlainText options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
892     {
893         completionBlock([textPayload dataUsingEncoding:NSUTF8StringEncoding], nil);
894         return [NSProgress discreteProgressWithTotalUnitCount:100];
895     }];
896     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider.get() ]];
897     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
898     EXPECT_WK_STREQ(textPayload.UTF8String, [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String);
899     checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 1936, 227) ], [dataInteractionSimulator finalSelectionRects]);
900 }
901
902 TEST(DataInteractionTests, ExternalSourceJPEGOnly)
903 {
904     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
905     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
906
907     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
908     auto simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
909     [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJPEG options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
910     {
911         completionBlock(UIImageJPEGRepresentation(testIconImage(), 0.5), nil);
912         return [NSProgress discreteProgressWithTotalUnitCount:100];
913     }];
914     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider.get() ]];
915     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
916     EXPECT_TRUE([webView editorContainsImageElement]);
917     checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 215, 174) ], [dataInteractionSimulator finalSelectionRects]);
918 }
919
920 TEST(DataInteractionTests, ExternalSourceTitledNSURL)
921 {
922     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
923     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
924     [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
925
926     NSURL *titledURL = [NSURL URLWithString:@"https://www.apple.com"];
927     titledURL._title = @"Apple";
928     auto simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
929     [simulatedItemProvider registerObject:titledURL visibility:UIItemProviderRepresentationOptionsVisibilityAll];
930
931     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
932     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider.get() ]];
933     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
934
935     EXPECT_WK_STREQ("Apple", [webView stringByEvaluatingJavaScript:@"editor.querySelector('a').textContent"]);
936     EXPECT_WK_STREQ("https://www.apple.com/", [webView stringByEvaluatingJavaScript:@"editor.querySelector('a').href"]);
937 }
938
939 TEST(DataInteractionTests, ExternalSourceFileURL)
940 {
941     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
942     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
943     [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
944
945     NSURL *URL = [NSURL URLWithString:@"file:///some/file/that/is/not/real"];
946     UIItemProvider *simulatedItemProvider = [UIItemProvider itemProviderWithURL:URL title:@""];
947
948     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
949     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider ]];
950     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
951
952     EXPECT_FALSE([[webView stringByEvaluatingJavaScript:@"!!editor.querySelector('a')"] boolValue]);
953     EXPECT_WK_STREQ("Hello world\nfile:///some/file/that/is/not/real", [webView stringByEvaluatingJavaScript:@"document.body.innerText"]);
954 }
955
956 TEST(DataInteractionTests, ExternalSourceOverrideDropFileUpload)
957 {
958     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
959     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
960
961     auto simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
962     NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5);
963     [simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG withData:imageData];
964
965     auto simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
966     NSData *firstHTMLData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
967     [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:firstHTMLData];
968
969     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
970     [dataInteractionSimulator setOverridePerformDropBlock:^NSArray<UIDragItem *> *(id <UIDropSession> session)
971     {
972         EXPECT_EQ(2UL, session.items.count);
973         UIDragItem *firstItem = session.items[0];
974         UIDragItem *secondItem = session.items[1];
975         EXPECT_TRUE([firstItem.itemProvider.registeredTypeIdentifiers isEqual:@[ (NSString *)kUTTypeJPEG ]]);
976         EXPECT_TRUE([secondItem.itemProvider.registeredTypeIdentifiers isEqual:@[ (NSString *)kUTTypeHTML ]]);
977         return @[ secondItem ];
978     }];
979     [dataInteractionSimulator setExternalItemProviders:@[ simulatedImageItemProvider.get(), simulatedHTMLItemProvider.get() ]];
980     [dataInteractionSimulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)];
981
982     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
983     EXPECT_WK_STREQ("text/html", outputValue.UTF8String);
984 }
985
986 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
987
988 static RetainPtr<TestWKWebView> setUpTestWebViewForDataTransferItems()
989 {
990     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
991     [webView synchronouslyLoadTestPageNamed:@"DataTransferItem-getAsEntry"];
992
993     auto preferences = (WKPreferencesRef)[[webView configuration] preferences];
994     WKPreferencesSetDataTransferItemsEnabled(preferences, true);
995     WKPreferencesSetDirectoryUploadEnabled(preferences, true);
996
997     return webView;
998 }
999
1000 TEST(DataInteractionTests, ExternalSourceDataTransferItemGetFolderAsEntry)
1001 {
1002     // The expected output is sorted by alphabetical order here for consistent behavior across different test environments.
1003     // See DataTransferItem-getAsEntry.html for more details.
1004     NSArray<NSString *> *expectedOutput = @[
1005         @"Found data transfer item (kind: 'file', type: '')",
1006         @"DIR: /somedirectory",
1007         @"DIR: /somedirectory/subdirectory1",
1008         @"DIR: /somedirectory/subdirectory2",
1009         @"FILE: /somedirectory/archive.zip ('application/zip', 988 bytes)",
1010         @"FILE: /somedirectory/icon.png ('image/png', 42130 bytes)",
1011         @"FILE: /somedirectory/subdirectory1/text-file-1.txt ('text/plain', 43 bytes)",
1012         @"FILE: /somedirectory/subdirectory2/text-file-2.txt ('text/plain', 44 bytes)"
1013     ];
1014
1015     auto webView = setUpTestWebViewForDataTransferItems();
1016     __block bool done = false;
1017     [webView performAfterReceivingMessage:@"dropped" action:^() {
1018         done = true;
1019     }];
1020
1021     runTestWithTemporaryFolder(^(NSURL *folderURL) {
1022         auto itemProvider = adoptNS([[NSItemProvider alloc] init]);
1023         [itemProvider setSuggestedName:@"somedirectory"];
1024         [itemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeFolder fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[capturedFolderURL = retainPtr(folderURL)] (FileLoadCompletionBlock completionHandler) -> NSProgress * {
1025             completionHandler(capturedFolderURL.get(), NO, nil);
1026             return nil;
1027         }];
1028
1029         auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1030         [dataInteractionSimulator setExternalItemProviders:@[ itemProvider.get() ]];
1031         [dataInteractionSimulator runFrom:CGPointMake(50, 50) to:CGPointMake(150, 50)];
1032     });
1033
1034     TestWebKitAPI::Util::run(&done);
1035     EXPECT_WK_STREQ([expectedOutput componentsJoinedByString:@"\n"], [webView stringByEvaluatingJavaScript:@"output.value"]);
1036 }
1037
1038 TEST(DataInteractionTests, ExternalSourceDataTransferItemGetPlainTextFileAsEntry)
1039 {
1040     NSArray<NSString *> *expectedOutput = @[
1041         @"Found data transfer item (kind: 'file', type: 'text/plain')",
1042         @"FILE: /foo.txt ('text/plain', 28 bytes)"
1043     ];
1044
1045     auto webView = setUpTestWebViewForDataTransferItems();
1046     __block bool done = false;
1047     [webView performAfterReceivingMessage:@"dropped" action:^() {
1048         done = true;
1049     }];
1050
1051     runTestWithTemporaryTextFile(^(NSURL *fileURL) {
1052         auto itemProvider = adoptNS([[NSItemProvider alloc] init]);
1053         [itemProvider setSuggestedName:@"foo"];
1054         [itemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeUTF8PlainText fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[capturedFileURL = retainPtr(fileURL)](FileLoadCompletionBlock completionHandler) -> NSProgress * {
1055             completionHandler(capturedFileURL.get(), NO, nil);
1056             return nil;
1057         }];
1058
1059         auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1060         [dataInteractionSimulator setExternalItemProviders:@[ itemProvider.get() ]];
1061         [dataInteractionSimulator runFrom:CGPointMake(50, 50) to:CGPointMake(150, 50)];
1062     });
1063
1064     TestWebKitAPI::Util::run(&done);
1065     EXPECT_WK_STREQ([expectedOutput componentsJoinedByString:@"\n"], [webView stringByEvaluatingJavaScript:@"output.value"]);
1066 }
1067
1068 #endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
1069
1070 TEST(DataInteractionTests, ExternalSourceOverrideDropInsertURL)
1071 {
1072     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1073     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
1074     [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
1075
1076     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1077     [dataInteractionSimulator setOverridePerformDropBlock:^NSArray<UIDragItem *> *(id <UIDropSession> session)
1078     {
1079         NSMutableArray<UIDragItem *> *allowedItems = [NSMutableArray array];
1080         for (UIDragItem *item in session.items) {
1081             if ([item.itemProvider.registeredTypeIdentifiers containsObject:(NSString *)kUTTypeURL])
1082                 [allowedItems addObject:item];
1083         }
1084         EXPECT_EQ(1UL, allowedItems.count);
1085         return allowedItems;
1086     }];
1087
1088     auto firstItemProvider = adoptNS([[UIItemProvider alloc] init]);
1089     [firstItemProvider registerObject:@"This is a string." visibility:UIItemProviderRepresentationOptionsVisibilityAll];
1090     auto secondItemProvider = adoptNS([[UIItemProvider alloc] init]);
1091     [secondItemProvider registerObject:[NSURL URLWithString:@"https://webkit.org/"] visibility:UIItemProviderRepresentationOptionsVisibilityAll];
1092     [dataInteractionSimulator setExternalItemProviders:@[ firstItemProvider.get(), secondItemProvider.get() ]];
1093     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
1094
1095     EXPECT_WK_STREQ("https://webkit.org/", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
1096 }
1097
1098 TEST(DataInteractionTests, OverrideDataInteractionOperation)
1099 {
1100     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1101     [webView synchronouslyLoadTestPageNamed:@"simple"];
1102
1103     auto simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
1104     [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:[@"<body></body>" dataUsingEncoding:NSUTF8StringEncoding]];
1105
1106     __block bool finishedLoadingData = false;
1107     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1108     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider.get() ]];
1109     [dataInteractionSimulator setOverrideDataInteractionOperationBlock:^NSUInteger(NSUInteger operation, id session)
1110     {
1111         EXPECT_EQ(0U, operation);
1112         return 1;
1113     }];
1114     [dataInteractionSimulator setDataInteractionOperationCompletionBlock:^(BOOL handled, NSArray *itemProviders) {
1115         EXPECT_FALSE(handled);
1116         [itemProviders.firstObject loadDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML completionHandler:^(NSData *data, NSError *error) {
1117             NSString *text = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
1118             EXPECT_WK_STREQ("<body></body>", text.UTF8String);
1119             EXPECT_FALSE(!!error);
1120             finishedLoadingData = true;
1121         }];
1122     }];
1123     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
1124     TestWebKitAPI::Util::run(&finishedLoadingData);
1125 }
1126
1127 TEST(DataInteractionTests, InjectedBundleOverridePerformTwoStepDrop)
1128 {
1129     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"];
1130     [configuration.processPool _setObject:@YES forBundleParameter:@"BundleOverridePerformTwoStepDrop"];
1131
1132     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration]);
1133     [webView loadTestPageNamed:@"autofocus-contenteditable"];
1134     [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
1135
1136     auto simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
1137     [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeUTF8PlainText options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
1138     {
1139         completionBlock([@"Hello world" dataUsingEncoding:NSUTF8StringEncoding], nil);
1140         return [NSProgress discreteProgressWithTotalUnitCount:100];
1141     }];
1142
1143     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1144     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider.get() ]];
1145     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
1146
1147     EXPECT_EQ(0UL, [webView stringByEvaluatingJavaScript:@"editor.textContent"].length);
1148 }
1149
1150 TEST(DataInteractionTests, InjectedBundleAllowPerformTwoStepDrop)
1151 {
1152     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"];
1153     [configuration.processPool _setObject:@NO forBundleParameter:@"BundleOverridePerformTwoStepDrop"];
1154
1155     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration]);
1156     [webView loadTestPageNamed:@"autofocus-contenteditable"];
1157     [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
1158
1159     auto simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
1160     [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeUTF8PlainText options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
1161     {
1162         completionBlock([@"Hello world" dataUsingEncoding:NSUTF8StringEncoding], nil);
1163         return [NSProgress discreteProgressWithTotalUnitCount:100];
1164     }];
1165
1166     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1167     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider.get() ]];
1168     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
1169
1170     EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String);
1171 }
1172
1173 TEST(DataInteractionTests, InjectedBundleImageElementData)
1174 {
1175     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"];
1176     [configuration _setAttachmentElementEnabled:YES];
1177     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration]);
1178     [webView synchronouslyLoadTestPageNamed:@"image-and-contenteditable"];
1179
1180     __block RetainPtr<NSString> injectedString;
1181     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1182     [dataInteractionSimulator setConvertItemProvidersBlock:^NSArray *(UIItemProvider *itemProvider, NSArray *, NSDictionary *data)
1183     {
1184         injectedString = adoptNS([[NSString alloc] initWithData:data[InjectedBundlePasteboardDataType] encoding:NSUTF8StringEncoding]);
1185         return @[ itemProvider ];
1186     }];
1187
1188     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 250)];
1189
1190     EXPECT_WK_STREQ("hello", [injectedString UTF8String]);
1191 }
1192
1193 TEST(DataInteractionTests, InjectedBundleAttachmentElementData)
1194 {
1195     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"];
1196     [configuration _setAttachmentElementEnabled:YES];
1197     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration]);
1198     [webView synchronouslyLoadTestPageNamed:@"attachment-element"];
1199
1200     __block RetainPtr<NSString> injectedString;
1201     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1202     [dataInteractionSimulator setConvertItemProvidersBlock:^NSArray *(UIItemProvider *itemProvider, NSArray *, NSDictionary *data)
1203     {
1204         injectedString = adoptNS([[NSString alloc] initWithData:data[InjectedBundlePasteboardDataType] encoding:NSUTF8StringEncoding]);
1205         return @[ itemProvider ];
1206     }];
1207
1208     [dataInteractionSimulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 400)];
1209
1210     EXPECT_WK_STREQ("hello", [injectedString UTF8String]);
1211     EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"getSelection().isCollapsed"].boolValue);
1212 }
1213
1214 TEST(DataInteractionTests, LargeImageToTargetDiv)
1215 {
1216     auto testWebViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1217     [[testWebViewConfiguration preferences] _setLargeImageAsyncDecodingEnabled:NO];
1218
1219     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:testWebViewConfiguration.get()]);
1220     [webView synchronouslyLoadTestPageNamed:@"div-and-large-image"];
1221
1222     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1223     [dataInteractionSimulator runFrom:CGPointMake(200, 400) to:CGPointMake(200, 150)];
1224     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"].UTF8String);
1225     checkFirstTypeIsPresentAndSecondTypeIsMissing(dataInteractionSimulator.get(), kUTTypePNG, kUTTypeFileURL);
1226     checkEstimatedSize(dataInteractionSimulator.get(), { 2000, 2000 });
1227 }
1228
1229 TEST(DataInteractionTests, LinkWithEmptyHREF)
1230 {
1231     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1232     [webView synchronouslyLoadTestPageNamed:@"link-and-input"];
1233     [webView stringByEvaluatingJavaScript:@"document.querySelector('a').href = ''"];
1234
1235     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1236     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
1237
1238     EXPECT_EQ(DataInteractionCancelled, [dataInteractionSimulator phase]);
1239     EXPECT_WK_STREQ("", [webView editorValue].UTF8String);
1240 }
1241
1242 TEST(DataInteractionTests, CancelledLiftDoesNotCauseSubsequentDragsToFail)
1243 {
1244     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1245     [webView synchronouslyLoadTestPageNamed:@"link-and-target-div"];
1246
1247     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1248     [dataInteractionSimulator setConvertItemProvidersBlock:^NSArray *(UIItemProvider *, NSArray *, NSDictionary *)
1249     {
1250         return @[ ];
1251     }];
1252     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
1253     EXPECT_EQ(DataInteractionCancelled, [dataInteractionSimulator phase]);
1254     EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"target.textContent"]);
1255     NSString *outputText = [webView stringByEvaluatingJavaScript:@"output.textContent"];
1256     checkStringArraysAreEqual(@[@"dragstart", @"dragend"], [outputText componentsSeparatedByString:@" "]);
1257
1258     [webView stringByEvaluatingJavaScript:@"output.innerHTML = ''"];
1259     [dataInteractionSimulator setConvertItemProvidersBlock:^NSArray *(UIItemProvider *itemProvider, NSArray *, NSDictionary *)
1260     {
1261         return @[ itemProvider ];
1262     }];
1263     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
1264     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"]);
1265     [webView stringByEvaluatingJavaScript:@"output.textContent"];
1266     checkStringArraysAreEqual(@[@"dragstart", @"dragend"], [outputText componentsSeparatedByString:@" "]);
1267 }
1268
1269 static void testDragAndDropOntoTargetElements(TestWKWebView *webView)
1270 {
1271     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView]);
1272     [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 250)];
1273     EXPECT_WK_STREQ("rgb(0, 128, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(target1).backgroundColor"]);
1274     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target1.textContent"]);
1275
1276     [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(250, 50)];
1277     EXPECT_WK_STREQ("rgb(0, 128, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(target2).backgroundColor"]);
1278     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target2.textContent"]);
1279
1280     [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(250, 250)];
1281     EXPECT_WK_STREQ("rgb(0, 128, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(target3).backgroundColor"]);
1282     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target3.textContent"]);
1283 }
1284
1285 TEST(DataInteractionTests, DragEventClientCoordinatesBasic)
1286 {
1287     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1288     [webView synchronouslyLoadTestPageNamed:@"drop-targets"];
1289
1290     testDragAndDropOntoTargetElements(webView.get());
1291 }
1292
1293 TEST(DataInteractionTests, DragEventClientCoordinatesWithScrollOffset)
1294 {
1295     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1296     [webView synchronouslyLoadTestPageNamed:@"drop-targets"];
1297     [webView stringByEvaluatingJavaScript:@"document.body.style.margin = '1000px 0'"];
1298     [webView stringByEvaluatingJavaScript:@"document.scrollingElement.scrollTop = 1000"];
1299     [webView waitForNextPresentationUpdate];
1300
1301     testDragAndDropOntoTargetElements(webView.get());
1302 }
1303
1304 TEST(DataInteractionTests, DragEventPageCoordinatesBasic)
1305 {
1306     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1307     [webView synchronouslyLoadTestPageNamed:@"drop-targets"];
1308     [webView stringByEvaluatingJavaScript:@"window.usePageCoordinates = true"];
1309
1310     testDragAndDropOntoTargetElements(webView.get());
1311 }
1312
1313 TEST(DataInteractionTests, DragEventPageCoordinatesWithScrollOffset)
1314 {
1315     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1316     [webView synchronouslyLoadTestPageNamed:@"drop-targets"];
1317     [webView stringByEvaluatingJavaScript:@"document.body.style.margin = '1000px 0'"];
1318     [webView stringByEvaluatingJavaScript:@"document.scrollingElement.scrollTop = 1000"];
1319     [webView stringByEvaluatingJavaScript:@"window.usePageCoordinates = true"];
1320     [webView waitForNextPresentationUpdate];
1321
1322     testDragAndDropOntoTargetElements(webView.get());
1323 }
1324
1325 TEST(DataInteractionTests, DoNotCrashWhenSelectionIsClearedInDragStart)
1326 {
1327     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1328     [webView synchronouslyLoadTestPageNamed:@"dragstart-clear-selection"];
1329
1330     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1331     [simulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 100)];
1332
1333     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"paragraph.textContent"]);
1334 }
1335
1336 TEST(DataInteractionTests, CustomActionSheetPopover)
1337 {
1338     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1339     [webView synchronouslyLoadTestPageNamed:@"link-and-target-div"];
1340
1341     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1342     [dataInteractionSimulator setShouldEnsureUIApplication:YES];
1343
1344     __block BOOL didInvokeCustomActionSheet = NO;
1345     [dataInteractionSimulator setShowCustomActionSheetBlock:^BOOL(_WKActivatedElementInfo *element)
1346     {
1347         EXPECT_EQ(_WKActivatedElementTypeLink, element.type);
1348         EXPECT_WK_STREQ("Hello world", element.title.UTF8String);
1349         didInvokeCustomActionSheet = YES;
1350         return YES;
1351     }];
1352     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
1353     EXPECT_TRUE(didInvokeCustomActionSheet);
1354     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"].UTF8String);
1355 }
1356
1357 TEST(DataInteractionTests, UnresponsivePageDoesNotHangUI)
1358 {
1359     _WKProcessPoolConfiguration *processPoolConfiguration = [[[_WKProcessPoolConfiguration alloc] init] autorelease];
1360     processPoolConfiguration.ignoreSynchronousMessagingTimeoutsForTesting = YES;
1361
1362     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:[[[WKWebViewConfiguration alloc] init] autorelease] processPoolConfiguration:processPoolConfiguration]);
1363     [webView synchronouslyLoadTestPageNamed:@"simple"];
1364     [webView evaluateJavaScript:@"while(1);" completionHandler:nil];
1365
1366     // The test passes if we can prepare for data interaction without timing out.
1367     auto dragSession = adoptNS([[MockDragSession alloc] init]);
1368     [(id <UIDragInteractionDelegate_ForWebKitOnly>)[webView dragInteractionDelegate] _dragInteraction:[webView dragInteraction] prepareForSession:dragSession.get() completion:^() { }];
1369 }
1370
1371 TEST(DataInteractionTests, WebItemProviderPasteboardLoading)
1372 {
1373     static NSString *fastString = @"This data loads quickly";
1374     static NSString *slowString = @"This data loads slowly";
1375
1376     WebItemProviderPasteboard *pasteboard = [WebItemProviderPasteboard sharedInstance];
1377     auto fastItem = adoptNS([[UIItemProvider alloc] init]);
1378     [fastItem registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeUTF8PlainText options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
1379     {
1380         completionBlock([fastString dataUsingEncoding:NSUTF8StringEncoding], nil);
1381         return nil;
1382     }];
1383
1384     auto slowItem = adoptNS([[UIItemProvider alloc] init]);
1385     [slowItem registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeUTF8PlainText options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
1386     {
1387         sleep(2);
1388         completionBlock([slowString dataUsingEncoding:NSUTF8StringEncoding], nil);
1389         return nil;
1390     }];
1391
1392     __block bool hasRunFirstCompletionBlock = false;
1393     pasteboard.itemProviders = @[ fastItem.get(), slowItem.get() ];
1394     [pasteboard doAfterLoadingProvidedContentIntoFileURLs:^(NSArray<NSURL *> *urls) {
1395         EXPECT_EQ(2UL, urls.count);
1396         auto firstString = adoptNS([[NSString alloc] initWithContentsOfURL:urls[0] encoding:NSUTF8StringEncoding error:nil]);
1397         auto secondString = adoptNS([[NSString alloc] initWithContentsOfURL:urls[1] encoding:NSUTF8StringEncoding error:nil]);
1398         EXPECT_WK_STREQ(fastString, [firstString UTF8String]);
1399         EXPECT_WK_STREQ(slowString, [secondString UTF8String]);
1400         hasRunFirstCompletionBlock = true;
1401     } synchronousTimeout:600];
1402     EXPECT_TRUE(hasRunFirstCompletionBlock);
1403
1404     __block bool hasRunSecondCompletionBlock = false;
1405     [pasteboard doAfterLoadingProvidedContentIntoFileURLs:^(NSArray<NSURL *> *urls) {
1406         EXPECT_EQ(2UL, urls.count);
1407         auto firstString = adoptNS([[NSString alloc] initWithContentsOfURL:urls[0] encoding:NSUTF8StringEncoding error:nil]);
1408         auto secondString = adoptNS([[NSString alloc] initWithContentsOfURL:urls[1] encoding:NSUTF8StringEncoding error:nil]);
1409         EXPECT_WK_STREQ(fastString, [firstString UTF8String]);
1410         EXPECT_WK_STREQ(slowString, [secondString UTF8String]);
1411         hasRunSecondCompletionBlock = true;
1412     } synchronousTimeout:0];
1413     EXPECT_FALSE(hasRunSecondCompletionBlock);
1414     TestWebKitAPI::Util::run(&hasRunSecondCompletionBlock);
1415 }
1416
1417 TEST(DataInteractionTests, DoNotCrashWhenSelectionMovesOffscreenAfterDragStart)
1418 {
1419     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1420     [webView synchronouslyLoadTestPageNamed:@"dragstart-change-selection-offscreen"];
1421
1422     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1423     [simulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 100)];
1424
1425     EXPECT_WK_STREQ("FAR OFFSCREEN", [webView stringByEvaluatingJavaScript:@"getSelection().getRangeAt(0).toString()"]);
1426 }
1427
1428 TEST(DataInteractionTests, AdditionalItemsCanBePreventedOnDragStart)
1429 {
1430     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1431     [webView synchronouslyLoadTestPageNamed:@"selected-text-image-link-and-editable"];
1432     [webView stringByEvaluatingJavaScript:@"link.addEventListener('dragstart', e => e.preventDefault())"];
1433     [webView stringByEvaluatingJavaScript:@"image.addEventListener('dragstart', e => e.preventDefault())"];
1434
1435     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1436     [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 400) additionalItemRequestLocations:@{
1437         @0.33: [NSValue valueWithCGPoint:CGPointMake(50, 150)],
1438         @0.66: [NSValue valueWithCGPoint:CGPointMake(50, 250)]
1439     }];
1440     EXPECT_WK_STREQ("ABCD", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
1441 }
1442
1443 TEST(DataInteractionTests, AdditionalLinkAndImageIntoContentEditable)
1444 {
1445     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1446     [webView synchronouslyLoadTestPageNamed:@"selected-text-image-link-and-editable"];
1447
1448     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1449     [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 400) additionalItemRequestLocations:@{
1450         @0.33: [NSValue valueWithCGPoint:CGPointMake(50, 150)],
1451         @0.66: [NSValue valueWithCGPoint:CGPointMake(50, 250)]
1452     }];
1453     EXPECT_WK_STREQ("ABCD A link", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
1454     EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!!editor.querySelector('img')"]);
1455     EXPECT_WK_STREQ("https://www.apple.com/", [webView stringByEvaluatingJavaScript:@"editor.querySelector('a').href"]);
1456 }
1457
1458 TEST(DataInteractionTests, DragLiftPreviewDataTransferSetDragImage)
1459 {
1460     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1461     [webView synchronouslyLoadTestPageNamed:@"DataTransfer-setDragImage"];
1462     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1463
1464     // Use DataTransfer.setDragImage to specify an existing image element in the DOM.
1465     [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(250, 50)];
1466     checkCGRectIsEqualToCGRectWithLogging({{100, 50}, {215, 174}}, [simulator liftPreviews][0].view.frame);
1467
1468     // Use DataTransfer.setDragImage to specify an existing image element in the DOM, with x and y offsets.
1469     [simulator runFrom:CGPointMake(10, 150) to:CGPointMake(250, 150)];
1470     checkCGRectIsEqualToCGRectWithLogging({{-90, 50}, {215, 174}}, [simulator liftPreviews][0].view.frame);
1471
1472     // Use DataTransfer.setDragImage to specify a newly created Image, disconnected from the DOM.
1473     [simulator runFrom:CGPointMake(100, 250) to:CGPointMake(250, 250)];
1474     checkCGRectIsEqualToCGRectWithLogging({{100, 250}, {215, 174}}, [simulator liftPreviews][0].view.frame);
1475
1476     // Don't use DataTransfer.setDragImage and fall back to snapshotting the dragged element.
1477     [simulator runFrom:CGPointMake(50, 350) to:CGPointMake(250, 350)];
1478     checkCGRectIsEqualToCGRectWithLogging({{0, 300}, {300, 100}}, [simulator liftPreviews][0].view.frame);
1479
1480     // Perform a normal drag on an image.
1481     [simulator runFrom:CGPointMake(50, 450) to:CGPointMake(250, 450)];
1482     checkCGRectIsEqualToCGRectWithLogging({{0, 400}, {215, 174}}, [simulator liftPreviews][0].view.frame);
1483 }
1484
1485 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
1486
1487 TEST(DataInteractionTests, DataTransferGetDataWhenDroppingPlainText)
1488 {
1489     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1490     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1491     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1492
1493     [webView stringByEvaluatingJavaScript:@"select(plain)"];
1494     [simulator runFrom:CGPointMake(50, 75) to:CGPointMake(50, 375)];
1495     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1496         @"dragover": @{ @"text/plain" : @"" },
1497         @"drop": @{ @"text/plain" : @"Plain text" }
1498     });
1499 }
1500
1501 TEST(DataInteractionTests, DataTransferGetDataWhenDroppingCustomData)
1502 {
1503     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1504     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1505     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1506
1507     [webView stringByEvaluatingJavaScript:@"select(rich)"];
1508     [webView stringByEvaluatingJavaScript:@"writeCustomData = true"];
1509     [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
1510     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1511         @"dragover" : @{
1512             @"text/plain" : @"",
1513             @"foo/plain" : @"",
1514             @"text/html" : @"",
1515             @"bar/html" : @"",
1516             @"text/uri-list" : @"",
1517             @"baz/uri-list" : @""
1518         },
1519         @"drop" : @{
1520             @"text/plain" : @"ben bitdiddle",
1521             @"foo/plain" : @"eva lu ator",
1522             @"text/html" : @"<b>bold text</b>",
1523             @"bar/html" : @"<i>italic text</i>",
1524             @"text/uri-list" : @"https://www.apple.com",
1525             @"baz/uri-list" : @"https://www.webkit.org"
1526         }
1527     });
1528 }
1529
1530 TEST(DataInteractionTests, DataTransferGetDataWhenDroppingURL)
1531 {
1532     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1533     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1534     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1535
1536     [webView stringByEvaluatingJavaScript:@"rich.innerHTML = '<a href=\"https://www.apple.com/\">This is a link.</a>'"];
1537     [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
1538     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1539         @"dragover": @{ @"text/uri-list" : @"" },
1540         @"drop": @{ @"text/uri-list" : @"https://www.apple.com/" }
1541     });
1542 }
1543
1544 TEST(DataInteractionTests, DataTransferGetDataWhenDroppingImageWithFileURL)
1545 {
1546     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1547     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1548     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1549
1550     auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
1551     NSURL *iconURL = [[NSBundle mainBundle] URLForResource:@"icon" withExtension:@"png" subdirectory:@"TestWebKitAPI.resources"];
1552     [itemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypePNG fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[protectedIconURL = retainPtr(iconURL)] (FileLoadCompletionBlock completionHandler) -> NSProgress * {
1553         completionHandler(protectedIconURL.get(), NO, nil);
1554         return nil;
1555     }];
1556     [itemProvider registerObject:iconURL visibility:UIItemProviderRepresentationOptionsVisibilityAll];
1557     [simulator setExternalItemProviders:@[ itemProvider.get() ]];
1558
1559     [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)];
1560
1561     // File URLs should never be exposed directly to web content, so DataTransfer.getData should return an empty string here.
1562     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1563         @"dragover": @{ @"Files": @"", @"text/uri-list": @"" },
1564         @"drop": @{ @"Files": @"", @"text/uri-list": @"" }
1565     });
1566 }
1567
1568 TEST(DataInteractionTests, DataTransferGetDataWhenDroppingImageWithHTTPURL)
1569 {
1570     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1571     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1572     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1573
1574     auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
1575     [itemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler)
1576     {
1577         completionHandler(UIImageJPEGRepresentation(testIconImage(), 0.5), nil);
1578         return nil;
1579     }];
1580     [itemProvider registerObject:[NSURL URLWithString:@"http://webkit.org"] visibility:UIItemProviderRepresentationOptionsVisibilityAll];
1581     [simulator setExternalItemProviders:@[ itemProvider.get() ]];
1582
1583     [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)];
1584     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1585         @"dragover": @{ @"Files": @"", @"text/uri-list": @"" },
1586         @"drop": @{ @"Files": @"", @"text/uri-list": @"http://webkit.org/" }
1587     });
1588 }
1589
1590 TEST(DataInteractionTests, DataTransferGetDataWhenDroppingRespectsPresentationStyle)
1591 {
1592     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1593     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1594     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1595
1596     runTestWithTemporaryTextFile(^(NSURL *fileURL) {
1597         auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
1598         [itemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeUTF8PlainText fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[protectedFileURL = retainPtr(fileURL)] (FileLoadCompletionBlock completionHandler) -> NSProgress * {
1599             completionHandler(protectedFileURL.get(), NO, nil);
1600             return nil;
1601         }];
1602         [simulator setExternalItemProviders:@[ itemProvider.get() ]];
1603
1604         [itemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
1605         [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)];
1606         checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1607             @"dragover": @{ @"Files" : @"" },
1608             @"drop": @{ @"Files" : @"" }
1609         });
1610
1611         [itemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleInline];
1612         [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)];
1613         checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1614             @"dragover": @{ @"text/plain" : @"" },
1615             @"drop": @{ @"text/plain" : @"This is a tiny blob of text." }
1616         });
1617     });
1618 }
1619
1620 TEST(DataInteractionTests, DataTransferSetDataCannotWritePlatformTypes)
1621 {
1622     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1623     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1624     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1625
1626     [webView stringByEvaluatingJavaScript:@"select(rich)"];
1627     [webView stringByEvaluatingJavaScript:@"customData = { 'text/plain' : 'foo', 'com.adobe.pdf' : 'try and decode me!' }"];
1628     [webView stringByEvaluatingJavaScript:@"writeCustomData = true"];
1629
1630     [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
1631     checkFirstTypeIsPresentAndSecondTypeIsMissing(simulator.get(), kUTTypeUTF8PlainText, kUTTypePDF);
1632     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1633         @"dragover": @{
1634             @"text/plain": @"",
1635             @"com.adobe.pdf": @""
1636         },
1637         @"drop": @{
1638             @"text/plain": @"foo",
1639             @"com.adobe.pdf": @"try and decode me!"
1640         }
1641     });
1642 }
1643
1644 TEST(DataInteractionTests, DataTransferGetDataCannotReadPrivateArbitraryTypes)
1645 {
1646     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1647     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1648     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1649
1650     auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
1651     [itemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeMP3 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler)
1652     {
1653         completionHandler([@"this is a test" dataUsingEncoding:NSUTF8StringEncoding], nil);
1654         return nil;
1655     }];
1656     [itemProvider registerDataRepresentationForTypeIdentifier:@"org.WebKit.TestWebKitAPI.custom-pasteboard-type" visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler)
1657     {
1658         completionHandler([@"this is another test" dataUsingEncoding:NSUTF8StringEncoding], nil);
1659         return nil;
1660     }];
1661     [simulator setExternalItemProviders:@[ itemProvider.get() ]];
1662     [itemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleInline];
1663     [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)];
1664     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1665         @"dragover": @{ },
1666         @"drop": @{ }
1667     });
1668 }
1669
1670 TEST(DataInteractionTests, DataTransferSetDataValidURL)
1671 {
1672     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1673     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1674     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1675
1676     [webView stringByEvaluatingJavaScript:@"select(rich)"];
1677     [webView stringByEvaluatingJavaScript:@"customData = { 'url' : 'https://webkit.org/b/123' }"];
1678     [webView stringByEvaluatingJavaScript:@"writeCustomData = true"];
1679
1680     __block bool done = false;
1681     [simulator.get() setOverridePerformDropBlock:^NSArray<UIDragItem *> *(id <UIDropSession> session)
1682     {
1683         EXPECT_EQ(1UL, session.items.count);
1684         auto *item = session.items[0].itemProvider;
1685         EXPECT_TRUE([item.registeredTypeIdentifiers containsObject:(NSString *)kUTTypeURL]);
1686         EXPECT_TRUE([item canLoadObjectOfClass: [NSURL class]]);
1687         [item loadObjectOfClass:[NSURL class] completionHandler:^(id<NSItemProviderReading> url, NSError *error) {
1688             EXPECT_TRUE([url isKindOfClass: [NSURL class]]);
1689             EXPECT_WK_STREQ([(NSURL *)url absoluteString], @"https://webkit.org/b/123");
1690             done = true;
1691         }];
1692         return session.items;
1693     }];
1694     [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
1695
1696     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1697         @"dragover": @{
1698             @"text/uri-list": @"",
1699         },
1700         @"drop": @{
1701             @"text/uri-list": @"https://webkit.org/b/123",
1702         }
1703     });
1704     TestWebKitAPI::Util::run(&done);
1705 }
1706
1707 TEST(DataInteractionTests, DataTransferSetDataUnescapedURL)
1708 {
1709     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1710     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1711     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1712
1713     [webView stringByEvaluatingJavaScript:@"select(rich)"];
1714     [webView stringByEvaluatingJavaScript:@"customData = { 'url' : 'http://webkit.org/b/\u4F60\u597D;?x=8 + 6' }"];
1715     [webView stringByEvaluatingJavaScript:@"writeCustomData = true"];
1716
1717     __block bool done = false;
1718     [simulator.get() setOverridePerformDropBlock:^NSArray<UIDragItem *> *(id <UIDropSession> session)
1719     {
1720         EXPECT_EQ(1UL, session.items.count);
1721         auto *item = session.items[0].itemProvider;
1722         EXPECT_TRUE([item.registeredTypeIdentifiers containsObject:(NSString *)kUTTypeURL]);
1723         EXPECT_TRUE([item canLoadObjectOfClass: [NSURL class]]);
1724         [item loadObjectOfClass:[NSURL class] completionHandler:^(id<NSItemProviderReading> url, NSError *error) {
1725             EXPECT_TRUE([url isKindOfClass: [NSURL class]]);
1726             EXPECT_WK_STREQ([(NSURL *)url absoluteString], @"http://webkit.org/b/%E4%BD%A0%E5%A5%BD;?x=8%20+%206");
1727             done = true;
1728         }];
1729         return session.items;
1730     }];
1731     [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
1732
1733     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1734         @"dragover": @{
1735             @"text/uri-list": @"",
1736         },
1737         @"drop": @{
1738             @"text/uri-list": @"http://webkit.org/b/\u4F60\u597D;?x=8 + 6",
1739         }
1740     });
1741     TestWebKitAPI::Util::run(&done);
1742 }
1743
1744 TEST(DataInteractionTests, DataTransferSetDataInvalidURL)
1745 {
1746     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1747     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1748     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1749
1750     [webView stringByEvaluatingJavaScript:@"select(rich)"];
1751     [webView stringByEvaluatingJavaScript:@"customData = { 'url' : 'some random string' }"];
1752     [webView stringByEvaluatingJavaScript:@"writeCustomData = true"];
1753
1754     [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
1755     NSArray *registeredTypes = [simulator.get().sourceItemProviders.firstObject registeredTypeIdentifiers];
1756     EXPECT_FALSE([registeredTypes containsObject:(NSString *)kUTTypeURL]);
1757     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1758         @"dragover": @{
1759             @"text/uri-list": @"",
1760         },
1761         @"drop": @{
1762             @"text/uri-list": @"some random string",
1763         }
1764     });
1765 }
1766
1767 TEST(DataInteractionTests, DataTransferSanitizeHTML)
1768 {
1769     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
1770     [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
1771     auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1772
1773     [webView stringByEvaluatingJavaScript:@"select(rich)"];
1774     [webView stringByEvaluatingJavaScript:@"customData = { 'text/html' : '<meta content=\"secret\">"
1775         "<b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>' }"];
1776     [webView stringByEvaluatingJavaScript:@"writeCustomData = true"];
1777
1778     __block bool done = false;
1779     [simulator.get() setOverridePerformDropBlock:^NSArray<UIDragItem *> *(id <UIDropSession> session)
1780     {
1781         EXPECT_EQ(1UL, session.items.count);
1782         auto *item = session.items[0].itemProvider;
1783         EXPECT_TRUE([item.registeredTypeIdentifiers containsObject:(NSString *)kUTTypeHTML]);
1784         [item loadDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML completionHandler:^(NSData *data, NSError *error) {
1785             NSString *markup = [[[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding] autorelease];
1786             EXPECT_TRUE([markup containsString:@"hello"]);
1787             EXPECT_TRUE([markup containsString:@", world"]);
1788             EXPECT_FALSE([markup containsString:@"secret"]);
1789             EXPECT_FALSE([markup containsString:@"dangerousCode"]);
1790             done = true;
1791         }];
1792         return session.items;
1793     }];
1794     [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
1795
1796     checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
1797         @"dragover": @{
1798             @"text/html": @"",
1799         },
1800         @"drop": @{
1801             @"text/html": @"<meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>",
1802         }
1803     });
1804     TestWebKitAPI::Util::run(&done);
1805 }
1806
1807 #endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
1808
1809 } // namespace TestWebKitAPI
1810
1811 #endif // ENABLE(DATA_INTERACTION)