| /* |
| * Copyright (C) 2014 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "config.h" |
| |
| #import "DeprecatedGlobalValues.h" |
| #import "PlatformUtilities.h" |
| #import "Test.h" |
| #import "TestInputDelegate.h" |
| #import "TestNavigationDelegate.h" |
| #import "TestUIDelegate.h" |
| #import "TestWKWebView.h" |
| #import "WKWebViewConfigurationExtras.h" |
| #import <WebKit/WKContentWorld.h> |
| #import <WebKit/WKContentWorldPrivate.h> |
| #import <WebKit/WKProcessPoolPrivate.h> |
| #import <WebKit/WKScriptMessage.h> |
| #import <WebKit/WKScriptMessageHandlerWithReply.h> |
| #import <WebKit/WKUserContentControllerPrivate.h> |
| #import <WebKit/WKUserScript.h> |
| #import <WebKit/WKUserScriptPrivate.h> |
| #import <WebKit/WKWebViewConfigurationPrivate.h> |
| #import <WebKit/WebKit.h> |
| #import <WebKit/_WKContentWorldConfiguration.h> |
| #import <WebKit/_WKFrameTreeNode.h> |
| #import <WebKit/_WKInputDelegate.h> |
| #import <WebKit/_WKProcessPoolConfiguration.h> |
| #import <WebKit/_WKUserContentWorld.h> |
| #import <WebKit/_WKUserStyleSheet.h> |
| #import <wtf/RetainPtr.h> |
| #import <wtf/Vector.h> |
| #import <wtf/WeakObjCPtr.h> |
| |
| static bool isDoneWithNavigation; |
| static bool isDoneWithFormSubmission; |
| |
| @interface InputDelegateForFormSubmission : NSObject <_WKInputDelegate> |
| @end |
| |
| @implementation InputDelegateForFormSubmission { |
| } |
| |
| - (void)_webView:(WKWebView *)webView willSubmitFormValues:(NSDictionary *)values frameInfo:(WKFrameInfo *)frameInfo sourceFrameInfo:(WKFrameInfo *)sourceFrameInfo userObject:(NSObject <NSSecureCoding> *)userObject requestURL:(NSURL *)requestURL method:(NSString *)method submissionHandler:(void (^)(void))submissionHandler |
| { |
| auto *dictionary = (NSDictionary *)userObject; |
| EXPECT_WK_STREQ((NSString *)dictionary[@"foo"], @"bar"); |
| submissionHandler(); |
| isDoneWithFormSubmission = true; |
| } |
| |
| @end |
| |
| @interface SimpleNavigationDelegate : NSObject <WKNavigationDelegate> |
| @end |
| |
| @implementation SimpleNavigationDelegate |
| |
| - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation |
| { |
| isDoneWithNavigation = true; |
| } |
| |
| @end |
| |
| static Vector<RetainPtr<WKScriptMessage>> scriptMessagesVector; |
| |
| @interface ScriptMessageHandler : NSObject <WKScriptMessageHandler> |
| @end |
| |
| @implementation ScriptMessageHandler |
| |
| - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message |
| { |
| receivedScriptMessage = true; |
| scriptMessagesVector.append(message); |
| } |
| |
| @end |
| |
| TEST(WKUserContentController, ScriptMessageHandlerBasicPost) |
| { |
| scriptMessagesVector.clear(); |
| receivedScriptMessage = false; |
| |
| RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| [webView loadRequest:request]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| [webView evaluateJavaScript:@"window.webkit.messageHandlers.testHandler.postMessage('Hello')" completionHandler:nil]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| receivedScriptMessage = false; |
| |
| EXPECT_WK_STREQ(@"Hello", (NSString *)[scriptMessagesVector[0] body]); |
| } |
| |
| TEST(WKUserContentController, ScriptMessageHandlerBasicPostIsolatedWorld) |
| { |
| scriptMessagesVector.clear(); |
| receivedScriptMessage = false; |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| |
| RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr<WKUserScript> userScript = adoptNS([[WKUserScript alloc] _initWithSource:@"window.webkit.messageHandlers.testHandler.postMessage('Hello')" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| [[configuration userContentController] _addScriptMessageHandler:handler.get() name:@"testHandler" contentWorld:world.get()]; |
| [[configuration userContentController] addUserScript:userScript.get()]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| RetainPtr<SimpleNavigationDelegate> delegate = adoptNS([[SimpleNavigationDelegate alloc] init]); |
| [webView setNavigationDelegate:delegate.get()]; |
| |
| NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| isDoneWithNavigation = false; |
| [webView loadRequest:request]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| receivedScriptMessage = false; |
| |
| EXPECT_WK_STREQ(@"Hello", (NSString *)[scriptMessagesVector[0] body]); |
| |
| if (!isDoneWithNavigation) |
| TestWebKitAPI::Util::run(&isDoneWithNavigation); |
| |
| __block bool isDoneEvaluatingScript = false; |
| __block RetainPtr<NSString> resultValue = @""; |
| [webView evaluateJavaScript: |
| @"var result;" |
| "if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.testHandler) {" |
| " result = { 'result': 'FAIL' };" |
| "} else {" |
| " result = { 'result': 'PASS' };" |
| "} " |
| "result;" |
| completionHandler:^(id value, NSError *error) { |
| resultValue = ((NSDictionary *)value)[@"result"]; |
| isDoneEvaluatingScript = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| |
| EXPECT_WK_STREQ(@"PASS", resultValue.get()); |
| } |
| |
| TEST(WKUserContentController, ScriptMessageHandlerBasicRemove) |
| { |
| scriptMessagesVector.clear(); |
| receivedScriptMessage = false; |
| |
| RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr<WKUserContentController> userContentController = [configuration userContentController]; |
| [userContentController addScriptMessageHandler:handler.get() name:@"handlerToRemove"]; |
| [userContentController addScriptMessageHandler:handler.get() name:@"handlerToPost"]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| [webView loadRequest:request]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| // Test that handlerToRemove was succesfully added. |
| [webView evaluateJavaScript: |
| @"if (window.webkit.messageHandlers.handlerToRemove) {" |
| " window.webkit.messageHandlers.handlerToPost.postMessage('PASS');" |
| "} else {" |
| " window.webkit.messageHandlers.handlerToPost.postMessage('FAIL');" |
| "}" completionHandler:nil]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| receivedScriptMessage = false; |
| |
| EXPECT_WK_STREQ(@"PASS", (NSString *)[scriptMessagesVector[0] body]); |
| |
| [userContentController removeScriptMessageHandlerForName:@"handlerToRemove"]; |
| |
| // Test that handlerToRemove has been removed. |
| [webView evaluateJavaScript: |
| @"if (window.webkit.messageHandlers.handlerToRemove) {" |
| " window.webkit.messageHandlers.handlerToPost.postMessage('FAIL');" |
| "} else {" |
| " window.webkit.messageHandlers.handlerToPost.postMessage('PASS');" |
| "}" completionHandler:nil]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| receivedScriptMessage = false; |
| |
| EXPECT_WK_STREQ(@"PASS", (NSString *)[scriptMessagesVector[0] body]); |
| } |
| |
| TEST(WKUserContentController, ScriptMessageHandlerCallRemovedHandler) |
| { |
| receivedScriptMessage = false; |
| |
| RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr<WKUserContentController> userContentController = [configuration userContentController]; |
| [userContentController addScriptMessageHandler:handler.get() name:@"handlerToRemove"]; |
| [userContentController addScriptMessageHandler:handler.get() name:@"handlerToPost"]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| [webView loadRequest:request]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| [webView evaluateJavaScript:@"var handlerToRemove = window.webkit.messageHandlers.handlerToRemove;" completionHandler:nil]; |
| |
| [userContentController removeScriptMessageHandlerForName:@"handlerToRemove"]; |
| |
| __block bool done = false; |
| // Test that we throw an exception if you try to use a message handler that has been removed. |
| [webView callAsyncJavaScript:@"return handlerToRemove.postMessage('FAIL')" arguments:nil inFrame:nil inContentWorld:[WKContentWorld pageWorld] completionHandler:^ (id value, NSError * error) { |
| EXPECT_NULL(value); |
| EXPECT_NOT_NULL(error); |
| EXPECT_TRUE([[error description] containsString:@"InvalidAccessError"]); |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| receivedScriptMessage = false; |
| } |
| |
| static RetainPtr<WKWebView> webViewForScriptMessageHandlerMultipleHandlerRemovalTest(WKWebViewConfiguration *configuration) |
| { |
| receivedScriptMessage = false; |
| |
| RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr<WKWebViewConfiguration> configurationCopy = adoptNS([configuration copy]); |
| [configurationCopy setUserContentController:adoptNS([[WKUserContentController alloc] init]).get()]; |
| [[configurationCopy userContentController] addScriptMessageHandler:handler.get() name:@"handlerToRemove"]; |
| [[configurationCopy userContentController] addScriptMessageHandler:handler.get() name:@"handlerToPost"]; |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configurationCopy.get()]); |
| NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| [webView loadRequest:request]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| return webView; |
| } |
| |
| TEST(WKUserContentController, ScriptMessageHandlerMultipleHandlerRemoval) |
| { |
| scriptMessagesVector.clear(); |
| receivedScriptMessage = false; |
| |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr<_WKProcessPoolConfiguration> processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]); |
| [configuration setProcessPool:adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]).get()]; |
| |
| RetainPtr<WKWebView> webView = webViewForScriptMessageHandlerMultipleHandlerRemovalTest(configuration.get()); |
| RetainPtr<WKWebView> webView2 = webViewForScriptMessageHandlerMultipleHandlerRemovalTest(configuration.get()); |
| |
| [[[webView configuration] userContentController] removeScriptMessageHandlerForName:@"handlerToRemove"]; |
| [[[webView2 configuration] userContentController] removeScriptMessageHandlerForName:@"handlerToRemove"]; |
| |
| [webView evaluateJavaScript: |
| @"try {" |
| " handlerToRemove.postMessage('FAIL');" |
| "} catch (e) {" |
| " window.webkit.messageHandlers.handlerToPost.postMessage('PASS');" |
| "}" completionHandler:nil]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| receivedScriptMessage = false; |
| |
| EXPECT_WK_STREQ(@"PASS", (NSString *)[scriptMessagesVector[0] body]); |
| } |
| |
| #if !PLATFORM(IOS_FAMILY) // FIXME: hangs in the iOS simulator |
| TEST(WKUserContentController, ScriptMessageHandlerWithNavigation) |
| { |
| scriptMessagesVector.clear(); |
| receivedScriptMessage = false; |
| |
| RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| [webView loadRequest:request]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| [webView evaluateJavaScript:@"window.webkit.messageHandlers.testHandler.postMessage('First Message')" completionHandler:nil]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| |
| EXPECT_WK_STREQ(@"First Message", (NSString *)[scriptMessagesVector[0] body]); |
| |
| receivedScriptMessage = false; |
| |
| [webView loadRequest:request]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| [webView evaluateJavaScript:@"window.webkit.messageHandlers.testHandler.postMessage('Second Message')" completionHandler:nil]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| |
| EXPECT_WK_STREQ(@"Second Message", (NSString *)[scriptMessagesVector[1] body]); |
| } |
| #endif |
| |
| TEST(WKUserContentController, ScriptMessageHandlerReplaceWithSameName) |
| { |
| scriptMessagesVector.clear(); |
| receivedScriptMessage = false; |
| |
| RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr<WKUserContentController> userContentController = [configuration userContentController]; |
| [userContentController addScriptMessageHandler:handler.get() name:@"handlerToReplace"]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| [webView loadRequest:request]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| // Test that handlerToReplace was succesfully added. |
| [webView evaluateJavaScript:@"window.webkit.messageHandlers.handlerToReplace.postMessage('PASS1');" completionHandler:nil]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| receivedScriptMessage = false; |
| |
| EXPECT_WK_STREQ(@"PASS1", (NSString *)[scriptMessagesVector[0] body]); |
| |
| [userContentController removeScriptMessageHandlerForName:@"handlerToReplace"]; |
| [userContentController addScriptMessageHandler:handler.get() name:@"handlerToReplace"]; |
| |
| // Test that handlerToReplace still works. |
| [webView evaluateJavaScript:@"window.webkit.messageHandlers.handlerToReplace.postMessage('PASS2');" completionHandler:nil]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| receivedScriptMessage = false; |
| |
| EXPECT_WK_STREQ(@"PASS2", (NSString *)[scriptMessagesVector[1] body]); |
| } |
| |
| static NSString *styleSheetSource = @"body { background-color: green !important; }"; |
| static NSString *backgroundColorScript = @"window.getComputedStyle(document.body, null).getPropertyValue('background-color')"; |
| static NSString *frameBackgroundColorScript = @"window.getComputedStyle(document.getElementsByTagName('iframe')[0].contentDocument.body, null).getPropertyValue('background-color')"; |
| static NSString *styleSheetSourceForSpecificityLevelTests = @"#body { background-color: green; }"; |
| static const char* greenInRGB = "rgb(0, 128, 0)"; |
| static const char* redInRGB = "rgb(255, 0, 0)"; |
| static const char* whiteInRGB = "rgba(0, 0, 0, 0)"; |
| static const char* blueInRGB = "rgb(0, 0, 255)"; |
| |
| static void expectScriptEvaluatesToColor(WKWebView *webView, NSString *script, const char* color) |
| { |
| static bool didCheckBackgroundColor; |
| |
| [webView evaluateJavaScript:script completionHandler:^ (id value, NSError * error) { |
| EXPECT_TRUE([value isKindOfClass:[NSString class]]); |
| EXPECT_WK_STREQ(color, value); |
| didCheckBackgroundColor = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&didCheckBackgroundColor); |
| didCheckBackgroundColor = false; |
| } |
| |
| TEST(WKUserContentController, AddUserStyleSheetBeforeCreatingView) |
| { |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:nil forMainFrameOnly:YES includeMatchPatternStrings:nil excludeMatchPatternStrings:nil baseURL:nil level:_WKUserStyleUserLevel contentWorld:nil]); |
| [[configuration userContentController] _addUserStyleSheet:styleSheet.get()]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB); |
| } |
| |
| TEST(WKUserContentController, NonCanonicalizedURL) |
| { |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:nil forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] baseURL:adoptNS([[NSURL alloc] initWithString:@"http://CamelCase/"]).get() level:_WKUserStyleUserLevel contentWorld:world.get()]); |
| } |
| |
| TEST(WKUserContentController, AddUserStyleSheetAfterCreatingView) |
| { |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB); |
| |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:YES]); |
| [[webView configuration].userContentController _addUserStyleSheet:styleSheet.get()]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetAffectingOnlyMainFrame) |
| { |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:YES]); |
| [[configuration userContentController] _addUserStyleSheet:styleSheet.get()]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| [webView loadHTMLString:@"<body style='background-color: red;'><iframe></iframe></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| // The main frame should be affected. |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB); |
| |
| // The subframe shouldn't be affected. |
| expectScriptEvaluatesToColor(webView.get(), frameBackgroundColorScript, whiteInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetAffectingAllFrames) |
| { |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:NO]); |
| [[configuration userContentController] _addUserStyleSheet:styleSheet.get()]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| [webView loadHTMLString:@"<body style='background-color: red;'><iframe></iframe></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| // The main frame should be affected. |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB); |
| |
| // The subframe should also be affected. |
| expectScriptEvaluatesToColor(webView.get(), frameBackgroundColorScript, greenInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetRemove) |
| { |
| RetainPtr<WKUserContentController> userContentController = adoptNS([[WKUserContentController alloc] init]); |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:NO]); |
| [userContentController _addUserStyleSheet:styleSheet.get()]; |
| |
| EXPECT_EQ(1u, [userContentController _userStyleSheets].count); |
| EXPECT_EQ(styleSheet.get(), [userContentController _userStyleSheets][0]); |
| |
| [userContentController _removeUserStyleSheet:styleSheet.get()]; |
| |
| EXPECT_EQ(0u, [userContentController _userStyleSheets].count); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetRemoveAll) |
| { |
| RetainPtr<WKUserContentController> userContentController = adoptNS([[WKUserContentController alloc] init]); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:NO]); |
| RetainPtr<_WKUserStyleSheet> styleSheetAssociatedWithWorld = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:nil forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] baseURL:nil level:_WKUserStyleUserLevel contentWorld:world.get()]); |
| |
| [userContentController _addUserStyleSheet:styleSheet.get()]; |
| [userContentController _addUserStyleSheet:styleSheetAssociatedWithWorld.get()]; |
| |
| EXPECT_EQ(2u, [userContentController _userStyleSheets].count); |
| EXPECT_EQ(styleSheet.get(), [userContentController _userStyleSheets][0]); |
| EXPECT_EQ(styleSheetAssociatedWithWorld.get(), [userContentController _userStyleSheets][1]); |
| |
| [userContentController _removeAllUserStyleSheets]; |
| |
| EXPECT_EQ(0u, [userContentController _userStyleSheets].count); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetRemoveAllByWorld) |
| { |
| RetainPtr<WKUserContentController> userContentController = adoptNS([[WKUserContentController alloc] init]); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:NO]); |
| RetainPtr<_WKUserStyleSheet> styleSheetAssociatedWithWorld = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:nil forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] baseURL:nil level:_WKUserStyleUserLevel contentWorld:world.get()]); |
| RetainPtr<_WKUserStyleSheet> styleSheetAssociatedWithWorld2 = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:nil forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] baseURL:nil level:_WKUserStyleUserLevel contentWorld:world.get()]); |
| |
| [userContentController _addUserStyleSheet:styleSheet.get()]; |
| [userContentController _addUserStyleSheet:styleSheetAssociatedWithWorld.get()]; |
| [userContentController _addUserStyleSheet:styleSheetAssociatedWithWorld2.get()]; |
| |
| EXPECT_EQ(3u, [userContentController _userStyleSheets].count); |
| EXPECT_EQ(styleSheet.get(), [userContentController _userStyleSheets][0]); |
| EXPECT_EQ(styleSheetAssociatedWithWorld.get(), [userContentController _userStyleSheets][1]); |
| EXPECT_EQ(styleSheetAssociatedWithWorld2.get(), [userContentController _userStyleSheets][2]); |
| |
| [userContentController _removeAllUserStyleSheetsAssociatedWithContentWorld:world.get()]; |
| |
| EXPECT_EQ(1u, [userContentController _userStyleSheets].count); |
| EXPECT_EQ(styleSheet.get(), [userContentController _userStyleSheets][0]); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetRemoveAllByNormalWorld) |
| { |
| RetainPtr<WKUserContentController> userContentController = adoptNS([[WKUserContentController alloc] init]); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:NO]); |
| RetainPtr<_WKUserStyleSheet> styleSheetAssociatedWithWorld = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:nil forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] baseURL:nil level:_WKUserStyleUserLevel contentWorld:world.get()]); |
| RetainPtr<_WKUserStyleSheet> styleSheetAssociatedWithWorld2 = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:nil forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] baseURL:nil level:_WKUserStyleUserLevel contentWorld:world.get()]); |
| |
| [userContentController _addUserStyleSheet:styleSheet.get()]; |
| [userContentController _addUserStyleSheet:styleSheetAssociatedWithWorld.get()]; |
| [userContentController _addUserStyleSheet:styleSheetAssociatedWithWorld2.get()]; |
| |
| EXPECT_EQ(3u, [userContentController _userStyleSheets].count); |
| EXPECT_EQ(styleSheet.get(), [userContentController _userStyleSheets][0]); |
| EXPECT_EQ(styleSheetAssociatedWithWorld.get(), [userContentController _userStyleSheets][1]); |
| EXPECT_EQ(styleSheetAssociatedWithWorld2.get(), [userContentController _userStyleSheets][2]); |
| |
| [userContentController _removeAllUserStyleSheetsAssociatedWithContentWorld:[WKContentWorld pageWorld]]; |
| |
| EXPECT_EQ(2u, [userContentController _userStyleSheets].count); |
| EXPECT_EQ(styleSheetAssociatedWithWorld.get(), [userContentController _userStyleSheets][0]); |
| EXPECT_EQ(styleSheetAssociatedWithWorld2.get(), [userContentController _userStyleSheets][1]); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetAffectingOnlySpecificWebView) |
| { |
| RetainPtr<WKWebView> targetWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| RetainPtr<WKWebView> otherWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| [targetWebView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| [targetWebView _test_waitForDidFinishNavigation]; |
| |
| [otherWebView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| [otherWebView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(targetWebView.get(), backgroundColorScript, redInRGB); |
| expectScriptEvaluatesToColor(otherWebView.get(), backgroundColorScript, redInRGB); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| auto styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:targetWebView.get() forMainFrameOnly:YES includeMatchPatternStrings:nil excludeMatchPatternStrings:nil baseURL:nil level:_WKUserStyleAuthorLevel contentWorld:world.get()]); |
| [[targetWebView configuration].userContentController _addUserStyleSheet:styleSheet.get()]; |
| |
| expectScriptEvaluatesToColor(targetWebView.get(), backgroundColorScript, greenInRGB); |
| expectScriptEvaluatesToColor(otherWebView.get(), backgroundColorScript, redInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetAffectingSpecificWebViewInjectionImmediatelyAfterCreation) |
| { |
| RetainPtr<WKWebView> targetWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| [targetWebView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:targetWebView.get() forMainFrameOnly:YES includeMatchPatternStrings:nil excludeMatchPatternStrings:nil baseURL:nil level:_WKUserStyleAuthorLevel contentWorld:world.get()]); |
| [[targetWebView configuration].userContentController _addUserStyleSheet:styleSheet.get()]; |
| |
| [targetWebView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(targetWebView.get(), backgroundColorScript, greenInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetAffectingSpecificWebViewInjectionAndRemovalImmediatelyAfterCreation) |
| { |
| RetainPtr<WKWebView> targetWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| [targetWebView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:targetWebView.get() forMainFrameOnly:YES includeMatchPatternStrings:nil excludeMatchPatternStrings:nil baseURL:nil level:_WKUserStyleAuthorLevel contentWorld:world.get()]); |
| [[targetWebView configuration].userContentController _addUserStyleSheet:styleSheet.get()]; |
| [[targetWebView configuration].userContentController _removeUserStyleSheet:styleSheet.get()]; |
| |
| [targetWebView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(targetWebView.get(), backgroundColorScript, redInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetAffectingOnlySpecificWebViewSharedConfiguration) |
| { |
| auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| |
| RetainPtr<WKWebView> targetWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| RetainPtr<WKWebView> otherWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| [targetWebView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| [targetWebView _test_waitForDidFinishNavigation]; |
| |
| [otherWebView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| [otherWebView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(targetWebView.get(), backgroundColorScript, redInRGB); |
| expectScriptEvaluatesToColor(otherWebView.get(), backgroundColorScript, redInRGB); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:targetWebView.get() forMainFrameOnly:YES includeMatchPatternStrings:nil excludeMatchPatternStrings:nil baseURL:nil level:_WKUserStyleAuthorLevel contentWorld:world.get()]); |
| [[targetWebView configuration].userContentController _addUserStyleSheet:styleSheet.get()]; |
| |
| expectScriptEvaluatesToColor(targetWebView.get(), backgroundColorScript, greenInRGB); |
| expectScriptEvaluatesToColor(otherWebView.get(), backgroundColorScript, redInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetAffectingOnlySpecificWebViewRemoval) |
| { |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:webView.get() forMainFrameOnly:YES includeMatchPatternStrings:nil excludeMatchPatternStrings:nil baseURL:nil level:_WKUserStyleAuthorLevel contentWorld:world.get()]); |
| [[webView configuration].userContentController _addUserStyleSheet:styleSheet.get()]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB); |
| |
| [[webView configuration].userContentController _removeUserStyleSheet:styleSheet.get()]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetAffectingOnlySpecificWebViewRemovalAfterMultipleAdditions) |
| { |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:webView.get() forMainFrameOnly:YES includeMatchPatternStrings:nil excludeMatchPatternStrings:nil baseURL:nil level:_WKUserStyleAuthorLevel contentWorld:world.get()]); |
| [[webView configuration].userContentController _addUserStyleSheet:styleSheet.get()]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB); |
| |
| [[webView configuration].userContentController _addUserStyleSheet:styleSheet.get()]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB); |
| |
| [[webView configuration].userContentController _removeUserStyleSheet:styleSheet.get()]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetAffectingOnlySpecificWebViewRemovalAfterNavigation) |
| { |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:webView.get() forMainFrameOnly:YES includeMatchPatternStrings:nil excludeMatchPatternStrings:nil baseURL:nil level:_WKUserStyleAuthorLevel contentWorld:world.get()]); |
| [[webView configuration].userContentController _addUserStyleSheet:styleSheet.get()]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB); |
| |
| [webView loadHTMLString:@"<body style='background-color: blue;'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, blueInRGB); |
| } |
| |
| static RetainPtr<_WKProcessPoolConfiguration> psonProcessPoolConfiguration() |
| { |
| auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]); |
| processPoolConfiguration.get().processSwapsOnNavigation = YES; |
| processPoolConfiguration.get().usesWebProcessCache = YES; |
| processPoolConfiguration.get().prewarmsProcessesAutomatically = YES; |
| return processPoolConfiguration; |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetAffectingOnlySpecificWebViewRemovalAfterNavigationPSON) |
| { |
| auto processPoolConfiguration = psonProcessPoolConfiguration(); |
| auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); |
| |
| auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| [webViewConfiguration setProcessPool:processPool.get()]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| |
| [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:webView.get() forMainFrameOnly:YES includeMatchPatternStrings:nil excludeMatchPatternStrings:nil baseURL:nil level:_WKUserStyleAuthorLevel contentWorld:world.get()]); |
| [[webView configuration].userContentController _addUserStyleSheet:styleSheet.get()]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB); |
| |
| [webView loadHTMLString:@"<body style='background-color: blue;'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, blueInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetSpecificityLevelNotSpecified) |
| { |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSourceForSpecificityLevelTests forMainFrameOnly:YES]); |
| [[configuration userContentController] _addUserStyleSheet:styleSheet.get()]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| [webView loadHTMLString:@"<style>body { background-color: red }</style><body id='body'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| // By default, the injected style sheet will not be able to override due to the "user" level being used. |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetSpecificityLevelUser) |
| { |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSourceForSpecificityLevelTests forWKWebView:nil forMainFrameOnly:YES includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] baseURL:[[NSURL alloc] initWithString:@"http://example.com/"] level:_WKUserStyleUserLevel contentWorld:world.get()]); |
| [[configuration userContentController] _addUserStyleSheet:styleSheet.get()]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| [webView loadHTMLString:@"<style>body { background-color: red }</style><body id='body'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| // The injected style sheet will not be able to override due to the "user" level being used. |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB); |
| } |
| |
| TEST(WKUserContentController, UserStyleSheetSpecificityLevelAuthor) |
| { |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSourceForSpecificityLevelTests forWKWebView:nil forMainFrameOnly:YES includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] baseURL:[[NSURL alloc] initWithString:@"http://example.com/"] level:_WKUserStyleAuthorLevel contentWorld:world.get()]); |
| [[configuration userContentController] _addUserStyleSheet:styleSheet.get()]; |
| |
| RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| [webView loadHTMLString:@"<style>body { background-color: red }</style><body id='body'></body>" baseURL:nil]; |
| [webView _test_waitForDidFinishNavigation]; |
| |
| // The injected style sheet *will* be able to override due to the "author" level being used. |
| expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB); |
| } |
| |
| TEST(WKUserContentController, UserScriptRemove) |
| { |
| RetainPtr<WKUserContentController> userContentController = adoptNS([[WKUserContentController alloc] init]); |
| RetainPtr<WKUserScript> userScript = adoptNS([[WKUserScript alloc] initWithSource:@"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]); |
| [userContentController addUserScript:userScript.get()]; |
| |
| EXPECT_EQ(1u, [userContentController userScripts].count); |
| EXPECT_EQ(userScript.get(), [userContentController userScripts][0]); |
| |
| [userContentController _removeUserScript:userScript.get()]; |
| |
| EXPECT_EQ(0u, [userContentController userScripts].count); |
| } |
| |
| TEST(WKUserContentController, UserScriptRemoveAll) |
| { |
| RetainPtr<WKUserContentController> userContentController = adoptNS([[WKUserContentController alloc] init]); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| |
| RetainPtr<WKUserScript> userScript = adoptNS([[WKUserScript alloc] initWithSource:@"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]); |
| auto userScriptAssociatedWithWorld = adoptNS([[WKUserScript alloc] _initWithSource:@"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| |
| [userContentController addUserScript:userScript.get()]; |
| [userContentController addUserScript:userScriptAssociatedWithWorld.get()]; |
| |
| EXPECT_EQ(2u, [userContentController userScripts].count); |
| EXPECT_EQ(userScript.get(), [userContentController userScripts][0]); |
| EXPECT_EQ(userScriptAssociatedWithWorld.get(), [userContentController userScripts][1]); |
| |
| [userContentController removeAllUserScripts]; |
| |
| EXPECT_EQ(0u, [userContentController userScripts].count); |
| } |
| |
| TEST(WKUserContentController, UserScriptRemoveAllByWorld) |
| { |
| RetainPtr<WKUserContentController> userContentController = adoptNS([[WKUserContentController alloc] init]); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| |
| RetainPtr<WKUserScript> userScript = adoptNS([[WKUserScript alloc] initWithSource:@"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]); |
| RetainPtr<WKUserScript> userScriptAssociatedWithWorld = adoptNS([[WKUserScript alloc] _initWithSource:@"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| RetainPtr<WKUserScript> userScriptAssociatedWithWorld2 = adoptNS([[WKUserScript alloc] _initWithSource:@"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| |
| [userContentController addUserScript:userScript.get()]; |
| [userContentController addUserScript:userScriptAssociatedWithWorld.get()]; |
| [userContentController addUserScript:userScriptAssociatedWithWorld2.get()]; |
| |
| EXPECT_EQ(3u, [userContentController userScripts].count); |
| EXPECT_EQ(userScript.get(), [userContentController userScripts][0]); |
| EXPECT_EQ(userScriptAssociatedWithWorld.get(), [userContentController userScripts][1]); |
| EXPECT_EQ(userScriptAssociatedWithWorld2.get(), [userContentController userScripts][2]); |
| |
| [userContentController _removeAllUserScriptsAssociatedWithContentWorld:world.get()]; |
| |
| EXPECT_EQ(1u, [userContentController userScripts].count); |
| EXPECT_EQ(userScript.get(), [userContentController userScripts][0]); |
| } |
| |
| TEST(WKUserContentController, UserScriptRemoveAllByNormalWorld) |
| { |
| RetainPtr<WKUserContentController> userContentController = adoptNS([[WKUserContentController alloc] init]); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"TestWorld"]; |
| |
| RetainPtr<WKUserScript> userScript = adoptNS([[WKUserScript alloc] initWithSource:@"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]); |
| RetainPtr<WKUserScript> userScriptAssociatedWithWorld = adoptNS([[WKUserScript alloc] _initWithSource:@"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| RetainPtr<WKUserScript> userScriptAssociatedWithWorld2 = adoptNS([[WKUserScript alloc] _initWithSource:@"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| |
| [userContentController addUserScript:userScript.get()]; |
| [userContentController addUserScript:userScriptAssociatedWithWorld.get()]; |
| [userContentController addUserScript:userScriptAssociatedWithWorld2.get()]; |
| |
| EXPECT_EQ(3u, [userContentController userScripts].count); |
| EXPECT_EQ(userScript.get(), [userContentController userScripts][0]); |
| EXPECT_EQ(userScriptAssociatedWithWorld.get(), [userContentController userScripts][1]); |
| EXPECT_EQ(userScriptAssociatedWithWorld2.get(), [userContentController userScripts][2]); |
| |
| [userContentController _removeAllUserScriptsAssociatedWithContentWorld:[WKContentWorld pageWorld]]; |
| |
| EXPECT_EQ(2u, [userContentController userScripts].count); |
| EXPECT_EQ(userScriptAssociatedWithWorld.get(), [userContentController userScripts][0]); |
| EXPECT_EQ(userScriptAssociatedWithWorld2.get(), [userContentController userScripts][1]); |
| } |
| |
| static void waitForMessages(size_t expectedCount) |
| { |
| while (scriptMessagesVector.size() < expectedCount) { |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| receivedScriptMessage = false; |
| } |
| } |
| |
| static void compareMessages(Vector<const char*>&& expectedMessages) |
| { |
| EXPECT_EQ(expectedMessages.size(), scriptMessagesVector.size()); |
| for (size_t i = 0; i < expectedMessages.size(); ++i) |
| EXPECT_STREQ([[scriptMessagesVector[i] body] UTF8String], expectedMessages[i]); |
| } |
| |
| TEST(WKUserContentController, InjectUserScriptImmediately) |
| { |
| scriptMessagesVector.clear(); |
| receivedScriptMessage = false; |
| |
| auto handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| auto startAllFrames = adoptNS([[WKUserScript alloc] initWithSource:@"window.webkit.messageHandlers.testHandler.postMessage('start all')" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]); |
| auto endMainFrameOnly = adoptNS([[WKUserScript alloc] initWithSource:@"window.webkit.messageHandlers.testHandler.postMessage('end main')" injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]); |
| |
| auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"]; |
| |
| auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| auto delegate = adoptNS([[SimpleNavigationDelegate alloc] init]); |
| [webView setNavigationDelegate:delegate.get()]; |
| |
| NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple-iframe" withExtension:@"html"]]; |
| |
| isDoneWithNavigation = false; |
| [webView loadRequest:request]; |
| TestWebKitAPI::Util::run(&isDoneWithNavigation); |
| |
| receivedScriptMessage = false; |
| [[configuration userContentController] _addUserScriptImmediately:startAllFrames.get()]; |
| // simple-iframe.html has a main frame and one iframe. |
| waitForMessages(2); |
| [[configuration userContentController] _addUserScriptImmediately:endMainFrameOnly.get()]; |
| waitForMessages(3); |
| [webView reload]; |
| waitForMessages(6); |
| compareMessages({"start all", "start all", "end main", "start all", "end main", "start all"}); |
| } |
| |
| TEST(WKUserContentController, AddUserScriptInWorldWithGlobalObjectAvailableInIframe) |
| { |
| RetainPtr<WKContentWorld> testWorld = [WKContentWorld worldWithName:@"testWorld"]; |
| WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"ContentWorldPlugIn"]; |
| |
| auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration]); |
| |
| auto delegate = adoptNS([[SimpleNavigationDelegate alloc] init]); |
| [webView setNavigationDelegate:delegate.get()]; |
| |
| NSURLRequest *request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple-iframe" withExtension:@"html"]]; |
| |
| isDoneWithNavigation = false; |
| [webView loadRequest:request]; |
| TestWebKitAPI::Util::run(&isDoneWithNavigation); |
| |
| auto contentScript = adoptNS([[WKUserScript alloc] _initWithSource:@"window.worldName" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:testWorld.get() deferRunningUntilNotification:NO]); |
| [[webView configuration].userContentController _addUserScriptImmediately:contentScript.get()]; |
| |
| __block bool done = false; |
| [webView _frames:^(_WKFrameTreeNode *mainFrame) { |
| [webView _evaluateJavaScript:@"window.worldName" inFrame:mainFrame.childFrames[0].info inContentWorld:testWorld.get() completionHandler:^(id result, NSError *error) { |
| EXPECT_WK_STREQ(result, "testWorld"); |
| done = true; |
| }]; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| } |
| |
| @interface AsyncScriptMessageHandler : NSObject <WKScriptMessageHandlerWithReply> |
| @end |
| |
| @implementation AsyncScriptMessageHandler |
| |
| - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message replyHandler:(void (^)(id, NSString *errorMessage))replyHandler |
| { |
| if ([message.name isEqualToString:@"otherWorldHandler"]) |
| EXPECT_TRUE(message.world != nil); |
| if ([message.body isKindOfClass:[NSString class]]) { |
| if ([message.body isEqualToString:@"Fulfill"]) { |
| replyHandler(@"Fulfilled!", nil); |
| return; |
| } |
| if ([message.body isEqualToString:@"Reject"]) { |
| replyHandler(nil, @"Rejected!"); |
| return; |
| } |
| if ([message.body isEqualToString:@"Undefined"]) { |
| replyHandler(nil, nil); |
| bool caught = false; |
| @try { |
| replyHandler(nil, nil); |
| } @catch (NSException *exception) { |
| caught = true; |
| } |
| EXPECT_TRUE(caught); |
| return; |
| } |
| if ([message.body isEqualToString:@"Do nothing"]) { |
| // Drop the reply handler without responding to see what happens |
| return; |
| } |
| if ([message.body isEqualToString:@"Invalid reply"]) { |
| replyHandler(adoptNS([[NSData alloc] init]).get(), nil); |
| return; |
| } |
| } |
| |
| // All other inputs should just be round tripped back to the message handler |
| replyHandler(message.body, nil); |
| } |
| |
| @end |
| |
| TEST(WKUserContentController, MessageHandlerAPI) |
| { |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| auto handler = adoptNS([[AsyncScriptMessageHandler alloc] init]); |
| [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:WKContentWorld.pageWorld name:@"testHandler1"]; |
| |
| bool hadException = false; |
| @try { |
| [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:WKContentWorld.pageWorld name:@"testHandler1"]; |
| } @catch (NSException *exception) { |
| hadException = true; |
| } |
| |
| EXPECT_TRUE(hadException); |
| [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:WKContentWorld.defaultClientWorld name:@"testHandler2"]; |
| |
| auto *world = [WKContentWorld worldWithName:@"otherWorld"]; |
| [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:world name:@"testHandler3"]; |
| [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:world name:@"testHandler4"]; |
| [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:world name:@"testHandler5"]; |
| |
| auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| bool done = false; |
| NSString *functionBody = @"var p = window.webkit.messageHandlers[handler].postMessage(arg); await p; return p;"; |
| |
| // pageWorld is where testhandler1 lives |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler1", @"arg" : @1 } inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isEqualToNumber:@1]); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| // Trying to find testHandler1 in the defaultClientWorld should fail |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler1", @"arg" : @1 } inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(result); |
| EXPECT_NOT_NULL(error); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| // defaultClientWorld is where testhandler2 lives |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler2", @"arg" : @1 } inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isEqualToNumber:@1]); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| // But if we remvoe it, it should no longer live there, and using it should cause an error. |
| [[configuration userContentController] removeScriptMessageHandlerForName:@"testHandler2" contentWorld:WKContentWorld.defaultClientWorld]; |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler2", @"arg" : @1 } inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(result); |
| EXPECT_NOT_NULL(error); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| // Verify handlers 3, 4, and 5 are all in the custom world. |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler3", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isEqualToNumber:@1]); |
| }]; |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler4", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isEqualToNumber:@1]); |
| }]; |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler5", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isEqualToNumber:@1]); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| // Remove 3 from the wrong world, verify it is still there in the custom world. |
| [[configuration userContentController] removeScriptMessageHandlerForName:@"testHandler3" contentWorld:WKContentWorld.defaultClientWorld]; |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler3", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isEqualToNumber:@1]); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| // Remove 3 from the correct world, verify it is gone, but 4 and 5 are still there. |
| [[configuration userContentController] removeScriptMessageHandlerForName:@"testHandler3" contentWorld:world]; |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler3", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(result); |
| EXPECT_NOT_NULL(error); |
| }]; |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler4", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isEqualToNumber:@1]); |
| }]; |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler5", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isEqualToNumber:@1]); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| // Remove "all" in the custom world, verify 4 and 5 are now gone. |
| [[configuration userContentController] removeAllScriptMessageHandlersFromContentWorld:world]; |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler4", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(result); |
| EXPECT_NOT_NULL(error); |
| }]; |
| [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler5", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(result); |
| EXPECT_NOT_NULL(error); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| } |
| |
| TEST(WKUserContentController, AsyncScriptMessageHandlerBasicPost) |
| { |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| auto handler = adoptNS([[AsyncScriptMessageHandler alloc] init]); |
| [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:WKContentWorld.pageWorld name:@"testHandler"]; |
| |
| auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| bool done = false; |
| NSString *functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage('Fulfill'); await p; return p;"; |
| [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isKindOfClass:[NSString class]]); |
| EXPECT_TRUE([result isEqualToString:@"Fulfilled!"]); |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| |
| done = false; |
| functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage('Reject'); await p; return p;"; |
| [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(result); |
| EXPECT_TRUE(!!error); |
| EXPECT_TRUE([[error description] containsString:@"Rejected!"]); |
| |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| |
| done = false; |
| functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage('Undefined'); var result = await p; return result == undefined ? 'Yes' : 'No'"; |
| [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isKindOfClass:[NSString class]]); |
| EXPECT_TRUE([result isEqualToString:@"Yes"]); |
| |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| |
| done = false; |
| functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage('Do nothing'); await p; return p;"; |
| [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(result); |
| EXPECT_TRUE(!!error); |
| EXPECT_TRUE([[error description] containsString:@"did not respond to this postMessage"]); |
| |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| |
| done = false; |
| functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage('Invalid reply'); await p; return p;"; |
| [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(result); |
| EXPECT_TRUE(!!error); |
| EXPECT_TRUE([[error description] containsString:@"unable to be serialized"]); |
| |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| } |
| |
| TEST(WKUserContentController, WorldLifetime) |
| { |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| auto handler = adoptNS([[AsyncScriptMessageHandler alloc] init]); |
| |
| RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"otherWorld"]; |
| [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:world.get() name:@"testHandler"]; |
| auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| // Set a variable in the world. |
| bool done = false; |
| [webView evaluateJavaScript:@"var foo = 'bar'" inFrame:nil inContentWorld:world.get() completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| // Have the message handler bounce back that value. |
| NSString *functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage(foo); await p; return p;"; |
| [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:world.get() completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isKindOfClass:[NSString class]]); |
| EXPECT_TRUE([result isEqualToString:@"bar"]); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| // Remove the message handler, which used to cause the world to be destroyed in the web process. |
| // But by evaluating JS make sure the value is still there. |
| [[configuration userContentController] removeAllScriptMessageHandlersFromContentWorld:world.get()]; |
| [webView evaluateJavaScript:@"foo" inFrame:nil inContentWorld:world.get() completionHandler:[&] (id result, NSError *error) { |
| EXPECT_NULL(error); |
| EXPECT_TRUE([result isKindOfClass:[NSString class]]); |
| EXPECT_TRUE([result isEqualToString:@"bar"]); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| } |
| |
| TEST(WKUserContentController, RemoveAllScriptMessageHandlers) |
| { |
| WeakObjCPtr<ScriptMessageHandler> weakHandler; |
| auto handler = adoptNS([ScriptMessageHandler new]); |
| weakHandler = handler.get(); |
| auto controller = adoptNS([WKUserContentController new]); |
| [controller addScriptMessageHandler:handler.get() name:@"testname"]; |
| handler = nullptr; |
| EXPECT_NOT_NULL(weakHandler.get()); |
| [controller removeAllScriptMessageHandlers]; |
| EXPECT_NULL(weakHandler.get()); |
| } |
| |
| TEST(WKUserContentController, AllowAutofill) |
| { |
| scriptMessagesVector.clear(); |
| isDoneWithNavigation = false; |
| receivedScriptMessage = false; |
| |
| RetainPtr contentWorldConfiguration = adoptNS([[_WKContentWorldConfiguration alloc] init]); |
| [contentWorldConfiguration setName:@"TestWorldAllowingAutofill"]; |
| [contentWorldConfiguration setAllowAutofill:YES]; |
| |
| RetainPtr world = [WKContentWorld _worldWithConfiguration:contentWorldConfiguration.get()]; |
| RetainPtr handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr userScript = adoptNS([[WKUserScript alloc] _initWithSource:@"window.webkit.messageHandlers.testHandler.postMessage(typeof(document.createElement('input').autofillAvailable))" |
| injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| |
| RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| [[configuration userContentController] _addScriptMessageHandler:handler.get() name:@"testHandler" contentWorld:world.get()]; |
| [[configuration userContentController] addUserScript:userScript.get()]; |
| |
| RetainPtr webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| RetainPtr delegate = adoptNS([[SimpleNavigationDelegate alloc] init]); |
| [webView setNavigationDelegate:delegate.get()]; |
| |
| RetainPtr request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| isDoneWithNavigation = false; |
| [webView loadRequest:request.get()]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| receivedScriptMessage = false; |
| EXPECT_WK_STREQ(@"boolean", (NSString *)[scriptMessagesVector[0] body]); |
| |
| if (!isDoneWithNavigation) |
| TestWebKitAPI::Util::run(&isDoneWithNavigation); |
| |
| __block bool isDoneEvaluatingScript = false; |
| __block RetainPtr<NSString> resultValue = @""; |
| [webView evaluateJavaScript:@"typeof(document.createElement('input').autofillAvailable)" completionHandler:^(id value, NSError *error) { |
| resultValue = value; |
| isDoneEvaluatingScript = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| EXPECT_WK_STREQ(@"undefined", resultValue.get()); |
| } |
| |
| TEST(WKUserContentController, DidAssociateFormControls) |
| { |
| RetainPtr webView = adoptNS([TestWKWebView new]); |
| RetainPtr configuration = adoptNS([_WKContentWorldConfiguration new]); |
| configuration.get().allowAutofill = YES; |
| RetainPtr autofillWorld = [WKContentWorld _worldWithConfiguration:configuration.get()]; |
| NSString *pageWorldJS = @"window.addEventListener('webkitassociateformcontrols', () => alert('fail') )"; |
| NSString *autofillWorldJS = @"window.addEventListener('webkitassociateformcontrols', (e) => { setTimeout(() => alert('pass ' + e.target), 50)})"; |
| RetainPtr pageWorldScript = adoptNS([[WKUserScript alloc] initWithSource:pageWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]); |
| RetainPtr autofillWorldScript = adoptNS([[WKUserScript alloc] initWithSource:autofillWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES inContentWorld:autofillWorld.get()]); |
| RetainPtr<WKUserContentController> userContentController = [webView configuration].userContentController; |
| [userContentController addUserScript:pageWorldScript.get()]; |
| [userContentController addUserScript:autofillWorldScript.get()]; |
| [webView loadRequest:[NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"associate-form-controls" withExtension:@"html"]]]; |
| EXPECT_WK_STREQ([webView _test_waitForAlert], "pass [object HTMLFormElement]"); |
| [webView evaluateJavaScript:@"addPasswordFieldToForm()" completionHandler:nil]; |
| EXPECT_WK_STREQ([webView _test_waitForAlert], "pass [object HTMLInputElement]"); |
| } |
| |
| TEST(WKUserContentController, DidAssociateFormControlsFromShadowTree) |
| { |
| RetainPtr webView = adoptNS([TestWKWebView new]); |
| RetainPtr configuration = adoptNS([_WKContentWorldConfiguration new]); |
| configuration.get().allowAutofill = YES; |
| RetainPtr autofillWorld = [WKContentWorld _worldWithConfiguration:configuration.get()]; |
| NSString *pageWorldJS = @"window.addEventListener('webkitassociateformcontrols', () => alert('fail') )"; |
| NSString *autofillWorldJS = @"window.addEventListener('webkitassociateformcontrols', (e) => { let composedTarget = e.composedPath()[0]; setTimeout(() => alert('pass ' + composedTarget), 50)})"; |
| RetainPtr pageWorldScript = adoptNS([[WKUserScript alloc] initWithSource:pageWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]); |
| RetainPtr autofillWorldScript = adoptNS([[WKUserScript alloc] initWithSource:autofillWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES inContentWorld:autofillWorld.get()]); |
| RetainPtr<WKUserContentController> userContentController = [webView configuration].userContentController; |
| [userContentController addUserScript:pageWorldScript.get()]; |
| [userContentController addUserScript:autofillWorldScript.get()]; |
| |
| NSString *html = @"" |
| "<div id='host'></div>" |
| "<script>" |
| "var shadow = host.attachShadow({mode: 'open'});" |
| "shadow.innerHTML = `<form id='loginForm'><input id='username' type='text'></form>`;" |
| "" |
| "function addPasswordFieldToForm() {" |
| " var form = shadow.getElementById('loginForm');" |
| " form.innerHTML += `<input id='password' type='password'>`;" |
| "}" |
| "</script>" |
| "<button onclick='addPasswordFieldToForm()'>Add Password Field</button>"; |
| [webView synchronouslyLoadHTMLString:html]; |
| |
| EXPECT_WK_STREQ([webView _test_waitForAlert], "pass [object HTMLFormElement]"); |
| [webView evaluateJavaScript:@"addPasswordFieldToForm()" completionHandler:nil]; |
| EXPECT_WK_STREQ([webView _test_waitForAlert], "pass [object HTMLInputElement]"); |
| } |
| |
| #if PLATFORM(MAC) |
| |
| TEST(WKUserContentController, BeforeFocusEvent) |
| { |
| RetainPtr webView = adoptNS([TestWKWebView new]); |
| RetainPtr configuration = adoptNS([_WKContentWorldConfiguration new]); |
| configuration.get().allowAutofill = YES; |
| RetainPtr autofillWorld = [WKContentWorld _worldWithConfiguration:configuration.get()]; |
| NSString *pageWorldJS = @"window.addEventListener('webkitbeforefocus', () => alert('focus-fail') )"; |
| NSString *autofillWorldJS = @"window.addEventListener('webkitbeforefocus', () => { setTimeout(() => alert('focus-pass'), 50); })"; |
| RetainPtr pageWorldScript = adoptNS([[WKUserScript alloc] initWithSource:pageWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]); |
| RetainPtr autofillWorldScript = adoptNS([[WKUserScript alloc] initWithSource:autofillWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES inContentWorld:autofillWorld.get()]); |
| RetainPtr<WKUserContentController> userContentController = [webView configuration].userContentController; |
| [userContentController addUserScript:pageWorldScript.get()]; |
| [userContentController addUserScript:autofillWorldScript.get()]; |
| |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html>" |
| "<div id='host'></div>" |
| "<script>" |
| "shadowRoot = host.attachShadow({mode: 'closed'});" |
| "shadowRoot.innerHTML = `<input id='text1' type='text'><input id='text2' type='text'>`;" |
| "shadowRoot.querySelector('#text1').focus();" |
| "</script>"]; |
| |
| EXPECT_WK_STREQ([webView _test_waitForAlert], "focus-pass"); |
| [webView evaluateJavaScript:@"shadowRoot.querySelector('#text2').focus()" completionHandler:nil]; |
| EXPECT_WK_STREQ([webView _test_waitForAlert], "focus-pass"); |
| } |
| |
| #endif // PLATFORM(MAC) |
| |
| TEST(WKUserContentController, BeforeBlurEvent) |
| { |
| RetainPtr webView = adoptNS([TestWKWebView new]); |
| RetainPtr configuration = adoptNS([_WKContentWorldConfiguration new]); |
| configuration.get().allowAutofill = YES; |
| RetainPtr autofillWorld = [WKContentWorld _worldWithConfiguration:configuration.get()]; |
| NSString *pageWorldJS = @"window.addEventListener('webkitbeforeblur', () => alert('blur-fail') )"; |
| NSString *autofillWorldJS = @"window.addEventListener('webkitbeforeblur', () => { setTimeout(() => alert('blur-pass'), 50); })"; |
| RetainPtr pageWorldScript = adoptNS([[WKUserScript alloc] initWithSource:pageWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]); |
| RetainPtr autofillWorldScript = adoptNS([[WKUserScript alloc] initWithSource:autofillWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES inContentWorld:autofillWorld.get()]); |
| RetainPtr<WKUserContentController> userContentController = [webView configuration].userContentController; |
| [userContentController addUserScript:pageWorldScript.get()]; |
| [userContentController addUserScript:autofillWorldScript.get()]; |
| |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html>" |
| "<div id='host'></div>" |
| "<script>" |
| "shadowRoot = host.attachShadow({mode: 'closed'});" |
| "shadowRoot.innerHTML = `<input id='text1' type='text'><input id='text2' type='text'>`;" |
| "shadowRoot.querySelector('#text1').focus();" |
| "</script>"]; |
| |
| [webView evaluateJavaScript:@"shadowRoot.querySelector('#text2').focus()" completionHandler:nil]; |
| EXPECT_WK_STREQ([webView _test_waitForAlert], "blur-pass"); |
| } |
| |
| TEST(WKUserContentController, ShadowRootAttachedEvent) |
| { |
| RetainPtr webView = adoptNS([TestWKWebView new]); |
| RetainPtr configuration = adoptNS([_WKContentWorldConfiguration new]); |
| configuration.get().allowAccessToClosedShadowRoots = YES; |
| RetainPtr shadowRootWorld = [WKContentWorld _worldWithConfiguration:configuration.get()]; |
| NSString *pageWorldJS = @"window.addEventListener('webkitshadowrootattached', () => alert('fail') ); onload = () => { setTimeout(() => alert('fail'), 100); }"; |
| NSString *shadowRootWorldJS = @"window.addEventListener('webkitshadowrootattached', (e) => { setTimeout(() => alert('pass ' + e.target.localName), 50)})"; |
| RetainPtr pageWorldScript = adoptNS([[WKUserScript alloc] initWithSource:pageWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]); |
| RetainPtr shadowRootScript = adoptNS([[WKUserScript alloc] initWithSource:shadowRootWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES inContentWorld:shadowRootWorld.get()]); |
| RetainPtr<WKUserContentController> userContentController = [webView configuration].userContentController; |
| [userContentController addUserScript:pageWorldScript.get()]; |
| [userContentController addUserScript:shadowRootScript.get()]; |
| [webView loadRequest:[NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"closed-shadow-tree-test" withExtension:@"html"]]]; |
| EXPECT_WK_STREQ([webView _test_waitForAlert], "pass shadow-host"); |
| } |
| |
| #if WK_HAVE_C_SPI |
| |
| TEST(WKUserContentController, DisableAutofillSpellcheck) |
| { |
| scriptMessagesVector.clear(); |
| isDoneWithNavigation = false; |
| receivedScriptMessage = false; |
| |
| RetainPtr contentWorldConfiguration = adoptNS([[_WKContentWorldConfiguration alloc] init]); |
| [contentWorldConfiguration setName:@"TestWorldAllowingAutofill"]; |
| [contentWorldConfiguration setAllowAutofill:YES]; |
| |
| RetainPtr world = [WKContentWorld _worldWithConfiguration:contentWorldConfiguration.get()]; |
| RetainPtr handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr userScript = adoptNS([[WKUserScript alloc] _initWithSource:@"onload = () => { " |
| " document.body.innerHTML = '<input><input>'; document.querySelectorAll('input')[1].autofillSpellcheck = false;" |
| " webkit.messageHandlers.testHandler.postMessage(Array.from(document.querySelectorAll('input')).map((input) => input.autofillSpellcheck).join(','))" |
| " }" |
| injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| |
| RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| WKRetainPtr<WKContextRef> context = adoptWK(TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest")); |
| [configuration setProcessPool:(WKProcessPool *)context.get()]; |
| [[configuration userContentController] _addScriptMessageHandler:handler.get() name:@"testHandler" contentWorld:world.get()]; |
| [[configuration userContentController] addUserScript:userScript.get()]; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| RetainPtr delegate = adoptNS([[SimpleNavigationDelegate alloc] init]); |
| [webView setNavigationDelegate:delegate.get()]; |
| |
| RetainPtr request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| isDoneWithNavigation = false; |
| [webView loadRequest:request.get()]; |
| |
| TestWebKitAPI::Util::run(&receivedScriptMessage); |
| receivedScriptMessage = false; |
| EXPECT_WK_STREQ(@"true,false", (NSString *)[scriptMessagesVector[0] body]); |
| |
| EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.isSpellcheckDisabledExceptTextReplacement(document.querySelectorAll('input')[0])"] boolValue]); |
| EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"internals.isSpellcheckDisabledExceptTextReplacement(document.querySelectorAll('input')[1])"] boolValue]); |
| } |
| |
| #endif |
| |
| #if PLATFORM(MAC) |
| bool didCallDidClickAutoFillButtonWithUserInfo = NO; |
| |
| @interface AutoFillDelegateForElementUserInfo : NSObject <WKUIDelegatePrivate> |
| @end |
| |
| @implementation AutoFillDelegateForElementUserInfo |
| |
| - (void)_webView:(WKWebView *)webView didClickAutoFillButtonWithUserInfo:(id<NSSecureCoding>)userInfo |
| { |
| auto *dictionary = (NSDictionary *)userInfo; |
| EXPECT_WK_STREQ(dictionary[@"string"], @"PASS"); |
| EXPECT_EQ(((NSNumber *)dictionary[@"int"]).intValue, 123); |
| EXPECT_EQ(((NSNumber *)dictionary[@"double"]).doubleValue, 0.25); |
| EXPECT_EQ(((NSNumber *)dictionary[@"bool"]).boolValue, true); |
| EXPECT_WK_STREQ((NSString *)dictionary[@"null"], ""); // FIXME: This should be nil. |
| auto *array = (NSArray *)dictionary[@"array"]; |
| EXPECT_EQ(array.count, 2UL); |
| EXPECT_EQ(((NSNumber *)array[0]).intValue, 1); |
| EXPECT_WK_STREQ((NSString *)array[1], @"abc"); |
| didCallDidClickAutoFillButtonWithUserInfo = YES; |
| } |
| |
| @end |
| |
| TEST(WKUserContentController, AllowElementUserInfo) |
| { |
| scriptMessagesVector.clear(); |
| isDoneWithNavigation = false; |
| receivedScriptMessage = false; |
| |
| RetainPtr contentWorldConfiguration = adoptNS([[_WKContentWorldConfiguration alloc] init]); |
| [contentWorldConfiguration setName:@"TestWorldAllowingAutofill"]; |
| [contentWorldConfiguration setAllowElementUserInfo:YES]; |
| [contentWorldConfiguration setAllowAutofill:YES]; |
| |
| RetainPtr world = [WKContentWorld _worldWithConfiguration:contentWorldConfiguration.get()]; |
| RetainPtr handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr userScript = adoptNS([[WKUserScript alloc] _initWithSource:@"input.autofillButtonType = 'credentials';" |
| "input.addEventListener('webkitautofillrequest', () => { input.setUserInfo({'string': 'PASS', 'int': 123, 'double': 0.25, 'bool': true, 'null': null, 'array': [1, 'abc']});" |
| "result.textContent = 'true'; });" |
| injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| |
| RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| [[configuration userContentController] _addScriptMessageHandler:handler.get() name:@"testHandler" contentWorld:world.get()]; |
| [[configuration userContentController] addUserScript:userScript.get()]; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| [webView addToTestWindow]; |
| RetainPtr uiUelegate = adoptNS([[AutoFillDelegateForElementUserInfo alloc] init]); |
| [webView setUIDelegate:uiUelegate.get()]; |
| |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><input id='input' style='width: 200px; height: 20px'><div id='result'></div>"]; |
| [webView stringByEvaluatingJavaScript:@"didCallMainWorldEventListener = false; input.addEventListener('webkitautofillrequest', () => didCallMainWorldEventListener = true);"]; |
| |
| [webView waitForNextPresentationUpdate]; |
| |
| auto inputLeft = [webView stringByEvaluatingJavaScript:@"input.getBoundingClientRect().left"].floatValue; |
| auto inputTop = [webView stringByEvaluatingJavaScript:@"input.getBoundingClientRect().top"].floatValue; |
| auto inputWidth = [webView stringByEvaluatingJavaScript:@"input.getBoundingClientRect().width"].floatValue; |
| auto inputHeight = [webView stringByEvaluatingJavaScript:@"input.getBoundingClientRect().height"].floatValue; |
| |
| auto point = NSMakePoint(inputLeft + inputWidth - inputHeight / 2, 600 - (inputTop + inputHeight / 2)); |
| [webView sendClickAtPoint:point]; |
| [webView waitForNextPresentationUpdate]; |
| |
| TestWebKitAPI::Util::run(&didCallDidClickAutoFillButtonWithUserInfo); |
| EXPECT_TRUE(didCallDidClickAutoFillButtonWithUserInfo); |
| EXPECT_WK_STREQ([webView stringByEvaluatingJavaScript:@"result.textContent"], @"true"); |
| EXPECT_FALSE([[webView stringByEvaluatingJavaScript:@"didCallMainWorldEventListener"] boolValue]); |
| [webView stringByEvaluatingJavaScript:@"input.dispatchEvent(new Event('webkitautofillrequest'))"]; |
| EXPECT_TRUE([[webView stringByEvaluatingJavaScript:@"didCallMainWorldEventListener"] boolValue]); |
| } |
| |
| TEST(WKUserContentController, AllowElementUserInfoFromShadowTree) |
| { |
| scriptMessagesVector.clear(); |
| isDoneWithNavigation = false; |
| receivedScriptMessage = false; |
| |
| RetainPtr contentWorldConfiguration = adoptNS([[_WKContentWorldConfiguration alloc] init]); |
| [contentWorldConfiguration setName:@"TestWorldAllowingAutofill"]; |
| [contentWorldConfiguration setAllowElementUserInfo:YES]; |
| [contentWorldConfiguration setAllowAutofill:YES]; |
| |
| RetainPtr world = [WKContentWorld _worldWithConfiguration:contentWorldConfiguration.get()]; |
| RetainPtr handler = adoptNS([[ScriptMessageHandler alloc] init]); |
| RetainPtr userScript = adoptNS([[WKUserScript alloc] _initWithSource:@"var input = document.getElementById('host').shadowRoot.getElementById('input');" |
| "input.autofillButtonType = 'credentials';" |
| "input.addEventListener('webkitautofillrequest', () => { input.setUserInfo({'string': 'PASS', 'int': 123, 'double': 0.25, 'bool': true, 'null': null, 'array': [1, 'abc']});" |
| "result.textContent = 'true'; });" |
| injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| |
| RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| [[configuration userContentController] _addScriptMessageHandler:handler.get() name:@"testHandler" contentWorld:world.get()]; |
| [[configuration userContentController] addUserScript:userScript.get()]; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| [webView addToTestWindow]; |
| RetainPtr uiUelegate = adoptNS([[AutoFillDelegateForElementUserInfo alloc] init]); |
| [webView setUIDelegate:uiUelegate.get()]; |
| |
| NSString *html = @"<!DOCTYPE html>" |
| "<div id='host'></div>" |
| "<div id='result'></div>" |
| "<script>" |
| "var shadow = host.attachShadow({mode: 'open'});" |
| "shadow.innerHTML = `<input id='input' style='width: 200px; height: 20px'>`;" |
| "var input = shadow.getElementById('input');" |
| "</script>"; |
| [webView synchronouslyLoadHTMLString:html]; |
| [webView stringByEvaluatingJavaScript:@"didCallMainWorldEventListener = false; input.addEventListener('webkitautofillrequest', () => didCallMainWorldEventListener = true);"]; |
| |
| [webView waitForNextPresentationUpdate]; |
| |
| auto inputLeft = [webView stringByEvaluatingJavaScript:@"input.getBoundingClientRect().left"].floatValue; |
| auto inputTop = [webView stringByEvaluatingJavaScript:@"input.getBoundingClientRect().top"].floatValue; |
| auto inputWidth = [webView stringByEvaluatingJavaScript:@"input.getBoundingClientRect().width"].floatValue; |
| auto inputHeight = [webView stringByEvaluatingJavaScript:@"input.getBoundingClientRect().height"].floatValue; |
| |
| auto point = NSMakePoint(inputLeft + inputWidth - inputHeight / 2, 600 - (inputTop + inputHeight / 2)); |
| [webView sendClickAtPoint:point]; |
| [webView waitForNextPresentationUpdate]; |
| |
| TestWebKitAPI::Util::run(&didCallDidClickAutoFillButtonWithUserInfo); |
| EXPECT_TRUE(didCallDidClickAutoFillButtonWithUserInfo); |
| EXPECT_WK_STREQ([webView stringByEvaluatingJavaScript:@"result.textContent"], @"true"); |
| EXPECT_FALSE([[webView stringByEvaluatingJavaScript:@"didCallMainWorldEventListener"] boolValue]); |
| [webView stringByEvaluatingJavaScript:@"input.dispatchEvent(new Event('webkitautofillrequest'))"]; |
| EXPECT_TRUE([[webView stringByEvaluatingJavaScript:@"didCallMainWorldEventListener"] boolValue]); |
| } |
| #endif |
| |
| TEST(WKUserContentController, LastChangeWasUserEdit) |
| { |
| scriptMessagesVector.clear(); |
| isDoneWithNavigation = false; |
| receivedScriptMessage = false; |
| |
| RetainPtr contentWorldConfiguration = adoptNS([[_WKContentWorldConfiguration alloc] init]); |
| [contentWorldConfiguration setName:@"TestWorldAllowingAutofill"]; |
| [contentWorldConfiguration setAllowAutofill:YES]; |
| |
| RetainPtr world = [WKContentWorld _worldWithConfiguration:contentWorldConfiguration.get()]; |
| |
| RetainPtr webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| RetainPtr delegate = adoptNS([[SimpleNavigationDelegate alloc] init]); |
| [webView setNavigationDelegate:delegate.get()]; |
| |
| RetainPtr request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| isDoneWithNavigation = false; |
| [webView loadRequest:request.get()]; |
| TestWebKitAPI::Util::run(&isDoneWithNavigation); |
| |
| __block bool isDoneEvaluatingScript = false; |
| __block RetainPtr resultValue = @""; |
| [webView evaluateJavaScript:@"typeof(document.createElement('input').lastChangeWasUserEdit)" inFrame:nil inContentWorld:world.get() completionHandler:^(id value, NSError *error) { |
| resultValue = value; |
| isDoneEvaluatingScript = true; |
| }]; |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| isDoneEvaluatingScript = false; |
| EXPECT_WK_STREQ(@"boolean", resultValue.get()); |
| |
| // check in main world |
| [webView evaluateJavaScript:@"typeof(document.createElement('input').lastChangeWasUserEdit)" completionHandler:^(id value, NSError *error) { |
| resultValue = value; |
| isDoneEvaluatingScript = true; |
| }]; |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| isDoneEvaluatingScript = false; |
| EXPECT_WK_STREQ(@"undefined", resultValue.get()); |
| } |
| |
| TEST(WKUserContentController, LastChangeWasUserEditNonAutofillWorld) |
| { |
| scriptMessagesVector.clear(); |
| isDoneWithNavigation = false; |
| receivedScriptMessage = false; |
| |
| RetainPtr contentWorldConfiguration = adoptNS([[_WKContentWorldConfiguration alloc] init]); |
| [contentWorldConfiguration setName:@"TestWorldNotAllowingAutofill"]; |
| [contentWorldConfiguration setAllowAutofill:NO]; |
| |
| RetainPtr world = [WKContentWorld _worldWithConfiguration:contentWorldConfiguration.get()]; |
| |
| RetainPtr webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| RetainPtr delegate = adoptNS([[SimpleNavigationDelegate alloc] init]); |
| [webView setNavigationDelegate:delegate.get()]; |
| |
| RetainPtr request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| isDoneWithNavigation = false; |
| [webView loadRequest:request.get()]; |
| TestWebKitAPI::Util::run(&isDoneWithNavigation); |
| |
| __block bool isDoneEvaluatingScript = false; |
| __block RetainPtr resultValue = @""; |
| [webView evaluateJavaScript:@"typeof(document.createElement('input').lastChangeWasUserEdit)" inFrame:nil inContentWorld:world.get() completionHandler:^(id value, NSError *error) { |
| resultValue = value; |
| isDoneEvaluatingScript = true; |
| }]; |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| isDoneEvaluatingScript = false; |
| EXPECT_WK_STREQ(@"undefined", resultValue.get()); |
| |
| // check in main world |
| [webView evaluateJavaScript:@"typeof(document.createElement('input').lastChangeWasUserEdit)" completionHandler:^(id value, NSError *error) { |
| resultValue = value; |
| isDoneEvaluatingScript = true; |
| }]; |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| isDoneEvaluatingScript = false; |
| EXPECT_WK_STREQ(@"undefined", resultValue.get()); |
| } |
| |
| #if PLATFORM(MAC) |
| |
| TEST(WKUserContentController, AllowAccessToClosedShadowRoots) |
| { |
| scriptMessagesVector.clear(); |
| isDoneWithNavigation = false; |
| receivedScriptMessage = false; |
| |
| RetainPtr contentWorldConfiguration = adoptNS([[_WKContentWorldConfiguration alloc] init]); |
| [contentWorldConfiguration setName:@"TestWorldAllowingAccessToClosedShadowRoots"]; |
| [contentWorldConfiguration setAllowAccessToClosedShadowRoots:YES]; |
| |
| RetainPtr world = [WKContentWorld _worldWithConfiguration:contentWorldConfiguration.get()]; |
| RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| RetainPtr delegate = adoptNS([[SimpleNavigationDelegate alloc] init]); |
| [webView setNavigationDelegate:delegate.get()]; |
| |
| RetainPtr request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| isDoneWithNavigation = false; |
| [webView loadRequest:request.get()]; |
| TestWebKitAPI::Util::run(&isDoneWithNavigation); |
| |
| __block bool isDoneEvaluatingScript = false; |
| __block RetainPtr resultValue = @""; |
| [webView evaluateJavaScript:@"host = document.createElement('div');" |
| "document.body.appendChild(host);" |
| "host.innerHTML = '<span><input id=target></span>';" |
| "host.attachShadow({mode: 'closed'}).innerHTML = 'PASS<slot></slot>';" completionHandler:^(id value, NSError *error) { |
| resultValue = value; |
| isDoneEvaluatingScript = true; |
| }]; |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| isDoneEvaluatingScript = false; |
| |
| [webView evaluateJavaScript:@"document.querySelector('div').shadowRoot?.textContent" inFrame:nil inContentWorld:world.get() completionHandler:^(id value, NSError *error) { |
| resultValue = value; |
| isDoneEvaluatingScript = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| isDoneEvaluatingScript = false; |
| EXPECT_WK_STREQ(@"PASS", resultValue.get()); |
| |
| [webView evaluateJavaScript:@"let path = [];" |
| "target.addEventListener('mousedown', (event) => path = Array.from(event.composedPath()));" |
| "target.focus();" inFrame:nil inContentWorld:world.get() completionHandler:^(id value, NSError *error) { |
| isDoneEvaluatingScript = true; |
| }]; |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| isDoneEvaluatingScript = false; |
| |
| auto inputLeft = [webView stringByEvaluatingJavaScript:@"target.getBoundingClientRect().left"].floatValue; |
| auto inputTop = [webView stringByEvaluatingJavaScript:@"target.getBoundingClientRect().top"].floatValue; |
| auto inputWidth = [webView stringByEvaluatingJavaScript:@"target.getBoundingClientRect().width"].floatValue; |
| auto inputHeight = [webView stringByEvaluatingJavaScript:@"target.getBoundingClientRect().height"].floatValue; |
| |
| [webView sendClickAtPoint:NSMakePoint(inputLeft + inputWidth - inputHeight / 2, 600 - (inputTop + inputHeight / 2))]; |
| |
| NSString *path = [webView objectByEvaluatingJavaScript:@"path.map((target) => target.localName || target.__proto__.constructor.name).join(',')" inFrame:nil inContentWorld:world.get()]; |
| EXPECT_WK_STREQ(@"input,span,slot,ShadowRoot,div,body,html,HTMLDocument,Window", path); |
| } |
| |
| #endif |
| |
| TEST(WKUserContentController, DisableLegacyBuiltinOverrides) |
| { |
| scriptMessagesVector.clear(); |
| isDoneWithNavigation = false; |
| receivedScriptMessage = false; |
| |
| RetainPtr contentWorldConfiguration = adoptNS([[_WKContentWorldConfiguration alloc] init]); |
| [contentWorldConfiguration setName:@"TestWorldDisablingLegacyBuiltinOverrides"]; |
| [contentWorldConfiguration setDisableLegacyBuiltinOverrides:YES]; |
| |
| RetainPtr world = [WKContentWorld _worldWithConfiguration:contentWorldConfiguration.get()]; |
| RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| |
| RetainPtr webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| |
| RetainPtr delegate = adoptNS([[SimpleNavigationDelegate alloc] init]); |
| [webView setNavigationDelegate:delegate.get()]; |
| |
| RetainPtr request = [NSURLRequest requestWithURL:[NSBundle.test_resourcesBundle URLForResource:@"simple" withExtension:@"html"]]; |
| |
| isDoneWithNavigation = false; |
| [webView loadRequest:request.get()]; |
| TestWebKitAPI::Util::run(&isDoneWithNavigation); |
| |
| __block bool isDoneEvaluatingScript = false; |
| __block RetainPtr resultValue = @""; |
| [webView evaluateJavaScript:@"document.body.innerHTML = `<img name='getElementById' src='https://webkit.org/'>`" completionHandler:^(id value, NSError *error) { |
| resultValue = value; |
| isDoneEvaluatingScript = true; |
| }]; |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| isDoneEvaluatingScript = false; |
| |
| [webView evaluateJavaScript:@"typeof(document.getElementById)" inFrame:nil inContentWorld:world.get() completionHandler:^(id value, NSError *error) { |
| resultValue = value; |
| isDoneEvaluatingScript = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&isDoneEvaluatingScript); |
| isDoneEvaluatingScript = false; |
| EXPECT_WK_STREQ(@"function", resultValue.get()); |
| } |
| |
| TEST(WKUserContentController, FormSubmissionWithUserInfo) |
| { |
| auto inputDelegate = adoptNS([[InputDelegateForFormSubmission alloc] init]); |
| isDoneWithFormSubmission = false; |
| |
| RetainPtr contentWorldConfiguration = adoptNS([[_WKContentWorldConfiguration alloc] init]); |
| [contentWorldConfiguration setName:@"TestWillWithSubmitFormWithAllowElementUserInfo"]; |
| [contentWorldConfiguration setAllowElementUserInfo:YES]; |
| |
| RetainPtr world = [WKContentWorld _worldWithConfiguration:contentWorldConfiguration.get()]; |
| RetainPtr userScript = adoptNS([[WKUserScript alloc] _initWithSource:@"window.addEventListener(\"load\", function() { document.getElementById(\"formID\").setUserInfo({foo: \"bar\"}); document.getElementById(\"formID\").submit()});" |
| injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO includeMatchPatternStrings:@[] excludeMatchPatternStrings:@[] associatedURL:nil contentWorld:world.get() deferRunningUntilNotification:NO]); |
| |
| RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| [[configuration userContentController] addUserScript:userScript.get()]; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| [webView _setInputDelegate:inputDelegate.get()]; |
| [webView loadHTMLString:@"<body><form id='formID' method='post' action='https://webkit.org/'>" |
| "<input type='text' name='testname1' value='testvalue1'/>" |
| "<input type='password' name='testname2' value='testvalue2'/>" |
| "<input type='hidden' name='testname3' value='testvalue3'/>" |
| "</form></body>" baseURL:nil]; |
| TestWebKitAPI::Util::run(&isDoneWithFormSubmission); |
| } |
| |
| #if PLATFORM(MAC) |
| |
| TEST(WKUserContentController, AutoFillWorldTrustedEventHandler) |
| { |
| RetainPtr contentWorldConfiguration = adoptNS([[_WKContentWorldConfiguration alloc] init]); |
| [contentWorldConfiguration setAllowAutofill:YES]; |
| |
| __block int autoFillWorldEventCount = 0; |
| __block int pageWorldEventCount = 0; |
| __block bool done = false; |
| |
| RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| |
| RetainPtr world = [WKContentWorld _worldWithConfiguration:contentWorldConfiguration.get()]; |
| RetainPtr autoFillHandler = adoptNS([[TestMessageHandler alloc] init]); |
| [autoFillHandler addMessage:@"event" withHandler:^{ |
| autoFillWorldEventCount++; |
| }]; |
| [[configuration userContentController] addScriptMessageHandler:autoFillHandler.get() contentWorld:world.get() name:@"autofill"]; |
| |
| RetainPtr handler = adoptNS([[TestMessageHandler alloc] init]); |
| [handler addMessage:@"event" withHandler:^{ |
| pageWorldEventCount++; |
| done = true; |
| }]; |
| [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"page"]; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get() addToWindow:YES]); |
| [webView synchronouslyLoadHTMLString:@"<input id='input' type='text'><script>input.focus()</script>"]; |
| |
| // webkitTrustedOnly should only have an effect in the autofill world. |
| [webView objectByEvaluatingJavaScript:@"window.addEventListener('keydown', (e) => { window.webkit.messageHandlers.autofill.postMessage('event'); }, { webkitTrustedOnly: true })" inFrame:nil inContentWorld:world.get()]; |
| [webView objectByEvaluatingJavaScript:@"window.addEventListener('keydown', (e) => { window.webkit.messageHandlers.page.postMessage('event'); }, { webkitTrustedOnly: true });"]; |
| |
| [webView typeCharacter:'c']; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| EXPECT_EQ(autoFillWorldEventCount, 1); |
| EXPECT_EQ(pageWorldEventCount, 1); |
| |
| [webView objectByEvaluatingJavaScript:@"input.dispatchEvent(new KeyboardEvent('keydown', { key: 'a', code: 'keyA', keyCode: 65, which: 65, bubbles: true }))"]; |
| TestWebKitAPI::Util::run(&done); |
| done = false; |
| |
| EXPECT_EQ(autoFillWorldEventCount, 1); |
| EXPECT_EQ(pageWorldEventCount, 2); |
| } |
| |
| #endif |
| |
| TEST(WKUserContentController, WebKitSubmitEvent) |
| { |
| RetainPtr webView = adoptNS([TestWKWebView new]); |
| RetainPtr configuration = adoptNS([_WKContentWorldConfiguration new]); |
| configuration.get().allowAutofill = YES; |
| RetainPtr autofillWorld = [WKContentWorld _worldWithConfiguration:configuration.get()]; |
| NSString *pageWorldJS = @"window.addEventListener('webkitsubmit', () => alert('fail') )"; |
| NSString *autofillWorldJS = @"window.addEventListener('webkitsubmit', (e) => { let composedTargetID = e.composedPath()[0].id; setTimeout(() => alert('pass ' + composedTargetID), 50)})"; |
| RetainPtr pageWorldScript = adoptNS([[WKUserScript alloc] initWithSource:pageWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]); |
| RetainPtr autofillWorldScript = adoptNS([[WKUserScript alloc] initWithSource:autofillWorldJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES inContentWorld:autofillWorld.get()]); |
| RetainPtr<WKUserContentController> userContentController = [webView configuration].userContentController; |
| [userContentController addUserScript:pageWorldScript.get()]; |
| [userContentController addUserScript:autofillWorldScript.get()]; |
| |
| NSString *html = @"" |
| "<form id='outerForm' action='javascript:void(0)'><button>Submit</button></form>" |
| "<div id='host'></div>" |
| "<script>" |
| "var shadow = host.attachShadow({mode: 'open'});" |
| "shadow.innerHTML = `<form id='innerForm' action='javascript:void(0)'><button>Submit</button></form>`;" |
| "</script>"; |
| [webView synchronouslyLoadHTMLString:html]; |
| |
| [webView objectByEvaluatingJavaScript:@"outerForm.requestSubmit()"]; |
| EXPECT_WK_STREQ([webView _test_waitForAlert], "pass outerForm"); |
| |
| [webView objectByEvaluatingJavaScript:@"shadow.getElementById('innerForm').requestSubmit()"]; |
| EXPECT_WK_STREQ([webView _test_waitForAlert], "pass innerForm"); |
| } |
| |
| TEST(WKUserContentController, EvaluateLargeJavaScriptStringInAutoFillWorld) |
| { |
| RetainPtr webView = adoptNS([TestWKWebView new]); |
| RetainPtr configuration = adoptNS([_WKContentWorldConfiguration new]); |
| configuration.get().allowAutofill = YES; |
| RetainPtr autofillWorld = [WKContentWorld _worldWithConfiguration:configuration.get()]; |
| |
| [webView synchronouslyLoadHTMLString:@"<p>Hello<p>World"]; |
| |
| NSString *script = @"document.querySelectorAll('p').length"; |
| id result = [webView objectByEvaluatingJavaScript:script inFrame:nil inContentWorld:autofillWorld.get()]; |
| EXPECT_EQ([result intValue], 2); |
| |
| NSString *largeScript = [NSString stringWithFormat:@"%131072s", script.UTF8String]; |
| result = [webView objectByEvaluatingJavaScript:largeScript inFrame:nil inContentWorld:autofillWorld.get()]; |
| EXPECT_EQ([result intValue], 2); |
| |
| // Check that script still works after caching. |
| result = [webView objectByEvaluatingJavaScript:largeScript inFrame:nil inContentWorld:autofillWorld.get()]; |
| EXPECT_EQ([result intValue], 2); |
| } |