[iOS] Specify -[NSURL _title] for the associated URL when copying an image element
[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 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
144
145 static void presentActionSheetAndChooseAction(WKWebView *webView, ActionSheetObserver *observer, CGPoint location, _WKElementActionType actionType)
146 {
147     __block RetainPtr<_WKElementAction> copyAction;
148     __block RetainPtr<_WKActivatedElementInfo> copyElement;
149     __block bool done = false;
150     [observer setPresentationHandler:^(_WKActivatedElementInfo *element, NSArray *actions) {
151         copyElement = element;
152         for (_WKElementAction *action in actions) {
153             if (action.type == actionType)
154                 copyAction = action;
155         }
156         done = true;
157         return @[ copyAction.get() ];
158     }];
159     [webView _simulateLongPressActionAtLocation:location];
160     TestWebKitAPI::Util::run(&done);
161
162     EXPECT_TRUE(!!copyAction);
163     EXPECT_TRUE(!!copyElement);
164     [copyAction runActionWithElementInfo:copyElement.get()];
165 }
166
167 TEST(ActionSheetTests, CopyImageElementWithHREFAndTitle)
168 {
169     UIApplicationInitialize();
170     [UIPasteboard generalPasteboard].items = @[ ];
171
172     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
173     auto observer = adoptNS([[ActionSheetObserver alloc] init]);
174     [webView setUIDelegate:observer.get()];
175     [webView synchronouslyLoadTestPageNamed:@"image-in-link-and-input"];
176     [webView stringByEvaluatingJavaScript:@"document.querySelector('a').setAttribute('title', 'hello world')"];
177
178     presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 50), _WKElementActionTypeCopy);
179
180     __block bool done = false;
181     __block RetainPtr<NSItemProvider> itemProvider;
182     [webView _doAfterNextPresentationUpdate:^() {
183         NSArray <NSString *> *pasteboardTypes = [[UIPasteboard generalPasteboard] pasteboardTypes];
184         EXPECT_EQ(2UL, pasteboardTypes.count);
185         EXPECT_WK_STREQ((NSString *)kUTTypePNG, pasteboardTypes.firstObject);
186         EXPECT_WK_STREQ((NSString *)kUTTypeURL, pasteboardTypes.lastObject);
187         NSArray <NSItemProvider *> *itemProviders = [[UIPasteboard generalPasteboard] itemProviders];
188         EXPECT_EQ(1UL, itemProviders.count);
189         itemProvider = itemProviders.firstObject;
190         EXPECT_EQ(2UL, [itemProvider registeredTypeIdentifiers].count);
191         EXPECT_WK_STREQ((NSString *)kUTTypePNG, [itemProvider registeredTypeIdentifiers].firstObject);
192         EXPECT_WK_STREQ((NSString *)kUTTypeURL, [itemProvider registeredTypeIdentifiers].lastObject);
193         done = true;
194     }];
195     TestWebKitAPI::Util::run(&done);
196
197     __block bool doneLoading = false;
198     [itemProvider loadObjectOfClass:[NSURL class] completionHandler:^(id <NSItemProviderReading> result, NSError *) {
199         EXPECT_TRUE([result isKindOfClass:[NSURL class]]);
200         NSURL *url = (NSURL *)result;
201         EXPECT_WK_STREQ("https://www.apple.com/", url.absoluteString);
202         EXPECT_WK_STREQ("hello world", url._title);
203         doneLoading = true;
204     }];
205     TestWebKitAPI::Util::run(&doneLoading);
206 }
207
208 TEST(ActionSheetTests, CopyImageElementWithHREF)
209 {
210     UIApplicationInitialize();
211     [UIPasteboard generalPasteboard].items = @[ ];
212
213     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
214     auto observer = adoptNS([[ActionSheetObserver alloc] init]);
215     [webView setUIDelegate:observer.get()];
216     [webView synchronouslyLoadTestPageNamed:@"image-in-link-and-input"];
217
218     presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 50), _WKElementActionTypeCopy);
219
220     __block bool done = false;
221     __block RetainPtr<NSItemProvider> itemProvider;
222     [webView _doAfterNextPresentationUpdate:^() {
223         NSArray <NSString *> *pasteboardTypes = [[UIPasteboard generalPasteboard] pasteboardTypes];
224         EXPECT_EQ(2UL, pasteboardTypes.count);
225         EXPECT_WK_STREQ((NSString *)kUTTypePNG, pasteboardTypes.firstObject);
226         EXPECT_WK_STREQ((NSString *)kUTTypeURL, pasteboardTypes.lastObject);
227         NSArray <NSItemProvider *> *itemProviders = [[UIPasteboard generalPasteboard] itemProviders];
228         EXPECT_EQ(1UL, itemProviders.count);
229         itemProvider = itemProviders.firstObject;
230         EXPECT_EQ(2UL, [itemProvider registeredTypeIdentifiers].count);
231         EXPECT_WK_STREQ((NSString *)kUTTypePNG, [itemProvider registeredTypeIdentifiers].firstObject);
232         EXPECT_WK_STREQ((NSString *)kUTTypeURL, [itemProvider registeredTypeIdentifiers].lastObject);
233         done = true;
234     }];
235     TestWebKitAPI::Util::run(&done);
236
237     __block bool doneLoading = false;
238     [itemProvider loadObjectOfClass:[NSURL class] completionHandler:^(id <NSItemProviderReading> result, NSError *) {
239         EXPECT_TRUE([result isKindOfClass:[NSURL class]]);
240         NSURL *url = (NSURL *)result;
241         EXPECT_WK_STREQ("https://www.apple.com/", url.absoluteString);
242         EXPECT_WK_STREQ("https://www.apple.com/", url._title);
243         doneLoading = true;
244     }];
245     TestWebKitAPI::Util::run(&doneLoading);
246 }
247
248 TEST(ActionSheetTests, CopyImageElementWithoutHREF)
249 {
250     UIApplicationInitialize();
251     [UIPasteboard generalPasteboard].items = @[ ];
252
253     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
254     auto observer = adoptNS([[ActionSheetObserver alloc] init]);
255     [webView setUIDelegate:observer.get()];
256     [webView synchronouslyLoadTestPageNamed:@"image-and-contenteditable"];
257
258     presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 100), _WKElementActionTypeCopy);
259
260     __block bool done = false;
261     [webView _doAfterNextPresentationUpdate:^() {
262         NSArray <NSString *> *pasteboardTypes = [[UIPasteboard generalPasteboard] pasteboardTypes];
263         EXPECT_EQ(1UL, pasteboardTypes.count);
264         EXPECT_WK_STREQ((NSString *)kUTTypePNG, pasteboardTypes.firstObject);
265         NSArray <NSItemProvider *> *itemProviders = [[UIPasteboard generalPasteboard] itemProviders];
266         EXPECT_EQ(1UL, itemProviders.count);
267         NSItemProvider *itemProvider = itemProviders.firstObject;
268         EXPECT_EQ(1UL, itemProvider.registeredTypeIdentifiers.count);
269         EXPECT_WK_STREQ((NSString *)kUTTypePNG, itemProvider.registeredTypeIdentifiers.firstObject);
270         done = true;
271     }];
272     TestWebKitAPI::Util::run(&done);
273 }
274
275 #endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
276
277 } // namespace TestWebKitAPI
278
279 #endif // PLATFORM(IOS)