2 * Copyright (C) 2017 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #import "InstanceMethodSwizzler.h"
31 #import "PlatformUtilities.h"
32 #import "TestWKWebView.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>
43 @interface ActionSheetObserver : NSObject<WKUIDelegatePrivate>
44 @property (nonatomic) BlockPtr<NSArray *(_WKActivatedElementInfo *, NSArray *)> presentationHandler;
45 @property (nonatomic) BlockPtr<NSDictionary *(void)> dataDetectionContextHandler;
48 @implementation ActionSheetObserver
50 - (NSArray *)_webView:(WKWebView *)webView actionsForElement:(_WKActivatedElementInfo *)element defaultActions:(NSArray<_WKElementAction *> *)defaultActions
52 return _presentationHandler ? _presentationHandler(element, defaultActions) : defaultActions;
55 - (NSDictionary *)_dataDetectionContextForWebView:(WKWebView *)WebView
57 return _dataDetectionContextHandler ? _dataDetectionContextHandler() : @{ };
62 namespace TestWebKitAPI {
64 class IPadUserInterfaceSwizzler {
66 IPadUserInterfaceSwizzler()
67 : m_swizzler([UIDevice class], @selector(userInterfaceIdiom), reinterpret_cast<IMP>(padUserInterfaceIdiom))
71 static UIUserInterfaceIdiom padUserInterfaceIdiom()
73 return UIUserInterfaceIdiomPad;
75 InstanceMethodSwizzler m_swizzler;
78 TEST(ActionSheetTests, ImageMapDoesNotDestroySelection)
80 IPadUserInterfaceSwizzler iPadUserInterface;
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])"];
88 EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]);
90 __block bool done = false;
91 [observer setPresentationHandler:^(_WKActivatedElementInfo *element, NSArray *actions) {
95 [webView _simulateLongPressActionAtLocation:CGPointMake(200, 200)];
96 TestWebKitAPI::Util::run(&done);
98 EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]);
101 TEST(ActionSheetTests, DataDetectorsLinkIsNotPresentedAsALink)
103 IPadUserInterfaceSwizzler iPadUserInterface;
105 auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)]);
106 auto observer = adoptNS([[ActionSheetObserver alloc] init]);
107 [webView setUIDelegate:observer.get()];
109 auto runTest = ^(NSString *phoneNumber) {
110 [webView synchronouslyLoadHTMLString:[NSString stringWithFormat:@"<a style='position: absolute; top: 0; left: 0;' href='tel:%@'>telephone number</a>", phoneNumber]];
112 __block bool done = false;
113 __block bool succeeded = true;
115 // We shouldn't present a normal action sheet, but instead a data detectors sheet.
116 [observer setDataDetectionContextHandler:^{
120 [observer setPresentationHandler:^(_WKActivatedElementInfo *, NSArray *) {
125 [webView _simulateLongPressActionAtLocation:CGPointMake(5, 5)];
126 TestWebKitAPI::Util::run(&done);
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"));
143 static void presentActionSheetAndChooseAction(WKWebView *webView, ActionSheetObserver *observer, CGPoint location, _WKElementActionType actionType)
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)
155 return @[ copyAction.get() ];
157 [webView _simulateLongPressActionAtLocation:location];
158 TestWebKitAPI::Util::run(&done);
160 EXPECT_TRUE(!!copyAction);
161 EXPECT_TRUE(!!copyElement);
162 [copyAction runActionWithElementInfo:copyElement.get()];
165 TEST(ActionSheetTests, CopyImageElementWithHREFAndTitle)
167 UIApplicationInitialize();
168 [UIPasteboard generalPasteboard].items = @[ ];
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')"];
176 presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 50), _WKElementActionTypeCopy);
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);
193 TestWebKitAPI::Util::run(&done);
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);
203 TestWebKitAPI::Util::run(&doneLoading);
206 TEST(ActionSheetTests, CopyImageElementWithHREF)
208 UIApplicationInitialize();
209 [UIPasteboard generalPasteboard].items = @[ ];
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"];
216 presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 50), _WKElementActionTypeCopy);
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);
233 TestWebKitAPI::Util::run(&done);
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);
243 TestWebKitAPI::Util::run(&doneLoading);
246 TEST(ActionSheetTests, CopyImageElementWithoutHREF)
248 UIApplicationInitialize();
249 [UIPasteboard generalPasteboard].items = @[ ];
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"];
256 presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 100), _WKElementActionTypeCopy);
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);
270 TestWebKitAPI::Util::run(&done);
273 TEST(ActionSheetTests, CopyLinkWritesURLAndPlainText)
275 UIApplicationInitialize();
276 [UIPasteboard generalPasteboard].items = @[ ];
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"];
283 presentActionSheetAndChooseAction(webView.get(), observer.get(), CGPointMake(100, 100), _WKElementActionTypeCopy);
285 [webView synchronouslyLoadTestPageNamed:@"DataTransfer"];
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"]);
297 } // namespace TestWebKitAPI
299 #endif // PLATFORM(IOS)