Skip to content

Commit

Permalink
Unable to opt out of lockdown mode on sites that serve COOP + COEP HT…
Browse files Browse the repository at this point in the history
…TP headers

https://bugs.webkit.org/show_bug.cgi?id=267766
rdar://119503109

Reviewed by Brent Fulgham.

If lockdown mode is enabled system-wide and the user attempts to navigate to
a site where lockdown mode has been disabled by the user, we expect this
request to be satisfied. However, in the event where the site serves the
proper COOP + COEP HTTP headers to trigger a process-swap, we would fail to
disable lockdown mode.

The reason is that WebPageProxy::triggerBrowsingContextGroupSwitchForNavigation(),
which gets called on network response in case of a COOP + COEP process swap,
would not carry over correctly the lockdown mode state from the old WebProcess
to the new one. In particular, if there is a ProvisionalPageProxy (because the
current navigation caused a PSON process-swap), we should be getting the lockdown
mode from this ProvisionalPageProxy's WebProcessProxy, instead of from the
WebPageProxy's WebPageProxy (which is the committed process, not the provisional
one associated with the current navigation).

* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::triggerBrowsingContextGroupSwitchForNavigation):
* Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:

Canonical link: https://commits.webkit.org/273243@main
  • Loading branch information
cdumez committed Jan 19, 2024
1 parent 3794834 commit ce9d6af
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 2 deletions.
5 changes: 3 additions & 2 deletions Source/WebKit/UIProcess/WebPageProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7055,10 +7055,11 @@ void WebPageProxy::triggerBrowsingContextGroupSwitchForNavigation(uint64_t navig
return completionHandler(false);

RefPtr<WebProcessProxy> processForNavigation;
auto lockdownMode = m_provisionalPage ? m_provisionalPage->process().lockdownMode() : m_process->lockdownMode();
if (browsingContextGroupSwitchDecision == BrowsingContextGroupSwitchDecision::NewIsolatedGroup)
processForNavigation = m_process->protectedProcessPool()->createNewWebProcess(protectedWebsiteDataStore().ptr(), m_process->lockdownMode(), WebProcessProxy::IsPrewarmed::No, CrossOriginMode::Isolated);
processForNavigation = m_process->protectedProcessPool()->createNewWebProcess(protectedWebsiteDataStore().ptr(), lockdownMode, WebProcessProxy::IsPrewarmed::No, CrossOriginMode::Isolated);
else
processForNavigation = m_process->protectedProcessPool()->processForRegistrableDomain(protectedWebsiteDataStore(), responseDomain, m_process->lockdownMode(), protectedConfiguration());
processForNavigation = m_process->protectedProcessPool()->processForRegistrableDomain(protectedWebsiteDataStore(), responseDomain, lockdownMode, protectedConfiguration());

auto processIdentifier = processForNavigation->coreProcessIdentifier();
auto preventProcessShutdownScope = processForNavigation->shutdownPreventingScope();
Expand Down
65 changes: 65 additions & 0 deletions Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm
Original file line number Diff line number Diff line change
Expand Up @@ -8104,6 +8104,71 @@ static bool isJITEnabled(WKWebView *webView)
return isJITEnabledResult;
}

// On iOS, we require the browser entitlement to change the lockdown mode, which TestWebKitAPI doesn't have.
#if !PLATFORM(IOS_FAMILY)

TEST(ProcessSwap, COEPProcessSwapOnSiteWhereLockdownModeIsDisabled)
{
RetainPtr kvo = adoptNS([LockdownModeKVO new]);
didChangeLockdownMode = false;

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 }, { "Cross-Origin-Embedder-Policy"_s, "require-corp"_s } }, "bar"_s } },
}, HTTPServer::Protocol::Https);

RetainPtr processPoolConfiguration = psonProcessPoolConfiguration();
RetainPtr processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);

RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
[webViewConfiguration.get().defaultWebpagePreferences addObserver:kvo.get() forKeyPath:@"lockdownModeEnabled" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
[webViewConfiguration setProcessPool:processPool.get()];
for (_WKFeature *feature in [WKPreferences _features]) {
if ([feature.key isEqualToString:@"CrossOriginOpenerPolicyEnabled"])
[[webViewConfiguration preferences] _setEnabled:YES forFeature:feature];
else if ([feature.key isEqualToString:@"CrossOriginEmbedderPolicyEnabled"])
[[webViewConfiguration preferences] _setEnabled:YES forFeature:feature];
}

// Enable lockdown mode globally.
[WKProcessPool _setCaptivePortalModeEnabledGloballyForTesting:YES];

TestWebKitAPI::Util::run(&didChangeLockdownMode);

RetainPtr webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
RetainPtr navigationDelegate = adoptNS([TestNavigationDelegate new]);
[navigationDelegate allowAnyTLSCertificate];
__block bool finishedNavigation = false;
navigationDelegate.get().didFinishNavigation = ^(WKWebView *, WKNavigation *) {
finishedNavigation = true;
};
[webView setNavigationDelegate:navigationDelegate.get()];

[webView loadRequest:server.request("/source.html"_s)];

TestWebKitAPI::Util::run(&finishedNavigation);
finishedNavigation = false;

EXPECT_FALSE(isJITEnabled(webView.get()));

// Opt next navigation out of lockdown mode.
navigationDelegate.get().decidePolicyForNavigationActionWithPreferences = ^(WKNavigationAction *action, WKWebpagePreferences *preferences, void (^completionHandler)(WKNavigationActionPolicy, WKWebpagePreferences *)) {
EXPECT_TRUE(preferences.lockdownModeEnabled);
preferences.lockdownModeEnabled = NO;
completionHandler(WKNavigationActionPolicyAllow, preferences);
};

[webView loadRequest:server.requestWithLocalhost("/destination.html"_s)];

TestWebKitAPI::Util::run(&finishedNavigation);
finishedNavigation = false;

EXPECT_TRUE(isJITEnabled(webView.get()));
}

#endif // !PLATFORM(IOS_FAMILY)

enum class ShouldBeEnabled : bool { No, Yes };
enum class IsShowingInitialEmptyDocument : bool { No, Yes };
static void checkSettingsControlledByLockdownMode(WKWebView *webView, ShouldBeEnabled shouldBeEnabled, IsShowingInitialEmptyDocument isShowingInitialEmptyDocument = IsShowingInitialEmptyDocument::No)
Expand Down

0 comments on commit ce9d6af

Please sign in to comment.