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