File inputs only accept UTI types that can be inserted into contenteditable areas...
[WebKit.git] / Tools / TestWebKitAPI / Tests / ios / DataInteractionTests.mm
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if ENABLE(DATA_INTERACTION)
29
30 #import "DataInteractionSimulator.h"
31 #import "PlatformUtilities.h"
32 #import "TestWKWebView.h"
33 #import "WKWebViewConfigurationExtras.h"
34 #import <MobileCoreServices/MobileCoreServices.h>
35 #import <UIKit/NSURL+UIItemProvider.h>
36 #import <UIKit/UIItemProvider_Private.h>
37 #import <WebKit/WKPreferencesPrivate.h>
38 #import <WebKit/WKWebViewConfigurationPrivate.h>
39 #import <WebKit/_WKProcessPoolConfiguration.h>
40
41 typedef void (^FileLoadCompletionBlock)(NSURL *, BOOL, NSError *);
42 typedef void (^DataLoadCompletionBlock)(NSData *, NSError *);
43
44 static UIImage *testIconImage()
45 {
46     return [UIImage imageNamed:@"TestWebKitAPI.resources/icon.png"];
47 }
48
49 static NSURL *temporaryURLForDataInteractionFileLoad(NSString *temporaryFileName)
50 {
51     NSString *temporaryDirectoryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"data-interaction"];
52     if (![[NSFileManager defaultManager] fileExistsAtPath:temporaryDirectoryPath])
53         [[NSFileManager defaultManager] createDirectoryAtPath:temporaryDirectoryPath withIntermediateDirectories:YES attributes:nil error:nil];
54     return [NSURL fileURLWithPath:[temporaryDirectoryPath stringByAppendingPathComponent:temporaryFileName]];
55 }
56
57 static void cleanUpDataInteractionTemporaryPath()
58 {
59     NSArray *temporaryDirectoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:[NSURL fileURLWithPath:NSTemporaryDirectory()] includingPropertiesForKeys:nil options:0 error:nil];
60     for (NSURL *url in temporaryDirectoryContents) {
61         if ([url.lastPathComponent rangeOfString:@"data-interaction"].location != NSNotFound)
62             [[NSFileManager defaultManager] removeItemAtURL:url error:nil];
63     }
64 }
65
66 @implementation UIItemProvider (DataInteractionTests)
67
68 - (void)registerFileRepresentationForTypeIdentifier:(NSString *)typeIdentifier withData:(NSData *)data filename:(NSString *)filename
69 {
70     RetainPtr<NSData> retainedData = data;
71     RetainPtr<NSURL> retainedTemporaryURL = temporaryURLForDataInteractionFileLoad(filename);
72     [self registerFileRepresentationForTypeIdentifier:typeIdentifier fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler: [retainedData, retainedTemporaryURL] (FileLoadCompletionBlock block) -> NSProgress * {
73         [retainedData writeToFile:[retainedTemporaryURL path] atomically:YES];
74         dispatch_async(dispatch_get_main_queue(), [retainedTemporaryURL, capturedBlock = makeBlockPtr(block)] {
75             capturedBlock(retainedTemporaryURL.get(), NO, nil);
76         });
77         return nil;
78     }];
79 }
80
81 - (void)registerDataRepresentationForTypeIdentifier:(NSString *)typeIdentifier withData:(NSData *)data
82 {
83     RetainPtr<NSData> retainedData = data;
84     [self registerDataRepresentationForTypeIdentifier:typeIdentifier visibility:NSItemProviderRepresentationVisibilityAll loadHandler: [retainedData] (DataLoadCompletionBlock block) -> NSProgress * {
85         block(retainedData.get(), nil);
86         return [NSProgress discreteProgressWithTotalUnitCount:100];
87     }];
88 }
89
90 @end
91
92 @implementation TestWKWebView (DataInteractionTests)
93
94 - (BOOL)editorContainsImageElement
95 {
96     return [self stringByEvaluatingJavaScript:@"!!editor.querySelector('img')"].boolValue;
97 }
98
99 - (NSString *)editorValue
100 {
101     return [self stringByEvaluatingJavaScript:@"editor.value"];
102 }
103
104 @end
105
106 static NSValue *makeCGRectValue(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
107 {
108     return [NSValue valueWithCGRect:CGRectMake(x, y, width, height)];
109 }
110
111 static void checkSelectionRectsWithLogging(NSArray *expected, NSArray *observed)
112 {
113     if (![expected isEqualToArray:observed])
114         NSLog(@"Expected selection rects: %@ but observed: %@", expected, observed);
115     EXPECT_TRUE([expected isEqualToArray:observed]);
116 }
117
118 static void checkTypeIdentifierPrecedesOtherTypeIdentifier(DataInteractionSimulator *simulator, NSString *firstType, NSString *secondType)
119 {
120     NSArray *registeredTypes = [simulator.sourceItemProviders.firstObject registeredTypeIdentifiers];
121     EXPECT_TRUE([registeredTypes containsObject:firstType]);
122     EXPECT_TRUE([registeredTypes containsObject:secondType]);
123     EXPECT_TRUE([registeredTypes indexOfObject:firstType] < [registeredTypes indexOfObject:secondType]);
124 }
125
126 namespace TestWebKitAPI {
127
128 TEST(DataInteractionTests, ImageToContentEditable)
129 {
130     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
131     [webView synchronouslyLoadTestPageNamed:@"image-and-contenteditable"];
132
133     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
134     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
135
136     EXPECT_TRUE([webView editorContainsImageElement]);
137
138     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
139     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
140     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
141     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
142     checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 215, 174) ], [dataInteractionSimulator finalSelectionRects]);
143     checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypePNG, (NSString *)kUTTypeFileURL);
144 }
145
146 TEST(DataInteractionTests, ImageToTextarea)
147 {
148     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
149     [webView synchronouslyLoadTestPageNamed:@"image-and-textarea"];
150
151     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
152     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
153
154     NSURL *imageURL = [NSURL fileURLWithPath:[webView editorValue]];
155     EXPECT_WK_STREQ("icon.png", imageURL.lastPathComponent);
156
157     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
158     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
159     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
160     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
161
162     checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypePNG, (NSString *)kUTTypeFileURL);
163 }
164
165 TEST(DataInteractionTests, ImageInLinkToInput)
166 {
167     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
168     [webView synchronouslyLoadTestPageNamed:@"image-in-link-and-input"];
169
170     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
171     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
172
173     EXPECT_WK_STREQ("https://www.apple.com/", [webView editorValue].UTF8String);
174     checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 241, 2057, 232) ], [dataInteractionSimulator finalSelectionRects]);
175 }
176
177 TEST(DataInteractionTests, ImageInLinkWithoutHREFToInput)
178 {
179     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
180     [webView synchronouslyLoadTestPageNamed:@"image-in-link-and-input"];
181     [webView stringByEvaluatingJavaScript:@"link.href = ''"];
182
183     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
184     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
185
186     NSURL *imageURL = [NSURL fileURLWithPath:[webView editorValue]];
187     EXPECT_WK_STREQ("icon.png", imageURL.lastPathComponent);
188 }
189
190 TEST(DataInteractionTests, ContentEditableToContentEditable)
191 {
192     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
193     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
194
195     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
196     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
197
198     EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.textContent"].length, 0UL);
199     EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String);
200
201     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
202     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
203     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
204     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
205     checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 961, 227) ], [dataInteractionSimulator finalSelectionRects]);
206     checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypeRTFD, (NSString *)kUTTypeUTF8PlainText);
207 }
208
209 TEST(DataInteractionTests, ContentEditableToTextarea)
210 {
211     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
212     [webView synchronouslyLoadTestPageNamed:@"contenteditable-and-textarea"];
213
214     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
215     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
216
217     EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.textContent"].length, 0UL);
218     EXPECT_WK_STREQ("Hello world", [webView editorValue].UTF8String);
219
220     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
221     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
222     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
223     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
224     checkSelectionRectsWithLogging(@[ makeCGRectValue(6, 203, 990, 232) ], [dataInteractionSimulator finalSelectionRects]);
225     checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypeRTFD, (NSString *)kUTTypeUTF8PlainText);
226 }
227
228 TEST(DataInteractionTests, TextAreaToInput)
229 {
230     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
231     [webView synchronouslyLoadTestPageNamed:@"textarea-to-input"];
232
233     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
234     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
235
236     EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.value"].length, 0UL);
237     EXPECT_WK_STREQ("Hello world", [webView editorValue].UTF8String);
238     checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 241, 990, 232) ], [dataInteractionSimulator finalSelectionRects]);
239 }
240
241 TEST(DataInteractionTests, LinkToInput)
242 {
243     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
244     [webView synchronouslyLoadTestPageNamed:@"link-and-input"];
245
246     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
247     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
248
249     EXPECT_WK_STREQ("https://www.apple.com/", [webView editorValue].UTF8String);
250
251     __block bool doneLoadingURL = false;
252     UIItemProvider *sourceItemProvider = [dataInteractionSimulator sourceItemProviders].firstObject;
253     [sourceItemProvider loadObjectOfClass:[NSURL class] completionHandler:^(NSURL *url, NSError *error) {
254         EXPECT_WK_STREQ("Hello world", url._title.UTF8String ?: "");
255         doneLoadingURL = true;
256     }];
257     TestWebKitAPI::Util::run(&doneLoadingURL);
258
259     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
260     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
261     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
262     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
263     checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 273, 2057, 232) ], [dataInteractionSimulator finalSelectionRects]);
264     checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypeURL, (NSString *)kUTTypeUTF8PlainText);
265 }
266
267 TEST(DataInteractionTests, BackgroundImageLinkToInput)
268 {
269     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
270     [webView synchronouslyLoadTestPageNamed:@"background-image-link-and-input"];
271
272     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
273     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
274
275     EXPECT_WK_STREQ("https://www.apple.com/", [webView editorValue].UTF8String);
276
277     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
278     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
279     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
280     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
281     checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 241, 2057, 232) ], [dataInteractionSimulator finalSelectionRects]);
282     checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypeURL, (NSString *)kUTTypeUTF8PlainText);
283 }
284
285 TEST(DataInteractionTests, CanPreventStart)
286 {
287     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
288     [webView synchronouslyLoadTestPageNamed:@"prevent-start"];
289
290     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
291     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
292
293     EXPECT_EQ(DataInteractionCancelled, [dataInteractionSimulator phase]);
294     EXPECT_FALSE([webView editorContainsImageElement]);
295
296     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
297     EXPECT_FALSE([observedEventNames containsObject:DataInteractionEnterEventName]);
298     EXPECT_FALSE([observedEventNames containsObject:DataInteractionOverEventName]);
299     checkSelectionRectsWithLogging(@[ ], [dataInteractionSimulator finalSelectionRects]);
300 }
301
302 TEST(DataInteractionTests, CanPreventOperation)
303 {
304     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
305     [webView synchronouslyLoadTestPageNamed:@"prevent-operation"];
306
307     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
308     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
309
310     EXPECT_FALSE([webView editorContainsImageElement]);
311
312     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
313     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
314     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
315     checkSelectionRectsWithLogging(@[ ], [dataInteractionSimulator finalSelectionRects]);
316 }
317
318 TEST(DataInteractionTests, EnterAndLeaveEvents)
319 {
320     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
321     [webView synchronouslyLoadTestPageNamed:@"link-and-input"];
322
323     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
324     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 450)];
325
326     EXPECT_WK_STREQ("", [webView editorValue].UTF8String);
327
328     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
329     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
330     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
331     EXPECT_TRUE([observedEventNames containsObject:DataInteractionLeaveEventName]);
332     EXPECT_FALSE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
333     checkSelectionRectsWithLogging(@[ ], [dataInteractionSimulator finalSelectionRects]);
334 }
335
336 TEST(DataInteractionTests, ExternalSourceJSONToFileInput)
337 {
338     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
339     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
340
341     RetainPtr<UIItemProvider> simulatedJSONItemProvider = adoptNS([[UIItemProvider alloc] init]);
342     NSData *jsonData = [@"{ \"foo\": \"bar\",  \"bar\": \"baz\" }" dataUsingEncoding:NSUTF8StringEncoding];
343     [simulatedJSONItemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeJSON withData:jsonData filename:@"data.json"];
344
345     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
346     [dataInteractionSimulator setExternalItemProviders:@[ simulatedJSONItemProvider.get() ]];
347     [dataInteractionSimulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)];
348
349     EXPECT_WK_STREQ("application/json", [webView stringByEvaluatingJavaScript:@"output.value"]);
350     cleanUpDataInteractionTemporaryPath();
351 }
352
353 TEST(DataInteractionTests, ExternalSourceImageToFileInput)
354 {
355     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
356     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
357
358     RetainPtr<UIItemProvider> simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
359     NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5);
360     [simulatedImageItemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG withData:imageData filename:@"image.png"];
361
362     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
363     [dataInteractionSimulator setExternalItemProviders:@[ simulatedImageItemProvider.get() ]];
364     [dataInteractionSimulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)];
365
366     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
367     EXPECT_WK_STREQ("image/jpeg", outputValue.UTF8String);
368
369     cleanUpDataInteractionTemporaryPath();
370 }
371
372 TEST(DataInteractionTests, ExternalSourceHTMLToUploadArea)
373 {
374     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
375     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
376
377     RetainPtr<UIItemProvider> simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
378     NSData *htmlData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
379     [simulatedHTMLItemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:htmlData filename:@"index.html"];
380
381     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
382     [dataInteractionSimulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get() ]];
383     [dataInteractionSimulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)];
384
385     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
386     EXPECT_WK_STREQ("text/html", outputValue.UTF8String);
387
388     cleanUpDataInteractionTemporaryPath();
389 }
390
391 TEST(DataInteractionTests, ExternalSourceImageAndHTMLToSingleFileInput)
392 {
393     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
394     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
395
396     RetainPtr<UIItemProvider> simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
397     NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5);
398     [simulatedImageItemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG withData:imageData filename:@"image.png"];
399
400     RetainPtr<UIItemProvider> simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
401     NSData *htmlData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
402     [simulatedHTMLItemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:htmlData filename:@"index.html"];
403
404     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
405     [dataInteractionSimulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get(), simulatedImageItemProvider.get() ]];
406     [dataInteractionSimulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)];
407
408     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
409     EXPECT_WK_STREQ("", outputValue.UTF8String);
410
411     cleanUpDataInteractionTemporaryPath();
412 }
413
414 TEST(DataInteractionTests, ExternalSourceImageAndHTMLToMultipleFileInput)
415 {
416     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
417     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
418     [webView stringByEvaluatingJavaScript:@"input.setAttribute('multiple', '')"];
419
420     RetainPtr<UIItemProvider> simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
421     NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5);
422     [simulatedImageItemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG withData:imageData filename:@"image.png"];
423
424     RetainPtr<UIItemProvider> simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
425     NSData *htmlData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
426     [simulatedHTMLItemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:htmlData filename:@"index.html"];
427
428     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
429     [dataInteractionSimulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get(), simulatedImageItemProvider.get() ]];
430     [dataInteractionSimulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)];
431
432     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
433     EXPECT_WK_STREQ("image/jpeg, text/html", outputValue.UTF8String);
434
435     cleanUpDataInteractionTemporaryPath();
436 }
437
438 TEST(DataInteractionTests, ExternalSourceImageAndHTMLToUploadArea)
439 {
440     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
441     [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
442
443     RetainPtr<UIItemProvider> simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
444     NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5);
445     [simulatedImageItemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG withData:imageData filename:@"image.png"];
446
447     RetainPtr<UIItemProvider> firstSimulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
448     NSData *firstHTMLData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
449     [firstSimulatedHTMLItemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:firstHTMLData filename:@"index.html"];
450
451     RetainPtr<UIItemProvider> secondSimulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
452     NSData *secondHTMLData = [@"<html><body>hello world</body></html>" dataUsingEncoding:NSUTF8StringEncoding];
453     [secondSimulatedHTMLItemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:secondHTMLData filename:@"index.html"];
454
455     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
456     [dataInteractionSimulator setExternalItemProviders:@[ simulatedImageItemProvider.get(), firstSimulatedHTMLItemProvider.get(), secondSimulatedHTMLItemProvider.get() ]];
457     [dataInteractionSimulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)];
458
459     NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"];
460     EXPECT_WK_STREQ("image/jpeg, text/html, text/html", outputValue.UTF8String);
461
462     cleanUpDataInteractionTemporaryPath();
463 }
464
465 TEST(DataInteractionTests, ExternalSourceUTF8PlainTextOnly)
466 {
467     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
468     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
469
470     NSString *textPayload = @"Ceci n'est pas une string";
471     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
472     RetainPtr<UIItemProvider> simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
473     [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeUTF8PlainText options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
474     {
475         completionBlock([textPayload dataUsingEncoding:NSUTF8StringEncoding], nil);
476         return [NSProgress discreteProgressWithTotalUnitCount:100];
477     }];
478     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider.get() ]];
479     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
480     EXPECT_WK_STREQ(textPayload.UTF8String, [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String);
481     checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 1936, 227) ], [dataInteractionSimulator finalSelectionRects]);
482 }
483
484 TEST(DataInteractionTests, ExternalSourceJPEGOnly)
485 {
486     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
487     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
488
489     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
490     RetainPtr<UIItemProvider> simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
491     [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJPEG options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
492     {
493         completionBlock(UIImageJPEGRepresentation(testIconImage(), 0.5), nil);
494         return [NSProgress discreteProgressWithTotalUnitCount:100];
495     }];
496     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider.get() ]];
497     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
498     EXPECT_TRUE([webView editorContainsImageElement]);
499     checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 215, 174) ], [dataInteractionSimulator finalSelectionRects]);
500 }
501
502 TEST(DataInteractionTests, OverrideDataInteractionOperation)
503 {
504     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
505     [webView synchronouslyLoadTestPageNamed:@"simple"];
506
507     RetainPtr<UIItemProvider> simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
508     [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:[@"<body></body>" dataUsingEncoding:NSUTF8StringEncoding]];
509
510     __block bool finishedLoadingData = false;
511     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
512     [dataInteractionSimulator setExternalItemProviders:@[ simulatedItemProvider.get() ]];
513     [dataInteractionSimulator setOverrideDataInteractionOperationBlock:^NSUInteger(NSUInteger operation, id session)
514     {
515         EXPECT_EQ(0U, operation);
516         return 1;
517     }];
518     [dataInteractionSimulator setDataInteractionOperationCompletionBlock:^(BOOL handled, NSArray *itemProviders) {
519         EXPECT_FALSE(handled);
520         [itemProviders.firstObject loadDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML completionHandler:^(NSData *data, NSError *error) {
521             NSString *text = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
522             EXPECT_WK_STREQ("<body></body>", text.UTF8String);
523             EXPECT_FALSE(!!error);
524             finishedLoadingData = true;
525         }];
526     }];
527     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
528     TestWebKitAPI::Util::run(&finishedLoadingData);
529 }
530
531 TEST(DataInteractionTests, AttachmentElementItemProviders)
532 {
533     RetainPtr<WKWebViewConfiguration> configuration = [WKWebViewConfiguration testwebkitapi_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"];
534     [configuration _setAttachmentElementEnabled:YES];
535     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get()]);
536     [webView synchronouslyLoadTestPageNamed:@"attachment-element"];
537
538     NSString *injectedTypeIdentifier = @"org.webkit.data";
539     __block RetainPtr<NSString> injectedString;
540     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
541     [dataInteractionSimulator setConvertItemProvidersBlock:^NSArray *(NSArray *originalItemProviders)
542     {
543         for (UIItemProvider *provider in originalItemProviders) {
544             NSData *injectedData = [provider copyDataRepresentationForTypeIdentifier:injectedTypeIdentifier error:nil];
545             if (!injectedData.length)
546                 continue;
547             injectedString = adoptNS([[NSString alloc] initWithData:injectedData encoding:NSUTF8StringEncoding]);
548             break;
549         }
550         return originalItemProviders;
551     }];
552
553     [dataInteractionSimulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 400)];
554
555     EXPECT_WK_STREQ("hello", [injectedString UTF8String]);
556 }
557
558 TEST(DataInteractionTests, LargeImageToTargetDiv)
559 {
560     auto testWebViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
561     [[testWebViewConfiguration preferences] _setLargeImageAsyncDecodingEnabled:NO];
562
563     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:testWebViewConfiguration.get()]);
564     [webView synchronouslyLoadTestPageNamed:@"div-and-large-image"];
565
566     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
567     [dataInteractionSimulator runFrom:CGPointMake(200, 400) to:CGPointMake(200, 150)];
568     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"].UTF8String);
569     checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypePNG, (NSString *)kUTTypeFileURL);
570 }
571
572 TEST(DataInteractionTests, LinkWithEmptyHREF)
573 {
574     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
575     [webView synchronouslyLoadTestPageNamed:@"link-and-input"];
576     [webView stringByEvaluatingJavaScript:@"document.querySelector('a').href = ''"];
577
578     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
579     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
580
581     EXPECT_EQ(DataInteractionCancelled, [dataInteractionSimulator phase]);
582     EXPECT_WK_STREQ("", [webView editorValue].UTF8String);
583 }
584
585 TEST(DataInteractionTests, CustomActionSheetPopover)
586 {
587     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
588     [webView synchronouslyLoadTestPageNamed:@"link-and-target-div"];
589
590     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
591     [dataInteractionSimulator setShouldEnsureUIApplication:YES];
592
593     __block BOOL didInvokeCustomActionSheet = NO;
594     [dataInteractionSimulator setShowCustomActionSheetBlock:^BOOL(_WKActivatedElementInfo *element)
595     {
596         EXPECT_EQ(_WKActivatedElementTypeLink, element.type);
597         EXPECT_WK_STREQ("Hello world", element.title.UTF8String);
598         didInvokeCustomActionSheet = YES;
599         return YES;
600     }];
601     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
602     EXPECT_TRUE(didInvokeCustomActionSheet);
603     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"].UTF8String);
604 }
605
606 TEST(DataInteractionTests, UnresponsivePageDoesNotHangUI)
607 {
608     _WKProcessPoolConfiguration *processPoolConfiguration = [[[_WKProcessPoolConfiguration alloc] init] autorelease];
609     processPoolConfiguration.ignoreSynchronousMessagingTimeoutsForTesting = YES;
610
611     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:[[[WKWebViewConfiguration alloc] init] autorelease] processPoolConfiguration:processPoolConfiguration]);
612     [webView synchronouslyLoadTestPageNamed:@"simple"];
613     [webView evaluateJavaScript:@"while(1);" completionHandler:nil];
614
615     // The test passes if we can prepare for data interaction without timing out.
616     [webView _simulatePrepareForDataInteractionSession:nil completion:^() { }];
617 }
618
619 } // namespace TestWebKitAPI
620
621 #endif // ENABLE(DATA_INTERACTION)