Links with empty hrefs should not be drag sources
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / ios / DataInteractionTests.mm
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if ENABLE(DATA_INTERACTION)
29
30 #import "DataInteractionSimulator.h"
31 #import "PlatformUtilities.h"
32 #import "TestWKWebView.h"
33 #import "WKWebViewConfigurationExtras.h"
34 #import <MobileCoreServices/MobileCoreServices.h>
35 #import <UIKit/UIItemProvider_Private.h>
36 #import <WebKit/WKPreferencesPrivate.h>
37 #import <WebKit/WKWebViewConfigurationPrivate.h>
38
39 @implementation TestWKWebView (DataInteractionTests)
40
41 - (BOOL)editorContainsImageElement
42 {
43     return [self stringByEvaluatingJavaScript:@"!!editor.querySelector('img')"].boolValue;
44 }
45
46 - (NSString *)editorValue
47 {
48     return [self stringByEvaluatingJavaScript:@"editor.value"];
49 }
50
51 @end
52
53 static NSValue *makeCGRectValue(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
54 {
55     return [NSValue valueWithCGRect:CGRectMake(x, y, width, height)];
56 }
57
58 namespace TestWebKitAPI {
59
60 TEST(DataInteractionTests, ImageToContentEditable)
61 {
62     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
63     [webView synchronouslyLoadTestPageNamed:@"image-and-contenteditable"];
64
65     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
66     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
67
68     EXPECT_TRUE([webView editorContainsImageElement]);
69
70     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
71     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
72     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
73     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
74     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:@[ makeCGRectValue(1, 201, 215, 174) ]]);
75 }
76
77 TEST(DataInteractionTests, ImageToTextarea)
78 {
79     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
80     [webView synchronouslyLoadTestPageNamed:@"image-and-textarea"];
81
82     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
83     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
84
85     NSURL *imageURL = [NSURL fileURLWithPath:[webView editorValue]];
86     EXPECT_WK_STREQ("icon.png", imageURL.lastPathComponent);
87
88     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
89     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
90     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
91     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
92
93     NSArray *expectedSelectionRects = [NSArray arrayWithObjects:makeCGRectValue(6, 203, 188, 14), makeCGRectValue(6, 217, 188, 14), makeCGRectValue(6, 231, 66, 14), nil];
94     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:expectedSelectionRects]);
95 }
96
97 TEST(DataInteractionTests, ContentEditableToContentEditable)
98 {
99     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
100     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
101
102     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
103     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
104
105     EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.textContent"].length, 0UL);
106     EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String);
107
108     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
109     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
110     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
111     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
112     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:@[ makeCGRectValue(1, 201, 961, 227) ]]);
113 }
114
115 TEST(DataInteractionTests, ContentEditableToTextarea)
116 {
117     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
118     [webView synchronouslyLoadTestPageNamed:@"contenteditable-and-textarea"];
119
120     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
121     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
122
123     EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.textContent"].length, 0UL);
124     EXPECT_WK_STREQ("Hello world", [webView editorValue].UTF8String);
125
126     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
127     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
128     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
129     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
130     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:@[ makeCGRectValue(6, 203, 990, 232) ]]);
131 }
132
133 TEST(DataInteractionTests, LinkToInput)
134 {
135     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
136     [webView synchronouslyLoadTestPageNamed:@"link-and-input"];
137
138     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
139     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
140
141     EXPECT_WK_STREQ("https://www.daringfireball.net/", [webView editorValue].UTF8String);
142
143     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
144     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
145     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
146     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
147     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:@[ makeCGRectValue(101, 273, 2613, 232) ]]);
148 }
149
150 TEST(DataInteractionTests, BackgroundImageLinkToInput)
151 {
152     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
153     [webView synchronouslyLoadTestPageNamed:@"background-image-link-and-input"];
154
155     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
156     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
157
158     EXPECT_WK_STREQ("https://www.daringfireball.net/", [webView editorValue].UTF8String);
159
160     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
161     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
162     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
163     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
164     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:@[ makeCGRectValue(101, 241, 2613, 232) ]]);
165 }
166
167 TEST(DataInteractionTests, CanPreventStart)
168 {
169     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
170     [webView synchronouslyLoadTestPageNamed:@"prevent-start"];
171
172     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
173     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
174
175     EXPECT_EQ(DataInteractionCancelled, [dataInteractionSimulator phase]);
176     EXPECT_FALSE([webView editorContainsImageElement]);
177
178     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
179     EXPECT_FALSE([observedEventNames containsObject:DataInteractionEnterEventName]);
180     EXPECT_FALSE([observedEventNames containsObject:DataInteractionOverEventName]);
181     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:@[ ]]);
182 }
183
184 TEST(DataInteractionTests, CanPreventOperation)
185 {
186     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
187     [webView synchronouslyLoadTestPageNamed:@"prevent-operation"];
188
189     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
190     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
191
192     EXPECT_FALSE([webView editorContainsImageElement]);
193
194     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
195     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
196     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
197     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:@[ ]]);
198 }
199
200 TEST(DataInteractionTests, EnterAndLeaveEvents)
201 {
202     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
203     [webView synchronouslyLoadTestPageNamed:@"link-and-input"];
204
205     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
206     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 450)];
207
208     EXPECT_WK_STREQ("", [webView editorValue].UTF8String);
209
210     NSArray *observedEventNames = [dataInteractionSimulator observedEventNames];
211     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
212     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
213     EXPECT_TRUE([observedEventNames containsObject:DataInteractionLeaveEventName]);
214     EXPECT_FALSE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
215     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:@[ ]]);
216 }
217
218 TEST(DataInteractionTests, ExternalSourceUTF8PlainTextOnly)
219 {
220     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
221     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
222
223     NSString *textPayload = @"Ceci n'est pas une string";
224     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
225     RetainPtr<UIItemProvider> simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
226     [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeUTF8PlainText options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
227     {
228         completionBlock([textPayload dataUsingEncoding:NSUTF8StringEncoding], nil);
229         return [NSProgress discreteProgressWithTotalUnitCount:100];
230     }];
231     [dataInteractionSimulator setExternalItemProvider:simulatedItemProvider.get()];
232     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
233     EXPECT_WK_STREQ(textPayload.UTF8String, [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String);
234     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:@[ makeCGRectValue(1, 201, 1936, 227) ]]);
235 }
236
237 TEST(DataInteractionTests, ExternalSourceJPEGOnly)
238 {
239     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
240     [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"];
241
242     UIImage *testImage = [UIImage imageNamed:@"TestWebKitAPI.resources/icon.png"];
243     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
244     RetainPtr<UIItemProvider> simulatedItemProvider = adoptNS([[UIItemProvider alloc] init]);
245     [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJPEG options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
246     {
247         completionBlock(UIImageJPEGRepresentation(testImage, 0.5), nil);
248         return [NSProgress discreteProgressWithTotalUnitCount:100];
249     }];
250     [dataInteractionSimulator setExternalItemProvider:simulatedItemProvider.get()];
251     [dataInteractionSimulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)];
252     EXPECT_TRUE([webView editorContainsImageElement]);
253     EXPECT_TRUE([[dataInteractionSimulator finalSelectionRects] isEqualToArray:@[ makeCGRectValue(1, 201, 215, 174) ]]);
254 }
255
256 TEST(DataInteractionTests, AttachmentElementItemProviders)
257 {
258     RetainPtr<WKWebViewConfiguration> configuration = [WKWebViewConfiguration testwebkitapi_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"];
259     [configuration _setAttachmentElementEnabled:YES];
260     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get()]);
261     [webView synchronouslyLoadTestPageNamed:@"attachment-element"];
262
263     NSString *injectedTypeIdentifier = @"org.webkit.data";
264     __block RetainPtr<NSString> injectedString;
265     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
266     [dataInteractionSimulator setConvertItemProvidersBlock:^NSArray *(NSArray *originalItemProviders)
267     {
268         for (UIItemProvider *provider in originalItemProviders) {
269             NSData *injectedData = [provider copyDataRepresentationForTypeIdentifier:injectedTypeIdentifier error:nil];
270             if (!injectedData.length)
271                 continue;
272             injectedString = adoptNS([[NSString alloc] initWithData:injectedData encoding:NSUTF8StringEncoding]);
273             break;
274         }
275         return originalItemProviders;
276     }];
277
278     [dataInteractionSimulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 400)];
279
280     EXPECT_WK_STREQ("hello", [injectedString UTF8String]);
281 }
282
283 TEST(DataInteractionTests, LargeImageToTargetDiv)
284 {
285     auto testWebViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
286     [[testWebViewConfiguration preferences] _setLargeImageAsyncDecodingEnabled:NO];
287
288     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:testWebViewConfiguration.get()]);
289     [webView synchronouslyLoadTestPageNamed:@"div-and-large-image"];
290
291     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
292     [dataInteractionSimulator runFrom:CGPointMake(200, 400) to:CGPointMake(200, 150)];
293     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"].UTF8String);
294 }
295
296 TEST(DataInteractionTests, LinkWithEmptyHREF)
297 {
298     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
299     [webView synchronouslyLoadTestPageNamed:@"link-and-input"];
300     [webView stringByEvaluatingJavaScript:@"document.querySelector('a').href = ''"];
301
302     RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
303     [dataInteractionSimulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
304
305     EXPECT_EQ(DataInteractionCancelled, [dataInteractionSimulator phase]);
306     EXPECT_WK_STREQ("", [webView editorValue].UTF8String);
307 }
308
309 } // namespace TestWebKitAPI
310
311 #endif // ENABLE(DATA_INTERACTION)