[iOS 11.3 Beta] Can't copy a URL from Safari and paste it into the Gmail app
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / ios / ActionSheetTests.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 PLATFORM(IOS)
29
30 #import "InstanceMethodSwizzler.h"
31 #import "PlatformUtilities.h"
32 #import "TestWKWebView.h"
33 #import "UIKitSPI.h"
34 #import <MobileCoreServices/MobileCoreServices.h>
35 #import <WebKit/WKUIDelegatePrivate.h>
36 #import <WebKit/WKWebViewPrivate.h>
37 #import <WebKit/_WKActivatedElementInfo.h>
38 #import <WebKit/_WKElementAction.h>
39 #import <wtf/BlockPtr.h>
40 #import <wtf/RetainPtr.h>
41 #import <wtf/SoftLinking.h>
42
43 @interface ActionSheetObserver : NSObject<WKUIDelegatePrivate>
44 @property (nonatomic) BlockPtr<NSArray *(_WKActivatedElementInfo *, NSArray *)> presentationHandler;
45 @property (nonatomic) BlockPtr<NSDictionary *(void)> dataDetectionContextHandler;
46 @end
47
48 @implementation ActionSheetObserver
49
50 - (NSArray *)_webView:(WKWebView *)webView actionsForElement:(_WKActivatedElementInfo *)element defaultActions:(NSArray<_WKElementAction *> *)defaultActions
51 {
52     return _presentationHandler ? _presentationHandler(element, defaultActions) : defaultActions;
53 }
54
55 - (NSDictionary *)_dataDetectionContextForWebView:(WKWebView *)WebView
56 {
57     return _dataDetectionContextHandler ? _dataDetectionContextHandler() : @{ };
58 }
59
60 @end
61
62 namespace TestWebKitAPI {
63
64 class IPadUserInterfaceSwizzler {
65 public:
66     IPadUserInterfaceSwizzler()
67         : m_swizzler([UIDevice class], @selector(userInterfaceIdiom), reinterpret_cast<IMP>(padUserInterfaceIdiom))
68     {
69     }
70 private:
71     static UIUserInterfaceIdiom padUserInterfaceIdiom()
72     {
73         return UIUserInterfaceIdiomPad;
74     }
75     InstanceMethodSwizzler m_swizzler;
76 };
77
78 TEST(ActionSheetTests, ImageMapDoesNotDestroySelection)
79 {
80     IPadUserInterfaceSwizzler iPadUserInterface;
81
82     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)]);
83     auto observer = adoptNS([[ActionSheetObserver alloc] init]);
84     [webView setUIDelegate:observer.get()];
85     [webView synchronouslyLoadTestPageNamed:@"image-map"];
86     [webView stringByEvaluatingJavaScript:@"selectTextNode(h1.childNodes[0])"];
87
88     EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]);
89
90     __block bool done = false;
91     [observer setPresentationHandler:^(_WKActivatedElementInfo *element, NSArray *actions) {
92         done = true;
93         return actions;
94     }];
95     [webView _simulateLongPressActionAtLocation:CGPointMake(200, 200)];
96     TestWebKitAPI::Util::run(&done);
97
98     EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]);
99 }
100
101 TEST(ActionSheetTests, DataDetectorsLinkIsNotPresentedAsALink)
102 {
103     IPadUserInterfaceSwizzler iPadUserInterface;
104
105     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)]);
106     auto observer = adoptNS([[ActionSheetObserver alloc] init]);
107     [webView setUIDelegate:observer.get()];
108
109     auto runTest = ^(NSString *phoneNumber) {
110         [webView synchronouslyLoadHTMLString:[NSString stringWithFormat:@"<a style='position: absolute; top: 0; left: 0;' href='tel:%@'>telephone number</a>", phoneNumber]];
111
112         __block bool done = false;
113         __block bool succeeded = true;
114
115         // We shouldn't present a normal action sheet, but instead a data detectors sheet.
116         [observer setDataDetectionContextHandler:^{
117             done = true;
118             return @{ };
119         }];
120         [observer setPresentationHandler:^(_WKActivatedElementInfo *, NSArray *) {
121             done = true;
122             succeeded = false;
123             return @[ ];
124         }];
125         [webView _simulateLongPressActionAtLocation:CGPointMake(5, 5)];
126         TestWebKitAPI::Util::run(&done);
127
128         return succeeded;
129     };
130
131     EXPECT_TRUE(runTest(@"4089961010"));
132     EXPECT_TRUE(runTest(@"408 996 1010"));
133     EXPECT_TRUE(runTest(@"1-866-MY-APPLE"));
134     EXPECT_TRUE(runTest(@"(123) 456 - 7890"));
135     EXPECT_TRUE(runTest(@"01 23 45 67 00"));
136     EXPECT_TRUE(runTest(@"+33 (0)1 23 34 45 56"));
137     EXPECT_TRUE(runTest(@"(0)1 12 23 34 56"));
138     EXPECT_TRUE(runTest(@"010-1-800-MY-APPLE"));
139     EXPECT_TRUE(runTest(@"+1-408-1234567"));
140     EXPECT_TRUE(runTest(@"08080808080"));
141 }
142
143 static void presentActionSheetAndChooseAction(WKWebView *webView, ActionSheetObserver *observer, CGPoint location, _WKElementActionType actionType)
144 {
145     __block RetainPtr<_WKElementAction> copyAction;
146     __block RetainPtr<_WKActivatedElementInfo> copyElement;
147     __block bool done = false;
148     [observer setPresentationHandler:^(_WKActivatedElementInfo *element, NSArray *actions) {
149         copyElement = element;
150         for (_WKElementAction *action in actions) {
151             if (action.type == actionType)
152                 copyAction = action;
153         }
154         done = true;
155         return @[ copyAction.get() ];
156     }];
157     [webView _simulateLongPressActionAtLocation:location];
158     TestWebKitAPI::Util::run(&done);
159
160     EXPECT_TRUE(!!copyAction);
161     EXPECT_TRUE(!!copyElement);
162     [copyAction runActionWithElementInfo:copyElement.get()];
163 }
164
165 TEST(ActionSheetTests, CopyImageElementWithHREFAndTitle)
166 {
167     UIApplicationInitialize();
168     [UIPasteboard generalPasteboard].items = @[ ];
169
170     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
171     auto observer = adoptNS([[ActionSheetObserver alloc] init]);
172     [webView setUIDelegate:observer.get()];
173     [webView synchronouslyLoadTestPageNamed:@"image-in-link-and-input"];
174     [webView stringByEvaluatingJavaScript:@"document.querySelector('a').setAttribute('title', 'hello world')"];
175
176     presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 50), _WKElementActionTypeCopy);
177
178     __block bool done = false;
179     __block RetainPtr<NSItemProvider> itemProvider;
180     [webView _doAfterNextPresentationUpdate:^() {
181         NSArray <NSString *> *pasteboardTypes = [[UIPasteboard generalPasteboard] pasteboardTypes];
182         EXPECT_EQ(2UL, pasteboardTypes.count);
183         EXPECT_WK_STREQ((NSString *)kUTTypePNG, pasteboardTypes.firstObject);
184         EXPECT_WK_STREQ((NSString *)kUTTypeURL, pasteboardTypes.lastObject);
185         NSArray <NSItemProvider *> *itemProviders = [[UIPasteboard generalPasteboard] itemProviders];
186         EXPECT_EQ(1UL, itemProviders.count);
187         itemProvider = itemProviders.firstObject;
188         EXPECT_EQ(2UL, [itemProvider registeredTypeIdentifiers].count);
189         EXPECT_WK_STREQ((NSString *)kUTTypePNG, [itemProvider registeredTypeIdentifiers].firstObject);
190         EXPECT_WK_STREQ((NSString *)kUTTypeURL, [itemProvider registeredTypeIdentifiers].lastObject);
191         done = true;
192     }];
193     TestWebKitAPI::Util::run(&done);
194
195     __block bool doneLoading = false;
196     [itemProvider loadObjectOfClass:[NSURL class] completionHandler:^(id <NSItemProviderReading> result, NSError *) {
197         EXPECT_TRUE([result isKindOfClass:[NSURL class]]);
198         NSURL *url = (NSURL *)result;
199         EXPECT_WK_STREQ("https://www.apple.com/", url.absoluteString);
200         EXPECT_WK_STREQ("hello world", url._title);
201         doneLoading = true;
202     }];
203     TestWebKitAPI::Util::run(&doneLoading);
204 }
205
206 TEST(ActionSheetTests, CopyImageElementWithHREF)
207 {
208     UIApplicationInitialize();
209     [UIPasteboard generalPasteboard].items = @[ ];
210
211     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
212     auto observer = adoptNS([[ActionSheetObserver alloc] init]);
213     [webView setUIDelegate:observer.get()];
214     [webView synchronouslyLoadTestPageNamed:@"image-in-link-and-input"];
215
216     presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 50), _WKElementActionTypeCopy);
217
218     __block bool done = false;
219     __block RetainPtr<NSItemProvider> itemProvider;
220     [webView _doAfterNextPresentationUpdate:^() {
221         NSArray <NSString *> *pasteboardTypes = [[UIPasteboard generalPasteboard] pasteboardTypes];
222         EXPECT_EQ(2UL, pasteboardTypes.count);
223         EXPECT_WK_STREQ((NSString *)kUTTypePNG, pasteboardTypes.firstObject);
224         EXPECT_WK_STREQ((NSString *)kUTTypeURL, pasteboardTypes.lastObject);
225         NSArray <NSItemProvider *> *itemProviders = [[UIPasteboard generalPasteboard] itemProviders];
226         EXPECT_EQ(1UL, itemProviders.count);
227         itemProvider = itemProviders.firstObject;
228         EXPECT_EQ(2UL, [itemProvider registeredTypeIdentifiers].count);
229         EXPECT_WK_STREQ((NSString *)kUTTypePNG, [itemProvider registeredTypeIdentifiers].firstObject);
230         EXPECT_WK_STREQ((NSString *)kUTTypeURL, [itemProvider registeredTypeIdentifiers].lastObject);
231         done = true;
232     }];
233     TestWebKitAPI::Util::run(&done);
234
235     __block bool doneLoading = false;
236     [itemProvider loadObjectOfClass:[NSURL class] completionHandler:^(id <NSItemProviderReading> result, NSError *) {
237         EXPECT_TRUE([result isKindOfClass:[NSURL class]]);
238         NSURL *url = (NSURL *)result;
239         EXPECT_WK_STREQ("https://www.apple.com/", url.absoluteString);
240         EXPECT_WK_STREQ("https://www.apple.com/", url._title);
241         doneLoading = true;
242     }];
243     TestWebKitAPI::Util::run(&doneLoading);
244 }
245
246 TEST(ActionSheetTests, CopyImageElementWithoutHREF)
247 {
248     UIApplicationInitialize();
249     [UIPasteboard generalPasteboard].items = @[ ];
250
251     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
252     auto observer = adoptNS([[ActionSheetObserver alloc] init]);
253     [webView setUIDelegate:observer.get()];
254     [webView synchronouslyLoadTestPageNamed:@"image-and-contenteditable"];
255
256     presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 100), _WKElementActionTypeCopy);
257
258     __block bool done = false;
259     [webView _doAfterNextPresentationUpdate:^() {
260         NSArray <NSString *> *pasteboardTypes = [[UIPasteboard generalPasteboard] pasteboardTypes];
261         EXPECT_EQ(1UL, pasteboardTypes.count);
262         EXPECT_WK_STREQ((NSString *)kUTTypePNG, pasteboardTypes.firstObject);
263         NSArray <NSItemProvider *> *itemProviders = [[UIPasteboard generalPasteboard] itemProviders];
264         EXPECT_EQ(1UL, itemProviders.count);
265         NSItemProvider *itemProvider = itemProviders.firstObject;
266         EXPECT_EQ(1UL, itemProvider.registeredTypeIdentifiers.count);
267         EXPECT_WK_STREQ((NSString *)kUTTypePNG, itemProvider.registeredTypeIdentifiers.firstObject);
268         done = true;
269     }];
270     TestWebKitAPI::Util::run(&done);
271 }
272
273 TEST(ActionSheetTests, CopyLinkWritesURLAndPlainText)
274 {
275     UIApplicationInitialize();
276     [UIPasteboard generalPasteboard].items = @[ ];
277
278     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
279     auto observer = adoptNS([[ActionSheetObserver alloc] init]);
280     [webView setUIDelegate:observer.get()];
281     [webView synchronouslyLoadTestPageNamed:@"link-and-input"];
282
283     presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 100), _WKElementActionTypeCopy);
284
285     [webView synchronouslyLoadTestPageNamed:@"DataTransfer"];
286     [webView paste:nil];
287
288     EXPECT_WK_STREQ("text/uri-list, text/plain", [webView stringByEvaluatingJavaScript:@"types.textContent"]);
289     EXPECT_WK_STREQ("(STRING, text/uri-list), (STRING, text/plain)", [webView stringByEvaluatingJavaScript:@"items.textContent"]);
290     EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"files.textContent"]);
291     EXPECT_WK_STREQ("https://www.apple.com/", [webView stringByEvaluatingJavaScript:@"textData.textContent"]);
292     EXPECT_WK_STREQ("https://www.apple.com/", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]);
293     EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"htmlData.textContent"]);
294     EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"rawHTMLData.textContent"]);
295 }
296
297 } // namespace TestWebKitAPI
298
299 #endif // PLATFORM(IOS)