/* * Copyright (C) 2017-2020 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 "HTTPServer.h" #import "PlatformUtilities.h" #import "Test.h" #import "TestNavigationDelegate.h" #import "TestWKWebView.h" #import "UserMediaCaptureUIDelegate.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import @interface WKProcessPool () - (WKContextRef)_contextForTesting; @end static bool didStartProvisionalLoad; static bool failed; static int numberOfDecidePolicyCalls; static bool didRepondToPolicyDecisionCall; #if PLATFORM(IOS_FAMILY) static bool requestedQuickLookPassword; static bool didStartQuickLookLoad; static bool didFinishQuickLookLoad; #endif bool didReceiveAlert; static bool receivedMessage; static bool serverRedirected; static HashSet seenPIDs; static bool willPerformClientRedirect; static bool didPerformClientRedirect; static bool shouldConvertToDownload; static bool didCloseWindow; static RetainPtr clientRedirectSourceURL; static RetainPtr clientRedirectDestinationURL; static bool didChangeCaptivePortalMode; static bool captivePortalModeBeforeChange; static bool captivePortalModeAfterChange; @interface CaptivePortalModeKVO : NSObject { @public } @end @implementation CaptivePortalModeKVO - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { ASSERT([keyPath isEqualToString:@"_captivePortalModeEnabled"]); ASSERT([[object class] isEqual:[WKWebpagePreferences class]]); captivePortalModeBeforeChange = [[change objectForKey:NSKeyValueChangeOldKey] boolValue]; captivePortalModeAfterChange = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; didChangeCaptivePortalMode = true; } @end @interface PSONMessageHandler : NSObject @end @implementation PSONMessageHandler - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { if ([message body]) [receivedMessages addObject:[message body]]; else [receivedMessages addObject:@""]; receivedMessage = true; if ([message.webView _webProcessIdentifier]) seenPIDs.add([message.webView _webProcessIdentifier]); } @end @interface PSONNavigationDelegate : NSObject { @public void (^decidePolicyForNavigationAction)(WKNavigationAction *, void (^)(WKNavigationActionPolicy)); @public void (^didStartProvisionalNavigationHandler)(); @public void (^didCommitNavigationHandler)(); } @end @implementation PSONNavigationDelegate - (instancetype) init { self = [super init]; return self; } - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error { seenPIDs.add([webView _webProcessIdentifier]); failed = true; } - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { seenPIDs.add([webView _webProcessIdentifier]); done = true; } - (void)_webView:(WKWebView *)webView navigation:(WKNavigation *)navigation didSameDocumentNavigation:(_WKSameDocumentNavigationType)navigationType { if (navigationType != _WKSameDocumentNavigationTypeAnchorNavigation) return; seenPIDs.add([webView _webProcessIdentifier]); done = true; } - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { EXPECT_WK_STREQ(challenge.protectionSpace.authenticationMethod, NSURLAuthenticationMethodServerTrust); completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); } - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { didStartProvisionalLoad = true; if (didStartProvisionalNavigationHandler) didStartProvisionalNavigationHandler(); } - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation { if (didCommitNavigationHandler) didCommitNavigationHandler(); } - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { ++numberOfDecidePolicyCalls; seenPIDs.add([webView _webProcessIdentifier]); if (decidePolicyForNavigationAction) decidePolicyForNavigationAction(navigationAction, decisionHandler); else decisionHandler(WKNavigationActionPolicyAllow); didRepondToPolicyDecisionCall = true; } - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { decisionHandler(shouldConvertToDownload ? WKNavigationResponsePolicyDownload : WKNavigationResponsePolicyAllow); } - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation { seenPIDs.add([webView _webProcessIdentifier]); serverRedirected = true; } - (void)_webView:(WKWebView *)webView willPerformClientRedirectToURL:(NSURL *)URL delay:(NSTimeInterval)delay { clientRedirectDestinationURL = URL; willPerformClientRedirect = true; } - (void)_webView:(WKWebView *)webView didPerformClientRedirectFromURL:(NSURL *)sourceURL toURL:(NSURL *)destinationURL { EXPECT_TRUE(willPerformClientRedirect); EXPECT_WK_STREQ([clientRedirectDestinationURL absoluteString], [destinationURL absoluteString]); clientRedirectSourceURL = sourceURL; didPerformClientRedirect = true; } - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView { [webView reload]; } #if PLATFORM(IOS_FAMILY) - (void)_webViewDidRequestPasswordForQuickLookDocument:(WKWebView *)webView { requestedQuickLookPassword = true; } - (void)_webView:(WKWebView *)webView didStartLoadForQuickLookDocumentInMainFrameWithFileName:(NSString *)fileName uti:(NSString *)uti { didStartQuickLookLoad = true; } - (void)_webView:(WKWebView *)webView didFinishLoadForQuickLookDocumentInMainFrame:(NSData *)documentData { didFinishQuickLookLoad = true; } #endif @end static RetainPtr createdWebView; @interface PSONUIDelegate : NSObject - (instancetype)initWithNavigationDelegate:(PSONNavigationDelegate *)navigationDelegate; @end @implementation PSONUIDelegate { RetainPtr _navigationDelegate; } - (instancetype)initWithNavigationDelegate:(PSONNavigationDelegate *)navigationDelegate { if (!(self = [super init])) return nil; _navigationDelegate = navigationDelegate; return self; } - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures { createdWebView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]); [createdWebView setNavigationDelegate:_navigationDelegate.get()]; [createdWebView setUIDelegate:self]; didCreateWebView = true; return createdWebView.get(); } - (void)webViewDidClose:(WKWebView *)webView { EXPECT_EQ(createdWebView.get(), webView); createdWebView = nullptr; didCloseWindow = true; } - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)())completionHandler { didReceiveAlert = true; completionHandler(); } @end @interface PSONScheme : NSObject { const char* _bytes; HashMap _redirects; HashMap> _dataMappings; HashSet> _runningTasks; bool _shouldRespondAsynchronously; } - (instancetype)initWithBytes:(const char*)bytes; - (void)addRedirectFromURLString:(NSString *)sourceURLString toURLString:(NSString *)destinationURLString; - (void)addMappingFromURLString:(NSString *)urlString toData:(const char*)data; @end @implementation PSONScheme - (instancetype)initWithBytes:(const char*)bytes { self = [super init]; _bytes = bytes; return self; } - (void)addRedirectFromURLString:(NSString *)sourceURLString toURLString:(NSString *)destinationURLString { _redirects.set(sourceURLString, destinationURLString); } - (void)addMappingFromURLString:(NSString *)urlString toData:(const char*)data { _dataMappings.set(urlString, [NSData dataWithBytesNoCopy:(void*)data length:strlen(data) freeWhenDone:NO]); } - (void)setShouldRespondAsynchronously:(BOOL)value { _shouldRespondAsynchronously = value; } - (void)webView:(WKWebView *)webView startURLSchemeTask:(id )task { if ([(id)task _requestOnlyIfCached]) { [task didFailWithError:[NSError errorWithDomain:@"TestWebKitAPI" code:1 userInfo:nil]]; return; } _runningTasks.add(task); auto doAsynchronouslyIfNecessary = [self, strongSelf = retainPtr(self), task = retainPtr(task)](Function)>&& f, double delay) { if (!_shouldRespondAsynchronously) return f(task.get()); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), dispatch_get_main_queue(), makeBlockPtr([self, strongSelf, task, f = WTFMove(f)] { if (_runningTasks.contains(task.get())) f(task.get()); }).get()); }; NSURL *finalURL = task.request.URL; auto target = _redirects.get(task.request.URL.absoluteString); if (!target.isEmpty()) { auto redirectResponse = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:nil expectedContentLength:0 textEncodingName:nil]); finalURL = [NSURL URLWithString:(NSString *)target]; auto request = adoptNS([[NSURLRequest alloc] initWithURL:finalURL]); [(id)task _didPerformRedirection:redirectResponse.get() newRequest:request.get()]; } doAsynchronouslyIfNecessary([finalURL = retainPtr(finalURL)](id task) { NSMutableDictionary* headerDictionary = [NSMutableDictionary dictionary]; [headerDictionary setObject:@"text/html" forKey:@"Content-Type"]; [headerDictionary setObject:@"1" forKey:@"Content-Length"]; auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:finalURL.get() statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headerDictionary]); [task didReceiveResponse:response.get()]; }, 0.1); doAsynchronouslyIfNecessary([self, finalURL = retainPtr(finalURL)](id task) { if (auto data = _dataMappings.get([finalURL absoluteString])) [task didReceiveData:data.get()]; else if (_bytes) { RetainPtr data = adoptNS([[NSData alloc] initWithBytesNoCopy:(void *)_bytes length:strlen(_bytes) freeWhenDone:NO]); [task didReceiveData:data.get()]; } else [task didReceiveData:[@"Hello" dataUsingEncoding:NSUTF8StringEncoding]]; }, 0.2); doAsynchronouslyIfNecessary([self](id task) { [task didFinish]; _runningTasks.remove(task); }, 0.3); } - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id )task { _runningTasks.remove(task); } @end static const char* testBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* linkToCrossSiteClientSideRedirectBytes = R"PSONRESOURCE( Link to cross-site client-side redirect )PSONRESOURCE"; static const char* crossSiteClientSideRedirectBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* navigationWithLockedHistoryBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* pageCache1Bytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* windowOpenCrossSiteNoOpenerTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* windowOpenCrossOriginButSameSiteNoOpenerTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* windowOpenCrossSiteWithOpenerTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* windowOpenSameSiteWithOpenerTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* windowOpenSameSiteNoOpenerTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* windowOpenWithNameSameSiteNoOpenerTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* targetBlankCrossSiteWithExplicitOpenerTestBytes = R"PSONRESOURCE( Link )PSONRESOURCE"; static const char* targetBlankCrossSiteWithImplicitNoOpenerTestBytes = R"PSONRESOURCE( Link )PSONRESOURCE"; static const char* targetBlankCrossSiteNoOpenerTestBytes = R"PSONRESOURCE( Link )PSONRESOURCE"; static const char* targetBlankSameSiteNoOpenerTestBytes = R"PSONRESOURCE( Link )PSONRESOURCE"; #if PLATFORM(MAC) static const char* linkToAppleTestBytes = R"PSONRESOURCE( Navigate )PSONRESOURCE"; #endif static RetainPtr<_WKProcessPoolConfiguration> psonProcessPoolConfiguration() { auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]); processPoolConfiguration.get().processSwapsOnNavigation = YES; processPoolConfiguration.get().usesWebProcessCache = YES; processPoolConfiguration.get().prewarmsProcessesAutomatically = YES; processPoolConfiguration.get().processSwapsOnNavigationWithinSameNonHTTPFamilyProtocol = YES; return processPoolConfiguration; } enum class SchemeHandlerShouldBeAsync { No, Yes }; static void runBasicTest(SchemeHandlerShouldBeAsync schemeHandlerShouldBeAsync) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler setShouldRespondAsynchronously:(schemeHandlerShouldBeAsync == SchemeHandlerShouldBeAsync::Yes)]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid3 = [webView _webProcessIdentifier]; EXPECT_EQ(pid1, pid2); EXPECT_FALSE(pid2 == pid3); // 3 loads, 3 decidePolicy calls (e.g. the load that did perform a process swap should not have generated an additional decidePolicy call) EXPECT_EQ(numberOfDecidePolicyCalls, 3); } TEST(ProcessSwap, Basic) { runBasicTest(SchemeHandlerShouldBeAsync::No); } TEST(ProcessSwap, BasicWithAsyncSchemeHandler) { runBasicTest(SchemeHandlerShouldBeAsync::Yes); } TEST(ProcessSwap, NoProcessSwappingWithinSameNonHTTPFamilyProtocol) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().processSwapsOnNavigationWithinSameNonHTTPFamilyProtocol = NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"CUSTOM"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"custom://abc/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"custom://def/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(pid1, [webView _webProcessIdentifier]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"custom://ghi/main3.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(pid1, [webView _webProcessIdentifier]); // Switch to the file protocol. [webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); [webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(pid2, [webView _webProcessIdentifier]); } TEST(ProcessSwap, LoadAfterPolicyDecision) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction *, void (^decisionHandler)(WKNavigationActionPolicy)) { decisionHandler(WKNavigationActionPolicyAllow); // Synchronously navigate again right after answering the policy delegate for the previous navigation. navigationDelegate->decidePolicyForNavigationAction = nil; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; }; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; while (![[[webView URL] absoluteString] isEqualToString:@"pson://www.webkit.org/main2.html"]) TestWebKitAPI::Util::spinRunLoop(); } TEST(ProcessSwap, KillWebContentProcessAfterServerRedirectPolicyDecision) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addRedirectFromURLString:@"pson://www.webkit.org/main2.html" toURLString:@"pson://www.apple.com/main.html"]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; [webView configuration].preferences.fraudulentWebsiteWarningEnabled = NO; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; __block BOOL isRedirection = NO; navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction * action, void (^decisionHandler)(WKNavigationActionPolicy)) { decisionHandler(WKNavigationActionPolicyAllow); if (!isRedirection) { isRedirection = YES; return; } navigationDelegate->decidePolicyForNavigationAction = nil; [webView _killWebContentProcessAndResetState]; done = true; }; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::spinRunLoop(10); [webView reload]; TestWebKitAPI::Util::run(&done); done = false; } TEST(ProcessSwap, KillProvisionalWebContentProcessThenStartNewLoad) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; // When the provisional load starts in the provisional process, kill the WebView's processes. navigationDelegate->didStartProvisionalNavigationHandler = ^{ [webView _killWebContentProcessAndResetState]; done = true; }; // Start a new cross-site load, which should happen in a new provisional process. request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; navigationDelegate->didStartProvisionalNavigationHandler = nil; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; } TEST(ProcessSwap, NoSwappingForeTLDPlus2) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www1.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www2.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_EQ(pid1, pid2); EXPECT_EQ(numberOfDecidePolicyCalls, 2); } TEST(ProcessSwap, Back) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:testBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:testBytes]; [handler addMappingFromURLString:@"pson://www.google.com/main.html" toData:testBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; RetainPtr messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; EXPECT_GT([processPool _maximumSuspendedPageCount], 0U); NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto googlePID = [webView _webProcessIdentifier]; EXPECT_NE(applePID, googlePID); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.bing.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto bingPID = [webView _webProcessIdentifier]; EXPECT_NE(googlePID, bingPID); [webView goBack]; // Back to google.com. TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterFirstBackNavigation = [webView _webProcessIdentifier]; EXPECT_EQ(googlePID, pidAfterFirstBackNavigation); [webView goBack]; // Back to apple.com. TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterSecondBackNavigation = [webView _webProcessIdentifier]; if ([processPool _maximumSuspendedPageCount] > 1) EXPECT_EQ(applePID, pidAfterSecondBackNavigation); else { EXPECT_NE(applePID, pidAfterSecondBackNavigation); EXPECT_NE(googlePID, pidAfterSecondBackNavigation); } // 6 loads, 6 decidePolicy calls (e.g. any load that performs a process swap should not have generated an // additional decidePolicy call as a result of the process swap) EXPECT_EQ(6, numberOfDecidePolicyCalls); EXPECT_EQ(5u, [receivedMessages count]); EXPECT_WK_STREQ(@"PageShow called. Persisted: false, and window.history.state is: null", receivedMessages.get()[0]); EXPECT_WK_STREQ(@"PageShow called. Persisted: false, and window.history.state is: null", receivedMessages.get()[1]); EXPECT_WK_STREQ(@"PageShow called. Persisted: false, and window.history.state is: null", receivedMessages.get()[2]); EXPECT_WK_STREQ(@"PageShow called. Persisted: true, and window.history.state is: onloadCalled", receivedMessages.get()[3]); // The number of suspended pages we keep around is determined at runtime. if ([processPool _maximumSuspendedPageCount] > 1) { EXPECT_WK_STREQ(@"PageShow called. Persisted: true, and window.history.state is: onloadCalled", receivedMessages.get()[4]); EXPECT_EQ(4u, seenPIDs.size()); } else EXPECT_EQ(5u, seenPIDs.size()); } static const char* pageWithFragmentTestBytes = R"PSONRESOURCE(
TEST
)PSONRESOURCE"; TEST(ProcessSwap, HistoryNavigationToFragmentURL) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.apple.com/main.html#foo" toData:pageWithFragmentTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html#foo"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); [webView goForward]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(applePID, [webView _webProcessIdentifier]); bool finishedRunningScript = false; [webView evaluateJavaScript:@"document.getElementById('foo').innerText" completionHandler: [&] (id result, NSError *error) { NSString *innerText = (NSString *)result; EXPECT_WK_STREQ(@"TEST", innerText); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); } TEST(ProcessSwap, SuspendedPageDiesAfterBackForwardListItemIsGone) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; EXPECT_GT([processPool _maximumSuspendedPageCount], 0U); NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); // webkit.org + apple.com processes. EXPECT_EQ(2U, [processPool _webProcessCountIgnoringPrewarmedAndCached]); [webView goBack]; // Back to webkit.org. TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); // webkit.org + apple.com processes. EXPECT_EQ(2U, [processPool _webProcessCountIgnoringPrewarmedAndCached]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); // apple.com is not longer present in the back/forward list and there should therefore be no-suspended page for it. while ([processPool _webProcessCountIgnoringPrewarmedAndCached] > 1u) TestWebKitAPI::Util::spinRunLoop(); } #if PLATFORM(MAC) TEST(ProcessSwap, SuspendedPagesInActivityMonitor) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:testBytes]; [handler addMappingFromURLString:@"pson://www.google.com/main.html" toData:testBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; RetainPtr messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; [processPool _getActivePagesOriginsInWebProcessForTesting:webkitPID completionHandler:^(NSArray *activeDomains) { EXPECT_EQ(1u, activeDomains.count); EXPECT_WK_STREQ(@"pson://www.webkit.org", activeDomains[0]); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto googlePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, googlePID); [processPool _getActivePagesOriginsInWebProcessForTesting:googlePID completionHandler:^(NSArray *activeDomains) { EXPECT_EQ(1u, activeDomains.count); EXPECT_WK_STREQ(@"pson://www.google.com", activeDomains[0]); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [processPool _getActivePagesOriginsInWebProcessForTesting:webkitPID completionHandler:^(NSArray *activeDomains) { EXPECT_EQ(1u, activeDomains.count); EXPECT_WK_STREQ(@"pson://www.webkit.org", activeDomains[0]); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView goBack]; // Back to webkit.org. TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterBackNavigation = [webView _webProcessIdentifier]; EXPECT_EQ(webkitPID, pidAfterBackNavigation); [processPool _getActivePagesOriginsInWebProcessForTesting:googlePID completionHandler:^(NSArray *activeDomains) { EXPECT_EQ(1u, activeDomains.count); EXPECT_WK_STREQ(@"pson://www.google.com", activeDomains[0]); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [processPool _getActivePagesOriginsInWebProcessForTesting:webkitPID completionHandler:^(NSArray *activeDomains) { EXPECT_EQ(1u, activeDomains.count); EXPECT_WK_STREQ(@"pson://www.webkit.org", activeDomains[0]); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } #endif // PLATFORM(MAC) TEST(ProcessSwap, BackWithoutSuspendedPage) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:testBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; RetainPtr messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView1 setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView1 loadRequest:request]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView1 _webProcessIdentifier]; RetainPtr<_WKSessionState> sessionState = [webView1 _sessionState]; webView1 = nullptr; auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView2 setNavigationDelegate:delegate.get()]; [webView2 _restoreSessionState:sessionState.get() andNavigate:NO]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView2 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView2 _webProcessIdentifier]; [webView2 goBack]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pid3 = [webView2 _webProcessIdentifier]; EXPECT_FALSE(pid1 == pid2); EXPECT_FALSE(pid2 == pid3); } TEST(ProcessSwap, BackNavigationAfterSessionRestore) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView1 setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView1 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView1 _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView1 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView1 _webProcessIdentifier]; EXPECT_NE(pid1, pid2); RetainPtr<_WKSessionState> sessionState = [webView1 _sessionState]; webView1 = nullptr; auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView2 setNavigationDelegate:delegate.get()]; [webView2 _restoreSessionState:sessionState.get() andNavigate:YES]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView2 URL] absoluteString]); auto pid3 = [webView2 _webProcessIdentifier]; [webView2 goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView2 URL] absoluteString]); auto pid4 = [webView2 _webProcessIdentifier]; EXPECT_NE(pid3, pid4); } TEST(ProcessSwap, CrossSiteWindowOpenNoOpener) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:windowOpenCrossSiteNoOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); EXPECT_EQ(2, numberOfDecidePolicyCalls); auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); EXPECT_NE(pid1, pid2); } TEST(ProcessSwap, CrossOriginButSameSiteWindowOpenNoOpener) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:windowOpenCrossOriginButSameSiteNoOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); EXPECT_EQ(2, numberOfDecidePolicyCalls); auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); // Since there is no opener, we process-swap, even though the navigation is same-site. EXPECT_NE(pid1, pid2); } TEST(ProcessSwap, CrossSiteWindowOpenWithOpener) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().processSwapsOnWindowOpenWithOpener = YES; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:windowOpenCrossSiteWithOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); EXPECT_EQ(2, numberOfDecidePolicyCalls); auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); EXPECT_NE(pid1, pid2); } enum class ExpectSwap { No, Yes }; enum class WindowHasName : bool { No, Yes }; static void runSameSiteWindowOpenNoOpenerTest(WindowHasName windowHasName, ExpectSwap expectSwap) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; auto handler = adoptNS([[PSONScheme alloc] init]); if (windowHasName == WindowHasName::Yes) [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:windowOpenWithNameSameSiteNoOpenerTestBytes]; else [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:windowOpenSameSiteNoOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); EXPECT_EQ(2, numberOfDecidePolicyCalls); auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); // Since there is no opener, we process-swap, even though the navigation is same-site. if (expectSwap == ExpectSwap::Yes) EXPECT_NE(pid1, pid2); else EXPECT_EQ(pid1, pid2); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/popup2.html"]]; [createdWebView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(pid2, [createdWebView _webProcessIdentifier]); // Since the window was opened via JS, it should be able to close itself. didCloseWindow = false; [createdWebView evaluateJavaScript:@"window.close()" completionHandler:nil]; TestWebKitAPI::Util::run(&didCloseWindow); } TEST(ProcessSwap, SameSiteWindowOpenNoOpener) { // We process-swap even though the navigation is same-site, because the popup has no opener. runSameSiteWindowOpenNoOpenerTest(WindowHasName::No, ExpectSwap::Yes); } TEST(ProcessSwap, SameSiteWindowOpenWithNameNoOpener) { // We currently do no process-swap when navigating same-site a popup without opener if the window // has a name. We should be able to support this but we would need to pass the window name over // to the new process. runSameSiteWindowOpenNoOpenerTest(WindowHasName::Yes, ExpectSwap::No); } TEST(ProcessSwap, CrossSiteBlankTargetWithOpener) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:targetBlankCrossSiteWithExplicitOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); EXPECT_EQ(3, numberOfDecidePolicyCalls); auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); EXPECT_EQ(pid1, pid2); } TEST(ProcessSwap, CrossSiteBlankTargetImplicitNoOpener) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:targetBlankCrossSiteWithImplicitNoOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); EXPECT_EQ(3, numberOfDecidePolicyCalls); auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); EXPECT_NE(pid1, pid2); } TEST(ProcessSwap, CrossSiteBlankTargetNoOpener) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:targetBlankCrossSiteNoOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); EXPECT_EQ(3, numberOfDecidePolicyCalls); auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); EXPECT_NE(pid1, pid2); } TEST(ProcessSwap, SameSiteBlankTargetNoOpener) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:targetBlankSameSiteNoOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); EXPECT_EQ(3, numberOfDecidePolicyCalls); auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); // Since there is no opener, we process-swap, even though the navigation is same-site. EXPECT_NE(pid1, pid2); } TEST(ProcessSwap, ServerRedirectFromNewWebView) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addRedirectFromURLString:@"pson://www.webkit.org/main.html" toURLString:@"pson://www.apple.com/main.html"]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&serverRedirected); serverRedirected = false; seenPIDs.add([webView _webProcessIdentifier]); TestWebKitAPI::Util::run(&done); done = false; seenPIDs.add([webView _webProcessIdentifier]); EXPECT_FALSE(serverRedirected); EXPECT_EQ(2, numberOfDecidePolicyCalls); EXPECT_EQ(1u, seenPIDs.size()); } TEST(ProcessSwap, ServerRedirect) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addRedirectFromURLString:@"pson://www.webkit.org/main.html" toURLString:@"pson://www.apple.com/main.html"]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterFirstLoad = [webView _webProcessIdentifier]; EXPECT_EQ(1, numberOfDecidePolicyCalls); EXPECT_EQ(1u, seenPIDs.size()); EXPECT_TRUE(*seenPIDs.begin() == pidAfterFirstLoad); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&serverRedirected); serverRedirected = false; seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); TestWebKitAPI::Util::run(&done); done = false; seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); EXPECT_FALSE(serverRedirected); EXPECT_EQ(3, numberOfDecidePolicyCalls); EXPECT_EQ(2u, seenPIDs.size()); } TEST(ProcessSwap, ServerRedirect2) { // This tests a load that *starts out* to the same origin as the previous load, but then redirects to a new origin. auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler1 = adoptNS([[PSONScheme alloc] init]); [handler1 addRedirectFromURLString:@"pson://www.webkit.org/main2.html" toURLString:@"pson://www.apple.com/main.html"]; [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"pson"]; auto handler2 = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"psonredirected"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterFirstLoad = [webView _webProcessIdentifier]; EXPECT_FALSE(serverRedirected); EXPECT_EQ(1, numberOfDecidePolicyCalls); EXPECT_EQ(1u, seenPIDs.size()); EXPECT_TRUE(*seenPIDs.begin() == pidAfterFirstLoad); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&serverRedirected); serverRedirected = false; seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); TestWebKitAPI::Util::run(&done); done = false; seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); EXPECT_FALSE(serverRedirected); EXPECT_EQ(3, numberOfDecidePolicyCalls); EXPECT_EQ(2u, seenPIDs.size()); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.webkit.org/main1.html", [[webView URL] absoluteString]); } TEST(ProcessSwap, ServerRedirectToAboutBlank) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addRedirectFromURLString:@"pson://www.webkit.org/main.html" toURLString:@"about:blank"]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterFirstLoad = [webView _webProcessIdentifier]; EXPECT_EQ(1, numberOfDecidePolicyCalls); EXPECT_EQ(1u, seenPIDs.size()); EXPECT_TRUE(*seenPIDs.begin() == pidAfterFirstLoad); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&serverRedirected); serverRedirected = false; seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); TestWebKitAPI::Util::run(&done); done = false; seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); EXPECT_FALSE(serverRedirected); EXPECT_EQ(3, numberOfDecidePolicyCalls); EXPECT_EQ(2u, seenPIDs.size()); } enum class ShouldCacheProcessFirst { No, Yes }; static void runSameOriginServerRedirectTest(ShouldCacheProcessFirst shouldCacheProcessFirst) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().processSwapsOnNavigation = YES; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:crossSiteClientSideRedirectBytes]; [handler addRedirectFromURLString:@"pson://www.apple.com/main.html" toURLString:@"pson://www.apple.com/main2.html"]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request; if (shouldCacheProcessFirst == ShouldCacheProcessFirst::Yes) { request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main3.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; } delegate->didStartProvisionalNavigationHandler = ^{ seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); }; willPerformClientRedirect = false; didPerformClientRedirect = false; serverRedirected = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&willPerformClientRedirect); seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); TestWebKitAPI::Util::run(&didPerformClientRedirect); didPerformClientRedirect = false; willPerformClientRedirect = false; seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); TestWebKitAPI::Util::run(&serverRedirected); serverRedirected = false; seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); TestWebKitAPI::Util::run(&done); done = false; seenPIDs.add([webView _webProcessIdentifier]); if (auto provisionalPID = [webView _provisionalWebProcessIdentifier]) seenPIDs.add(provisionalPID); EXPECT_EQ(2u, seenPIDs.size()); } TEST(ProcessSwap, SameOriginServerRedirect) { runSameOriginServerRedirectTest(ShouldCacheProcessFirst::No); } TEST(ProcessSwap, SameOriginServerRedirectFromCachedProcess) { runSameOriginServerRedirectTest(ShouldCacheProcessFirst::Yes); } TEST(ProcessSwap, TerminateProcessRightAfterSwap) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; [webView configuration].preferences.fraudulentWebsiteWarningEnabled = NO; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; delegate->didStartProvisionalNavigationHandler = ^{ EXPECT_NE(0, [webView _provisionalWebProcessIdentifier]); kill([webView _provisionalWebProcessIdentifier], 9); }; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::sleep(0.5); } static const char* linkToWebKitBytes = R"PSONRESOURCE( Link )PSONRESOURCE"; TEST(ProcessSwap, PolicyCancelAfterServerRedirect) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.google.com/main.html" toData:linkToWebKitBytes]; [handler addRedirectFromURLString:@"pson://www.webkit.org/main.html" toURLString:@"pson://www.apple.com/ignore.html"]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction *action, void (^decisionHandler)(WKNavigationActionPolicy)) { if ([action.request.URL.absoluteString hasSuffix:@"ignore.html"]) { decisionHandler(WKNavigationActionPolicyCancel); return; } decisionHandler(WKNavigationActionPolicyAllow); }; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterFirstLoad = [webView _webProcessIdentifier]; EXPECT_EQ(1, numberOfDecidePolicyCalls); [webView evaluateJavaScript:@"testLink.click()" completionHandler:nil]; TestWebKitAPI::Util::run(&failed); failed = false; done = false; EXPECT_EQ(3, numberOfDecidePolicyCalls); // We should still be on google.com. EXPECT_EQ(pidAfterFirstLoad, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.google.com/main.html", [[webView URL] absoluteString]); [webView evaluateJavaScript:@"testLink.innerText" completionHandler: [&] (id innerText, NSError *error) { EXPECT_WK_STREQ(@"Link", innerText); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } TEST(ProcessSwap, CrossSiteDownload) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.google.com/main.html" toData:linkToWebKitBytes]; [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:"Hello"]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterFirstLoad = [webView _webProcessIdentifier]; shouldConvertToDownload = true; [webView evaluateJavaScript:@"testLink.click()" completionHandler:nil]; TestWebKitAPI::Util::run(&failed); failed = false; shouldConvertToDownload = false; // We should still be on google.com. EXPECT_EQ(pidAfterFirstLoad, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.google.com/main.html", [[webView URL] absoluteString]); [webView evaluateJavaScript:@"testLink.innerText" completionHandler: [&] (id innerText, NSError *error) { EXPECT_WK_STREQ(@"Link", innerText); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } #if USE(SYSTEM_PREVIEW) static const char* systemPreviewSameOriginTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* systemPreviewCrossOriginTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, SameOriginSystemPreview) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:systemPreviewSameOriginTestBytes]; [handler addMappingFromURLString:@"pson://www.webkit.org/whatever" toData:"Fake USDZ data"]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; [webViewConfiguration _setSystemPreviewEnabled:YES]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterFirstLoad = [webView _webProcessIdentifier]; didStartProvisionalLoad = false; [webView evaluateJavaScript:@"testLink.click()" completionHandler:nil]; TestWebKitAPI::Util::sleep(0.5); // We should still be on webkit.org. EXPECT_EQ(pidAfterFirstLoad, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView URL] absoluteString]); EXPECT_FALSE(didStartProvisionalLoad); } TEST(ProcessSwap, CrossOriginSystemPreview) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:systemPreviewCrossOriginTestBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/whatever" toData:"Fake USDZ data"]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; [webViewConfiguration _setSystemPreviewEnabled:YES]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterFirstLoad = [webView _webProcessIdentifier]; didStartProvisionalLoad = false; [webView evaluateJavaScript:@"testLink.click()" completionHandler:nil]; TestWebKitAPI::Util::sleep(0.5); // We should still be on webkit.org. EXPECT_EQ(pidAfterFirstLoad, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView URL] absoluteString]); EXPECT_FALSE(didStartProvisionalLoad); } #endif enum class ShouldEnablePSON { No, Yes }; static void runClientSideRedirectTest(ShouldEnablePSON shouldEnablePSON) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().processSwapsOnNavigation = shouldEnablePSON == ShouldEnablePSON::Yes ? YES : NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:linkToCrossSiteClientSideRedirectBytes]; [handler addMappingFromURLString:@"pson://www.google.com/clientSideRedirect.html" toData:crossSiteClientSideRedirectBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; // Navigate to the page doing a client-side redirect to apple.com. [webView evaluateJavaScript:@"testLink.click()" completionHandler:nil]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.google.com/clientSideRedirect.html", [[webView URL] absoluteString]); auto googlePID = [webView _webProcessIdentifier]; if (shouldEnablePSON == ShouldEnablePSON::Yes) EXPECT_NE(webkitPID, googlePID); else EXPECT_EQ(webkitPID, googlePID); TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); auto applePID = [webView _webProcessIdentifier]; if (shouldEnablePSON == ShouldEnablePSON::Yes) { EXPECT_NE(webkitPID, applePID); EXPECT_NE(webkitPID, googlePID); } else { EXPECT_EQ(webkitPID, applePID); EXPECT_EQ(webkitPID, googlePID); } EXPECT_TRUE(willPerformClientRedirect); EXPECT_TRUE(didPerformClientRedirect); EXPECT_WK_STREQ(@"pson://www.google.com/clientSideRedirect.html", [clientRedirectSourceURL absoluteString]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [clientRedirectDestinationURL absoluteString]); willPerformClientRedirect = false; didPerformClientRedirect = false; clientRedirectSourceURL = nullptr; clientRedirectDestinationURL = nullptr; // Validate Back/Forward list. auto* backForwardList = [webView backForwardList]; auto* currentItem = backForwardList.currentItem; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [currentItem.URL absoluteString]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [currentItem.initialURL absoluteString]); EXPECT_TRUE(!backForwardList.forwardItem); EXPECT_EQ(1U, backForwardList.backList.count); auto* backItem = backForwardList.backItem; EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backItem.URL absoluteString]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backItem.initialURL absoluteString]); // Navigate back. [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView URL] absoluteString]); EXPECT_FALSE(willPerformClientRedirect); EXPECT_FALSE(didPerformClientRedirect); auto pidAfterBackNavigation = [webView _webProcessIdentifier]; if ([processPool _maximumSuspendedPageCount] > 1) EXPECT_EQ(webkitPID, pidAfterBackNavigation); // Validate Back/Forward list. currentItem = backForwardList.currentItem; EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [currentItem.URL absoluteString]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [currentItem.initialURL absoluteString]); EXPECT_TRUE(!backForwardList.backItem); EXPECT_EQ(1U, backForwardList.forwardList.count); auto* forwardItem = backForwardList.forwardItem; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [forwardItem.URL absoluteString]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [forwardItem.initialURL absoluteString]); // Navigate forward. [webView goForward]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); EXPECT_FALSE(willPerformClientRedirect); EXPECT_FALSE(didPerformClientRedirect); auto pidAfterForwardNavigation = [webView _webProcessIdentifier]; EXPECT_EQ(applePID, pidAfterForwardNavigation); // Validate Back/Forward list. currentItem = backForwardList.currentItem; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [currentItem.URL absoluteString]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [currentItem.initialURL absoluteString]); EXPECT_TRUE(!backForwardList.forwardItem); EXPECT_EQ(1U, backForwardList.backList.count); backItem = backForwardList.backItem; EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backItem.URL absoluteString]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backItem.initialURL absoluteString]); } TEST(ProcessSwap, CrossSiteClientSideRedirectWithoutPSON) { runClientSideRedirectTest(ShouldEnablePSON::No); } TEST(ProcessSwap, CrossSiteClientSideRedirectWithPSON) { runClientSideRedirectTest(ShouldEnablePSON::Yes); } TEST(ProcessSwap, CrossSiteClientSideRedirectFromFileURL) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; willPerformClientRedirect = false; didPerformClientRedirect = false; NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"client-side-redirect" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); EXPECT_EQ(1U, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_TRUE(willPerformClientRedirect); EXPECT_TRUE(didPerformClientRedirect); } TEST(ProcessSwap, NavigateBackAfterClientSideRedirect) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:linkToCrossSiteClientSideRedirectBytes]; [handler addMappingFromURLString:@"pson://www.google.com/clientSideRedirect.html" toData:crossSiteClientSideRedirectBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; willPerformClientRedirect = false; didPerformClientRedirect = false; // Navigate to the page doing a client-side redirect to apple.com. [webView evaluateJavaScript:@"testLink.click()" completionHandler:nil]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.google.com/clientSideRedirect.html", [[webView URL] absoluteString]); auto googlePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, googlePID); TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); EXPECT_NE(webkitPID, googlePID); EXPECT_TRUE(willPerformClientRedirect); EXPECT_TRUE(didPerformClientRedirect); EXPECT_WK_STREQ(@"pson://www.google.com/clientSideRedirect.html", [clientRedirectSourceURL absoluteString]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [clientRedirectDestinationURL absoluteString]); willPerformClientRedirect = false; didPerformClientRedirect = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(applePID, [webView _webProcessIdentifier]); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(applePID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); EXPECT_FALSE(willPerformClientRedirect); EXPECT_FALSE(didPerformClientRedirect); } static void runNavigationWithLockedHistoryTest(ShouldEnablePSON shouldEnablePSON) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().processSwapsOnNavigation = shouldEnablePSON == ShouldEnablePSON::Yes ? YES : NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:navigationWithLockedHistoryBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:pageCache1Bytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; // Page redirects to apple.com. TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; if (shouldEnablePSON == ShouldEnablePSON::Yes) EXPECT_NE(webkitPID, applePID); else EXPECT_EQ(webkitPID, applePID); auto* backForwardList = [webView backForwardList]; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [backForwardList.currentItem.URL absoluteString]); EXPECT_TRUE(!backForwardList.forwardItem); EXPECT_EQ(1U, backForwardList.backList.count); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backForwardList.backItem.URL absoluteString]); receivedMessage = false; [webView goBack]; TestWebKitAPI::Util::run(&receivedMessage); // Should be restored from PageCache. receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backForwardList.currentItem.URL absoluteString]); EXPECT_TRUE(!backForwardList.backItem); EXPECT_EQ(1U, backForwardList.forwardList.count); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [backForwardList.forwardItem.URL absoluteString]); [webView goForward]; TestWebKitAPI::Util::run(&done); TestWebKitAPI::Util::run(&receivedMessage); // Should be restored from PageCache. receivedMessage = false; done = false; EXPECT_EQ(applePID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [backForwardList.currentItem.URL absoluteString]); EXPECT_TRUE(!backForwardList.forwardItem); EXPECT_EQ(1U, backForwardList.backList.count); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backForwardList.backItem.URL absoluteString]); } TEST(ProcessSwap, NavigationWithLockedHistoryWithPSON) { runNavigationWithLockedHistoryTest(ShouldEnablePSON::Yes); } static void runQuickBackForwardNavigationTest(ShouldEnablePSON shouldEnablePSON) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().processSwapsOnNavigation = shouldEnablePSON == ShouldEnablePSON::Yes ? YES : NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; [webView configuration].preferences.fraudulentWebsiteWarningEnabled = NO; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; if (shouldEnablePSON == ShouldEnablePSON::Yes) EXPECT_NE(webkitPID, applePID); else EXPECT_EQ(webkitPID, applePID); for (unsigned i = 0; i < 10; ++i) { [webView goBack]; TestWebKitAPI::Util::sleep(0.1); [webView goForward]; TestWebKitAPI::Util::spinRunLoop(); } TestWebKitAPI::Util::run(&done); done = false; Vector backForwardListURLs; auto* backForwardList = [webView backForwardList]; for (unsigned i = 0; i < backForwardList.backList.count; ++i) backForwardListURLs.append([backForwardList.backList[i].URL absoluteString]); backForwardListURLs.append([backForwardList.currentItem.URL absoluteString]); for (unsigned i = 0; i < backForwardList.forwardList.count; ++i) backForwardListURLs.append([backForwardList.forwardList[i].URL absoluteString]); RELEASE_ASSERT(backForwardListURLs.size() == 3u); EXPECT_WK_STREQ("pson://www.webkit.org/main1.html", backForwardListURLs[0]); EXPECT_WK_STREQ("pson://www.webkit.org/main2.html", backForwardListURLs[1]); EXPECT_WK_STREQ("pson://www.apple.com/main.html", backForwardListURLs[2]); } TEST(ProcessSwap, QuickBackForwardNavigationWithoutPSON) { runQuickBackForwardNavigationTest(ShouldEnablePSON::No); } TEST(ProcessSwap, QuickBackForwardNavigationWithPSON) { runQuickBackForwardNavigationTest(ShouldEnablePSON::Yes); } TEST(ProcessSwap, NavigationWithLockedHistoryWithoutPSON) { runNavigationWithLockedHistoryTest(ShouldEnablePSON::No); } static const char* sessionStorageTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, SessionStorage) { for (int i = 0; i < 5; ++i) { [receivedMessages removeAllObjects]; auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:sessionStorageTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; // Verify the web pages are in different processes EXPECT_NE(webkitPID, applePID); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; // We should have gone back to the webkit.org process for this load since we reuse SuspendedPages' process when possible. EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); // Verify the sessionStorage values were as expected EXPECT_EQ([receivedMessages count], 2u); EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@""]); EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"I exist!"]); } } TEST(ProcessSwap, ReuseSuspendedProcess) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().usesWebProcessCache = NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; // We should have gone back to the webkit.org process for this load since we reuse SuspendedPages' process when possible. EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; // We should have gone back to the apple.com process for this load since we reuse SuspendedPages' process when possible. EXPECT_EQ(applePID, [webView _webProcessIdentifier]); } static const char* failsToEnterPageCacheTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, ReuseSuspendedProcessEvenIfPageCacheFails) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main1.html" toData:failsToEnterPageCacheTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; // We should have gone back to the webkit.org process for this load since we reuse SuspendedPages' process when possible. EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; // We should have gone back to the apple.com process for this load since we reuse SuspendedPages' process when possible. EXPECT_EQ(applePID, [webView _webProcessIdentifier]); } TEST(ProcessSwap, ReuseSuspendedProcessOnBackEvenIfPageCacheFails) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main1.html" toData:failsToEnterPageCacheTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); } static const char* withSubframesTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, HistoryItemIDConfusion) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:failsToEnterPageCacheTestBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:withSubframesTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto googlePID = [webView _webProcessIdentifier]; EXPECT_NE(applePID, googlePID); EXPECT_NE(webkitPID, googlePID); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(applePID, [webView _webProcessIdentifier]); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); auto* backForwardList = [webView backForwardList]; EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backForwardList.currentItem.URL absoluteString]); EXPECT_EQ(2U, backForwardList.forwardList.count); EXPECT_EQ(0U, backForwardList.backList.count); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [backForwardList.forwardList[0].URL absoluteString]); EXPECT_WK_STREQ(@"pson://www.google.com/main.html", [backForwardList.forwardList[1].URL absoluteString]); } TEST(ProcessSwap, GoToSecondItemInBackHistory) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main1.html" toData:withSubframesTestBytes]; [handler addMappingFromURLString:@"pson://www.webkit.org/main2.html" toData:withSubframesTestBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:withSubframesTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); [webView goToBackForwardListItem:webView.get().backForwardList.backList[0]]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main1.html", [[webView URL] absoluteString]); [webView goToBackForwardListItem:webView.get().backForwardList.forwardList[1]]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(applePID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); } TEST(ProcessSwap, PrivateAndRegularSessionsShouldGetDifferentProcesses) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto privateWebViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [privateWebViewConfiguration setProcessPool:processPool.get()]; [privateWebViewConfiguration setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]]; auto handler = adoptNS([[PSONScheme alloc] init]); [privateWebViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto regularWebViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [regularWebViewConfiguration setProcessPool:processPool.get()]; [regularWebViewConfiguration setWebsiteDataStore:[WKWebsiteDataStore defaultDataStore]]; [regularWebViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); auto regularWebView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:regularWebViewConfiguration.get()]); [regularWebView1 setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [regularWebView1 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; [regularWebView1 _close]; regularWebView1 = nil; auto privateWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:privateWebViewConfiguration.get()]); [privateWebView setNavigationDelegate:delegate.get()]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [privateWebView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto privateSessionWebkitPID = [privateWebView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [privateWebView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto privateSessionApplePID = [privateWebView _webProcessIdentifier]; EXPECT_NE(privateSessionWebkitPID, privateSessionApplePID); [privateWebView _close]; privateWebView = nil; auto regularWebView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:regularWebViewConfiguration.get()]); [regularWebView2 setNavigationDelegate:delegate.get()]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [regularWebView2 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto regularSessionGooglePID = [regularWebView2 _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [regularWebView2 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto regularSessionWebkitPID = [regularWebView2 _webProcessIdentifier]; EXPECT_NE(regularSessionGooglePID, regularSessionWebkitPID); EXPECT_NE(privateSessionWebkitPID, regularSessionWebkitPID); } static const char* keepNavigatingFrameBytes = R"PSONRESOURCE( )PSONRESOURCE"; enum class RetainPageInBundle { No, Yes }; void testReuseSuspendedProcessForRegularNavigation(RetainPageInBundle retainPageInBundle) { auto processPoolConfiguration = psonProcessPoolConfiguration(); if (retainPageInBundle == RetainPageInBundle::Yes) [processPoolConfiguration setInjectedBundleURL:[[NSBundle mainBundle] URLForResource:@"TestWebKitAPI" withExtension:@"wkbundle"]]; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); if (retainPageInBundle == RetainPageInBundle::Yes) [processPool _setObject:@"BundleRetainPagePlugIn" forBundleParameter:TestWebKitAPI::Util::TestPlugInClassNameParameter]; auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main1.html" toData:keepNavigatingFrameBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); } TEST(ProcessSwap, ReuseSuspendedProcessForRegularNavigationRetainBundlePage) { testReuseSuspendedProcessForRegularNavigation(RetainPageInBundle::Yes); } TEST(ProcessSwap, ReuseSuspendedProcessForRegularNavigation) { testReuseSuspendedProcessForRegularNavigation(RetainPageInBundle::No); } static const char* mainFramesOnlyMainFrame = R"PSONRESOURCE( Some text )PSONRESOURCE"; static const char* mainFramesOnlySubframe = R"PSONRESOURCE( Some content )PSONRESOURCE"; static const char* mainFramesOnlySubframe2 = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, MainFramesOnly) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:mainFramesOnlyMainFrame]; [handler addMappingFromURLString:@"pson://www.webkit.org/iframe" toData:mainFramesOnlySubframe]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:mainFramesOnlySubframe2]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; EXPECT_EQ(1u, seenPIDs.size()); } #if PLATFORM(MAC) static const char* getClientWidthBytes = R"PSONRESOURCE( TEST )PSONRESOURCE"; static unsigned waitUntilClientWidthIs(WKWebView *webView, unsigned expectedClientWidth) { int timeout = 10; unsigned clientWidth = 0; do { if (timeout != 10) TestWebKitAPI::Util::sleep(0.1); [webView evaluateJavaScript:@"getClientWidth()" completionHandler: [&] (id result, NSError *error) { clientWidth = [result integerValue]; done = true; }]; TestWebKitAPI::Util::run(&done); done = false; --timeout; } while (clientWidth != expectedClientWidth && timeout >= 0); return clientWidth; } TEST(ProcessSwap, PageZoomLevelAfterSwap) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:getClientWidthBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:getClientWidthBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; delegate->didCommitNavigationHandler = ^ { [webView _setPageZoomFactor:2.0]; delegate->didCommitNavigationHandler = nil; }; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; unsigned clientWidth = waitUntilClientWidthIs(webView.get(), 400); EXPECT_EQ(400U, clientWidth); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; clientWidth = waitUntilClientWidthIs(webView.get(), 400); EXPECT_EQ(400U, clientWidth); // Kill the WebProcess, the page should reload automatically and the page zoom level should be maintained. kill([webView _webProcessIdentifier], 9); TestWebKitAPI::Util::run(&done); done = false; clientWidth = waitUntilClientWidthIs(webView.get(), 400); EXPECT_EQ(400U, clientWidth); } #endif // PLATFORM(MAC) static const char* mediaTypeBytes = R"PSONRESOURCE(
Screen
Print
)PSONRESOURCE"; TEST(ProcessSwap, MediaTypeAfterSwap) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:mediaTypeBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:mediaTypeBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; NSString *innerText = [[webView stringByEvaluatingJavaScript:@"document.body.innerText"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; EXPECT_TRUE([innerText isEqualToString:@"Screen"]); webView.get().mediaType = @"print"; innerText = [[webView stringByEvaluatingJavaScript:@"document.body.innerText"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; EXPECT_TRUE([innerText isEqualToString:@"Print"]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; innerText = [[webView stringByEvaluatingJavaScript:@"document.body.innerText"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; EXPECT_TRUE([innerText isEqualToString:@"Print"]); // Kill the WebProcess, the page should reload automatically and the media type should be maintained. kill([webView _webProcessIdentifier], 9); TestWebKitAPI::Util::run(&done); done = false; innerText = [[webView stringByEvaluatingJavaScript:@"document.body.innerText"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; EXPECT_TRUE([innerText isEqualToString:@"Print"]); } static const char* navigateBeforePageLoadEndBytes = R"PSONRESOURCE( Link

TEST

)PSONRESOURCE"; TEST(ProcessSwap, NavigateCrossSiteBeforePageLoadEnd) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:navigateBeforePageLoadEndBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; [webView configuration].preferences.fraudulentWebsiteWarningEnabled = NO; failed = false; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_FALSE(failed); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); } static void runCancelCrossSiteProvisionalLoadTest(ShouldEnablePSON shouldEnablePSON) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().processSwapsOnNavigation = shouldEnablePSON == ShouldEnablePSON::Yes; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; failed = false; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_FALSE(failed); navigationDelegate->didStartProvisionalNavigationHandler = ^{ [webView stopLoading]; }; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&failed); failed = false; } TEST(ProcessSwap, CancelCrossSiteProvisionalLoadWithoutPSON) { runCancelCrossSiteProvisionalLoadTest(ShouldEnablePSON::No); } TEST(ProcessSwap, CancelCrossSiteProvisionalLoadWithPSON) { runCancelCrossSiteProvisionalLoadTest(ShouldEnablePSON::Yes); } TEST(ProcessSwap, DoSameSiteNavigationAfterCrossSiteProvisionalLoadStarted) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; [webView configuration].preferences.fraudulentWebsiteWarningEnabled = NO; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; delegate->decidePolicyForNavigationAction = ^(WKNavigationAction *, void (^decisionHandler)(WKNavigationActionPolicy)) { decisionHandler(WKNavigationActionPolicyAllow); delegate->decidePolicyForNavigationAction = nil; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]]; }; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.webkit.org/main2.html", [[webView URL] absoluteString]); EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); } TEST(ProcessSwap, SuspendedPageLimit) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; auto maximumSuspendedPageCount = [processPool _maximumSuspendedPageCount]; EXPECT_GT(maximumSuspendedPageCount, 0U); NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.bing.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.yahoo.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; // Navigations to 5 different domains, we expect to have seen 5 different PIDs EXPECT_EQ(5u, seenPIDs.size()); // But not all of those processes should still be alive (1 visible, maximumSuspendedPageCount suspended). auto expectedProcessCount = 1 + maximumSuspendedPageCount; int timeout = 20; while ([processPool _webProcessCountIgnoringPrewarmedAndCached] != expectedProcessCount && timeout >= 0) { TestWebKitAPI::Util::sleep(0.1); --timeout; } EXPECT_EQ(expectedProcessCount, [processPool _webProcessCountIgnoringPrewarmedAndCached]); } TEST(ProcessSwap, PageCache1) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:pageCache1Bytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:pageCache1Bytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad1 = [webView _webProcessIdentifier]; EXPECT_EQ(1u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad2 = [webView _webProcessIdentifier]; EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_NE(pidAfterLoad1, pidAfterLoad2); [webView goBack]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad3 = [webView _webProcessIdentifier]; EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_EQ(pidAfterLoad1, pidAfterLoad3); EXPECT_EQ(1u, [receivedMessages count]); EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@"Was persisted" ]); EXPECT_EQ(2u, seenPIDs.size()); [webView goForward]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad4 = [webView _webProcessIdentifier]; EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_EQ(pidAfterLoad2, pidAfterLoad4); EXPECT_EQ(2u, [receivedMessages count]); EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"Was persisted" ]); EXPECT_EQ(2u, seenPIDs.size()); } TEST(ProcessSwap, NavigateBackAfterCrossOriginClientRedirect) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://webkit.org/navigated_from" toData:"hello"]; [handler addMappingFromURLString:@"pson://apple.com/" toData:"redirecting..."]; [handler addMappingFromURLString:@"pson://webkit.org/redirected_to" toData:"

hello again

"]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://webkit.org/navigated_from"]]]; [webView _test_waitForDidFinishNavigation]; [webView evaluateJavaScript:@"document.querySelector('a').click()" completionHandler:nil]; [webView _test_waitForDidFinishNavigation]; [webView _test_waitForDidFinishNavigation]; EXPECT_WK_STREQ([webView objectByEvaluatingJavaScript:@"window.location.href"], "pson://webkit.org/redirected_to"); [webView goBack]; [webView _test_waitForDidFinishNavigation]; EXPECT_WK_STREQ([webView objectByEvaluatingJavaScript:@"window.location.href"], "pson://webkit.org/navigated_from"); } TEST(ProcessSwap, BackForwardCacheSkipBackForwardListItem) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:pageCache1Bytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:pageCache1Bytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html#foo"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); EXPECT_EQ(1U, [[[webView backForwardList] backList] count]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); // Go 2 items back. EXPECT_EQ(2U, [[[webView backForwardList] backList] count]); [webView goToBackForwardListItem:[[webView backForwardList] itemAtIndex:-2]]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView URL] absoluteString]); // Go 2 items forward. EXPECT_EQ(2U, [[[webView backForwardList] forwardList] count]); [webView goToBackForwardListItem:[[webView backForwardList] itemAtIndex:2]]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(applePID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); // Go back. EXPECT_EQ(2U, [[[webView backForwardList] backList] count]); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html#foo", [[webView URL] absoluteString]); } TEST(ProcessSwap, ClearWebsiteDataWithSuspendedPage) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().usesWebProcessCache = NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:pageCache1Bytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:pageCache1Bytes]; [handler addMappingFromURLString:@"pson://www.google.com/main.html" toData:pageCache1Bytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad1 = [webView _webProcessIdentifier]; EXPECT_EQ(1u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad2 = [webView _webProcessIdentifier]; EXPECT_NE(pidAfterLoad1, pidAfterLoad2); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad3 = [webView _webProcessIdentifier]; EXPECT_NE(pidAfterLoad2, pidAfterLoad3); __block bool readyToContinue = false; [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore _allWebsiteDataTypesIncludingPrivate] modifiedSince:[NSDate distantPast] completionHandler:^() { readyToContinue = true; }]; TestWebKitAPI::Util::run(&readyToContinue); } TEST(ProcessSwap, PageCacheAfterProcessSwapByClient) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main1.html" toData:pageCache1Bytes]; [handler addMappingFromURLString:@"pson://www.webkit.org/main2.html" toData:pageCache1Bytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad1 = [webView _webProcessIdentifier]; EXPECT_EQ(1u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); // We force a proces-swap via client API. delegate->decidePolicyForNavigationAction = ^(WKNavigationAction *, void (^decisionHandler)(WKNavigationActionPolicy)) { decisionHandler(_WKNavigationActionPolicyAllowInNewProcess); }; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad2 = [webView _webProcessIdentifier]; EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_NE(pidAfterLoad1, pidAfterLoad2); delegate->decidePolicyForNavigationAction = nil; [webView goBack]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad3 = [webView _webProcessIdentifier]; EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_EQ(pidAfterLoad1, pidAfterLoad3); EXPECT_EQ(1u, [receivedMessages count]); EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@"Was persisted" ]); EXPECT_EQ(2u, seenPIDs.size()); [webView goForward]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad4 = [webView _webProcessIdentifier]; EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_EQ(pidAfterLoad2, pidAfterLoad4); EXPECT_EQ(2u, [receivedMessages count]); EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"Was persisted" ]); EXPECT_EQ(2u, seenPIDs.size()); } TEST(ProcessSwap, PageCacheWhenNavigatingFromJS) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:pageCache1Bytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:pageCache1Bytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad1 = [webView _webProcessIdentifier]; EXPECT_EQ(1u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad2 = [webView _webProcessIdentifier]; EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_NE(pidAfterLoad1, pidAfterLoad2); [webView evaluateJavaScript:@"history.back()" completionHandler: nil]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad3 = [webView _webProcessIdentifier]; EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_EQ(pidAfterLoad1, pidAfterLoad3); EXPECT_EQ(1u, [receivedMessages count]); EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@"Was persisted" ]); EXPECT_EQ(2u, seenPIDs.size()); [webView evaluateJavaScript:@"history.forward()" completionHandler: nil]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; auto pidAfterLoad4 = [webView _webProcessIdentifier]; EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_EQ(pidAfterLoad2, pidAfterLoad4); EXPECT_EQ(2u, [receivedMessages count]); EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"Was persisted" ]); EXPECT_EQ(2u, seenPIDs.size()); } #if PLATFORM(MAC) // WebProcessCache is disabled on devices with too little RAM. TEST(ProcessSwap, UseWebProcessCacheAfterTermination) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]); [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) { done = true; }]; int webkitPID = 0; @autoreleasepool { auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; webkitPID = [webView _webProcessIdentifier]; } EXPECT_EQ(1U, [processPool _webProcessCountIgnoringPrewarmed]); auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); EXPECT_EQ(2U, [processPool _webProcessCountIgnoringPrewarmed]); __block bool webProcessTerminated = false; [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView *) { webProcessTerminated = true; }]; kill(applePID, 9); TestWebKitAPI::Util::run(&webProcessTerminated); webProcessTerminated = false; EXPECT_EQ(0, [webView _webProcessIdentifier]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); EXPECT_EQ(1U, [processPool _webProcessCountIgnoringPrewarmed]); } TEST(ProcessSwap, ProcessCrashedWhileInTheCache) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]); [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) { done = true; }]; int webkitPID = 0; @autoreleasepool { auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; webkitPID = [webView _webProcessIdentifier]; } while ([processPool _processCacheSize] != 1) TestWebKitAPI::Util::sleep(0.1); kill(webkitPID, 9); while ([processPool _processCacheSize]) TestWebKitAPI::Util::sleep(0.1); auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_NE(webkitPID, [webView _webProcessIdentifier]); } TEST(ProcessSwap, ProcessTerminatedWhileInTheCache) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]); [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) { done = true; }]; int webkitPID = 0; @autoreleasepool { auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; webkitPID = [webView _webProcessIdentifier]; } while ([processPool _processCacheSize] != 1) TestWebKitAPI::Util::sleep(0.1); EXPECT_TRUE([processPool _requestWebProcessTermination:webkitPID]); TestWebKitAPI::Util::spinRunLoop(100); EXPECT_EQ(0U, [processPool _processCacheSize]); auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_NE(webkitPID, [webView _webProcessIdentifier]); } TEST(ProcessSwap, UseWebProcessCacheForLoadInNewView) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); int webkitPID = 0; @autoreleasepool { auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView setNavigationDelegate:navigationDelegate.get()]; // Process launch should be delayed until a load actually happens. EXPECT_EQ(0, [webView _webProcessIdentifier]); TestWebKitAPI::Util::sleep(0.1); EXPECT_EQ(0, [webView _webProcessIdentifier]); NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; webkitPID = [webView _webProcessIdentifier]; EXPECT_NE(0, webkitPID); } while ([processPool _processCacheSize] != 1) TestWebKitAPI::Util::sleep(0.1); EXPECT_EQ(1U, [processPool _webProcessCountIgnoringPrewarmed]); @autoreleasepool { auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); EXPECT_EQ(1U, [processPool _webProcessCountIgnoringPrewarmed]); } while ([processPool _processCacheSize] != 1) TestWebKitAPI::Util::sleep(0.1); EXPECT_EQ(1U, [processPool _webProcessCountIgnoringPrewarmed]); @autoreleasepool { auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView setNavigationDelegate:navigationDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_NE(webkitPID, [webView _webProcessIdentifier]); } EXPECT_EQ(2U, [processPool _webProcessCountIgnoringPrewarmed]); } #endif // PLATFORM(MAC) TEST(ProcessSwap, NumberOfPrewarmedProcesses) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(2u, [processPool _webProcessCount]); EXPECT_EQ(1u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_TRUE([processPool _hasPrewarmedWebProcess]); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(3u, [processPool _webProcessCount]); EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_TRUE([processPool _hasPrewarmedWebProcess]); } #if PLATFORM(MAC) TEST(ProcessSwap, NumberOfCachedProcesses) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().prewarmsProcessesAutomatically = NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); EXPECT_GT([processPool _maximumSuspendedPageCount], 0u); EXPECT_GT([processPool _processCacheCapacity], 0u); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); const unsigned maxSuspendedPageCount = [processPool _maximumSuspendedPageCount]; for (unsigned i = 0; i < maxSuspendedPageCount + 2; i++) [handler addMappingFromURLString:makeString("pson://www.domain-", i, ".com") toData:pageCache1Bytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; for (unsigned i = 0; i < maxSuspendedPageCount + 1; i++) { NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:makeString("pson://www.domain-", i, ".com")]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(i + 1, [processPool _webProcessCount]); EXPECT_EQ(i + 1, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_FALSE([processPool _hasPrewarmedWebProcess]); } NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:makeString("pson://www.domain-", maxSuspendedPageCount + 1, ".com")]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; int timeout = 100; while (([processPool _webProcessCount] > (maxSuspendedPageCount + 2) && [processPool _webProcessCountIgnoringPrewarmedAndCached] > (maxSuspendedPageCount + 1)) && timeout > 0) { TestWebKitAPI::Util::sleep(0.1); --timeout; } EXPECT_EQ(maxSuspendedPageCount + 2, [processPool _webProcessCount]); EXPECT_EQ(maxSuspendedPageCount + 1, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_FALSE([processPool _hasPrewarmedWebProcess]); static bool readyToContinue = false; [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore _allWebsiteDataTypesIncludingPrivate] modifiedSince:[NSDate distantPast] completionHandler:^() { readyToContinue = true; }]; TestWebKitAPI::Util::run(&readyToContinue); timeout = 100; while ([processPool _webProcessCount] > 1 && timeout > 0) { TestWebKitAPI::Util::sleep(0.1); --timeout; } EXPECT_EQ(1u, [processPool _webProcessCount]); EXPECT_EQ(1u, [processPool _webProcessCountIgnoringPrewarmedAndCached]); EXPECT_FALSE([processPool _hasPrewarmedWebProcess]); } #endif // PLATFORM(MAC) static const char* visibilityBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, PageShowHide) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:visibilityBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:visibilityBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; [webView goBack]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; [webView goForward]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; while ([receivedMessages count] < 7) TestWebKitAPI::Util::sleep(0.1); EXPECT_EQ(7u, [receivedMessages count]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pageshow NOT persisted", receivedMessages.get()[0]); if ([receivedMessages.get()[1] hasPrefix:@"pson://www.webkit.org/main.html"]) { EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pagehide persisted", receivedMessages.get()[1]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pageshow NOT persisted", receivedMessages.get()[2]); } else { EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pageshow NOT persisted", receivedMessages.get()[1]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pagehide persisted", receivedMessages.get()[2]); } if ([receivedMessages.get()[3] hasPrefix:@"pson://www.apple.com/main.html"]) { EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pagehide persisted", receivedMessages.get()[3]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pageshow persisted", receivedMessages.get()[4]); } else { EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pageshow persisted", receivedMessages.get()[3]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pagehide persisted", receivedMessages.get()[4]); } if ([receivedMessages.get()[5] hasPrefix:@"pson://www.webkit.org/main.html"]) { EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pagehide persisted", receivedMessages.get()[5]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pageshow persisted", receivedMessages.get()[6]); } else { EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pageshow persisted", receivedMessages.get()[5]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pagehide persisted", receivedMessages.get()[6]); } } // Disabling the back/forward cache explicitly is (for some reason) not available on iOS. #if !TARGET_OS_IPHONE static const char* loadUnloadBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, LoadUnload) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:loadUnloadBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:loadUnloadBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; [[webViewConfiguration preferences] _setUsesPageCache:NO]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; [webView goBack]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; [webView goForward]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; TestWebKitAPI::Util::run(&done); done = false; while ([receivedMessages count] < 7) TestWebKitAPI::Util::sleep(0.1); EXPECT_EQ(7u, [receivedMessages count]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - load", receivedMessages.get()[0]); if ([receivedMessages.get()[1] hasPrefix:@"pson://www.webkit.org/main.html"]) { EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - unload", receivedMessages.get()[1]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - load", receivedMessages.get()[2]); } else { EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - load", receivedMessages.get()[1]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - unload", receivedMessages.get()[2]); } if ([receivedMessages.get()[3] hasPrefix:@"pson://www.apple.com/main.html"]) { EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - unload", receivedMessages.get()[3]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - load", receivedMessages.get()[4]); } else { EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - load", receivedMessages.get()[3]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - unload", receivedMessages.get()[4]); } if ([receivedMessages.get()[5] hasPrefix:@"pson://www.webkit.org/main.html"]) { EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - unload", receivedMessages.get()[5]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - load", receivedMessages.get()[6]); } else { EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - load", receivedMessages.get()[5]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - unload", receivedMessages.get()[6]); } } TEST(ProcessSwap, WebInspector) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; webViewConfiguration.get().preferences._developerExtrasEnabled = YES; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; [[webView _inspector] show]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; [[webView _inspector] close]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid3 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); // We should have swapped. EXPECT_NE(pid2, pid3); // We should have swapped again. EXPECT_EQ(numberOfDecidePolicyCalls, 3); } TEST(ProcessSwap, WebInspectorDelayedProcessLaunch) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; webViewConfiguration.get().preferences._developerExtrasEnabled = YES; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); EXPECT_EQ(0, [webView _webProcessIdentifier]); TestWebKitAPI::Util::spinRunLoop(100); EXPECT_EQ(0, [webView _webProcessIdentifier]); [[webView _inspector] show]; EXPECT_TRUE([[webView _inspector] isConnected]); // Trying to inspect the view should launch a WebProcess. while (![webView _webProcessIdentifier]) TestWebKitAPI::Util::spinRunLoop(10); EXPECT_NE(0, [webView _webProcessIdentifier]); [[webView _inspector] close]; } #endif // !TARGET_OS_IPHONE static const char* sameOriginBlobNavigationTestBytes = R"PSONRESOURCE(

Click here

)PSONRESOURCE"; TEST(ProcessSwap, SameOriginBlobNavigation) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] initWithBytes:sameOriginBlobNavigationTestBytes]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); [webView _evaluateJavaScriptWithoutUserGesture:@"document.getElementById('link').click()" completionHandler: nil]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); EXPECT_EQ(2, numberOfDecidePolicyCalls); EXPECT_EQ(pid1, pid2); } TEST(ProcessSwap, CrossOriginBlobNavigation) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] initWithBytes:sameOriginBlobNavigationTestBytes]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); bool finishedRunningScript = false; String blobURL; [webView _evaluateJavaScriptWithoutUserGesture:@"document.getElementById('link').href" completionHandler: [&] (id result, NSError *error) { blobURL = String([NSString stringWithFormat:@"%@", result]); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); finishedRunningScript = false; String script = "document.getElementById('link').href = '" + blobURL + "'"; [webView _evaluateJavaScriptWithoutUserGesture:(NSString *)script completionHandler: [&] (id result, NSError *error) { finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); // This navigation will fail. [webView _evaluateJavaScriptWithoutUserGesture:@"document.getElementById('link').click()" completionHandler: [&] (id result, NSError *error) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; auto pid3 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid3); EXPECT_EQ(2, numberOfDecidePolicyCalls); EXPECT_NE(pid1, pid2); EXPECT_EQ(pid2, pid3); } TEST(ProcessSwap, NavigateToAboutBlank) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] initWithBytes:sameOriginBlobNavigationTestBytes]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); EXPECT_EQ(2, numberOfDecidePolicyCalls); EXPECT_EQ(pid1, pid2); } TEST(ProcessSwap, NavigateToDataURL) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] initWithBytes:sameOriginBlobNavigationTestBytes]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"data:text/plain,PASS"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); EXPECT_EQ(2, numberOfDecidePolicyCalls); EXPECT_EQ(pid1, pid2); } TEST(ProcessSwap, ProcessReuse) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid3 = [webView _webProcessIdentifier]; // Two process swaps have occurred, but we should only have ever seen 2 pids. EXPECT_EQ(2u, seenPIDs.size()); EXPECT_NE(pid1, pid2); EXPECT_NE(pid2, pid3); EXPECT_EQ(pid1, pid3); } TEST(ProcessSwap, ProcessReuseeTLDPlus2) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www1.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www2.webkit.org/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid3 = [webView _webProcessIdentifier]; // Two process swaps have occurred, but we should only have ever seen 2 pids. EXPECT_EQ(2u, seenPIDs.size()); EXPECT_NE(pid1, pid2); EXPECT_NE(pid2, pid3); EXPECT_EQ(pid1, pid3); } TEST(ProcessSwap, ConcurrentHistoryNavigations) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView URL] absoluteString]); auto* backForwardList = [webView backForwardList]; EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backForwardList.currentItem.URL absoluteString]); EXPECT_TRUE(!backForwardList.backItem); EXPECT_EQ(1U, backForwardList.forwardList.count); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [backForwardList.forwardItem.URL absoluteString]); // Concurrent requests to go forward, which process swaps. [webView goForward]; [webView goForward]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(applePID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [backForwardList.currentItem.URL absoluteString]); EXPECT_TRUE(!backForwardList.forwardItem); EXPECT_EQ(1U, backForwardList.backList.count); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backForwardList.backItem.URL absoluteString]); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_NE(applePID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView URL] absoluteString]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backForwardList.currentItem.URL absoluteString]); EXPECT_TRUE(!backForwardList.backItem); EXPECT_EQ(1U, backForwardList.forwardList.count); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [backForwardList.forwardItem.URL absoluteString]); } TEST(ProcessSwap, NavigateToInvalidURL) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); EXPECT_EQ(1, numberOfDecidePolicyCalls); __block bool evaluated = false; [webView evaluateJavaScript:@"var err = 'no error'; try { location.href = 'http://A=a%B=b' } catch(e) { err=e; }; err.message" completionHandler:^(id result, NSError *error) { EXPECT_WK_STREQ(result, "Invalid URL"); EXPECT_NULL(error); evaluated = true; }]; TestWebKitAPI::Util::run(&evaluated); TestWebKitAPI::Util::spinRunLoop(1); auto pid2 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); EXPECT_EQ(1, numberOfDecidePolicyCalls); EXPECT_EQ(pid1, pid2); } static const char* navigateToDataURLThenBackBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, NavigateToDataURLThenBack) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().pageCacheEnabled = NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; webViewConfiguration.get()._allowTopNavigationToDataURLs = YES; auto handler = adoptNS([[PSONScheme alloc] initWithBytes:navigateToDataURLThenBackBytes]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; numberOfDecidePolicyCalls = 0; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; TestWebKitAPI::Util::run(&done); done = false; auto pid3 = [webView _webProcessIdentifier]; EXPECT_EQ(3, numberOfDecidePolicyCalls); EXPECT_EQ(1u, seenPIDs.size()); EXPECT_EQ(pid1, pid2); EXPECT_EQ(pid2, pid3); } TEST(ProcessSwap, NavigateCrossSiteWithPageCacheDisabled) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().pageCacheEnabled = NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_NE(applePID, [webView _webProcessIdentifier]); } TEST(ProcessSwap, APIControlledProcessSwapping) { auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); auto handler = adoptNS([[PSONScheme alloc] initWithBytes:"Hello World!"]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; numberOfDecidePolicyCalls = 0; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/1"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; // Navigating from the above URL to this URL normally should not process swap. [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/2"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_EQ(1u, seenPIDs.size()); EXPECT_EQ(pid1, pid2); // Navigating from the above URL to this URL normally should not process swap, // but we'll explicitly ask for a swap. navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction *, void (^decisionHandler)(WKNavigationActionPolicy)) { decisionHandler(_WKNavigationActionPolicyAllowInNewProcess); }; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/3"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid3 = [webView _webProcessIdentifier]; EXPECT_EQ(3, numberOfDecidePolicyCalls); EXPECT_EQ(2u, seenPIDs.size()); EXPECT_NE(pid1, pid3); } enum class WithDelay : bool { No, Yes }; static void runAPIControlledProcessSwappingThenBackTest(WithDelay withDelay) { auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); auto handler = adoptNS([[PSONScheme alloc] initWithBytes:"Hello World!"]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction *, void (^decisionHandler)(WKNavigationActionPolicy)) { decisionHandler(_WKNavigationActionPolicyAllowInNewProcess); }; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); // Give time to the suspended WebPage to close. if (withDelay == WithDelay::Yes) TestWebKitAPI::Util::sleep(0.1); navigationDelegate->decidePolicyForNavigationAction = nil; [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(pid1, [webView _webProcessIdentifier]); } TEST(ProcessSwap, APIControlledProcessSwappingThenBackWithDelay) { runAPIControlledProcessSwappingThenBackTest(WithDelay::Yes); } TEST(ProcessSwap, APIControlledProcessSwappingThenBackWithoutDelay) { runAPIControlledProcessSwappingThenBackTest(WithDelay::No); } static const char* navigateToCrossSiteThenBackFromJSBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* navigateBackFromJSBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, NavigateToCrossSiteThenBackFromJS) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().pageCacheEnabled = NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:navigateToCrossSiteThenBackFromJSBytes]; // Navigates to "pson://www.apple.com/main.html". [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:navigateBackFromJSBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; numberOfDecidePolicyCalls = 0; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); // Should have process-swapped when going from webkit.org to apple.com. // Page now calls history.back() to navigate back to webkit.org. TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(3, numberOfDecidePolicyCalls); // Should have process-swapped when going from apple.com to webkit.org. // PID is not necessarily webkitPID because PageCache is disabled. EXPECT_NE(applePID, [webView _webProcessIdentifier]); } static const char* crossSiteFormSubmissionBytes = R"PSONRESOURCE(
Name:
)PSONRESOURCE"; TEST(ProcessSwap, SwapOnFormSubmission) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:crossSiteFormSubmissionBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView URL] absoluteString]); [webView evaluateJavaScript:@"submitButton.click()" completionHandler:nil]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView _webProcessIdentifier]; EXPECT_NE(webkitPID, applePID); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); [webView reload]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(applePID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView URL] absoluteString]); [webView goForward]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(applePID, [webView _webProcessIdentifier]); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; #if !PLATFORM(IOS_FAMILY) // This is not guaranteed on iOS because the WebProcess cache is disabled on devices with too little RAM. EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); #else EXPECT_NE(applePID, [webView _webProcessIdentifier]); #endif EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView URL] absoluteString]); } TEST(ProcessSwap, ClosePageAfterCrossSiteProvisionalLoad) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; [webView configuration].preferences.fraudulentWebsiteWarningEnabled = NO; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; didStartProvisionalLoad = false; [webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]]; navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction *, void (^decisionHandler)(WKNavigationActionPolicy)) { decisionHandler(WKNavigationActionPolicyAllow); [webView _close]; done = true; }; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::sleep(0.5); } static Vector loadingStateChanges; static unsigned urlChangeCount; @interface PSONLoadingObserver : NSObject @end @implementation PSONLoadingObserver -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { WKWebView *webView = (WKWebView *)context; if ([keyPath isEqualToString:@"loading"]) loadingStateChanges.append([webView isLoading]); else if ([keyPath isEqualToString:@"URL"]) ++urlChangeCount; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); } @end TEST(ProcessSwap, LoadingStateAfterPolicyDecision) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; [webView configuration].preferences.fraudulentWebsiteWarningEnabled = NO; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto loadObserver = adoptNS([[PSONLoadingObserver alloc] init]); [webView addObserver:loadObserver.get() forKeyPath:@"loading" options:0 context:webView.get()]; [webView addObserver:loadObserver.get() forKeyPath:@"URL" options:0 context:webView.get()]; urlChangeCount = 0; didStartProvisionalLoad = false; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]]; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); EXPECT_TRUE([webView isLoading]); EXPECT_EQ(1U, loadingStateChanges.size()); EXPECT_TRUE(loadingStateChanges[0]); EXPECT_EQ(1U, urlChangeCount); navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction *, void (^decisionHandler)(WKNavigationActionPolicy)) { EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); EXPECT_TRUE([webView isLoading]); decisionHandler(WKNavigationActionPolicyAllow); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); EXPECT_TRUE([webView isLoading]); }; EXPECT_EQ(1U, loadingStateChanges.size()); TestWebKitAPI::Util::run(&didStartProvisionalLoad); didStartProvisionalLoad = false; EXPECT_EQ(1U, loadingStateChanges.size()); EXPECT_EQ(1U, urlChangeCount); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); EXPECT_TRUE([webView isLoading]); TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); EXPECT_FALSE([webView isLoading]); EXPECT_EQ(2U, loadingStateChanges.size()); EXPECT_FALSE(loadingStateChanges[1]); EXPECT_EQ(1U, urlChangeCount); [webView removeObserver:loadObserver.get() forKeyPath:@"loading" context:webView.get()]; [webView removeObserver:loadObserver.get() forKeyPath:@"URL" context:webView.get()]; } static const char* saveOpenerTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, OpenerLinkAfterAPIControlledProcessSwappingOfOpener) { auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main1.html" toData:windowOpenSameSiteWithOpenerTestBytes]; // Opens "pson://www.webkit.org/main2.html". [handler addMappingFromURLString:@"pson://www.webkit.org/main2.html" toData:saveOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(pid1, [createdWebView _webProcessIdentifier]); // Auxiliary window should have an opener. [createdWebView evaluateJavaScript:@"window.opener.location.href" completionHandler: [&] (id hasOpener, NSError *error) { EXPECT_WK_STREQ(@"pson://www.webkit.org/main1.html", hasOpener); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; // We force a proces-swap via client API. navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction *, void (^decisionHandler)(WKNavigationActionPolicy)) { decisionHandler(_WKNavigationActionPolicyAllowInNewProcess); }; // Navigating from the above URL to this URL normally should not process swap. [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main3.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); bool hasOpener = true; int timeout = 50; do { if (timeout != 50) TestWebKitAPI::Util::sleep(0.1); // Auxiliary window's opener should no longer have an opener. [createdWebView evaluateJavaScript:@"window.opener ? 'true' : 'false'" completionHandler: [&] (id hasOpenerString, NSError *error) { hasOpener = [hasOpenerString isEqualToString:@"true"]; done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } while (hasOpener && (--timeout)); EXPECT_FALSE(hasOpener); [createdWebView evaluateJavaScript:@"savedOpener.closed ? 'true' : 'false'" completionHandler: [&] (id savedOpenerIsClosed, NSError *error) { EXPECT_WK_STREQ(@"true", savedOpenerIsClosed); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } TEST(ProcessSwap, OpenerLinkAfterAPIControlledProcessSwappingOfOpenee) { auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main1.html" toData:windowOpenSameSiteWithOpenerTestBytes]; // Opens "pson://www.webkit.org/main2.html". [handler addMappingFromURLString:@"pson://www.webkit.org/main2.html" toData:saveOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(pid1, [createdWebView _webProcessIdentifier]); // Auxiliary window should have an opener. [webView evaluateJavaScript:@"w.opener.location.href" completionHandler: [&] (id hasOpener, NSError *error) { EXPECT_WK_STREQ(@"pson://www.webkit.org/main1.html", hasOpener); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; // We force a proces-swap via client API. navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction *, void (^decisionHandler)(WKNavigationActionPolicy)) { decisionHandler(_WKNavigationActionPolicyAllowInNewProcess); }; // Navigating from the above URL to this URL normally should not process swap. [createdWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main3.html"]]]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); // Auxiliary window's opener should no longer have an opener. [webView evaluateJavaScript:@"w.opener ? 'true' : 'false'" completionHandler: [&] (id hasOpener, NSError *error) { EXPECT_WK_STREQ(@"false", hasOpener); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } static void runProcessSwapDueToRelatedWebViewTest(NSURL* relatedViewURL, NSURL* targetURL, ExpectSwap expectSwap) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webView1Configuration = adoptNS([[WKWebViewConfiguration alloc] init]); [webView1Configuration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webView1Configuration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webView1Configuration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView1 setNavigationDelegate:delegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:relatedViewURL]; [webView1 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView1 _webProcessIdentifier]; auto webView2Configuration = adoptNS([[WKWebViewConfiguration alloc] init]); [webView2Configuration setProcessPool:processPool.get()]; [webView2Configuration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; webView2Configuration.get()._relatedWebView = webView1.get(); // webView2 will be related to webView1 and webView1's URL will be used for process swap decision. auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webView2Configuration.get()]); [webView2 setNavigationDelegate:delegate.get()]; request = [NSURLRequest requestWithURL:targetURL]; [webView2 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView2 _webProcessIdentifier]; if (expectSwap == ExpectSwap::No) EXPECT_TRUE(pid1 == pid2); else EXPECT_FALSE(pid1 == pid2); EXPECT_EQ(2, numberOfDecidePolicyCalls); } TEST(ProcessSwap, ProcessSwapDueToRelatedView) { runProcessSwapDueToRelatedWebViewTest([NSURL URLWithString:@"pson://www.webkit.org/main1.html"], [NSURL URLWithString:@"pson://www.apple.com/main2.html"], ExpectSwap::Yes); } TEST(ProcessSwap, NoProcessSwapDueToRelatedView) { runProcessSwapDueToRelatedWebViewTest([NSURL URLWithString:@"pson://www.webkit.org/main1.html"], [NSURL URLWithString:@"pson://www.webkit.org/main2.html"], ExpectSwap::No); } TEST(ProcessSwap, RelatedWebViewBeforeWebProcessLaunch) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webView1Configuration = adoptNS([[WKWebViewConfiguration alloc] init]); [webView1Configuration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webView1Configuration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webView1Configuration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView1 setNavigationDelegate:delegate.get()]; auto webView2Configuration = adoptNS([[WKWebViewConfiguration alloc] init]); [webView2Configuration setProcessPool:processPool.get()]; [webView2Configuration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; webView2Configuration.get()._relatedWebView = webView1.get(); // webView2 will be related to webView1 and webView1's URL will be used for process swap decision. auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webView2Configuration.get()]); [webView2 setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView1 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView1 _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView2 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView2 _webProcessIdentifier]; EXPECT_EQ(pid1, pid2); // WebViews are related so they should share the same process. } TEST(ProcessSwap, ReloadRelatedWebViewAfterCrash) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webView1Configuration = adoptNS([[WKWebViewConfiguration alloc] init]); [webView1Configuration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webView1Configuration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webView1Configuration.get()]); auto delegate = adoptNS([[TestNavigationDelegate alloc] init]); __block bool didCrash = false; [delegate setWebContentProcessDidTerminate:^(WKWebView *view) { [view reload]; didCrash = true; }]; [delegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) { done = true; }]; [webView1 setNavigationDelegate:delegate.get()]; auto webView2Configuration = adoptNS([[WKWebViewConfiguration alloc] init]); [webView2Configuration setProcessPool:processPool.get()]; [webView2Configuration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; webView2Configuration.get()._relatedWebView = webView1.get(); // webView2 will be related to webView1 and webView1's URL will be used for process swap decision. auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webView2Configuration.get()]); [webView2 setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView1 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView1 _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]]; [webView2 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView2 _webProcessIdentifier]; EXPECT_EQ(pid1, pid2); // WebViews are related so they should share the same process. [webView1 _close]; webView1 = nullptr; kill(pid1, 9); TestWebKitAPI::Util::run(&didCrash); didCrash = false; TestWebKitAPI::Util::run(&done); done = false; } TEST(ProcessSwap, TerminatedSuspendedPageProcess) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; @autoreleasepool { auto webViewConfiguration2 = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration2 setProcessPool:processPool.get()]; [webViewConfiguration2 _setRelatedWebView:webView.get()]; // Make sure it uses the same process. auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration2.get()]); [webView2 setNavigationDelegate:delegate.get()]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]; [webView2 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView2 _webProcessIdentifier]; EXPECT_EQ(pid1, pid2); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; [webView2 _killWebContentProcessAndResetState]; webView2 = nullptr; webViewConfiguration2 = nullptr; } auto pid3 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid3); request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main2.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid4 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid4); EXPECT_NE(pid3, pid4); } TEST(ProcessSwap, CommittedProcessCrashDuringCrossSiteNavigation) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; done = false; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; static bool didKill = false; navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction *, void (^decisionHandler)(WKNavigationActionPolicy)) { decisionHandler(WKNavigationActionPolicyAllow); // Will ask the load to proceed in a new provisional WebProcess since the navigation is cross-site. // Simulate a crash of the committed WebProcess while the provisional navigation starts in the new provisional WebProcess. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ kill(pid1, 9); didKill = true; }); }; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&didKill); TestWebKitAPI::Util::sleep(0.5); } TEST(ProcessSwap, NavigateBackAndForth) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto* backForwardList = [webView backForwardList]; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [backForwardList.currentItem.URL absoluteString]); EXPECT_TRUE(!backForwardList.forwardItem); EXPECT_EQ(1U, backForwardList.backList.count); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backForwardList.backItem.URL absoluteString]); [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backForwardList.currentItem.URL absoluteString]); EXPECT_TRUE(!backForwardList.backItem); EXPECT_EQ(1U, backForwardList.forwardList.count); EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [backForwardList.forwardItem.URL absoluteString]); [webView goForward]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [backForwardList.currentItem.URL absoluteString]); EXPECT_TRUE(!backForwardList.forwardItem); EXPECT_EQ(1U, backForwardList.backList.count); EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backForwardList.backItem.URL absoluteString]); } TEST(ProcessSwap, SwapOnLoadHTMLString) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; NSString *htmlString = @"substituteData"; [webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"http://example.com"]]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); EXPECT_EQ(2, numberOfDecidePolicyCalls); [webView evaluateJavaScript:@"document.body.innerText" completionHandler:^(id innerText, NSError *error) { EXPECT_WK_STREQ(@"substituteData", (NSString *)innerText); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } TEST(ProcessSwap, EphemeralWebStorage) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; [webViewConfiguration setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/iframe.html" toData:""]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://webkit.org/"]]]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.localStorage.setItem('a','b')" completionHandler:^(id, NSError *) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.sessionStorage.setItem('b','a')" completionHandler:^(id, NSError *) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://example.com/"]]]; TestWebKitAPI::Util::run(&done); done = false; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://webkit.org/"]]]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.localStorage.getItem('a')" completionHandler:^(id result, NSError *) { EXPECT_TRUE([@"b" isEqualToString:result]); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.sessionStorage.getItem('b')" completionHandler:^(id result, NSError *) { EXPECT_TRUE([@"a" isEqualToString:result]); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView loadHTMLString:@"" baseURL:[NSURL URLWithString:@"http://www.example.com/"]]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.localStorage.getItem('a')" completionHandler:^(id result, NSError *) { EXPECT_FALSE([@"b" isEqualToString:result]); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.sessionStorage.getItem('b')" completionHandler:^(id result, NSError *) { EXPECT_FALSE([@"a" isEqualToString:result]); done = true; }]; TestWebKitAPI::Util::run(&done); } TEST(ProcessSwap, UsePrewarmedProcessAfterTerminatingNetworkProcess) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; webViewConfiguration.get().websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]).get(); auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::spinRunLoop(1); [webViewConfiguration.get().websiteDataStore _terminateNetworkProcess]; auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView2 setNavigationDelegate:delegate.get()]; request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]; [webView2 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; } TEST(ProcessSwap, UseSessionCookiesAfterProcessSwapInPrivateBrowsing) { auto originalCookieAcceptPolicy = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy]; auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); RetainPtr ephemeralStore = [WKWebsiteDataStore nonPersistentDataStore]; auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; webViewConfiguration.get().websiteDataStore = ephemeralStore.get(); auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; __block bool setPolicy = false; [webView.get().configuration.websiteDataStore.httpCookieStore _setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways completionHandler:^{ setPolicy = true; }]; TestWebKitAPI::Util::run(&setPolicy); NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"SetSessionCookie" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; // Should process-swap. request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); // Should process-swap. request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"GetSessionCookie" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid3 = [webView _webProcessIdentifier]; EXPECT_NE(pid2, pid3); TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; EXPECT_EQ(1u, [receivedMessages count]); EXPECT_WK_STREQ(@"foo=bar", receivedMessages.get()[0]); setPolicy = false; [webView.get().configuration.websiteDataStore.httpCookieStore _setCookieAcceptPolicy:originalCookieAcceptPolicy completionHandler:^{ setPolicy = true; }]; TestWebKitAPI::Util::run(&setPolicy); } TEST(ProcessSwap, UseSessionCookiesAfterProcessSwapInNonDefaultPersistentSession) { auto originalCookieAcceptPolicy = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy]; auto processPoolConfiguration = psonProcessPoolConfiguration(); // Prevent WebProcess reuse. processPoolConfiguration.get().usesWebProcessCache = NO; processPoolConfiguration.get().pageCacheEnabled = NO; processPoolConfiguration.get().prewarmsProcessesAutomatically = NO; auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]); auto customDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; webViewConfiguration.get().websiteDataStore = customDataStore.get(); auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; __block bool setPolicy = false; [webView.get().configuration.websiteDataStore.httpCookieStore _setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways completionHandler:^{ setPolicy = true; }]; TestWebKitAPI::Util::run(&setPolicy); NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"SetSessionCookie" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; // Should process-swap. request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); // Should process-swap. request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"GetSessionCookie" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid3 = [webView _webProcessIdentifier]; EXPECT_NE(pid2, pid3); EXPECT_NE(pid1, pid3); TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; EXPECT_EQ(1u, [receivedMessages count]); EXPECT_WK_STREQ(@"foo=bar", receivedMessages.get()[0]); setPolicy = false; [webView.get().configuration.websiteDataStore.httpCookieStore _setCookieAcceptPolicy:originalCookieAcceptPolicy completionHandler:^{ setPolicy = true; }]; TestWebKitAPI::Util::run(&setPolicy); } TEST(ProcessSwap, ProcessSwapInRelatedView) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webView1Configuration = adoptNS([[WKWebViewConfiguration alloc] init]); [webView1Configuration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webView1Configuration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webView1Configuration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView1 setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView1 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto applePID = [webView1 _webProcessIdentifier]; auto webView2Configuration = adoptNS([[WKWebViewConfiguration alloc] init]); [webView2Configuration setProcessPool:processPool.get()]; [webView2Configuration _setRelatedWebView:webView1.get()]; [webView2Configuration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webView2Configuration.get()]); [webView2 _restoreSessionState:webView1.get()._sessionState andNavigate:NO]; [webView2 setNavigationDelegate:delegate.get()]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView2 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView2 _webProcessIdentifier]; EXPECT_NE(applePID, webkitPID); } TEST(ProcessSwap, TerminateProcessAfterProcessSwap) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); [webView setAllowsBackForwardNavigationGestures:YES]; auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; __block bool webProcessTerminated = false; [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView *) { webProcessTerminated = true; }]; [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) { done = true; }]; // Make sure there is a gesture controller. #if PLATFORM(MAC) [webView _setCustomSwipeViewsTopContentInset:2.]; #endif NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto webkitPID = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_NE(webkitPID, [webView _webProcessIdentifier]); webProcessTerminated = false; kill([webView _webProcessIdentifier], 9); TestWebKitAPI::Util::run(&webProcessTerminated); TestWebKitAPI::Util::spinRunLoop(1); [webView reload]; TestWebKitAPI::Util::run(&done); done = false; } #if PLATFORM(IOS_FAMILY) static bool viewHasSwipeGestures(UIView *view) { unsigned swipeGestureCount = 0; for (UIGestureRecognizer *recognizer in view.gestureRecognizers) { if ([recognizer isKindOfClass:NSClassFromString(@"UIScreenEdgePanGestureRecognizer")]) swipeGestureCount++; } return swipeGestureCount == 2; } #endif TEST(ProcessSwap, SwapWithGestureController) { @autoreleasepool { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; // Ensure a ViewGestureController is created. [webView setAllowsBackForwardNavigationGestures:YES]; #if PLATFORM(MAC) [webView _setCustomSwipeViewsTopContentInset:2.]; #endif request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; #if PLATFORM(IOS_FAMILY) EXPECT_TRUE(viewHasSwipeGestures(webView.get())); #endif } } TEST(ProcessSwap, CrashWithGestureController) { @autoreleasepool { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; __block bool webProcessTerminated = false; [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView *) { webProcessTerminated = true; }]; [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) { done = true; }]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; // Ensure a ViewGestureController is created. [webView setAllowsBackForwardNavigationGestures:YES]; #if PLATFORM(MAC) [webView _setCustomSwipeViewsTopContentInset:2.]; #endif webProcessTerminated = false; kill([webView _webProcessIdentifier], 9); TestWebKitAPI::Util::run(&webProcessTerminated); TestWebKitAPI::Util::spinRunLoop(1); [webView reload]; TestWebKitAPI::Util::run(&done); done = false; #if PLATFORM(IOS_FAMILY) EXPECT_TRUE(viewHasSwipeGestures(webView.get())); #endif } } #if PLATFORM(MAC) TEST(ProcessSwap, NavigateCrossOriginWithOpenee) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main1.html" toData:windowOpenSameSiteWithOpenerTestBytes]; // Opens "pson://www.webkit.org/main2.html". [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ([webView _webProcessIdentifier], [createdWebView _webProcessIdentifier]); auto webkitPID = [webView _webProcessIdentifier]; EXPECT_WK_STREQ(@"pson://www.webkit.org/main1.html", [[webView URL] absoluteString]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main2.html", [[createdWebView URL] absoluteString]); // New window should have an opener. [createdWebView evaluateJavaScript:@"window.opener ? 'true' : 'false'" completionHandler: [&] (id hasOpener, NSError *error) { EXPECT_WK_STREQ(@"true", hasOpener); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; // Navigate cross-origin. request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]); // Auxiliary window should still have an opener. [createdWebView evaluateJavaScript:@"window.opener ? 'true' : 'false'" completionHandler: [&] (id hasOpener, NSError *error) { EXPECT_WK_STREQ(@"true", hasOpener); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; // We should not have process-swapped since an auxiliary window has an opener link to us. EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]); } static const char* crossSiteLinkWithOpenerTestBytes = R"PSONRESOURCE( Link )PSONRESOURCE"; TEST(ProcessSwap, NavigateCrossOriginWithOpener) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main1.html" toData:crossSiteLinkWithOpenerTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; // Opens "pson://www.webkit.org/main2.html" in an auxiliary window. [webView evaluateJavaScript:@"testLink.click()" completionHandler:nil]; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ([webView _webProcessIdentifier], [createdWebView _webProcessIdentifier]); auto webkitPID = [webView _webProcessIdentifier]; EXPECT_WK_STREQ(@"pson://www.webkit.org/main1.html", [[webView URL] absoluteString]); EXPECT_WK_STREQ(@"pson://www.webkit.org/main2.html", [[createdWebView URL] absoluteString]); [webView evaluateJavaScript:@"saveOpenee()" completionHandler: [&] (id, NSError *error) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"openee.location.href" completionHandler: [&] (id openeeURL, NSError *error) { EXPECT_WK_STREQ(@"pson://www.webkit.org/main2.html", openeeURL); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; // New window should have an opener. [createdWebView evaluateJavaScript:@"window.opener ? 'true' : 'false'" completionHandler: [&] (id hasOpener, NSError *error) { EXPECT_WK_STREQ(@"true", hasOpener); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; // Navigate auxiliary window cross-origin. request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [createdWebView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[createdWebView URL] absoluteString]); // Auxiliary window should still have an opener. [createdWebView evaluateJavaScript:@"window.opener ? 'true' : 'false'" completionHandler: [&] (id hasOpener, NSError *error) { EXPECT_WK_STREQ(@"true", hasOpener); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [createdWebView evaluateJavaScript:@"window.opener.closed ? 'true' : 'false'" completionHandler: [&] (id openerIsClosed, NSError *error) { EXPECT_WK_STREQ(@"false", openerIsClosed); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; // We should not have process-swapped since the auxiliary window has an opener. EXPECT_EQ(webkitPID, [createdWebView _webProcessIdentifier]); // Have the openee disown its opener. [createdWebView evaluateJavaScript:@"window.opener = null" completionHandler: [&] (id, NSError *error) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; // Auxiliary window should not have an opener. [createdWebView evaluateJavaScript:@"window.opener ? 'true' : 'false'" completionHandler: [&] (id hasOpener, NSError *error) { EXPECT_WK_STREQ(@"false", hasOpener); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; // Navigate openee cross-origin again. request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [createdWebView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; // Auxiliary window should not have an opener. [createdWebView evaluateJavaScript:@"window.opener ? 'true' : 'false'" completionHandler: [&] (id hasOpener, NSError *error) { EXPECT_WK_STREQ(@"false", hasOpener); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.google.com/main.html", [[createdWebView URL] absoluteString]); // We still should not have process-swapped since the auxiliary window's opener still has a handle to its openee. EXPECT_EQ(webkitPID, [createdWebView _webProcessIdentifier]); [webView evaluateJavaScript:@"openee.closed ? 'true' : 'false'" completionHandler: [&] (id openeeIsClosed, NSError *error) { EXPECT_WK_STREQ(@"false", openeeIsClosed); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } TEST(ProcessSwap, GoBackToSuspendedPageWithMainFrameIDThatIsNotOne) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main1.html" toData:targetBlankSameSiteNoOpenerTestBytes]; [handler addMappingFromURLString:@"pson://www.webkit.org/main2.html" toData:linkToAppleTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView1 setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView1 setUIDelegate:uiDelegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]]; [webView1 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.webkit.org/main1.html", [[webView1 URL] absoluteString]); auto pid1 = [webView1 _webProcessIdentifier]; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); done = false; // New WKWebView has now navigated to webkit.org. EXPECT_WK_STREQ(@"pson://www.webkit.org/main2.html", [[createdWebView URL] absoluteString]); auto pid2 = [createdWebView _webProcessIdentifier]; // We process-swap since there is no opener relationship. EXPECT_NE(pid1, pid2); // Click link in new WKWebView so that it navigates cross-site to apple.com. [createdWebView evaluateJavaScript:@"testLink.click()" completionHandler:nil]; TestWebKitAPI::Util::run(&done); done = false; // New WKWebView has now navigated to apple.com. EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[createdWebView URL] absoluteString]); auto pid3 = [createdWebView _webProcessIdentifier]; EXPECT_NE(pid1, pid3); // Should have process-swapped. // Navigate back to the suspended page (should use the back/forward cache). [createdWebView goBack]; TestWebKitAPI::Util::run(&receivedMessage); receivedMessage = false; EXPECT_WK_STREQ(@"pson://www.webkit.org/main2.html", [[createdWebView URL] absoluteString]); auto pid4 = [createdWebView _webProcessIdentifier]; EXPECT_EQ(pid2, pid4); // Should have process-swapped to the original "suspended" process. // Do a fragment navigation in the original WKWebView and make sure this does not crash. request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html#testLink"]]; [webView1 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_WK_STREQ(@"pson://www.webkit.org/main1.html#testLink", [[webView1 URL] absoluteString]); auto pid5 = [createdWebView _webProcessIdentifier]; EXPECT_EQ(pid2, pid5); } #endif // PLATFORM(MAC) static const char* tallPageBytes = R"PSONRESOURCE( Test )PSONRESOURCE"; static unsigned waitUntilScrollPositionIsRestored(WKWebView *webView) { unsigned scrollPosition = 0; do { [webView evaluateJavaScript:@"window.scrollY" completionHandler: [&] (id result, NSError *error) { scrollPosition = [result integerValue]; done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } while (!scrollPosition); return scrollPosition; } TEST(ProcessSwap, ScrollPositionRestoration) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:tallPageBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"scroll(0, 5000)" completionHandler: [&] (id result, NSError *error) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; do { TestWebKitAPI::Util::sleep(0.05); } while (lroundf([[[webView backForwardList] currentItem] _scrollPosition].y) != 5000); [webView evaluateJavaScript:@"testLink.click()" completionHandler: nil]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.scrollY" completionHandler: [&] (id result, NSError *error) { EXPECT_EQ(0, [result integerValue]); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; auto scrollPosition = waitUntilScrollPositionIsRestored(webView.get()); EXPECT_EQ(5000U, scrollPosition); [webView evaluateJavaScript:@"scroll(0, 4000)" completionHandler: [&] (id result, NSError *error) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; do { TestWebKitAPI::Util::sleep(0.05); } while (lroundf([[[webView backForwardList] currentItem] _scrollPosition].y) != 4000); [webView evaluateJavaScript:@"testLink.click()" completionHandler: nil]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.scrollY" completionHandler: [&] (id result, NSError *error) { EXPECT_EQ(0, [result integerValue]); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; scrollPosition = waitUntilScrollPositionIsRestored(webView.get()); EXPECT_EQ(4000U, scrollPosition); } static NSString *blockmeFilter = @"[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*blockme.html\"}}]"; static const char* contentBlockingAfterProcessSwapTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* markSubFrameAsLoadedTestBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, ContentBlockingAfterProcessSwap) { [[WKContentRuleListStore defaultStore] removeContentRuleListForIdentifier:@"ContentBlockingAfterProcessSwapExtension" completionHandler:^(NSError *error) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; __block bool doneCompiling = false; [[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"ContentBlockingAfterProcessSwapExtension" encodedContentRuleList:blockmeFilter completionHandler:^(WKContentRuleList *ruleList, NSError *error) { EXPECT_NOT_NULL(ruleList); EXPECT_NULL(error); [webViewConfiguration.get().userContentController addContentRuleList:ruleList]; doneCompiling = true; }]; TestWebKitAPI::Util::run(&doneCompiling); auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:contentBlockingAfterProcessSwapTestBytes]; [handler addMappingFromURLString:@"pson://www.webkit.org/blockme.html" toData:markSubFrameAsLoadedTestBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:contentBlockingAfterProcessSwapTestBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/blockme.html" toData:markSubFrameAsLoadedTestBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.wasSubframeLoaded ? 'FAIL' : 'PASS'" completionHandler: [&] (id result, NSError *error) { NSString *blockSuccess = (NSString *)result; EXPECT_WK_STREQ(@"PASS", blockSuccess); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.wasSubframeLoaded ? 'FAIL' : 'PASS'" completionHandler: [&] (id result, NSError *error) { NSString *blockSuccess = (NSString *)result; EXPECT_WK_STREQ(@"PASS", blockSuccess); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView goBack]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.wasSubframeLoaded ? 'FAIL' : 'PASS'" completionHandler: [&] (id result, NSError *error) { NSString *blockSuccess = (NSString *)result; EXPECT_WK_STREQ(@"PASS", blockSuccess); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [webView goForward]; TestWebKitAPI::Util::run(&done); done = false; [webView evaluateJavaScript:@"window.wasSubframeLoaded ? 'FAIL' : 'PASS'" completionHandler: [&] (id result, NSError *error) { NSString *blockSuccess = (NSString *)result; EXPECT_WK_STREQ(@"PASS", blockSuccess); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [[WKContentRuleListStore defaultStore] removeContentRuleListForIdentifier:@"ContentBlockingAfterProcessSwapExtension" completionHandler:^(NSError *error) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } static const char* notifyLoadedBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, ContentExtensionBlocksMainLoadThenReloadWithoutExtensions) { [[WKContentRuleListStore defaultStore] removeContentRuleListForIdentifier:@"ContentBlockingAfterProcessSwapExtension" completionHandler:^(NSError *error) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; webViewConfiguration.get()._allowTopNavigationToDataURLs = YES; RetainPtr messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; __block bool doneCompiling = false; [[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"ContentBlockingAfterProcessSwapExtension" encodedContentRuleList:blockmeFilter completionHandler:^(WKContentRuleList *ruleList, NSError *error) { EXPECT_NOT_NULL(ruleList); EXPECT_NULL(error); [webViewConfiguration.get().userContentController addContentRuleList:ruleList]; doneCompiling = true; }]; TestWebKitAPI::Util::run(&doneCompiling); auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.apple.com/blockme.html" toData:notifyLoadedBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; receivedMessage = false; failed = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/blockme.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&failed); failed = false; EXPECT_FALSE(receivedMessage); [webView _loadAlternateHTMLString:@"Blocked" baseURL:[NSURL URLWithString:@"data:text/html,"] forUnreachableURL:[NSURL URLWithString:@"pson://www.apple.com/blockme.html"]]; TestWebKitAPI::Util::run(&done); done = false; [webView _reloadWithoutContentBlockers]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_FALSE(failed); EXPECT_TRUE(receivedMessage); EXPECT_WK_STREQ(@"pson://www.apple.com/blockme.html", [[webView URL] absoluteString]); [[WKContentRuleListStore defaultStore] removeContentRuleListForIdentifier:@"ContentBlockingAfterProcessSwapExtension" completionHandler:^(NSError *error) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; } TEST(ProcessSwap, LoadAlternativeHTML) { [[WKContentRuleListStore defaultStore] removeContentRuleListForIdentifier:@"ContentBlockingAfterProcessSwapExtension" completionHandler:^(NSError *error) { done = true; }]; TestWebKitAPI::Util::run(&done); done = false; auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; webViewConfiguration.get()._allowTopNavigationToDataURLs = YES; RetainPtr messageHandler = adoptNS([[PSONMessageHandler alloc] init]); [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"]; __block bool doneCompiling = false; [[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"ContentBlockingAfterProcessSwapExtension" encodedContentRuleList:blockmeFilter completionHandler:^(WKContentRuleList *ruleList, NSError *error) { EXPECT_NOT_NULL(ruleList); EXPECT_NULL(error); [webViewConfiguration.get().userContentController addContentRuleList:ruleList]; doneCompiling = true; }]; TestWebKitAPI::Util::run(&doneCompiling); auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.apple.com/blockme.html" toData:notifyLoadedBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; receivedMessage = false; failed = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/blockme.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&failed); failed = false; EXPECT_FALSE(receivedMessage); [webView _loadAlternateHTMLString:@"Blocked" baseURL:[NSURL URLWithString:@"foo:blockedWarning.html"] forUnreachableURL:[NSURL URLWithString:@"pson://www.apple.com/blockme.html"]]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; } #if ENABLE(MEDIA_STREAM) static bool isCapturing = false; static bool isNotCapturing = false; @interface GetUserMediaUIDelegate : UserMediaCaptureUIDelegate - (void)_webView:(WKWebView *)webView mediaCaptureStateDidChange:(_WKMediaCaptureStateDeprecated)state; @end @implementation GetUserMediaUIDelegate - (void)_webView:(WKWebView *)webView mediaCaptureStateDidChange:(_WKMediaCaptureStateDeprecated)state { isCapturing = state == _WKMediaCaptureStateDeprecatedActiveCamera; isNotCapturing = !state; } @end static const char* getUserMediaBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, GetUserMediaCaptureState) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().pageCacheEnabled = NO; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); auto preferences = [webViewConfiguration.get() preferences]; preferences._mediaCaptureRequiresSecureConnection = NO; webViewConfiguration.get()._mediaCaptureEnabled = YES; preferences._mockCaptureDevicesEnabled = YES; preferences._getUserMediaRequiresFocus = NO; [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/getUserMedia.html" toData:getUserMediaBytes]; [handler addMappingFromURLString:@"pson://www.apple.org/test.html" toData:""]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); webView.get()._mediaCaptureReportingDelayForTesting = 1; auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[GetUserMediaUIDelegate alloc] init]); [webView setUIDelegate: uiDelegate.get()]; auto request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/getUserMedia.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&isCapturing); auto pid1 = [webView _webProcessIdentifier]; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.org/test.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; TestWebKitAPI::Util::run(&isNotCapturing); EXPECT_FALSE(isCapturing); EXPECT_FALSE(pid1 == pid2); isCapturing = false; [webView goBack]; TestWebKitAPI::Util::run(&isCapturing); isCapturing = false; isNotCapturing = true; } #endif #if !PLATFORM(MAC) static void traverseLayerTree(CALayer *layer, void(^block)(CALayer *)) { for (CALayer *child in layer.sublayers) traverseLayerTree(child, block); block(layer); } static bool hasOverlay(CALayer *layer) { __block bool hasViewOverlay = false; traverseLayerTree(layer, ^(CALayer *layer) { if ([layer.name containsString:@"View overlay container"]) hasViewOverlay = true; }); return hasViewOverlay; } #endif TEST(ProcessSwap, PageOverlayLayerPersistence) { auto processPoolConfiguration = psonProcessPoolConfiguration(); [processPoolConfiguration setInjectedBundleURL:[[NSBundle mainBundle] URLForResource:@"TestWebKitAPI" withExtension:@"wkbundle"]]; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); [processPool _setObject:@"PageOverlayPlugIn" forBundleParameter:TestWebKitAPI::Util::TestPlugInClassNameParameter]; auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/page-overlay" toData:""]; [handler addMappingFromURLString:@"pson://www.apple.com/page-overlay" toData:""]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/page-overlay"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; [webView waitForNextPresentationUpdate]; // We can only look for the overlay layer in the UI-side layer tree on platforms // that use UI-side compositing. #if !PLATFORM(MAC) EXPECT_TRUE(hasOverlay([webView layer])); #endif request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/page-overlay"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; [webView waitForNextPresentationUpdate]; [webView goBack]; // Back to webkit.org. [webView waitForNextPresentationUpdate]; #if !PLATFORM(MAC) EXPECT_TRUE(hasOverlay([webView layer])); #endif } #if PLATFORM(IOS) #if __IPHONE_OS_VERSION_MIN_REQUIRED > 130400 TEST(ProcessSwap, QuickLookRequestsPasswordAfterSwap) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"password-protected" withExtension:@"pages" subdirectory:@"TestWebKitAPI.resources"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&didStartQuickLookLoad); didStartQuickLookLoad = false; TestWebKitAPI::Util::run(&requestedQuickLookPassword); requestedQuickLookPassword = false; TestWebKitAPI::Util::run(&didFinishQuickLookLoad); didFinishQuickLookLoad = false; } #endif static const char* minimumWidthPageBytes = R"PSONRESOURCE(
Test )PSONRESOURCE"; TEST(ProcessSwap, PassMinimumDeviceWidthOnNewWebView) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:minimumWidthPageBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto preferences = [[webView configuration] preferences]; [preferences _setShouldIgnoreMetaViewport:YES]; [webView _setMinimumEffectiveDeviceWidth:1024]; auto* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; bool finishedRunningScript = false; [webView evaluateJavaScript:@"window.innerWidth" completionHandler: [&] (id result, NSError *error) { NSNumber *width = (NSNumber *)result; EXPECT_EQ(1024, [width intValue]); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); } #endif TEST(ProcessSwap, SuspendAllMediaPlayback) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); configuration.get().mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; #if TARGET_OS_IPHONE configuration.get().allowsInlineMediaPlayback = YES; #endif [configuration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [configuration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); __block bool loaded = false; [webView performAfterLoading:^{ loaded = true; }]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&loaded); [webView _suspendAllMediaPlayback]; __block bool notPlaying = false; [webView performAfterReceivingMessage:@"not playing" action:^() { notPlaying = true; }]; [webView synchronouslyLoadTestPageNamed:@"video-with-audio"]; TestWebKitAPI::Util::run(¬Playing); } TEST(ProcessSwap, PassSandboxExtension) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); configuration.get().mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; #if TARGET_OS_IPHONE configuration.get().allowsInlineMediaPlayback = YES; #endif [configuration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [configuration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]]; TestWebKitAPI::Util::run(&done); done = false; NSURL *file = [[NSBundle mainBundle] URLForResource:@"autoplay-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadFileURL:file allowingReadAccessToURL:file.URLByDeletingLastPathComponent]; [webView waitForMessage:@"loaded"]; EXPECT_WK_STREQ(webView.get()._resourceDirectoryURL.path, file.URLByDeletingLastPathComponent.path); } #if PLATFORM(MAC) static const char* pageThatOpensBytes = R"PSONRESOURCE( )PSONRESOURCE"; static const char* openedPage = "Hello World"; TEST(ProcessSwap, SameSiteWindowWithOpenerNavigateToFile) { auto processPoolConfiguration = psonProcessPoolConfiguration(); processPoolConfiguration.get().processSwapsOnWindowOpenWithOpener = YES; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:pageThatOpensBytes]; [handler addMappingFromURLString:@"pson://www.webkit.org/window.html" toData:openedPage]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; numberOfDecidePolicyCalls = 0; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(2, numberOfDecidePolicyCalls); auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); EXPECT_EQ(pid1, pid2); NSURL *url = [[NSBundle mainBundle] URLForResource:@"blinking-div" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; EXPECT_TRUE([url.scheme isEqualToString:@"file"]); [createdWebView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(3, numberOfDecidePolicyCalls); auto pid3 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid3); EXPECT_NE(pid2, pid3); [createdWebView goBack]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(4, numberOfDecidePolicyCalls); auto pid4 = [createdWebView _webProcessIdentifier]; EXPECT_NE(pid3, pid4); [createdWebView goForward]; TestWebKitAPI::Util::run(&done); done = false; EXPECT_EQ(5, numberOfDecidePolicyCalls); auto pid5 = [createdWebView _webProcessIdentifier]; EXPECT_NE(pid4, pid5); } #endif // PLATFORM(MAC) static const char* responsivePageBytes = R"PSONRESOURCE( )PSONRESOURCE"; TEST(ProcessSwap, ResizeWebViewDuringCrossSiteProvisionalNavigation) { auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:responsivePageBytes]; [handler addMappingFromURLString:@"pson://www.apple.com/main.html" toData:responsivePageBytes]; [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 800) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:delegate.get()]; [webView configuration].preferences.fraudulentWebsiteWarningEnabled = NO; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; __block bool finishedRunningScript = false; [webView evaluateJavaScript:@"window.innerWidth" completionHandler:^(id result, NSError *error) { NSNumber *width = (NSNumber *)result; EXPECT_EQ(800, [width intValue]); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); finishedRunningScript = false; delegate->didStartProvisionalNavigationHandler = ^{ EXPECT_NE(0, [webView _provisionalWebProcessIdentifier]); [webView setFrame:CGRectMake(0, 0, 200, 200)]; }; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; [webView _doAfterNextPresentationUpdate:^{ [webView evaluateJavaScript:@"window.innerWidth" completionHandler:^(id result, NSError *error) { NSNumber *width = (NSNumber *)result; EXPECT_EQ(200, [width intValue]); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); }]; } TEST(WebProcessCache, ClearWhenEnteringCache) { auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]); processPoolConfiguration.get().usesWebProcessCache = YES; auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto handler = adoptNS([[PSONScheme alloc] init]); [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"]; @autoreleasepool { auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 800) configuration:webViewConfiguration.get()]); auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 800) configuration:webViewConfiguration.get()]); auto webView3 = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 800) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView1 setNavigationDelegate:delegate.get()]; [webView2 setNavigationDelegate:delegate.get()]; [webView3 setNavigationDelegate:delegate.get()]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]]; [webView1 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]]; [webView2 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]]; [webView3 loadRequest:request]; TestWebKitAPI::Util::run(&done); done = false; } TestWebKitAPI::Util::spinRunLoop(); // Clear the WebProcess cache while the processes are being checked for responsiveness. [processPool _clearWebProcessCache]; } TEST(ProcessSwap, ResponsePolicyDownloadAfterCOOPProcessSwap) { using namespace TestWebKitAPI; HTTPServer server({ { "/source.html"_s, { "foo"_s } }, { "/destination.html"_s, { { { "Content-Type"_s, "text/html"_s }, { "Cross-Origin-Opener-Policy"_s, "same-origin"_s } }, "bar"_s } }, }, HTTPServer::Protocol::Https); auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; for (_WKExperimentalFeature *feature in [WKPreferences _experimentalFeatures]) { if ([feature.key isEqualToString:@"CrossOriginOpenerPolicyEnabled"]) [[webViewConfiguration preferences] _setEnabled:YES forExperimentalFeature:feature]; else if ([feature.key isEqualToString:@"CrossOriginEmbedderPolicyEnabled"]) [[webViewConfiguration preferences] _setEnabled:YES forExperimentalFeature:feature]; } auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; done = false; [webView loadRequest:server.request("/source.html"_s)]; Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; // The next navigation will get converted into a download via decidePolicyForNavigationResponse. shouldConvertToDownload = true; done = false; failed = false; [webView loadRequest:server.request("/destination.html"_s)]; Util::run(&failed); failed = false; shouldConvertToDownload = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_EQ(pid1, pid2); // The layer tree should no longer be frozen since the navigation didn't happen. __block bool isFrozen = true; do { Util::sleep(0.1); done = false; [webView _isLayerTreeFrozenForTesting:^(BOOL frozen) { isFrozen = frozen; done = true; }]; Util::run(&done); } while (isFrozen); } TEST(ProcessSwap, NavigateBackAfterNavigatingAwayFromCOOP) { using namespace TestWebKitAPI; HTTPServer server({ { "/source.html"_s, { { { "Content-Type"_s, "text/html"_s }, { "Cross-Origin-Opener-Policy"_s, "same-origin"_s } }, "foo"_s } }, { "/destination.html"_s, { "bar"_s } }, }, HTTPServer::Protocol::Https); auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; for (_WKExperimentalFeature *feature in [WKPreferences _experimentalFeatures]) { if ([feature.key isEqualToString:@"CrossOriginOpenerPolicyEnabled"]) [[webViewConfiguration preferences] _setEnabled:YES forExperimentalFeature:feature]; else if ([feature.key isEqualToString:@"CrossOriginEmbedderPolicyEnabled"]) [[webViewConfiguration preferences] _setEnabled:YES forExperimentalFeature:feature]; } auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; done = false; [webView loadRequest:server.request("/source.html"_s)]; Util::run(&done); done = false; [webView loadRequest:server.request("/destination.html"_s)]; Util::run(&done); done = false; [webView goBack]; Util::run(&done); done = false; } enum class IsSameOrigin : bool { No, Yes }; enum class DoServerSideRedirect : bool { No, Yes }; static void runCOOPProcessSwapTest(ASCIILiteral sourceCOOP, ASCIILiteral sourceCOEP, ASCIILiteral destinationCOOP, ASCIILiteral destinationCOEP, IsSameOrigin isSameOrigin, DoServerSideRedirect doServerSideRedirect, ExpectSwap expectSwap) { using namespace TestWebKitAPI; HashMap sourceHeaders; sourceHeaders.add("Content-Type"_s, "text/html"_s); if (sourceCOOP) sourceHeaders.add("Cross-Origin-Opener-Policy"_s, sourceCOOP); if (sourceCOEP) sourceHeaders.add("Cross-Origin-Embedder-Policy"_s, sourceCOEP); HashMap destinationHeaders; destinationHeaders.add("Content-Type"_s, "text/html"_s); if (destinationCOOP) destinationHeaders.add("Cross-Origin-Opener-Policy"_s, destinationCOOP); if (destinationCOEP) destinationHeaders.add("Cross-Origin-Embedder-Policy"_s, destinationCOEP); HTTPResponse destinationResponse(WTFMove(destinationHeaders), "popup"_s); HTTPServer server(std::initializer_list> { }, HTTPServer::Protocol::Https); auto popupURL = isSameOrigin == IsSameOrigin::Yes ? "popup.html"_str : makeString("https://localhost:", server.port(), "/popup.html"); auto popupSource = makeString(""); server.addResponse("/main.html"_s, HTTPResponse { WTFMove(sourceHeaders), WTFMove(popupSource) }); if (doServerSideRedirect == DoServerSideRedirect::Yes) { HashMap redirectHeaders; String redirectionURL = isSameOrigin == IsSameOrigin::Yes ? makeString("https://127.0.0.1:", server.port(), "/popup-after-redirection.html") : makeString("https://localhost:", server.port(), "/popup-after-redirection.html"); redirectHeaders.add("location"_s, WTFMove(redirectionURL)); HTTPResponse redirectResponse(301, WTFMove(redirectHeaders)); server.addResponse("/popup.html"_s, WTFMove(redirectResponse)); server.addResponse("/popup-after-redirection.html"_s, WTFMove(destinationResponse)); } else server.addResponse("/popup.html"_s, WTFMove(destinationResponse)); auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); bool sourceShouldBeCrossOriginIsolated = sourceCOOP == "same-origin"_s && sourceCOEP == "require-corp"_s; bool destinationShouldBeCrossOriginIsolated = destinationCOOP == "same-origin"_s && destinationCOEP == "require-corp"_s; EXPECT_TRUE(sourceShouldBeCrossOriginIsolated == destinationShouldBeCrossOriginIsolated || expectSwap == ExpectSwap::Yes); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; [webViewConfiguration preferences].javaScriptCanOpenWindowsAutomatically = YES; for (_WKExperimentalFeature *feature in [WKPreferences _experimentalFeatures]) { if ([feature.key isEqualToString:@"CrossOriginOpenerPolicyEnabled"]) [[webViewConfiguration preferences] _setEnabled:YES forExperimentalFeature:feature]; else if ([feature.key isEqualToString:@"CrossOriginEmbedderPolicyEnabled"]) [[webViewConfiguration preferences] _setEnabled:YES forExperimentalFeature:feature]; } auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); __block unsigned numberOfProvisionalLoads = 0; navigationDelegate->didStartProvisionalNavigationHandler = ^{ ++numberOfProvisionalLoads; }; [webView setNavigationDelegate:navigationDelegate.get()]; auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]); [webView setUIDelegate:uiDelegate.get()]; failed = false; serverRedirected = false; numberOfDecidePolicyCalls = 0; [webView loadRequest:server.request("/main.html"_s)]; TestWebKitAPI::Util::run(&done); done = false; TestWebKitAPI::Util::run(&didCreateWebView); didCreateWebView = false; TestWebKitAPI::Util::run(&done); if (doServerSideRedirect == DoServerSideRedirect::Yes) { EXPECT_EQ(3, numberOfDecidePolicyCalls); EXPECT_TRUE(serverRedirected); } else { EXPECT_EQ(2, numberOfDecidePolicyCalls); EXPECT_FALSE(serverRedirected); } EXPECT_EQ(2U, numberOfProvisionalLoads); // One in each view. EXPECT_FALSE(failed); // There should be no didFailProvisionalLoad call. auto pid1 = [webView _webProcessIdentifier]; EXPECT_TRUE(!!pid1); auto pid2 = [createdWebView _webProcessIdentifier]; EXPECT_TRUE(!!pid2); if (expectSwap == ExpectSwap::Yes) EXPECT_NE(pid1, pid2); else EXPECT_EQ(pid1, pid2); bool finishedRunningScript = false; [webView evaluateJavaScript:@"w.closed ? 'true' : 'false'" completionHandler: [&] (id result, NSError *error) { NSString *isClosed = (NSString *)result; if (expectSwap == ExpectSwap::Yes) EXPECT_WK_STREQ(@"true", isClosed); else EXPECT_WK_STREQ(@"false", isClosed); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); finishedRunningScript = false; [webView evaluateJavaScript:@"w.name" completionHandler: [&] (id result, NSError *error) { NSString *windowName = (NSString *)result; if (expectSwap == ExpectSwap::No && isSameOrigin == IsSameOrigin::Yes) EXPECT_WK_STREQ(@"foo", windowName); else EXPECT_WK_STREQ(@"", windowName); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); finishedRunningScript = false; [webView evaluateJavaScript:@"self.crossOriginIsolated ? 'isolated' : 'not-isolated'" completionHandler: [&] (id result, NSError *error) { NSString *crossOriginIsolated = (NSString *)result; if (sourceShouldBeCrossOriginIsolated) EXPECT_WK_STREQ(@"isolated", crossOriginIsolated); else EXPECT_WK_STREQ(@"not-isolated", crossOriginIsolated); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); finishedRunningScript = false; [webView evaluateJavaScript:@"self.SharedArrayBuffer ? 'has-sab' : 'does-not-have-sab'" completionHandler: [&] (id result, NSError *error) { NSString *hasSAB = (NSString *)result; if (sourceShouldBeCrossOriginIsolated) EXPECT_WK_STREQ(@"has-sab", hasSAB); else EXPECT_WK_STREQ(@"does-not-have-sab", hasSAB); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); // Openee should not have an opener or a name. finishedRunningScript = false; [createdWebView evaluateJavaScript:@"window.opener ? 'true' : 'false'" completionHandler: [&] (id result, NSError *error) { NSString *hasOpener = (NSString *)result; if (expectSwap == ExpectSwap::Yes) EXPECT_WK_STREQ(@"false", hasOpener); else EXPECT_WK_STREQ(@"true", hasOpener); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); finishedRunningScript = false; [createdWebView evaluateJavaScript:@"window.name" completionHandler: [&] (id result, NSError *error) { NSString *windowName = (NSString *)result; if (expectSwap == ExpectSwap::Yes) EXPECT_WK_STREQ(@"", windowName); else EXPECT_WK_STREQ(@"foo", windowName); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); finishedRunningScript = false; [createdWebView evaluateJavaScript:@"document.body.innerText" completionHandler: [&] (id result, NSError *error) { NSString *innerText = (NSString *)result; EXPECT_WK_STREQ(@"popup", innerText); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); finishedRunningScript = false; [createdWebView evaluateJavaScript:@"self.crossOriginIsolated ? 'isolated' : 'not-isolated'" completionHandler: [&] (id result, NSError *error) { NSString *crossOriginIsolated = (NSString *)result; if (destinationShouldBeCrossOriginIsolated) EXPECT_WK_STREQ(@"isolated", crossOriginIsolated); else EXPECT_WK_STREQ(@"not-isolated", crossOriginIsolated); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); finishedRunningScript = false; [createdWebView evaluateJavaScript:@"self.SharedArrayBuffer ? 'has-sab' : 'does-not-have-sab'" completionHandler: [&] (id result, NSError *error) { NSString *hasSAB = (NSString *)result; if (destinationShouldBeCrossOriginIsolated) EXPECT_WK_STREQ(@"has-sab", hasSAB); else EXPECT_WK_STREQ(@"does-not-have-sab", hasSAB); finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); createdWebView = nullptr; } TEST(ProcessSwap, NavigatingSameOriginToCOOPSameOrigin) { runCOOPProcessSwapTest({ }, { }, "same-origin"_s, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginToCOOPSameOrigin2) { runCOOPProcessSwapTest("unsafe-none"_s, { }, "same-origin"_s, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginToCOOPSameOrigin3) { runCOOPProcessSwapTest("unsafe-none"_s, { }, "same-origin"_s, "unsafe-none"_s, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginToCOOPSameOrigin4) { runCOOPProcessSwapTest("unsafe-none"_s, "unsafe-none"_s, "same-origin"_s, "unsafe-none"_s, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginToCOOPAndCOEPSameOrigin) { runCOOPProcessSwapTest({ }, { }, "same-origin"_s, "require-corp"_s, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOrigin) { runCOOPProcessSwapTest("same-origin"_s, { }, { }, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOrigin2) { runCOOPProcessSwapTest("same-origin"_s, { }, "unsafe-none"_s, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOrigin3) { runCOOPProcessSwapTest("same-origin"_s, "unsafe-none"_s, "unsafe-none"_s, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPAndCOEPSameOrigin) { runCOOPProcessSwapTest("same-origin"_s, "require-corp"_s, { }, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOriginAllowPopup) { runCOOPProcessSwapTest("same-origin-allow-popup"_s, { }, { }, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOriginAllowPopup2) { runCOOPProcessSwapTest("same-origin-allow-popup"_s, { }, "unsafe-none"_s, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOriginAllowPopup3) { runCOOPProcessSwapTest("same-origin-allow-popup"_s, "unsafe-none"_s, "unsafe-none"_s, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOriginAllowPopup4) { runCOOPProcessSwapTest("same-origin-allow-popup"_s, "unsafe-none"_s, "unsafe-none"_s, "unsafe-none"_s, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPAndCOEPSameOriginAllowPopup) { runCOOPProcessSwapTest("same-origin-allow-popup"_s, "require-corp"_s, { }, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOriginToCOOPSameOrigin) { runCOOPProcessSwapTest("same-origin"_s, { }, "same-origin"_s, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOriginToCOOPSameOrigin2) { runCOOPProcessSwapTest("same-origin"_s, "unsafe-none"_s, "same-origin"_s, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOriginToCOOPSameOrigin3) { runCOOPProcessSwapTest("same-origin"_s, "unsafe-none"_s, "same-origin"_s, "unsafe-none"_s, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPAndCOEPSameOriginToCOOPAndCOEPSameOrigin) { runCOOPProcessSwapTest("same-origin"_s, "require-corp"_s, "same-origin"_s, "require-corp"_s, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPAndCOEPSameOriginToCOOPSameOrigin) { // Should swap because the destination is missing COEP. runCOOPProcessSwapTest("same-origin"_s, "require-corp"_s, "same-origin"_s, { }, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOriginToCOOPAndCOEPSameOrigin) { // Should swap because the source is missing COEP. runCOOPProcessSwapTest("same-origin"_s, { }, "same-origin"_s, "require-corp"_s, IsSameOrigin::Yes, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPSameOriginToCOOPSameOriginWithRedirect) { // We expect a swap because the redirect doesn't have COOP=same-origin. runCOOPProcessSwapTest("same-origin"_s, { }, "same-origin"_s, { }, IsSameOrigin::Yes, DoServerSideRedirect::Yes, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginFromCOOPAndCOEPSameOriginToCOOPAndCOEPSameOriginWithRedirect) { // We expect a swap because the redirect doesn't have COOP=same-origin and COEP=require-corp. runCOOPProcessSwapTest("same-origin"_s, "require-corp"_s, "same-origin"_s, "require-corp"_s, IsSameOrigin::Yes, DoServerSideRedirect::Yes, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingSameOriginWithoutCOOPWithRedirect) { runCOOPProcessSwapTest({ }, { }, { }, { }, IsSameOrigin::Yes, DoServerSideRedirect::Yes, ExpectSwap::No); } TEST(ProcessSwap, NavigatingCrossOriginToCOOPSameOrigin) { runCOOPProcessSwapTest({ }, { }, "same-origin"_s, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginToCOOPSameOrigin2) { runCOOPProcessSwapTest("unsafe-none"_s, { }, "same-origin"_s, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginToCOOPSameOrigin3) { runCOOPProcessSwapTest("unsafe-none"_s, { }, "same-origin"_s, "unsafe-none"_s, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginToCOOPSameOrigin4) { runCOOPProcessSwapTest("unsafe-none"_s, "unsafe-none"_s, "same-origin"_s, "unsafe-none"_s, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginToCOOPAndCOEPSameOrigin) { runCOOPProcessSwapTest({ }, { }, "same-origin"_s, "require-corp"_s, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPSameOrigin) { runCOOPProcessSwapTest("same-origin"_s, { }, { }, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPSameOrigin2) { runCOOPProcessSwapTest("same-origin"_s, { }, "unsafe-none"_s, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPSameOrigin3) { runCOOPProcessSwapTest("same-origin"_s, "unsafe-none"_s, "unsafe-none"_s, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPSameOrigin4) { runCOOPProcessSwapTest("same-origin"_s, "unsafe-none"_s, "unsafe-none"_s, "unsafe-none"_s, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPAndCOEPSameOrigin) { runCOOPProcessSwapTest("same-origin"_s, "require-corp"_s, { }, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPSameOriginToCOOPSameOrigin) { runCOOPProcessSwapTest("same-origin"_s, { }, "same-origin"_s, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPAndCOEPSameOriginToCOOPAndCOEPSameOrigin) { runCOOPProcessSwapTest("same-origin"_s, "require-corp"_s, "same-origin"_s, "require-corp"_s, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPAndCOEPSameOriginToCOOPSameOrigin) { runCOOPProcessSwapTest("same-origin"_s, "require-corp"_s, "same-origin"_s, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPSameOriginToCOOPAndCOEPSameOrigin) { runCOOPProcessSwapTest("same-origin"_s, { }, "same-origin"_s, "require-corp"_s, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::Yes); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPSameOriginAllowPopup) { runCOOPProcessSwapTest("same-origin-allow-popup"_s, { }, { }, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPSameOriginAllowPopup2) { runCOOPProcessSwapTest("same-origin-allow-popup"_s, { }, "unsafe-none"_s, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPSameOriginAllowPopup3) { runCOOPProcessSwapTest("same-origin-allow-popup"_s, "unsafe-none"_s, "unsafe-none"_s, { }, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::No); } TEST(ProcessSwap, NavigatingCrossOriginFromCOOPSameOriginAllowPopup4) { runCOOPProcessSwapTest("same-origin-allow-popup"_s, "unsafe-none"_s, "unsafe-none"_s, "unsafe-none"_s, IsSameOrigin::No, DoServerSideRedirect::No, ExpectSwap::No); } static bool isJITEnabled(WKWebView *webView) { __block bool gotResponse = false; __block bool isJITEnabledResult = false; [webView _isJITEnabled:^(BOOL isJITEnabled) { isJITEnabledResult = isJITEnabled; gotResponse = true; }]; TestWebKitAPI::Util::run(&gotResponse); EXPECT_NE([webView _webProcessIdentifier], 0); return isJITEnabledResult; } enum class ShouldBeEnabled : bool { No, Yes }; enum class IsShowingInitialEmptyDocument : bool { No, Yes }; static void checkSettingsControlledByCaptivePortalMode(WKWebView *webView, ShouldBeEnabled shouldBeEnabled, IsShowingInitialEmptyDocument isShowingInitialEmptyDocument = IsShowingInitialEmptyDocument::No) { auto runJSCheck = [&](const String& js) -> bool { bool finishedRunningScript = false; bool checkResult = false; [webView evaluateJavaScript:js completionHandler:[&] (id result, NSError *error) { EXPECT_NULL(error); checkResult = [result boolValue]; finishedRunningScript = true; }]; TestWebKitAPI::Util::run(&finishedRunningScript); return checkResult; }; EXPECT_EQ(runJSCheck("!!window.WebGL2RenderingContext"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // WebGL2. EXPECT_EQ(runJSCheck("!!window.Gamepad"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // Gamepad API. EXPECT_EQ(runJSCheck("!!window.RemotePlayback"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // Remote Playback. EXPECT_EQ(runJSCheck("!!window.FileSystemHandle"_s), isShowingInitialEmptyDocument != IsShowingInitialEmptyDocument::Yes && shouldBeEnabled == ShouldBeEnabled::Yes); // File System Access. EXPECT_EQ(runJSCheck("!!window.HTMLModelElement"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // AR (Model) EXPECT_EQ(runJSCheck("!!window.PictureInPictureEvent"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // Picture in Picture API. EXPECT_EQ(runJSCheck("!!window.SpeechRecognitionEvent"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // Speech recognition. #if ENABLE(NOTIFICATIONS) EXPECT_EQ(runJSCheck("!!window.Notification"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // Notification API. #endif EXPECT_EQ(runJSCheck("!!window.WebXRSystem"_s), false); // WebXR (currently always disabled). EXPECT_EQ(runJSCheck("!!window.AudioContext"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // WebAudio. EXPECT_EQ(runJSCheck("!!window.RTCPeerConnection"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // WebRTC Peer Connection. EXPECT_EQ(runJSCheck("!!window.RTCRtpScriptTransform"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // WebRTC Script Transform EXPECT_EQ(runJSCheck("!!navigator.mediaDevices"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // GetUserMedia (Media Capture). EXPECT_EQ(runJSCheck("!!navigator.getUserMedia"_s), false); // Legacy GetUserMedia (currently always disabled). EXPECT_EQ(runJSCheck("!!window.MathMLElement"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // MathML. EXPECT_EQ(runJSCheck("!!window.MathMLMathElement"_s), shouldBeEnabled == ShouldBeEnabled::Yes); // MathML. EXPECT_EQ(runJSCheck("!!window.PushManager"_s), isShowingInitialEmptyDocument != IsShowingInitialEmptyDocument::Yes && shouldBeEnabled == ShouldBeEnabled::Yes); // Push API. EXPECT_EQ(runJSCheck("!!window.PushSubscription"_s), isShowingInitialEmptyDocument != IsShowingInitialEmptyDocument::Yes && shouldBeEnabled == ShouldBeEnabled::Yes); // Push API. EXPECT_EQ(runJSCheck("!!window.PushSubscriptionOptions"_s), isShowingInitialEmptyDocument != IsShowingInitialEmptyDocument::Yes && shouldBeEnabled == ShouldBeEnabled::Yes); // Push API. String mathMLCheck = makeString("document.createElementNS('http://www.w3.org/1998/Math/MathML','mspace').__proto__ == ", shouldBeEnabled == ShouldBeEnabled::Yes ? "MathMLElement" : "Element", ".prototype"); EXPECT_EQ(runJSCheck(mathMLCheck), true); // MathML. } @interface CaptivePortalMessageHandler : NSObject @end @implementation CaptivePortalMessageHandler - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { receivedScriptMessage = true; scriptMessages.append(message); } @end void configureCaptivePortalWKWebViewConfiguration(WKWebViewConfiguration *config) { [config.preferences _setMediaDevicesEnabled:YES]; config.preferences._mediaCaptureRequiresSecureConnection = NO; [config.preferences _setNotificationsEnabled:YES]; for (_WKExperimentalFeature *feature in [WKPreferences _experimentalFeatures]) { if ([feature.key isEqualToString:@"ModelElementEnabled"] || [feature.key isEqualToString:@"PushAPIEnabled"] || [feature.key isEqualToString:@"WebRTCEncodedTransformEnabled"]) { [config.preferences _setEnabled:YES forFeature:feature]; } } } TEST(ProcessSwap, NavigatingToCaptivePortalMode) { auto messageHandler = adoptNS([[CaptivePortalMessageHandler alloc] init]); auto webViewConfiguration = adoptNS([WKWebViewConfiguration new]); EXPECT_FALSE(webViewConfiguration.get().defaultWebpagePreferences._captivePortalModeEnabled); configureCaptivePortalWKWebViewConfiguration(webViewConfiguration.get()); [webViewConfiguration.get().userContentController addScriptMessageHandler:messageHandler.get() name:@"testHandler"]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([TestNavigationDelegate new]); [webView setNavigationDelegate:delegate.get()]; EXPECT_TRUE(isJITEnabled(webView.get())); checkSettingsControlledByCaptivePortalMode(webView.get(), ShouldBeEnabled::Yes, IsShowingInitialEmptyDocument::Yes); __block bool finishedNavigation = false; delegate.get().didFinishNavigation = ^(WKWebView *, WKNavigation *) { finishedNavigation = true; }; NSURL *url = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); pid_t pid1 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, 0); EXPECT_TRUE(isJITEnabled(webView.get())); checkSettingsControlledByCaptivePortalMode(webView.get(), ShouldBeEnabled::Yes); finishedNavigation = false; receivedScriptMessage = false; url = [[NSBundle mainBundle] URLForResource:@"CaptivePortalPDF" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); EXPECT_FALSE(receivedScriptMessage); EXPECT_TRUE(scriptMessages.isEmpty()); delegate.get().decidePolicyForNavigationActionWithPreferences = ^(WKNavigationAction *action, WKWebpagePreferences *preferences, void (^completionHandler)(WKNavigationActionPolicy, WKWebpagePreferences *)) { EXPECT_FALSE(preferences._captivePortalModeEnabled); [preferences _setCaptivePortalModeEnabled:YES]; completionHandler(WKNavigationActionPolicyAllow, preferences); }; finishedNavigation = false; url = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); // We should have process-swap for transitioning to captive portal mode. EXPECT_NE(pid1, [webView _webProcessIdentifier]); EXPECT_FALSE(isJITEnabled(webView.get())); checkSettingsControlledByCaptivePortalMode(webView.get(), ShouldBeEnabled::No); finishedNavigation = false; receivedScriptMessage = false; url = [[NSBundle mainBundle] URLForResource:@"CaptivePortalPDF" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); EXPECT_TRUE(receivedScriptMessage); EXPECT_WK_STREQ("Error loading PDF", getNextMessage().body); } TEST(ProcessSwap, CaptivePortalModeSystemSettingChange) { auto kvo = adoptNS([CaptivePortalModeKVO new]); auto webViewConfiguration = adoptNS([WKWebViewConfiguration new]); EXPECT_FALSE(webViewConfiguration.get().defaultWebpagePreferences._captivePortalModeEnabled); [webViewConfiguration.get().defaultWebpagePreferences addObserver:kvo.get() forKeyPath:@"_captivePortalModeEnabled" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([TestNavigationDelegate new]); [webView setNavigationDelegate:delegate.get()]; EXPECT_TRUE(isJITEnabled(webView.get())); __block bool finishedNavigation = false; delegate.get().didFinishNavigation = ^(WKWebView *, WKNavigation *) { finishedNavigation = true; }; NSURL *url = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); pid_t pid1 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, 0); EXPECT_TRUE(isJITEnabled(webView.get())); EXPECT_FALSE(didChangeCaptivePortalMode); finishedNavigation = false; // Now change the global setting. [WKProcessPool _setCaptivePortalModeEnabledGloballyForTesting:YES]; TestWebKitAPI::Util::run(&didChangeCaptivePortalMode); EXPECT_FALSE(captivePortalModeBeforeChange); EXPECT_TRUE(captivePortalModeAfterChange); EXPECT_TRUE(webViewConfiguration.get().defaultWebpagePreferences._captivePortalModeEnabled); // This should cause the WebView to reload with a WebProcess in captive portal mode. TestWebKitAPI::Util::run(&finishedNavigation); EXPECT_FALSE(isJITEnabled(webView.get())); pid_t pid2 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); didChangeCaptivePortalMode = false; finishedNavigation = false; [WKProcessPool _clearCaptivePortalModeEnabledGloballyForTesting]; TestWebKitAPI::Util::run(&didChangeCaptivePortalMode); EXPECT_TRUE(captivePortalModeBeforeChange); EXPECT_FALSE(captivePortalModeAfterChange); EXPECT_FALSE(webViewConfiguration.get().defaultWebpagePreferences._captivePortalModeEnabled); TestWebKitAPI::Util::run(&finishedNavigation); EXPECT_TRUE(isJITEnabled(webView.get())); pid_t pid3 = [webView _webProcessIdentifier]; EXPECT_NE(pid2, pid3); } #if PLATFORM(IOS) TEST(ProcessSwap, CannotDisableCaptivePortalModeWithoutBrowserEntitlement) { auto webViewConfiguration = adoptNS([WKWebViewConfiguration new]); EXPECT_FALSE(webViewConfiguration.get().defaultWebpagePreferences._captivePortalModeEnabled); configureCaptivePortalWKWebViewConfiguration(webViewConfiguration.get()); [webViewConfiguration.get().defaultWebpagePreferences _setCaptivePortalModeEnabled:YES]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([TestNavigationDelegate new]); [webView setNavigationDelegate:delegate.get()]; EXPECT_FALSE(isJITEnabled(webView.get())); checkSettingsControlledByCaptivePortalMode(webView.get(), ShouldBeEnabled::No, IsShowingInitialEmptyDocument::Yes); pid_t pid1 = [webView _webProcessIdentifier]; __block bool finishedNavigation = false; delegate.get().didFinishNavigation = ^(WKWebView *, WKNavigation *) { finishedNavigation = true; }; delegate.get().decidePolicyForNavigationActionWithPreferences = ^(WKNavigationAction *action, WKWebpagePreferences *preferences, void (^completionHandler)(WKNavigationActionPolicy, WKWebpagePreferences *)) { EXPECT_TRUE(preferences._captivePortalModeEnabled); bool didThrowWhenTryingToDisableCaptivePortalMode = false; // TestWebKitAPI doesn't have the web browser entitlement and thus shouldn't be able to disable captive portal mode. @try { [preferences _setCaptivePortalModeEnabled:NO]; } @catch (NSException *exception) { didThrowWhenTryingToDisableCaptivePortalMode = true; } EXPECT_TRUE(didThrowWhenTryingToDisableCaptivePortalMode); completionHandler(WKNavigationActionPolicyAllow, preferences); }; NSURL *url = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); EXPECT_EQ(pid1, [webView _webProcessIdentifier]); // Shouldn't have process-swapped since we're staying in captive portal mode. EXPECT_FALSE(isJITEnabled(webView.get())); checkSettingsControlledByCaptivePortalMode(webView.get(), ShouldBeEnabled::No); } #else TEST(ProcessSwap, CaptivePortalModeEnabledByDefaultThenOptOut) { auto webViewConfiguration = adoptNS([WKWebViewConfiguration new]); EXPECT_FALSE(webViewConfiguration.get().defaultWebpagePreferences._captivePortalModeEnabled); configureCaptivePortalWKWebViewConfiguration(webViewConfiguration.get()); [webViewConfiguration.get().defaultWebpagePreferences _setCaptivePortalModeEnabled:YES]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([TestNavigationDelegate new]); [webView setNavigationDelegate:delegate.get()]; EXPECT_FALSE(isJITEnabled(webView.get())); checkSettingsControlledByCaptivePortalMode(webView.get(), ShouldBeEnabled::No, IsShowingInitialEmptyDocument::Yes); pid_t pid1 = [webView _webProcessIdentifier]; __block bool finishedNavigation = false; delegate.get().didFinishNavigation = ^(WKWebView *, WKNavigation *) { finishedNavigation = true; }; delegate.get().decidePolicyForNavigationActionWithPreferences = ^(WKNavigationAction *action, WKWebpagePreferences *preferences, void (^completionHandler)(WKNavigationActionPolicy, WKWebpagePreferences *)) { EXPECT_TRUE(preferences._captivePortalModeEnabled); completionHandler(WKNavigationActionPolicyAllow, preferences); }; NSURL *url = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); EXPECT_FALSE(isJITEnabled(webView.get())); checkSettingsControlledByCaptivePortalMode(webView.get(), ShouldBeEnabled::No); EXPECT_EQ(pid1, [webView _webProcessIdentifier]); finishedNavigation = false; url = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); EXPECT_EQ(pid1, [webView _webProcessIdentifier]); // Shouldn't have process-swapped since we're staying in captive portal mode. EXPECT_FALSE(isJITEnabled(webView.get())); checkSettingsControlledByCaptivePortalMode(webView.get(), ShouldBeEnabled::No); delegate.get().decidePolicyForNavigationActionWithPreferences = ^(WKNavigationAction *action, WKWebpagePreferences *preferences, void (^completionHandler)(WKNavigationActionPolicy, WKWebpagePreferences *)) { EXPECT_TRUE(preferences._captivePortalModeEnabled); [preferences _setCaptivePortalModeEnabled:NO]; // Opt out of captive portal mode for this load. completionHandler(WKNavigationActionPolicyAllow, preferences); }; finishedNavigation = false; url = [[NSBundle mainBundle] URLForResource:@"simple3" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); // We should have process-swapped to get out of captive portal mode. EXPECT_NE(pid1, [webView _webProcessIdentifier]); EXPECT_TRUE(isJITEnabled(webView.get())); checkSettingsControlledByCaptivePortalMode(webView.get(), ShouldBeEnabled::Yes); // captive portal mode should be disabled in new WebViews since it is not enabled globally. auto webViewConfiguration2 = adoptNS([WKWebViewConfiguration new]); configureCaptivePortalWKWebViewConfiguration(webViewConfiguration2.get()); EXPECT_TRUE([webViewConfiguration2.get().defaultWebpagePreferences _captivePortalModeEnabled] == NO); auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration2.get()]); [webView2 setNavigationDelegate:delegate.get()]; EXPECT_TRUE(isJITEnabled(webView2.get())); checkSettingsControlledByCaptivePortalMode(webView2.get(), ShouldBeEnabled::Yes, IsShowingInitialEmptyDocument::Yes); pid_t pid2 = [webView2 _webProcessIdentifier]; delegate.get().decidePolicyForNavigationActionWithPreferences = nil; finishedNavigation = false; url = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView2 loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); EXPECT_TRUE(isJITEnabled(webView2.get())); checkSettingsControlledByCaptivePortalMode(webView2.get(), ShouldBeEnabled::Yes); EXPECT_EQ(pid2, [webView2 _webProcessIdentifier]); } TEST(ProcessSwap, CaptivePortalModeSystemSettingChangeDoesNotReloadViewsWhenModeIsSetExplicitly1) { // Captive portal mode is disabled globally and explicitly opted out via defaultWebpagePreferences. auto webViewConfiguration = adoptNS([WKWebViewConfiguration new]); EXPECT_FALSE(webViewConfiguration.get().defaultWebpagePreferences._captivePortalModeEnabled); [webViewConfiguration.get().defaultWebpagePreferences _setCaptivePortalModeEnabled:NO]; // Explicitly opt out via default WKWebpagePreferences. auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([TestNavigationDelegate new]); [webView setNavigationDelegate:delegate.get()]; EXPECT_TRUE(isJITEnabled(webView.get())); __block bool finishedNavigation = false; delegate.get().didFinishNavigation = ^(WKWebView *, WKNavigation *) { finishedNavigation = true; }; NSURL *url = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); pid_t pid1 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, 0); EXPECT_TRUE(isJITEnabled(webView.get())); finishedNavigation = false; // Now change the global setting. [WKProcessPool _setCaptivePortalModeEnabledGloballyForTesting:YES]; // This should not reload the web view since the view has explicitly opted out of captive portal mode. TestWebKitAPI::Util::sleep(0.5); EXPECT_FALSE(finishedNavigation); pid_t pid2 = [webView _webProcessIdentifier]; EXPECT_EQ(pid1, pid2); EXPECT_TRUE(isJITEnabled(webView.get())); [WKProcessPool _clearCaptivePortalModeEnabledGloballyForTesting]; } TEST(ProcessSwap, CaptivePortalModeSystemSettingChangeDoesNotReloadViewsWhenModeIsSetExplicitly2) { // Captive portal mode is enabled globally but explicitly opted out via defaultWebpagePreferences. [WKProcessPool _setCaptivePortalModeEnabledGloballyForTesting:YES]; auto webViewConfiguration = adoptNS([WKWebViewConfiguration new]); EXPECT_TRUE(webViewConfiguration.get().defaultWebpagePreferences._captivePortalModeEnabled); [webViewConfiguration.get().defaultWebpagePreferences _setCaptivePortalModeEnabled:NO]; // Explicitly opt out via default WKWebpagePreferences. auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([TestNavigationDelegate new]); [webView setNavigationDelegate:delegate.get()]; EXPECT_TRUE(isJITEnabled(webView.get())); __block bool finishedNavigation = false; delegate.get().didFinishNavigation = ^(WKWebView *, WKNavigation *) { finishedNavigation = true; }; NSURL *url = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); pid_t pid1 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, 0); EXPECT_TRUE(isJITEnabled(webView.get())); finishedNavigation = false; // Now change the global setting. [WKProcessPool _clearCaptivePortalModeEnabledGloballyForTesting]; // This should not reload the web view since the view has explicitly opted out of captive portal mode. TestWebKitAPI::Util::sleep(0.5); EXPECT_FALSE(finishedNavigation); pid_t pid2 = [webView _webProcessIdentifier]; EXPECT_EQ(pid1, pid2); EXPECT_TRUE(isJITEnabled(webView.get())); } TEST(ProcessSwap, CaptivePortalModeSystemSettingChangeDoesNotReloadViewsWhenModeIsSetExplicitly3) { // Captive portal mode is disabled globally and explicitly opted out for the load. auto webViewConfiguration = adoptNS([WKWebViewConfiguration new]); EXPECT_FALSE(webViewConfiguration.get().defaultWebpagePreferences._captivePortalModeEnabled); auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([TestNavigationDelegate new]); [webView setNavigationDelegate:delegate.get()]; EXPECT_TRUE(isJITEnabled(webView.get())); __block bool finishedNavigation = false; delegate.get().didFinishNavigation = ^(WKWebView *, WKNavigation *) { finishedNavigation = true; }; delegate.get().decidePolicyForNavigationActionWithPreferences = ^(WKNavigationAction *action, WKWebpagePreferences *preferences, void (^completionHandler)(WKNavigationActionPolicy, WKWebpagePreferences *)) { EXPECT_FALSE(preferences._captivePortalModeEnabled); [preferences _setCaptivePortalModeEnabled:NO]; // Explicitly Opt out of captive portal mode for this load. completionHandler(WKNavigationActionPolicyAllow, preferences); }; NSURL *url = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); pid_t pid1 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, 0); EXPECT_TRUE(isJITEnabled(webView.get())); finishedNavigation = false; // Now change the global setting. [WKProcessPool _setCaptivePortalModeEnabledGloballyForTesting:YES]; // This should not reload the web view since the view has explicitly opted out of captive portal mode. TestWebKitAPI::Util::sleep(0.5); EXPECT_FALSE(finishedNavigation); pid_t pid2 = [webView _webProcessIdentifier]; EXPECT_EQ(pid1, pid2); EXPECT_TRUE(isJITEnabled(webView.get())); [WKProcessPool _clearCaptivePortalModeEnabledGloballyForTesting]; } TEST(ProcessSwap, CaptivePortalModeSystemSettingChangeDoesNotReloadViewsWhenModeIsSetExplicitly4) { // Captive portal mode is enabled globally but explicitly opted out for the load. [WKProcessPool _setCaptivePortalModeEnabledGloballyForTesting:YES]; auto webViewConfiguration = adoptNS([WKWebViewConfiguration new]); EXPECT_TRUE(webViewConfiguration.get().defaultWebpagePreferences._captivePortalModeEnabled); auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); auto delegate = adoptNS([TestNavigationDelegate new]); [webView setNavigationDelegate:delegate.get()]; EXPECT_FALSE(isJITEnabled(webView.get())); __block bool finishedNavigation = false; delegate.get().didFinishNavigation = ^(WKWebView *, WKNavigation *) { finishedNavigation = true; }; delegate.get().decidePolicyForNavigationActionWithPreferences = ^(WKNavigationAction *action, WKWebpagePreferences *preferences, void (^completionHandler)(WKNavigationActionPolicy, WKWebpagePreferences *)) { EXPECT_TRUE(preferences._captivePortalModeEnabled); [preferences _setCaptivePortalModeEnabled:NO]; // Explicitly Opt out of captive portal mode for this load. completionHandler(WKNavigationActionPolicyAllow, preferences); }; NSURL *url = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]; [webView loadRequest:[NSURLRequest requestWithURL:url]]; TestWebKitAPI::Util::run(&finishedNavigation); pid_t pid1 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, 0); EXPECT_TRUE(isJITEnabled(webView.get())); finishedNavigation = false; // Now change the global setting. [WKProcessPool _clearCaptivePortalModeEnabledGloballyForTesting]; // This should not reload the web view since the view has explicitly opted out of captive portal mode. TestWebKitAPI::Util::sleep(0.5); EXPECT_FALSE(finishedNavigation); pid_t pid2 = [webView _webProcessIdentifier]; EXPECT_EQ(pid1, pid2); EXPECT_TRUE(isJITEnabled(webView.get())); } #endif #if PLATFORM(IOS_FAMILY) TEST(ProcessSwap, ContentModeInCaseOfCoopProcessSwap) { using namespace TestWebKitAPI; HTTPServer server({ { "/source.html"_s, { "foo"_s } }, { "/destination.html"_s, { { { "Content-Type"_s, "text/html"_s }, { "Cross-Origin-Opener-Policy"_s, "same-origin"_s } }, "bar"_s } }, }, HTTPServer::Protocol::Https); auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto webpagePreferences = adoptNS([[WKWebpagePreferences alloc] init]); [webpagePreferences setPreferredContentMode:WKContentModeDesktop]; [webViewConfiguration setDefaultWebpagePreferences:webpagePreferences.get()]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; done = false; [webView loadRequest:server.request("/source.html"_s)]; Util::run(&done); done = false; [webView evaluateJavaScript:@"navigator.userAgent;" completionHandler:^(id _Nullable response, NSError * _Nullable error) { done = true; ASSERT_TRUE(!error); NSString *userAgent = (NSString *)response; ASSERT_TRUE([userAgent containsString:@"Macintosh; Intel Mac"]); }]; Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; [webView loadRequest:server.request("/destination.html"_s)]; Util::run(&done); done = false; [webView evaluateJavaScript:@"navigator.userAgent;" completionHandler:^(id _Nullable response, NSError * _Nullable error) { done = true; ASSERT_TRUE(!error); NSString *userAgent = (NSString *)response; ASSERT_TRUE([userAgent containsString:@"Macintosh; Intel Mac"]); }]; Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); [webView goBack]; // Back to source.html. Util::run(&done); done = false; [webView evaluateJavaScript:@"navigator.userAgent;" completionHandler:^(id _Nullable response, NSError * _Nullable error) { done = true; ASSERT_TRUE(!error); NSString *userAgent = (NSString *)response; ASSERT_TRUE([userAgent containsString:@"Macintosh; Intel Mac"]); }]; Util::run(&done); done = false; } TEST(ProcessSwap, ContentModeInCaseOfPSONThenCoopProcessSwap) { using namespace TestWebKitAPI; HTTPServer server1({ { "/source.html"_s, { "foo"_s } }, }, HTTPServer::Protocol::Https); HTTPServer server2({ { "/destination.html"_s, { { { "Content-Type"_s, "text/html"_s }, { "Cross-Origin-Opener-Policy"_s, "same-origin"_s } }, "bar"_s } }, }, HTTPServer::Protocol::Http); auto processPoolConfiguration = psonProcessPoolConfiguration(); auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]); auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [webViewConfiguration setProcessPool:processPool.get()]; auto webpagePreferences = adoptNS([[WKWebpagePreferences alloc] init]); [webpagePreferences setPreferredContentMode:WKContentModeDesktop]; [webViewConfiguration setDefaultWebpagePreferences:webpagePreferences.get()]; auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768) configuration:webViewConfiguration.get()]); auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]); [webView setNavigationDelegate:navigationDelegate.get()]; done = false; [webView loadRequest:server1.request("/source.html"_s)]; Util::run(&done); done = false; [webView evaluateJavaScript:@"navigator.userAgent;" completionHandler:^(id _Nullable response, NSError * _Nullable error) { done = true; ASSERT_TRUE(!error); NSString *userAgent = (NSString *)response; ASSERT_TRUE([userAgent containsString:@"Macintosh; Intel Mac"]); }]; Util::run(&done); done = false; auto pid1 = [webView _webProcessIdentifier]; [webView loadRequest:server2.request("/destination.html"_s)]; Util::run(&done); done = false; [webView evaluateJavaScript:@"navigator.userAgent;" completionHandler:^(id _Nullable response, NSError * _Nullable error) { done = true; ASSERT_TRUE(!error); NSString *userAgent = (NSString *)response; ASSERT_TRUE([userAgent containsString:@"Macintosh; Intel Mac"]); }]; Util::run(&done); done = false; auto pid2 = [webView _webProcessIdentifier]; EXPECT_NE(pid1, pid2); [webView goBack]; // Back to source.html. Util::run(&done); done = false; [webView evaluateJavaScript:@"navigator.userAgent;" completionHandler:^(id _Nullable response, NSError * _Nullable error) { done = true; ASSERT_TRUE(!error); NSString *userAgent = (NSString *)response; ASSERT_TRUE([userAgent containsString:@"Macintosh; Intel Mac"]); }]; Util::run(&done); done = false; } #endif // PLATFORM(IOS_FAMILY)