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