blob: a4e6ed4299ccfc64a53030a8d63a1bb33da17f00 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "content/test/render_document_feature.h"
#include "net/base/escape.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "services/network/public/cpp/cross_origin_opener_policy.h"
#include "services/network/public/cpp/features.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::HasSubstr;
namespace content {
namespace {
network::CrossOriginOpenerPolicy CoopSameOrigin() {
network::CrossOriginOpenerPolicy coop;
coop.value = network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin;
return coop;
}
network::CrossOriginOpenerPolicy CoopSameOriginPlusCoep() {
network::CrossOriginOpenerPolicy coop;
coop.value =
network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep;
return coop;
}
network::CrossOriginOpenerPolicy CoopSameOriginAllowPopups() {
network::CrossOriginOpenerPolicy coop;
coop.value =
network::mojom::CrossOriginOpenerPolicyValue::kSameOriginAllowPopups;
return coop;
}
network::CrossOriginOpenerPolicy CoopUnsafeNone() {
network::CrossOriginOpenerPolicy coop;
// Using the default value.
return coop;
}
std::unique_ptr<net::test_server::HttpResponse>
CrossOriginIsolatedCrossOriginRedirectHandler(
const net::test_server::HttpRequest& request) {
GURL request_url = request.GetURL();
std::string dest =
base::UnescapeBinaryURLComponent(request_url.query_piece());
net::test_server::RequestQuery query =
net::test_server::ParseQuery(request_url);
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HttpStatusCode::HTTP_FOUND);
http_response->AddCustomHeader("Location", dest);
http_response->AddCustomHeader("Cross-Origin-Opener-Policy", "same-origin");
http_response->AddCustomHeader("Cross-Origin-Embedder-Policy",
"require-corp");
return http_response;
}
class CrossOriginOpenerPolicyBrowserTest
: public ContentBrowserTest,
public ::testing::WithParamInterface<std::tuple<std::string, bool>> {
public:
CrossOriginOpenerPolicyBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
// Enable COOP/COEP:
feature_list_.InitWithFeatures(
{network::features::kCrossOriginOpenerPolicy,
network::features::kCrossOriginOpenerPolicyReporting,
network::features::kCrossOriginIsolated},
{});
// Enable RenderDocument:
InitAndEnableRenderDocumentFeature(&feature_list_for_render_document_,
std::get<0>(GetParam()));
// Enable BackForwardCache:
if (std::get<1>(GetParam())) {
feature_list_for_back_forward_cache_.InitWithFeaturesAndParameters(
{{features::kBackForwardCache,
{{"TimeToLiveInBackForwardCacheInSeconds", "3600"}}}},
// Allow BackForwardCache for all devices regardless of their memory.
{features::kBackForwardCacheMemoryControls});
} else {
feature_list_for_back_forward_cache_.InitWithFeatures(
{}, {features::kBackForwardCache});
}
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kIgnoreCertificateErrors);
}
net::EmbeddedTestServer* https_server() { return &https_server_; }
protected:
WebContentsImpl* web_contents() const {
return static_cast<WebContentsImpl*>(shell()->web_contents());
}
RenderFrameHostImpl* current_frame_host() {
return web_contents()->GetMainFrame();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
https_server()->ServeFilesFromSourceDirectory(GetTestDataFilePath());
SetupCrossSiteRedirector(https_server());
net::test_server::RegisterDefaultHandlers(&https_server_);
https_server_.RegisterDefaultHandler(base::BindRepeating(
&net::test_server::HandlePrefixedRequest,
"/redirect-with-coop-coep-headers",
base::BindRepeating(CrossOriginIsolatedCrossOriginRedirectHandler)));
ASSERT_TRUE(https_server()->Start());
}
private:
void SetUpCommandLine(base::CommandLine* command_line) override {
ContentBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
}
base::test::ScopedFeatureList feature_list_;
base::test::ScopedFeatureList feature_list_for_render_document_;
base::test::ScopedFeatureList feature_list_for_back_forward_cache_;
net::EmbeddedTestServer https_server_;
};
// Same as CrossOriginOpenerPolicyBrowserTest, but disable SharedArrayBuffer by
// default for non crossOriginIsolated process. This is the state we will reach
// after resolving: https://crbug.com/1144104
class NoSharedArrayBufferByDefault : public CrossOriginOpenerPolicyBrowserTest {
public:
NoSharedArrayBufferByDefault() {
// Disable SharedArrayBuffer in non crossOriginIsolated process.
feature_list_.InitWithFeatures(
// Enabled:
{},
// Disabled:
{
features::kSharedArrayBuffer,
features::kWebAssemblyThreads,
});
}
private:
base::test::ScopedFeatureList feature_list_;
};
using VirtualBrowsingContextGroupTest = CrossOriginOpenerPolicyBrowserTest;
int VirtualBrowsingContextGroup(WebContents* wc) {
return static_cast<WebContentsImpl*>(wc)
->GetMainFrame()
->virtual_browsing_context_group();
}
} // namespace
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
NewPopupCOOP_InheritsSameOrigin) {
GURL starting_page(
https_server()->GetURL("a.com", "/cross_site_iframe_factory.html?a(a)"));
EXPECT_TRUE(NavigateToURL(shell(), starting_page));
RenderFrameHostImpl* main_frame = current_frame_host();
main_frame->set_cross_origin_opener_policy_for_testing(CoopSameOrigin());
ShellAddedObserver shell_observer;
RenderFrameHostImpl* iframe = main_frame->child_at(0)->current_frame_host();
EXPECT_TRUE(ExecJs(iframe, "window.open('about:blank')"));
RenderFrameHostImpl* popup_frame =
static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents())
->GetFrameTree()
->root()
->current_frame_host();
EXPECT_EQ(main_frame->cross_origin_opener_policy(), CoopSameOrigin());
EXPECT_EQ(popup_frame->cross_origin_opener_policy(), CoopSameOrigin());
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
NewPopupCOOP_InheritsSameOriginAllowPopups) {
GURL starting_page(
https_server()->GetURL("a.com", "/cross_site_iframe_factory.html?a(a)"));
EXPECT_TRUE(NavigateToURL(shell(), starting_page));
RenderFrameHostImpl* main_frame = current_frame_host();
main_frame->set_cross_origin_opener_policy_for_testing(
CoopSameOriginAllowPopups());
ShellAddedObserver shell_observer;
RenderFrameHostImpl* iframe = main_frame->child_at(0)->current_frame_host();
EXPECT_TRUE(ExecJs(iframe, "window.open('about:blank')"));
RenderFrameHostImpl* popup_frame =
static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents())
->GetFrameTree()
->root()
->current_frame_host();
EXPECT_EQ(main_frame->cross_origin_opener_policy(),
CoopSameOriginAllowPopups());
EXPECT_EQ(popup_frame->cross_origin_opener_policy(),
CoopSameOriginAllowPopups());
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
NewPopupCOOP_CrossOriginDoesNotInherit) {
GURL starting_page(
https_server()->GetURL("a.com", "/cross_site_iframe_factory.html?a(b)"));
EXPECT_TRUE(NavigateToURL(shell(), starting_page));
RenderFrameHostImpl* main_frame = current_frame_host();
main_frame->set_cross_origin_opener_policy_for_testing(CoopSameOrigin());
ShellAddedObserver shell_observer;
RenderFrameHostImpl* iframe = main_frame->child_at(0)->current_frame_host();
EXPECT_TRUE(ExecJs(iframe, "window.open('about:blank')"));
RenderFrameHostImpl* popup_frame =
static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents())
->GetFrameTree()
->root()
->current_frame_host();
EXPECT_EQ(main_frame->cross_origin_opener_policy(), CoopSameOrigin());
EXPECT_EQ(popup_frame->cross_origin_opener_policy(), CoopUnsafeNone());
}
IN_PROC_BROWSER_TEST_P(
CrossOriginOpenerPolicyBrowserTest,
NewPopupCOOP_SameOriginPolicyAndCrossOriginIframeSetsNoopener) {
for (auto coop_value : {CoopSameOriginPlusCoep(), CoopSameOrigin()}) {
GURL starting_page(https_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
EXPECT_TRUE(NavigateToURL(shell(), starting_page));
RenderFrameHostImpl* main_frame = current_frame_host();
main_frame->set_cross_origin_opener_policy_for_testing(coop_value);
ShellAddedObserver new_shell_observer;
RenderFrameHostImpl* iframe = main_frame->child_at(0)->current_frame_host();
EXPECT_TRUE(ExecJs(iframe, "window.open('about:blank')"));
Shell* new_shell = new_shell_observer.GetShell();
RenderFrameHostImpl* popup_frame =
static_cast<WebContentsImpl*>(new_shell->web_contents())
->GetFrameTree()
->root()
->current_frame_host();
scoped_refptr<SiteInstance> main_frame_site_instance(
main_frame->GetSiteInstance());
scoped_refptr<SiteInstance> iframe_site_instance(iframe->GetSiteInstance());
scoped_refptr<SiteInstance> popup_site_instance(
popup_frame->GetSiteInstance());
ASSERT_TRUE(main_frame_site_instance);
ASSERT_TRUE(iframe_site_instance);
ASSERT_TRUE(popup_site_instance);
EXPECT_FALSE(main_frame_site_instance->IsRelatedSiteInstance(
popup_site_instance.get()));
EXPECT_FALSE(
iframe_site_instance->IsRelatedSiteInstance(popup_site_instance.get()));
// Check that `window.opener` is not set.
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
new_shell,
"window.domAutomationController.send(window.opener == null);",
&success));
EXPECT_TRUE(success) << "window.opener is set";
}
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
NetworkErrorOnSandboxedPopups) {
GURL starting_page(https_server()->GetURL(
"a.com", "/cross-origin-opener-policy_sandbox_popup.html"));
GURL openee_url = https_server()->GetURL(
"a.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
EXPECT_TRUE(NavigateToURL(shell(), starting_page));
ShellAddedObserver shell_observer;
RenderFrameHostImpl* iframe =
current_frame_host()->child_at(0)->current_frame_host();
EXPECT_TRUE(ExecJs(iframe, JsReplace("window.open($1);", openee_url)));
auto* popup_webcontents =
static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents());
WaitForLoadStop(popup_webcontents);
EXPECT_EQ(
popup_webcontents->GetController().GetLastCommittedEntry()->GetPageType(),
PAGE_TYPE_ERROR);
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
NoNetworkErrorOnSandboxedDocuments) {
GURL starting_page(https_server()->GetURL(
"a.com", "/set-header?Content-Security-Policy: sandbox allow-scripts"));
EXPECT_TRUE(NavigateToURL(shell(), starting_page));
EXPECT_NE(current_frame_host()->active_sandbox_flags(),
network::mojom::WebSandboxFlags::kNone)
<< "Document should be sandboxed.";
GURL next_page = https_server()->GetURL(
"a.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
EXPECT_TRUE(NavigateToURL(shell(), next_page));
EXPECT_EQ(
web_contents()->GetController().GetLastCommittedEntry()->GetPageType(),
PAGE_TYPE_NORMAL);
}
class CrossOriginPolicyHeadersObserver : public WebContentsObserver {
public:
explicit CrossOriginPolicyHeadersObserver(
WebContents* web_contents,
network::mojom::CrossOriginEmbedderPolicyValue expected_coep,
network::CrossOriginOpenerPolicy expected_coop)
: WebContentsObserver(web_contents),
expected_coep_(expected_coep),
expected_coop_(expected_coop) {}
~CrossOriginPolicyHeadersObserver() override = default;
void DidRedirectNavigation(NavigationHandle* navigation_handle) override {
// Verify that the COOP/COEP headers were parsed.
NavigationRequest* navigation_request =
static_cast<NavigationRequest*>(navigation_handle);
CHECK(navigation_request->response()
->parsed_headers->cross_origin_embedder_policy.value ==
expected_coep_);
CHECK(navigation_request->response()
->parsed_headers->cross_origin_opener_policy == expected_coop_);
}
void DidFinishNavigation(NavigationHandle* navigation_handle) override {
// Verify that the COOP/COEP headers were parsed.
NavigationRequest* navigation_request =
static_cast<NavigationRequest*>(navigation_handle);
CHECK(navigation_request->response()
->parsed_headers->cross_origin_embedder_policy.value ==
expected_coep_);
CHECK(navigation_request->response()
->parsed_headers->cross_origin_opener_policy == expected_coop_);
}
private:
network::mojom::CrossOriginEmbedderPolicyValue expected_coep_;
network::CrossOriginOpenerPolicy expected_coop_;
};
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
RedirectsParseCoopAndCoepHeaders) {
GURL redirect_initial_page(https_server()->GetURL(
"a.com", "/cross-origin-opener-policy_redirect_initial.html"));
GURL redirect_final_page(https_server()->GetURL(
"a.com", "/cross-origin-opener-policy_redirect_final.html"));
CrossOriginPolicyHeadersObserver obs(
web_contents(),
network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp,
CoopSameOriginPlusCoep());
EXPECT_TRUE(
NavigateToURL(shell(), redirect_initial_page, redirect_final_page));
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
CoopIsIgnoredOverHttp) {
WebContentsConsoleObserver console_observer(shell()->web_contents());
console_observer.SetPattern("*Cross-Origin-Opener-Policy * ignored*");
GURL non_coop_page(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL coop_page = embedded_test_server()->GetURL(
"a.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
EXPECT_TRUE(NavigateToURL(shell(), non_coop_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
EXPECT_TRUE(NavigateToURL(shell(), coop_page));
if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
// When ProactivelySwapBrowsingInstance is enabled on same-site navigations,
// the SiteInstance will change on same-site navigations (but COOP should
// still be ignored).
EXPECT_NE(current_frame_host()->GetSiteInstance(), initial_site_instance);
} else {
EXPECT_EQ(current_frame_host()->GetSiteInstance(), initial_site_instance);
}
EXPECT_EQ(current_frame_host()->cross_origin_opener_policy(),
CoopUnsafeNone());
console_observer.Wait();
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
CoopIsIgnoredOnIframes) {
GURL starting_page(
https_server()->GetURL("a.com", "/cross_site_iframe_factory.html?a(b)"));
GURL iframe_navigation_url = https_server()->GetURL(
"b.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
EXPECT_TRUE(NavigateToURL(shell(), starting_page));
RenderFrameHostImpl* main_rfh = current_frame_host();
FrameTreeNode* iframe_ftn = main_rfh->child_at(0);
RenderFrameHostImpl* iframe_rfh = iframe_ftn->current_frame_host();
SiteInstanceImpl* non_coop_iframe_site_instance =
iframe_rfh->GetSiteInstance();
// Navigate the iframe same-origin to a document with the COOP header. The
// header must be ignored in iframes.
EXPECT_TRUE(NavigateToURLFromRenderer(iframe_ftn, iframe_navigation_url));
iframe_rfh = iframe_ftn->current_frame_host();
// We expect the navigation to have used the same SiteInstance that was used
// in the first place since they are same origin and COOP is ignored.
EXPECT_EQ(iframe_rfh->GetLastCommittedURL(), iframe_navigation_url);
EXPECT_EQ(iframe_rfh->GetSiteInstance(), non_coop_iframe_site_instance);
EXPECT_EQ(iframe_rfh->cross_origin_opener_policy(), CoopUnsafeNone());
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
NonCoopPageCrashIntoCoop) {
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
GURL non_coop_page(https_server()->GetURL("a.com", "/title1.html"));
GURL coop_page = https_server()->GetURL(
"a.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
// Test a crash before the navigation.
{
// Navigate to a non coop page.
EXPECT_TRUE(NavigateToURL(shell(), non_coop_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
// Ensure it has a RenderFrameHostProxy for another cross-site page.
GURL non_coop_cross_site_page(
https_server()->GetURL("b.com", "/title1.html"));
OpenPopup(current_frame_host(), non_coop_cross_site_page, "");
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
1u);
// Simulate the renderer process crashing.
RenderProcessHost* process = initial_site_instance->GetProcess();
ASSERT_TRUE(process);
std::unique_ptr<RenderProcessHostWatcher> crash_observer(
new RenderProcessHostWatcher(
process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT));
process->Shutdown(0);
crash_observer->Wait();
crash_observer.reset();
// Navigate to a COOP page.
EXPECT_TRUE(NavigateToURL(shell(), coop_page));
EXPECT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
initial_site_instance.get()));
EXPECT_EQ(current_frame_host()->cross_origin_opener_policy(),
CoopSameOrigin());
// The COOP page should no longer have any RenderFrameHostProxies.
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
0u);
}
// Test a crash during the navigation.
{
// Navigate to a non coop page.
EXPECT_TRUE(NavigateToURL(shell(), non_coop_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
GURL non_coop_cross_site_page(
https_server()->GetURL("b.com", "/title1.html"));
// Ensure it has a RenderFrameHostProxy for another cross-site page.
OpenPopup(current_frame_host(), non_coop_cross_site_page, "");
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
1u);
// Start navigating to a COOP page.
TestNavigationManager coop_navigation(web_contents(), coop_page);
shell()->LoadURL(coop_page);
EXPECT_TRUE(coop_navigation.WaitForRequestStart());
// Simulate the renderer process crashing.
RenderProcessHost* process = initial_site_instance->GetProcess();
ASSERT_TRUE(process);
std::unique_ptr<RenderProcessHostWatcher> crash_observer(
new RenderProcessHostWatcher(
process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT));
process->Shutdown(0);
crash_observer->Wait();
crash_observer.reset();
// Finish the navigation to the COOP page.
coop_navigation.WaitForNavigationFinished();
EXPECT_TRUE(coop_navigation.was_successful());
EXPECT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
initial_site_instance.get()));
EXPECT_EQ(current_frame_host()->cross_origin_opener_policy(),
CoopSameOrigin());
// The COOP page should no longer have any RenderFrameHostProxies.
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
0u);
}
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
CoopPageCrashIntoNonCoop) {
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
GURL coop_allow_popups_page(https_server()->GetURL(
"a.com",
"/set-header?Cross-Origin-Opener-Policy: same-origin-allow-popups"));
GURL non_coop_page(https_server()->GetURL("a.com", "/title1.html"));
GURL cross_origin_non_coop_page(
https_server()->GetURL("b.com", "/title1.html"));
// Test a crash before the navigation.
{
// Navigate to a COOP page.
EXPECT_TRUE(NavigateToURL(shell(), coop_allow_popups_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
// Ensure it has a RenderFrameHostProxy for another cross-site page.
OpenPopup(current_frame_host(), cross_origin_non_coop_page, "");
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
1u);
// Simulate the renderer process crashing.
RenderProcessHost* process = initial_site_instance->GetProcess();
ASSERT_TRUE(process);
std::unique_ptr<RenderProcessHostWatcher> crash_observer(
new RenderProcessHostWatcher(
process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT));
process->Shutdown(0);
crash_observer->Wait();
crash_observer.reset();
// Navigate to a non COOP page.
EXPECT_TRUE(NavigateToURL(shell(), non_coop_page));
EXPECT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
initial_site_instance.get()));
EXPECT_EQ(current_frame_host()->cross_origin_opener_policy(),
CoopUnsafeNone());
// The non COOP page should no longer have any RenderFrameHostProxies.
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
0u);
}
// Test a crash during the navigation.
{
// Navigate to a COOP page.
EXPECT_TRUE(NavigateToURL(shell(), coop_allow_popups_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
// Ensure it has a RenderFrameHostProxy for another cross-site page.
OpenPopup(current_frame_host(), cross_origin_non_coop_page, "");
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
1u);
// Start navigating to a non COOP page.
TestNavigationManager non_coop_navigation(web_contents(), non_coop_page);
shell()->LoadURL(non_coop_page);
EXPECT_TRUE(non_coop_navigation.WaitForRequestStart());
// Simulate the renderer process crashing.
RenderProcessHost* process = initial_site_instance->GetProcess();
ASSERT_TRUE(process);
std::unique_ptr<RenderProcessHostWatcher> crash_observer(
new RenderProcessHostWatcher(
process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT));
process->Shutdown(0);
crash_observer->Wait();
crash_observer.reset();
// Finish the navigation to the non COOP page.
non_coop_navigation.WaitForNavigationFinished();
EXPECT_TRUE(non_coop_navigation.was_successful());
EXPECT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
initial_site_instance.get()));
EXPECT_EQ(current_frame_host()->cross_origin_opener_policy(),
CoopUnsafeNone());
// The non COOP page should no longer have any RenderFrameHostProxies.
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
0u);
}
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
CoopPageCrashIntoCoop) {
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
GURL coop_allow_popups_page(https_server()->GetURL(
"a.com",
"/set-header?Cross-Origin-Opener-Policy: same-origin-allow-popups"));
GURL cross_origin_non_coop_page(
https_server()->GetURL("b.com", "/title1.html"));
// Test a crash before the navigation.
{
// Navigate to a COOP page.
EXPECT_TRUE(NavigateToURL(shell(), coop_allow_popups_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
EXPECT_EQ(current_frame_host()->cross_origin_opener_policy(),
CoopSameOriginAllowPopups());
// Ensure it has a RenderFrameHostProxy for another cross-site page.
OpenPopup(current_frame_host(), cross_origin_non_coop_page, "");
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
1u);
// Simulate the renderer process crashing.
RenderProcessHost* process = initial_site_instance->GetProcess();
ASSERT_TRUE(process);
std::unique_ptr<RenderProcessHostWatcher> crash_observer(
new RenderProcessHostWatcher(
process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT));
process->Shutdown(0);
crash_observer->Wait();
crash_observer.reset();
// Navigate to a COOP page.
EXPECT_TRUE(NavigateToURL(shell(), coop_allow_popups_page));
EXPECT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
initial_site_instance.get()));
EXPECT_EQ(current_frame_host()->cross_origin_opener_policy(),
CoopSameOriginAllowPopups());
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
1u);
}
// Test a crash during the navigation.
{
// Navigate to a COOP page.
EXPECT_TRUE(NavigateToURL(shell(), coop_allow_popups_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
// Ensure it has a RenderFrameHostProxy for another cross-site page.
OpenPopup(current_frame_host(), cross_origin_non_coop_page, "");
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
1u);
// Start navigating to a COOP page.
TestNavigationManager coop_navigation(web_contents(),
coop_allow_popups_page);
shell()->LoadURL(coop_allow_popups_page);
EXPECT_TRUE(coop_navigation.WaitForRequestStart());
// Simulate the renderer process crashing.
RenderProcessHost* process = initial_site_instance->GetProcess();
ASSERT_TRUE(process);
std::unique_ptr<RenderProcessHostWatcher> crash_observer(
new RenderProcessHostWatcher(
process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT));
process->Shutdown(0);
crash_observer->Wait();
crash_observer.reset();
// Finish the navigation to the COOP page.
coop_navigation.WaitForNavigationFinished();
EXPECT_TRUE(coop_navigation.was_successful());
EXPECT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
initial_site_instance.get()));
EXPECT_EQ(current_frame_host()->cross_origin_opener_policy(),
CoopSameOriginAllowPopups());
EXPECT_EQ(web_contents()
->GetFrameTree()
->root()
->render_manager()
->GetProxyCount(),
1u);
}
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
ProxiesAreRemovedWhenCrossingCoopBoundary) {
GURL non_coop_page(https_server()->GetURL("a.com", "/title1.html"));
GURL coop_page = https_server()->GetURL(
"a.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
RenderFrameHostManager* main_window_rfhm =
web_contents()->GetFrameTree()->root()->render_manager();
EXPECT_TRUE(NavigateToURL(shell(), non_coop_page));
EXPECT_EQ(main_window_rfhm->GetProxyCount(), 0u);
Shell* popup_shell = OpenPopup(shell(), coop_page, "");
// The main frame should not have the popup referencing it.
EXPECT_EQ(main_window_rfhm->GetProxyCount(), 0u);
// It should not have any other related SiteInstance.
EXPECT_EQ(
current_frame_host()->GetSiteInstance()->GetRelatedActiveContentsCount(),
1u);
// The popup should not have the main frame referencing it.
FrameTreeNode* popup =
static_cast<WebContentsImpl*>(popup_shell->web_contents())
->GetFrameTree()
->root();
RenderFrameHostManager* popup_rfhm = popup->render_manager();
EXPECT_EQ(popup_rfhm->GetProxyCount(), 0u);
// The popup should have an empty opener.
EXPECT_FALSE(popup->opener());
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
ProxiesAreKeptWhenNavigatingFromCoopToCoop) {
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
GURL coop_page = https_server()->GetURL(
"a.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
// Navigate to a COOP page.
EXPECT_TRUE(NavigateToURL(shell(), coop_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
// Ensure it has a RenderFrameHostProxy for another cross-site page.
Shell* popup_shell = OpenPopup(current_frame_host(), coop_page, "");
GURL cross_site_iframe(https_server()->GetURL("b.com", "/title1.html"));
TestNavigationManager iframe_navigation(popup_shell->web_contents(),
cross_site_iframe);
EXPECT_TRUE(ExecJs(popup_shell->web_contents(),
JsReplace("var iframe = document.createElement('iframe');"
"iframe.src = $1;"
"document.body.appendChild(iframe);",
cross_site_iframe)));
iframe_navigation.WaitForNavigationFinished();
EXPECT_EQ(
web_contents()->GetFrameTree()->root()->render_manager()->GetProxyCount(),
1u);
// Navigate to a COOP page.
EXPECT_TRUE(NavigateToURL(shell(), coop_page));
// The COOP page should still have a RenderFrameProxyHost.
EXPECT_EQ(
web_contents()->GetFrameTree()->root()->render_manager()->GetProxyCount(),
1u);
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
IsolateInNewProcessDespiteLimitReached) {
// Set a process limit of 1 for testing.
RenderProcessHostImpl::SetMaxRendererProcessCount(1);
// Navigate to a starting page.
GURL starting_page(https_server()->GetURL("a.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), starting_page));
// Open a popup with CrossOriginOpenerPolicy and CrossOriginEmbedderPolicy
// set.
GURL url_openee =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
ShellAddedObserver shell_observer;
EXPECT_TRUE(
ExecJs(current_frame_host(), JsReplace("window.open($1)", url_openee)));
auto* popup_webcontents =
static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents());
EXPECT_TRUE(WaitForLoadStop(popup_webcontents));
// The page and its popup should be in different processes even though the
// process limit was reached.
EXPECT_NE(current_frame_host()->GetProcess(),
popup_webcontents->GetMainFrame()->GetProcess());
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
NoProcessReuseForCOOPProcesses) {
// Set a process limit of 1 for testing.
RenderProcessHostImpl::SetMaxRendererProcessCount(1);
// Navigate to a starting page with CrossOriginOpenerPolicy and
// CrossOriginEmbedderPolicy set.
GURL starting_page =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
EXPECT_TRUE(NavigateToURL(shell(), starting_page));
// Open a popup without CrossOriginOpenerPolicy and CrossOriginEmbedderPolicy
// set.
ShellAddedObserver shell_observer;
EXPECT_TRUE(ExecJs(current_frame_host(), "window.open('/title1.html')"));
auto* popup_webcontents =
static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents());
EXPECT_TRUE(WaitForLoadStop(popup_webcontents));
// The page and its popup should be in different processes even though the
// process limit was reached.
EXPECT_NE(current_frame_host()->GetProcess(),
popup_webcontents->GetMainFrame()->GetProcess());
// Navigate to a new page without COOP and COEP. Because of process reuse, it
// is placed in the popup process.
GURL final_page(https_server()->GetURL("a.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), final_page));
EXPECT_EQ(current_frame_host()->GetProcess(),
popup_webcontents->GetMainFrame()->GetProcess());
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
SpeculativeRfhsAndCoop) {
GURL non_coop_page(https_server()->GetURL("a.com", "/title1.html"));
GURL coop_page =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
// Non-COOP into non-COOP.
{
// Start on a non COOP page.
EXPECT_TRUE(NavigateToURL(shell(), non_coop_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
// Navigate to a non COOP page.
TestNavigationManager non_coop_navigation(web_contents(), non_coop_page);
shell()->LoadURL(non_coop_page);
EXPECT_TRUE(non_coop_navigation.WaitForRequestStart());
// TODO(ahemery): RenderDocument will always create a Speculative RFH.
// Update these expectations to test the speculative RFH's SI relation when
// RenderDocument lands.
EXPECT_FALSE(web_contents()
->GetFrameTree()
->root()
->render_manager()
->speculative_frame_host());
non_coop_navigation.WaitForNavigationFinished();
EXPECT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
initial_site_instance.get()));
EXPECT_EQ(current_frame_host()->cross_origin_opener_policy().value,
network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone);
}
// Non-COOP into COOP.
{
// Start on a non COOP page.
EXPECT_TRUE(NavigateToURL(shell(), non_coop_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
// Navigate to a COOP page.
TestNavigationManager coop_navigation(web_contents(), coop_page);
shell()->LoadURL(coop_page);
EXPECT_TRUE(coop_navigation.WaitForRequestStart());
auto* speculative_rfh = web_contents()
->GetFrameTree()
->root()
->render_manager()
->speculative_frame_host();
if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
// When ProactivelySwapBrowsingInstance or RenderDocument is enabled on
// same-site main-frame navigations, the navigation will result in a new
// RFH, so it will create a pending RFH.
EXPECT_TRUE(speculative_rfh);
} else {
EXPECT_FALSE(speculative_rfh);
}
coop_navigation.WaitForNavigationFinished();
EXPECT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
initial_site_instance.get()));
EXPECT_EQ(
current_frame_host()->cross_origin_opener_policy().value,
network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep);
}
// COOP into non-COOP.
{
// Start on a COOP page.
EXPECT_TRUE(NavigateToURL(shell(), coop_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
// Navigate to a non COOP page.
TestNavigationManager non_coop_navigation(web_contents(), non_coop_page);
shell()->LoadURL(non_coop_page);
EXPECT_TRUE(non_coop_navigation.WaitForRequestStart());
auto* speculative_rfh = web_contents()
->GetFrameTree()
->root()
->render_manager()
->speculative_frame_host();
if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
// When ProactivelySwapBrowsingInstance or RenderDocument is enabled on
// same-site main-frame navigations, the navigation will result in a new
// RFH, so it will create a pending RFH.
EXPECT_TRUE(speculative_rfh);
} else {
EXPECT_FALSE(speculative_rfh);
}
non_coop_navigation.WaitForNavigationFinished();
EXPECT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
initial_site_instance.get()));
EXPECT_EQ(current_frame_host()->cross_origin_opener_policy().value,
network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone);
}
// COOP into COOP.
{
// Start on a COOP page.
EXPECT_TRUE(NavigateToURL(shell(), coop_page));
scoped_refptr<SiteInstance> initial_site_instance(
current_frame_host()->GetSiteInstance());
// Navigate to a COOP page.
TestNavigationManager coop_navigation(web_contents(), coop_page);
shell()->LoadURL(coop_page);
EXPECT_TRUE(coop_navigation.WaitForRequestStart());
// TODO(ahemery): RenderDocument will always create a Speculative RFH.
// Update these expectations to test the speculative RFH's SI relation when
// RenderDocument lands.
EXPECT_FALSE(web_contents()
->GetFrameTree()
->root()
->render_manager()
->speculative_frame_host());
coop_navigation.WaitForNavigationFinished();
EXPECT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
initial_site_instance.get()));
EXPECT_EQ(
current_frame_host()->cross_origin_opener_policy().value,
network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep);
}
}
// Try to host into the same cross-origin isolated process, two cross-origin
// documents. The second's response sets CSP:sandbox, so its origin is opaque
// and derived from the first.
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
CrossOriginIsolatedWithDifferentOrigin) {
GURL opener_url =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
GURL openee_url =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp&"
"Content-Security-Policy: sandbox");
// Load the first window.
EXPECT_TRUE(NavigateToURL(shell(), opener_url));
RenderFrameHostImpl* opener_current_main_document = current_frame_host();
// Load the second window.
ShellAddedObserver shell_observer;
EXPECT_TRUE(
ExecJs(current_frame_host(), JsReplace("window.open($1)", openee_url)));
WebContents* popup = shell_observer.GetShell()->web_contents();
WaitForLoadStop(popup);
RenderFrameHostImpl* openee_current_main_document =
static_cast<WebContentsImpl*>(popup)
->GetFrameTree()
->root()
->current_frame_host();
// Those documents aren't error pages.
EXPECT_EQ(opener_current_main_document->GetLastCommittedURL(), opener_url);
EXPECT_EQ(openee_current_main_document->GetLastCommittedURL(), openee_url);
EXPECT_EQ(opener_current_main_document->last_http_status_code(), 200);
EXPECT_EQ(openee_current_main_document->last_http_status_code(), 200);
// We have two main documents in the same cross-origin isolated process from a
// different origin.
// TODO(https://crbug.com/1115426): Investigate what needs to be done.
EXPECT_NE(opener_current_main_document->GetLastCommittedOrigin(),
openee_current_main_document->GetLastCommittedOrigin());
EXPECT_EQ(opener_current_main_document->GetProcess(),
openee_current_main_document->GetProcess());
EXPECT_EQ(opener_current_main_document->GetSiteInstance(),
openee_current_main_document->GetSiteInstance());
// TODO(arthursonzogni): Check whether the processes are marked as
// cross-origin isolated or not.
}
// Navigate in between two documents. Check the virtual browsing context group
// is properly updated.
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest, Navigation) {
const struct {
GURL url_a;
GURL url_b;
bool expect_different_virtual_browsing_context_group;
} kTestCases[] = {
// non-coop <-> non-coop
{
// same-origin => keep.
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL("a.com", "/title2.html"),
false,
},
{
// different-origin => keep.
https_server()->GetURL("a.a.com", "/title1.html"),
https_server()->GetURL("b.a.com", "/title2.html"),
false,
},
{
// different-site => keep.
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL("b.com", "/title2.html"),
false,
},
// non-coop <-> coop.
{
// same-origin => change.
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
{
// different-origin => change.
https_server()->GetURL("a.a.com", "/title1.html"),
https_server()->GetURL("b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
{
// different-site => change.
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL("b.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
// coop <-> coop.
{
// same-origin => keep.
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
false,
},
{
// different-origin => change.
https_server()->GetURL("a.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
{
// different-site => keep.
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("b.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
// non-coop <-> coop-ro.
{
// same-origin => change.
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
{
// different-origin => change.
https_server()->GetURL("a.a.com", "/title1.html"),
https_server()->GetURL(
"b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
{
// different-site => change.
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL(
"b.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
// coop-ro <-> coop-ro.
{
// same-origin => keep.
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
false,
},
{
// different-origin => change.
https_server()->GetURL(
"a.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
{
// different-site => keep.
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"b.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
// coop <-> coop-ro.
{
// same-origin => change.
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
{
// different-origin => change.
https_server()->GetURL("a.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
{
// different-site => change
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"b.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
// TODO(https://crbug.com/1101339). Test with COEP-RO.
// TODO(https://crbug.com/1101339). Test with COOP-RO+COOP.
};
for (const auto& test_case : kTestCases) {
SCOPED_TRACE(testing::Message()
<< std::endl
<< "url_a = " << test_case.url_a << std::endl
<< "url_b = " << test_case.url_b << std::endl);
ASSERT_TRUE(NavigateToURL(shell(), test_case.url_a));
int group_1 = VirtualBrowsingContextGroup(web_contents());
ASSERT_TRUE(NavigateToURL(shell(), test_case.url_b));
int group_2 = VirtualBrowsingContextGroup(web_contents());
ASSERT_TRUE(NavigateToURL(shell(), test_case.url_a));
int group_3 = VirtualBrowsingContextGroup(web_contents());
// Note: Navigating from A to B and navigating from B to A must lead to the
// same decision. We check both to avoid adding all the symmetric test
// cases.
if (test_case.expect_different_virtual_browsing_context_group) {
EXPECT_NE(group_1, group_2); // url_a -> url_b.
EXPECT_NE(group_2, group_3); // url_a <- url_b.
} else {
EXPECT_EQ(group_1, group_2); // url_a -> url_b.
EXPECT_EQ(group_2, group_3); // url_b <- url_b.
}
}
}
// Use window.open(url). Check the virtual browsing context group of the two
// window.
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest, WindowOpen) {
const struct {
GURL url_opener;
GURL url_openee;
bool expect_different_virtual_browsing_context_group;
} kTestCases[] = {
// Open with no URL => Always keep.
{
// From non-coop.
https_server()->GetURL("a.com", "/title1.html"),
GURL(),
false,
},
{
// From coop-ro.
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
GURL(),
false,
},
{
// From coop.
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
GURL(),
false,
},
// From here, we open a new window with an URL. This is equivalent to:
// 1. opening a new window
// 2. navigating the new window.
//
// (1) is tested by the 3 test cases above.
// (2) is tested by the test VirtualBrowsingContextGroup.
//
// Here we are only providing a few test cases to test the sequence 1 & 2.
// non-coop opens non-coop.
{
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL("a.com", "/title1.html"),
false,
},
// non-coop opens coop-ro.
{
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
// non-coop opens coop.
{
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
// coop opens non-coop.
{
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.com", "/title1.html"),
true,
},
// coop-ro opens coop-ro (same-origin).
{
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
false,
},
// coop-ro opens coop-ro (different-origin).
{
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"b.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
},
// TODO(https://crbug.com/1101339). Test with COEP-RO.
// TODO(https://crbug.com/1101339). Test with COOP-RO+COOP
};
for (const auto& test_case : kTestCases) {
SCOPED_TRACE(testing::Message()
<< std::endl
<< "url_opener = " << test_case.url_opener << std::endl
<< "url_openee = " << test_case.url_openee << std::endl);
ASSERT_TRUE(NavigateToURL(shell(), test_case.url_opener));
int group_opener = VirtualBrowsingContextGroup(web_contents());
ShellAddedObserver shell_observer;
EXPECT_TRUE(ExecJs(current_frame_host(),
JsReplace("window.open($1)", test_case.url_openee)));
WebContents* popup = shell_observer.GetShell()->web_contents();
// The virtual browser context group will change, only after the popup has
// navigated.
WaitForLoadStop(popup);
int group_openee = VirtualBrowsingContextGroup(popup);
if (test_case.expect_different_virtual_browsing_context_group)
EXPECT_NE(group_opener, group_openee);
else
EXPECT_EQ(group_opener, group_openee);
popup->Close();
}
}
namespace {
// Use two URLs, |url_a| and |url_b|. One of them at least uses
// COOP:same-origin-allow-popups, or COOP-Report-Only:same-origin-allow-popups,
// or both.
//
// Test two scenario:
// 1. From |url_a|, opens |url_b|
// 2. From |url_a|, navigates to |url_b|.
//
// In both cases, check whether a new virtual browsing context group has been
// used or not.
struct VirtualBcgAllowPopupTestCase {
GURL url_a;
GURL url_b;
bool expect_different_group_window_open;
bool expect_different_group_navigation;
};
void RunTest(const VirtualBcgAllowPopupTestCase& test_case, Shell* shell) {
SCOPED_TRACE(testing::Message()
<< std::endl
<< "url_a = " << test_case.url_a << std::endl
<< "url_b = " << test_case.url_b << std::endl);
ASSERT_TRUE(NavigateToURL(shell, test_case.url_a));
int group_initial = VirtualBrowsingContextGroup(shell->web_contents());
ShellAddedObserver shell_observer;
EXPECT_TRUE(ExecJs(shell->web_contents()->GetMainFrame(),
JsReplace("window.open($1)", test_case.url_b)));
WebContents* popup = shell_observer.GetShell()->web_contents();
WaitForLoadStop(popup);
int group_openee = VirtualBrowsingContextGroup(popup);
ASSERT_TRUE(NavigateToURL(shell, test_case.url_b));
int group_navigate = VirtualBrowsingContextGroup(shell->web_contents());
if (test_case.expect_different_group_window_open)
EXPECT_NE(group_initial, group_openee);
else
EXPECT_EQ(group_initial, group_openee);
if (test_case.expect_different_group_navigation)
EXPECT_NE(group_initial, group_navigate);
else
EXPECT_EQ(group_initial, group_navigate);
popup->Close();
}
} // namespace
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
NonCoopToCoopAllowPopup) {
const VirtualBcgAllowPopupTestCase kTestCases[] = {
{
// same-origin.
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
true,
},
{
// cross-origin.
https_server()->GetURL("a.a.com", "/title1.html"),
https_server()->GetURL(
"b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
true,
},
{
// cross-site.
https_server()->GetURL("a.com", "/title1.html"),
https_server()->GetURL(
"b.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
true,
},
};
for (const auto& test : kTestCases)
RunTest(test, shell());
}
// coop:same-origin-allow-popup -> coop:none.
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
CoopAllowPopup_NonCoop) {
const VirtualBcgAllowPopupTestCase kTestCases[] = {
{
// same-origin.
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.com", "/title1.html"), false,
true,
},
{
// cross-origin.
https_server()->GetURL(
"b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.a.com", "/title1.html"), false,
true,
},
{
// cross-site.
https_server()->GetURL(
"b.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.com", "/title1.html"), false,
true,
},
};
for (const auto& test : kTestCases)
RunTest(test, shell());
}
// coop:none -> coop:same-origin-allow-popup.
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
CoopRoAllowPopup_NonCoop) {
const VirtualBcgAllowPopupTestCase kTestCases[] = {
{
// same-origin.
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.com", "/title1.html"), false,
true,
},
{
// cross-origin.
https_server()->GetURL("b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.a.com", "/title1.html"), false,
true,
},
{
// cross-site.
https_server()->GetURL("b.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.com", "/title1.html"), false,
true,
},
};
for (const auto& test : kTestCases)
RunTest(test, shell());
}
// coop:same-origin-allow-popup -> coop:same-origin-allow-popup.
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
CoopAllowPopup_CoopAllowPopup) {
const VirtualBcgAllowPopupTestCase kTestCases[] = {
{
// same-origin.
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
false,
false,
},
{
// cross-origin.
https_server()->GetURL(
"a.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
true,
},
{
// cross-site.
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"b.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
true,
},
};
for (const auto& test : kTestCases)
RunTest(test, shell());
}
// coop:same-origin-allow-popup -> coop-ro:same-origin-allow-popup.
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
CoopAllowPopup_CoopRoAllowPopup) {
const VirtualBcgAllowPopupTestCase kTestCases[] = {
{
// same-origin.
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
false,
true,
},
{
// cross-origin.
https_server()->GetURL("a.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
true,
},
{
// cross-site.
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("b.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
true,
},
};
for (const auto& test : kTestCases)
RunTest(test, shell());
}
// coop-ro:same-origin-allow-popup -> coop:same-origin-allow-popup.
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
CoopRoAllowPopup_CoopAllowPopup) {
const VirtualBcgAllowPopupTestCase kTestCases[] = {
{
// same-origin.
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
true,
},
{
// cross-origin.
https_server()->GetURL("a.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"b.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
true,
},
{
// cross-site.
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL(
"b.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Embedder-Policy: require-corp"),
true,
true,
},
};
for (const auto& test : kTestCases)
RunTest(test, shell());
}
// coop:same-origin-allow-popup + coop-ro:same-origin-allow-popup -> coop:none.
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
CoopPopupRoSameOrigin_NonCoop) {
const VirtualBcgAllowPopupTestCase kTestCases[] = {
// coop:allow-popup, coop-ro:same-origin-> no-coop.
{
// same-origin.
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("a.com", "/title1.html"),
true,
true,
},
{
// cross-origin.
https_server()->GetURL(
"a.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("b.a.com", "/title1.html"),
true,
true,
},
{
// cross-site.
https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin-allow-popups&"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"),
https_server()->GetURL("b.com", "/title1.html"),
true,
true,
},
};
for (const auto& test : kTestCases)
RunTest(test, shell());
}
// Navigates in between two pages from a different browsing context group. Then
// use the history API to navigate back and forth. Check their virtual browsing
// context group isn't restored.
// The goal is to spot differences when the BackForwardCache is enabled. See
// https://crbug.com/1109648.
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest, HistoryNavigation) {
GURL url_a = https_server()->GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
GURL url_b = https_server()->GetURL(
"b.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
EXPECT_TRUE(NavigateToURL(shell(), url_a));
int group_1 = VirtualBrowsingContextGroup(web_contents());
EXPECT_TRUE(NavigateToURL(shell(), url_b));
int group_2 = VirtualBrowsingContextGroup(web_contents());
web_contents()->GetController().GoBack();
EXPECT_TRUE(WaitForLoadStop(web_contents()));
int group_3 = VirtualBrowsingContextGroup(web_contents());
web_contents()->GetController().GoForward();
EXPECT_TRUE(WaitForLoadStop(web_contents()));
int group_4 = VirtualBrowsingContextGroup(web_contents());
// No matter whether the BackForwardCache is enabled or not, the navigation in
// between the two URLs must always cross a virtual browsing context group.
EXPECT_NE(group_1, group_2);
EXPECT_NE(group_2, group_3);
EXPECT_NE(group_3, group_4);
EXPECT_NE(group_1, group_4);
// TODO(https://crbug.com/1112256) During history navigation, the virtual
// browsing context group must be restored whenever the SiteInstance is
// restored. Currently, the SiteInstance is restored, but the virtual browsing
// context group is new.
if (IsBackForwardCacheEnabled()) {
EXPECT_EQ(group_1, group_3);
EXPECT_EQ(group_2, group_4);
} else {
EXPECT_NE(group_1, group_3);
EXPECT_NE(group_2, group_4);
}
}
// 1. A1 opens B2 (same virtual browsing context group).
// 2. B2 navigates to C3 (different virtual browsing context group).
// 3. C3 navigates back to B4 using the history (different virtual browsing
// context group).
//
// A1 and B4 must not be in the same browsing context group.
IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
HistoryNavigationWithPopup) {
GURL url_a = https_server()->GetURL("a.com", "/title1.html");
GURL url_b = https_server()->GetURL("b.com", "/title1.html");
GURL url_c = https_server()->GetURL(
"c.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
// Navigate to A1.
EXPECT_TRUE(NavigateToURL(shell(), url_a));
int group_1 = VirtualBrowsingContextGroup(web_contents());
// A1 opens B2.
ShellAddedObserver shell_observer;
EXPECT_TRUE(
ExecJs(current_frame_host(), JsReplace("window.open($1)", url_b)));
WebContents* popup = shell_observer.GetShell()->web_contents();
EXPECT_TRUE(WaitForLoadStop(popup));
int group_2 = VirtualBrowsingContextGroup(popup);
// B2 navigates to C3.
EXPECT_TRUE(ExecJs(popup, JsReplace("location.href = $1;", url_c)));
EXPECT_TRUE(WaitForLoadStop(popup));
int group_3 = VirtualBrowsingContextGroup(popup);
// C3 navigates back to B4.
EXPECT_TRUE(ExecJs(popup, JsReplace("history.back()")));
EXPECT_TRUE(WaitForLoadStop(popup));
int group_4 = VirtualBrowsingContextGroup(popup);
EXPECT_EQ(group_1, group_2);
EXPECT_NE(group_2, group_3);
EXPECT_NE(group_3, group_4);
EXPECT_NE(group_4, group_1);
}
// This test is flaky on Win: https://crbug.com/1125998.
#if defined(OS_WIN)
#define MAYBE_CrossOriginIsolatedSiteInstance_MainFrame \
DISABLED_CrossOriginIsolatedSiteInstance_MainFrame
#else
#define MAYBE_CrossOriginIsolatedSiteInstance_MainFrame \
CrossOriginIsolatedSiteInstance_MainFrame
#endif
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
MAYBE_CrossOriginIsolatedSiteInstance_MainFrame) {
GURL isolated_page(
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"));
GURL isolated_page_b(
https_server()->GetURL("cdn.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"));
GURL non_isolated_page(https_server()->GetURL("a.com", "/title1.html"));
// Navigation from/to cross-origin isolated pages.
// Initial non cross-origin isolated page.
{
EXPECT_TRUE(NavigateToURL(shell(), non_isolated_page));
SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance();
EXPECT_FALSE(current_si->IsCoopCoepCrossOriginIsolated());
}
// Navigation to a cross-origin isolated page.
{
scoped_refptr<SiteInstanceImpl> previous_si =
current_frame_host()->GetSiteInstance();
EXPECT_TRUE(NavigateToURL(shell(), isolated_page));
SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(current_si->IsCoopCoepCrossOriginIsolated());
EXPECT_FALSE(current_si->IsRelatedSiteInstance(previous_si.get()));
EXPECT_NE(current_si->GetProcess(), previous_si->GetProcess());
}
// Navigation to the same cross-origin isolated page.
{
scoped_refptr<SiteInstanceImpl> previous_si =
current_frame_host()->GetSiteInstance();
EXPECT_TRUE(NavigateToURL(shell(), isolated_page));
SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(current_si->IsCoopCoepCrossOriginIsolated());
EXPECT_EQ(current_si, previous_si);
}
// Navigation to a non cross-origin isolated page.
{
scoped_refptr<SiteInstanceImpl> previous_si =
current_frame_host()->GetSiteInstance();
EXPECT_TRUE(NavigateToURL(shell(), non_isolated_page));
SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance();
EXPECT_FALSE(current_si->IsCoopCoepCrossOriginIsolated());
EXPECT_FALSE(current_si->IsRelatedSiteInstance(previous_si.get()));
EXPECT_NE(current_si->GetProcess(), previous_si->GetProcess());
}
// Back navigation from a cross-origin isolated page to a non cross-origin
// isolated page.
{
EXPECT_TRUE(NavigateToURL(shell(), isolated_page));
scoped_refptr<SiteInstanceImpl> cross_origin_isolated_site_instance =
current_frame_host()->GetSiteInstance();
EXPECT_TRUE(
cross_origin_isolated_site_instance->IsCoopCoepCrossOriginIsolated());
web_contents()->GetController().GoBack();
EXPECT_TRUE(WaitForLoadStop(web_contents()));
scoped_refptr<SiteInstanceImpl> non_cross_origin_isolated_site_instance =
current_frame_host()->GetSiteInstance();
EXPECT_FALSE(non_cross_origin_isolated_site_instance
->IsCoopCoepCrossOriginIsolated());
EXPECT_FALSE(non_cross_origin_isolated_site_instance->IsRelatedSiteInstance(
cross_origin_isolated_site_instance.get()));
EXPECT_NE(non_cross_origin_isolated_site_instance->GetProcess(),
cross_origin_isolated_site_instance->GetProcess());
}
// Cross origin navigation in between two cross-origin isolated pages.
{
EXPECT_TRUE(NavigateToURL(shell(), isolated_page));
scoped_refptr<SiteInstanceImpl> site_instance_1 =
current_frame_host()->GetSiteInstance();
EXPECT_TRUE(NavigateToURL(shell(), isolated_page_b));
SiteInstanceImpl* site_instance_2 = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(site_instance_1->IsCoopCoepCrossOriginIsolated());
EXPECT_TRUE(site_instance_2->IsCoopCoepCrossOriginIsolated());
EXPECT_FALSE(site_instance_1->IsRelatedSiteInstance(site_instance_2));
EXPECT_NE(site_instance_1->GetProcess(), site_instance_2->GetProcess());
}
}
IN_PROC_BROWSER_TEST_P(
CrossOriginOpenerPolicyBrowserTest,
CrossOriginIsolatedSiteInstance_MainFrameRendererInitiated) {
GURL isolated_page(
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"));
GURL isolated_page_b(
https_server()->GetURL("cdn.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"));
GURL non_isolated_page(https_server()->GetURL("a.com", "/title1.html"));
// Navigation from/to cross-origin isolated pages.
// Initial non cross-origin isolated page.
{
EXPECT_TRUE(NavigateToURL(shell(), non_isolated_page));
SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance();
EXPECT_FALSE(current_si->IsCoopCoepCrossOriginIsolated());
}
// Navigation to a cross-origin isolated page.
{
scoped_refptr<SiteInstanceImpl> previous_si =
current_frame_host()->GetSiteInstance();
EXPECT_TRUE(NavigateToURLFromRenderer(shell(), isolated_page));
SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(current_si->IsCoopCoepCrossOriginIsolated());
EXPECT_FALSE(current_si->IsRelatedSiteInstance(previous_si.get()));
EXPECT_NE(current_si->GetProcess(), previous_si->GetProcess());
}
// Navigation to the same cross-origin isolated page.
{
scoped_refptr<SiteInstanceImpl> previous_si =
current_frame_host()->GetSiteInstance();
EXPECT_TRUE(NavigateToURLFromRenderer(shell(), isolated_page));
SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(current_si->IsCoopCoepCrossOriginIsolated());
EXPECT_EQ(current_si, previous_si);
}
// Navigation to a non cross-origin isolated page.
{
scoped_refptr<SiteInstanceImpl> previous_si =
current_frame_host()->GetSiteInstance();
EXPECT_TRUE(NavigateToURLFromRenderer(shell(), non_isolated_page));
SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance();
EXPECT_FALSE(current_si->IsCoopCoepCrossOriginIsolated());
EXPECT_FALSE(current_si->IsRelatedSiteInstance(previous_si.get()));
EXPECT_NE(current_si->GetProcess(), previous_si->GetProcess());
}
// Cross origin navigation in between two cross-origin isolated pages.
{
EXPECT_TRUE(NavigateToURLFromRenderer(shell(), isolated_page));
scoped_refptr<SiteInstanceImpl> site_instance_1 =
current_frame_host()->GetSiteInstance();
EXPECT_TRUE(NavigateToURLFromRenderer(shell(), isolated_page_b));
SiteInstanceImpl* site_instance_2 = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(site_instance_1->IsCoopCoepCrossOriginIsolated());
EXPECT_TRUE(site_instance_2->IsCoopCoepCrossOriginIsolated());
EXPECT_FALSE(site_instance_1->IsRelatedSiteInstance(site_instance_2));
EXPECT_NE(site_instance_1->GetProcess(), site_instance_2->GetProcess());
}
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
CrossOriginIsolatedSiteInstance_IFrame) {
GURL isolated_page(
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"));
GURL isolated_page_b(
https_server()->GetURL("cdn.a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: require-corp&"
"Cross-Origin-Resource-Policy: cross-origin"));
// Initial cross-origin isolated page.
EXPECT_TRUE(NavigateToURL(shell(), isolated_page));
SiteInstanceImpl* main_si = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(main_si->IsCoopCoepCrossOriginIsolated());
// Same origin iframe.
{
TestNavigationManager same_origin_iframe_navigation(web_contents(),
isolated_page);
EXPECT_TRUE(
ExecJs(web_contents(),
JsReplace("var iframe = document.createElement('iframe'); "
"iframe.src = $1; "
"document.body.appendChild(iframe);",
isolated_page)));
same_origin_iframe_navigation.WaitForNavigationFinished();
EXPECT_TRUE(same_origin_iframe_navigation.was_successful());
RenderFrameHostImpl* iframe =
current_frame_host()->child_at(0)->current_frame_host();
SiteInstanceImpl* iframe_si = iframe->GetSiteInstance();
EXPECT_EQ(iframe_si, main_si);
}
// Cross origin iframe.
{
TestNavigationManager cross_origin_iframe_navigation(web_contents(),
isolated_page_b);
EXPECT_TRUE(
ExecJs(web_contents(),
JsReplace("var iframe = document.createElement('iframe'); "
"iframe.src = $1; "
"document.body.appendChild(iframe);",
isolated_page_b)));
cross_origin_iframe_navigation.WaitForNavigationFinished();
EXPECT_TRUE(cross_origin_iframe_navigation.was_successful());
RenderFrameHostImpl* iframe =
current_frame_host()->child_at(1)->current_frame_host();
SiteInstanceImpl* iframe_si = iframe->GetSiteInstance();
EXPECT_TRUE(iframe_si->IsCoopCoepCrossOriginIsolated());
EXPECT_TRUE(iframe_si->IsRelatedSiteInstance(main_si));
EXPECT_EQ(iframe_si->GetProcess(), main_si->GetProcess());
}
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
CrossOriginIsolatedSiteInstance_Popup) {
GURL isolated_page(
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"));
GURL isolated_page_b(
https_server()->GetURL("cdn.a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"));
GURL non_isolated_page(
embedded_test_server()->GetURL("a.com", "/title1.html"));
// Initial cross-origin isolated page.
EXPECT_TRUE(NavigateToURL(shell(), isolated_page));
SiteInstanceImpl* main_si = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(main_si->IsCoopCoepCrossOriginIsolated());
// Open a non isolated popup.
{
RenderFrameHostImpl* popup_frame =
static_cast<WebContentsImpl*>(
OpenPopup(current_frame_host(), non_isolated_page, "")
->web_contents())
->GetFrameTree()
->root()
->current_frame_host();
EXPECT_FALSE(
popup_frame->GetSiteInstance()->IsCoopCoepCrossOriginIsolated());
EXPECT_FALSE(popup_frame->GetSiteInstance()->IsRelatedSiteInstance(
current_frame_host()->GetSiteInstance()));
EXPECT_FALSE(popup_frame->frame_tree_node()->opener());
}
// Open an isolated popup.
{
RenderFrameHostImpl* popup_frame =
static_cast<WebContentsImpl*>(
OpenPopup(current_frame_host(), isolated_page, "")->web_contents())
->GetFrameTree()
->root()
->current_frame_host();
EXPECT_TRUE(
popup_frame->GetSiteInstance()->IsCoopCoepCrossOriginIsolated());
EXPECT_EQ(popup_frame->GetSiteInstance(),
current_frame_host()->GetSiteInstance());
}
// Open an isolated popup, but cross-origin.
{
RenderFrameHostImpl* popup_frame =
static_cast<WebContentsImpl*>(
OpenPopup(current_frame_host(), isolated_page_b, "")
->web_contents())
->GetFrameTree()
->root()
->current_frame_host();
EXPECT_TRUE(
popup_frame->GetSiteInstance()->IsCoopCoepCrossOriginIsolated());
EXPECT_FALSE(popup_frame->GetSiteInstance()->IsRelatedSiteInstance(
current_frame_host()->GetSiteInstance()));
EXPECT_FALSE(popup_frame->frame_tree_node()->opener());
EXPECT_NE(popup_frame->GetSiteInstance()->GetProcess(),
current_frame_host()->GetSiteInstance()->GetProcess());
}
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
CrossOriginIsolatedSiteInstance_ErrorPage) {
GURL isolated_page(
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"));
GURL non_coep_page(https_server()->GetURL("b.com",
"/set-header?"
"Access-Control-Allow-Origin: *"));
GURL invalid_url(
https_server()->GetURL("a.com", "/this_page_does_not_exist.html"));
GURL error_url(https_server()->GetURL("a.com", "/page404.html"));
// Initial cross-origin isolated page.
EXPECT_TRUE(NavigateToURL(shell(), isolated_page));
SiteInstanceImpl* main_si = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(main_si->IsCoopCoepCrossOriginIsolated());
// Iframe.
{
TestNavigationManager iframe_navigation(web_contents(), invalid_url);
EXPECT_TRUE(
ExecJs(web_contents(),
JsReplace("var iframe = document.createElement('iframe'); "
"iframe.src = $1; "
"document.body.appendChild(iframe);",
invalid_url)));
iframe_navigation.WaitForNavigationFinished();
EXPECT_FALSE(iframe_navigation.was_successful());
RenderFrameHostImpl* iframe =
current_frame_host()->child_at(0)->current_frame_host();
SiteInstanceImpl* iframe_si = iframe->GetSiteInstance();
// The load of the document with 404 status code is blocked by COEP.
// An error page is expected in lieu of that document.
EXPECT_EQ(GURL(kUnreachableWebDataURL),
EvalJs(iframe, "document.location.href;"));
EXPECT_TRUE(IsExpectedSubframeErrorTransition(main_si, iframe_si));
EXPECT_TRUE(iframe_si->IsCoopCoepCrossOriginIsolated());
}
// Iframe with a body added to the HTTP 404.
{
TestNavigationManager iframe_navigation(web_contents(), error_url);
EXPECT_TRUE(
ExecJs(web_contents(),
JsReplace("var iframe = document.createElement('iframe'); "
"iframe.src = $1; "
"document.body.appendChild(iframe);",
error_url)));
iframe_navigation.WaitForNavigationFinished();
EXPECT_FALSE(iframe_navigation.was_successful());
RenderFrameHostImpl* iframe =
current_frame_host()->child_at(0)->current_frame_host();
SiteInstanceImpl* iframe_si = iframe->GetSiteInstance();
EXPECT_TRUE(IsExpectedSubframeErrorTransition(main_si, iframe_si));
// The load of the document with 404 status code and custom body is blocked
// by COEP. An error page is expected in lieu of that document.
EXPECT_EQ(GURL(kUnreachableWebDataURL),
EvalJs(iframe, "document.location.href;"));
EXPECT_TRUE(iframe_si->IsCoopCoepCrossOriginIsolated());
}
// Iframe blocked by coep.
{
TestNavigationManager iframe_navigation(web_contents(), non_coep_page);
EXPECT_TRUE(
ExecJs(web_contents(),
JsReplace("var iframe = document.createElement('iframe'); "
"iframe.src = $1; "
"document.body.appendChild(iframe);",
non_coep_page)));
iframe_navigation.WaitForNavigationFinished();
EXPECT_FALSE(iframe_navigation.was_successful());
RenderFrameHostImpl* iframe =
current_frame_host()->child_at(0)->current_frame_host();
SiteInstanceImpl* iframe_si = iframe->GetSiteInstance();
EXPECT_TRUE(IsExpectedSubframeErrorTransition(main_si, iframe_si));
EXPECT_TRUE(iframe_si->IsCoopCoepCrossOriginIsolated());
}
// Top frame.
{
scoped_refptr<SiteInstanceImpl> previous_si =
current_frame_host()->GetSiteInstance();
EXPECT_FALSE(NavigateToURL(shell(), invalid_url));
SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance();
EXPECT_FALSE(current_si->IsRelatedSiteInstance(previous_si.get()));
EXPECT_NE(current_si->GetProcess(), previous_si->GetProcess());
EXPECT_FALSE(current_si->IsCoopCoepCrossOriginIsolated());
}
}
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
CrossOriginRedirectHasProperCrossOriginIsolatedState) {
GURL non_isolated_page(
embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL isolated_page(
https_server()->GetURL("c.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"));
GURL redirect_isolated_page(https_server()->GetURL(
"b.com", "/redirect-with-coop-coep-headers?" + isolated_page.spec()));
EXPECT_TRUE(NavigateToURL(shell(), non_isolated_page));
SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance();
EXPECT_FALSE(current_si->IsCoopCoepCrossOriginIsolated());
EXPECT_TRUE(NavigateToURL(shell(), redirect_isolated_page, isolated_page));
current_si = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(current_si->IsCoopCoepCrossOriginIsolated());
EXPECT_TRUE(current_si->GetCoopCoepCrossOriginIsolatedInfo()
.origin()
.IsSameOriginWith(url::Origin::Create(isolated_page)));
}
// Reproducer test for https://crbug.com/1150938.
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
MainFrameA_IframeB_Opens_WindowA) {
GURL isolated_page(
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp"));
GURL isolated_page_b(
https_server()->GetURL("cdn.a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: require-corp&"
"Cross-Origin-Resource-Policy: cross-origin"));
// Initial cross-origin isolated page.
EXPECT_TRUE(NavigateToURL(shell(), isolated_page));
SiteInstanceImpl* main_si = current_frame_host()->GetSiteInstance();
EXPECT_TRUE(main_si->IsCoopCoepCrossOriginIsolated());
TestNavigationManager cross_origin_iframe_navigation(web_contents(),
isolated_page_b);
EXPECT_TRUE(ExecJs(web_contents(),
JsReplace("var iframe = document.createElement('iframe'); "
"iframe.src = $1; "
"document.body.appendChild(iframe);",
isolated_page_b)));
cross_origin_iframe_navigation.WaitForNavigationFinished();
EXPECT_TRUE(cross_origin_iframe_navigation.was_successful());
RenderFrameHostImpl* iframe =
current_frame_host()->child_at(0)->current_frame_host();
SiteInstanceImpl* iframe_si = iframe->GetSiteInstance();
EXPECT_TRUE(iframe_si->IsCoopCoepCrossOriginIsolated());
EXPECT_TRUE(iframe_si->IsRelatedSiteInstance(main_si));
EXPECT_EQ(iframe_si->GetProcess(), main_si->GetProcess());
// Open an isolated popup, but cross-origin.
{
RenderFrameHostImpl* popup_frame =
static_cast<WebContentsImpl*>(
OpenPopup(iframe, isolated_page, "", "", false)->web_contents())
->GetFrameTree()
->root()
->current_frame_host();
EXPECT_TRUE(
popup_frame->GetSiteInstance()->IsCoopCoepCrossOriginIsolated());
EXPECT_FALSE(popup_frame->GetSiteInstance()->IsRelatedSiteInstance(
current_frame_host()->GetSiteInstance()));
EXPECT_FALSE(popup_frame->frame_tree_node()->opener());
EXPECT_NE(popup_frame->GetSiteInstance()->GetProcess(),
current_frame_host()->GetSiteInstance()->GetProcess());
}
}
// Regression test for https://crbug.com/1183571. This used to crash.
// A grand child, same-origin with its parent, but cross-origin with the main
// document is accessing a popup.
//
// TODO(arthursonzogni): Add a similar WPT test.
IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
GrandChildAccessCrash1183571) {
GURL a_url_coop(https_server()->GetURL(
"a.com",
"/set-header?Cross-Origin-Opener-Policy-Report-Only: same-origin"));
GURL b_url(https_server()->GetURL("b.com", "/empty.html"));
GURL c_url(https_server()->GetURL("c.com", "/empty.html"));
// 1. Start from COOP-Report-Only:same-origin. (a.com COOP-RO)
EXPECT_TRUE(NavigateToURL(shell(), a_url_coop));
RenderFrameHostImpl* opener_rfh = current_frame_host();
// 2. Add a window in a different (virtual) browsing context group.
//
// The new popup won't be used, but it is created to avoid the
// DOMWindow::ReportCoopAccess() fast early return. The original bug won't
// reproduce without this.
{
ShellAddedObserver shell_observer;
EXPECT_TRUE(ExecJs(opener_rfh, JsReplace(R"(
window.open($1);
)",
b_url)));
WaitForLoadStop(shell_observer.GetShell()->web_contents());
}
// 3. Insert a cross-origin iframe. (b.com)
EXPECT_TRUE(ExecJs(opener_rfh, JsReplace(R"(
const iframe = document.createElement("iframe");
iframe.src = $1;
document.body.appendChild(iframe);
)",
b_url)));
WaitForLoadStop(web_contents());
RenderFrameHostImpl* opener_child_rfh =
opener_rfh->child_at(0)->current_frame_host();
// 4. Insert a grand-child iframe (b.com).
EXPECT_TRUE(ExecJs(opener_child_rfh, JsReplace(R"(
const iframe = document.createElement("iframe");
iframe.src = $1;
document.body.appendChild(iframe);
)",
b_url)));
WaitForLoadStop(web_contents());
RenderFrameHostImpl* opener_grand_child_rfh =
opener_child_rfh->child_at(0)->current_frame_host();
// 5. The grand child creates a new cross-origin popup...
ShellAddedObserver shell_observer;
EXPECT_TRUE(ExecJs(opener_grand_child_rfh, JsReplace(R"(
window.openee = window.open($1);
)",
c_url)));
WaitForLoadStop(shell_observer.GetShell()->web_contents());
// 6. ... and tries to access it.
EXPECT_EQ("I didn't crash", EvalJs(opener_grand_child_rfh, R"(
window.openee.closed;
"I didn't crash";
)"));
}
// TODO(https://crbug.com/1101339). Test inheritance of the virtual browsing
// context group when using window.open from an iframe, same-origin and
// cross-origin.
static auto kTestParams =
testing::Combine(testing::ValuesIn(RenderDocumentFeatureLevelValues()),
testing::Bool());
INSTANTIATE_TEST_SUITE_P(All, CrossOriginOpenerPolicyBrowserTest, kTestParams);
INSTANTIATE_TEST_SUITE_P(All, VirtualBrowsingContextGroupTest, kTestParams);
INSTANTIATE_TEST_SUITE_P(All, NoSharedArrayBufferByDefault, kTestParams);
namespace {
// Ensure the CrossOriginOpenerPolicyReporting origin trial is correctly
// implemented.
class CoopReportingOriginTrialBrowserTest : public ContentBrowserTest {
public:
CoopReportingOriginTrialBrowserTest() {
feature_list_.InitWithFeatures(
{
// Enabled
network::features::kCrossOriginOpenerPolicy,
network::features::kCrossOriginOpenerPolicyAccessReporting,
network::features::kCrossOriginOpenerPolicyReportingOriginTrial,
},
{
// Disabled
network::features::kCrossOriginOpenerPolicyReporting,
});
}
// Origin Trials key generated with:
//
// tools/origin_trials/generate_token.py --expire-days 5000 --version 3
// https://coop.security:9999 CrossOriginOpenerPolicyReporting
static std::string OriginTrialToken() {
return "A5U4dXG9lYhhLSumDmXNObrt5xJ0XVpSfw/"
"w7q+MYzOziNnHfcl1ZShjKjecyEc3E5vDtHV+"
"wiLMbqukLwhs8gIAAABteyJvcmlnaW4iOiAiaHR0cHM6Ly9jb29wLnNlY3VyaXR5Ojk"
"5OTkiLCAiZmVhdHVyZSI6ICJDcm9zc09yaWdpbk9wZW5lclBvbGljeVJlcG9ydGluZy"
"IsICJleHBpcnkiOiAyMDI5NzA4MDA3fQ==";
}
// The OriginTrial token is bound to a given origin. Since the
// EmbeddedTestServer's port changes after every test run, it can't be used.
// As a result, response must be served using a URLLoaderInterceptor.
GURL OriginTrialURL() { return GURL("https://coop.security:9999"); }
WebContentsImpl* web_contents() const {
return static_cast<WebContentsImpl*>(shell()->web_contents());
}
RenderFrameHostImpl* current_frame_host() {
return web_contents()->GetMainFrame();
}
net::EmbeddedTestServer* https_server() { return &https_server_; }
private:
void SetUpOnMainThread() final {
ContentBrowserTest::TearDownOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
https_server()->ServeFilesFromSourceDirectory(GetTestDataFilePath());
SetupCrossSiteRedirector(https_server());
net::test_server::RegisterDefaultHandlers(&https_server_);
ASSERT_TRUE(https_server()->Start());
}
void TearDownOnMainThread() final {
ContentBrowserTest::TearDownOnMainThread();
}
void SetUpCommandLine(base::CommandLine* command_line) final {
ContentBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
}
private:
base::test::ScopedFeatureList feature_list_;
net::EmbeddedTestServer https_server_;
};
} // namespace
IN_PROC_BROWSER_TEST_F(CoopReportingOriginTrialBrowserTest,
CoopStateWithoutToken) {
URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
[&](URLLoaderInterceptor::RequestParams* params) {
if (params->url_request.url != OriginTrialURL())
return false;
URLLoaderInterceptor::WriteResponse(
"HTTP/1.1 200 OK\n"
"Content-type: text/html\n"
"Cross-Origin-Opener-Policy: same-origin; report-to=\"a\"\n"
"Cross-Origin-Opener-Policy-Report-Only: same-origin; "
"report-to=\"b\"\n"
"Cross-Origin-Embedder-Policy: require-corp\n"
"\n",
"", params->client.get());
return true;
}));
EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL()));
network::CrossOriginOpenerPolicy coop =
current_frame_host()->cross_origin_opener_policy();
EXPECT_EQ(coop.reporting_endpoint, base::nullopt);
EXPECT_EQ(coop.report_only_reporting_endpoint, base::nullopt);
EXPECT_EQ(coop.value,
network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep);
EXPECT_EQ(coop.report_only_value,
network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone);
}
IN_PROC_BROWSER_TEST_F(CoopReportingOriginTrialBrowserTest,
CoopStateWithToken) {
URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
[&](URLLoaderInterceptor::RequestParams* params) {
if (params->url_request.url != OriginTrialURL())
return false;
URLLoaderInterceptor::WriteResponse(
"HTTP/1.1 200 OK\n"
"Content-type: text/html\n"
"Cross-Origin-Opener-Policy: same-origin; report-to=\"a\"\n"
"Cross-Origin-Opener-Policy-Report-Only: same-origin; "
"report-to=\"b\"\n"
"Cross-Origin-Embedder-Policy: require-corp\n"
"Origin-Trial: " +
OriginTrialToken() + "\n\n",
"", params->client.get());
return true;
}));
EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL()));
network::CrossOriginOpenerPolicy coop =
current_frame_host()->cross_origin_opener_policy();
EXPECT_EQ(coop.reporting_endpoint, "a");
EXPECT_EQ(coop.report_only_reporting_endpoint, "b");
EXPECT_EQ(coop.value,
network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep);
EXPECT_EQ(coop.report_only_value,
network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep);
}
// TODO(http://crbug.com/1119555): Flaky on android-bfcache-rel.
IN_PROC_BROWSER_TEST_F(CoopReportingOriginTrialBrowserTest,
DISABLED_AccessReportingWithoutToken) {
URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
[&](URLLoaderInterceptor::RequestParams* params) {
if (params->url_request.url != OriginTrialURL())
return false;
URLLoaderInterceptor::WriteResponse(
"HTTP/1.1 200 OK\n"
"Content-type: text/html\n"
"Cross-Origin-Opener-Policy-Report-Only: same-origin; "
"report-to=\"b\"\n"
"Cross-Origin-Embedder-Policy: require-corp\n\n",
"", params->client.get());
return true;
}));
EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL()));
ShellAddedObserver shell_observer;
GURL openee_url = https_server()->GetURL("a.com", "/title1.html");
EXPECT_TRUE(ExecJs(current_frame_host(),
JsReplace("openee = window.open($1);", openee_url)));
auto* popup =
static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents());
WaitForLoadStop(popup);
auto eval = EvalJs(current_frame_host(), R"(
new Promise(resolve => {
let observer = new ReportingObserver(()=>{});
observer.observe();
openee.postMessage("hello");
let reports = observer.takeRecords();
resolve(JSON.stringify(reports));
});
)");
std::string reports = eval.ExtractString();
EXPECT_EQ("[]", reports);
}
IN_PROC_BROWSER_TEST_F(CoopReportingOriginTrialBrowserTest,
AccessReportingWithToken) {
URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
[&](URLLoaderInterceptor::RequestParams* params) {
if (params->url_request.url != OriginTrialURL())
return false;
URLLoaderInterceptor::WriteResponse(
"HTTP/1.1 200 OK\n"
"Content-type: text/html\n"
"Cross-Origin-Opener-Policy-Report-Only: same-origin; "
"report-to=\"b\"\n"
"Cross-Origin-Embedder-Policy: require-corp\n"
"Origin-Trial: " +
OriginTrialToken() + "\n\n",
"", params->client.get());
return true;
}));
EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL()));
ShellAddedObserver shell_observer;
GURL openee_url = https_server()->GetURL("a.com", "/title1.html");
EXPECT_TRUE(ExecJs(current_frame_host(),
JsReplace("openee = window.open($1);", openee_url)));
auto* popup =
static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents());
WaitForLoadStop(popup);
auto eval = EvalJs(current_frame_host(), R"(
new Promise(resolve => {
let observer = new ReportingObserver(()=>{});
observer.observe();
openee.postMessage("hello");
let reports = observer.takeRecords();
resolve(JSON.stringify(reports));
});
)");
std::string reports = eval.ExtractString();
EXPECT_THAT(reports, HasSubstr("coop-access-violation"));
}
IN_PROC_BROWSER_TEST_P(NoSharedArrayBufferByDefault, BaseCase) {
GURL url = https_server()->GetURL("a.com", "/empty.html");
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_EQ(false, EvalJs(current_frame_host(), "self.crossOriginIsolated"));
EXPECT_EQ(false,
EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis"));
}
IN_PROC_BROWSER_TEST_P(NoSharedArrayBufferByDefault, CoopCoepIsolated) {
GURL url =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_EQ(true, EvalJs(current_frame_host(), "self.crossOriginIsolated"));
EXPECT_EQ(true,
EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis"));
}
IN_PROC_BROWSER_TEST_P(NoSharedArrayBufferByDefault,
CoopCoepTransferSharedArrayBufferToIframe) {
CHECK(!base::FeatureList::IsEnabled(features::kSharedArrayBuffer));
GURL url =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_TRUE(ExecJs(current_frame_host(),
"g_iframe = document.createElement('iframe');"
"g_iframe.src = location.href;"
"document.body.appendChild(g_iframe);"));
WaitForLoadStop(web_contents());
RenderFrameHostImpl* main_document = current_frame_host();
RenderFrameHostImpl* sub_document =
current_frame_host()->child_at(0)->current_frame_host();
EXPECT_EQ(true, EvalJs(main_document, "self.crossOriginIsolated"));
EXPECT_EQ(true, EvalJs(sub_document, "self.crossOriginIsolated"));
EXPECT_TRUE(ExecJs(sub_document, R"(
g_sab_size = new Promise(resolve => {
addEventListener("message", event => resolve(event.data.byteLength));
});
)",
EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
EXPECT_TRUE(ExecJs(main_document, R"(
let sab = new SharedArrayBuffer(1234);
g_iframe.contentWindow.postMessage(sab, "*");
)"));
EXPECT_EQ(1234, EvalJs(sub_document, "g_sab_size"));
}
// Transfer a SharedArrayBuffer in between two COOP+COEP document with a
// parent/child relationship. The child has set Permissions-Policy:
// cross-origin-isolated=(). As a result, it can't receive the object.
IN_PROC_BROWSER_TEST_P(
NoSharedArrayBufferByDefault,
CoopCoepTransferSharedArrayBufferToNoCrossOriginIsolatedIframe) {
CHECK(!base::FeatureList::IsEnabled(features::kSharedArrayBuffer));
GURL main_url =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
GURL iframe_url =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: require-corp&"
"Cross-Origin-Resource-Policy: cross-origin&"
"Permissions-Policy: cross-origin-isolated=()");
EXPECT_TRUE(NavigateToURL(shell(), main_url));
EXPECT_TRUE(ExecJs(current_frame_host(),
JsReplace("g_iframe = document.createElement('iframe');"
"g_iframe.src = $1;"
"document.body.appendChild(g_iframe);",
iframe_url)));
WaitForLoadStop(web_contents());
RenderFrameHostImpl* main_document = current_frame_host();
RenderFrameHostImpl* sub_document =
current_frame_host()->child_at(0)->current_frame_host();
EXPECT_EQ(true, EvalJs(main_document, "self.crossOriginIsolated"));
EXPECT_EQ(false, EvalJs(sub_document, "self.crossOriginIsolated"));
auto postSharedArrayBuffer = EvalJs(main_document, R"(
let sab = new SharedArrayBuffer(1234);
g_iframe.contentWindow.postMessage(sab,"*");
)");
EXPECT_THAT(
postSharedArrayBuffer.error,
HasSubstr(
"Failed to execute 'postMessage' on 'Window': SharedArrayBuffer "
"transfer requires self.crossOriginIsolated"));
}
// Transfer a SharedArrayBuffer in between two COOP+COEP document with a
// parent/child relationship. The child has set Permissions-Policy:
// cross-origin-isolated=(). This non-cross-origin-isolated document can
// transfer a SharedArrayBuffer toward the cross-origin-isolated one.
// See https://crbug.com/1144838 for discussions about this behavior.
IN_PROC_BROWSER_TEST_P(
NoSharedArrayBufferByDefault,
CoopCoepTransferSharedArrayBufferFromNoCrossOriginIsolatedIframe) {
CHECK(!base::FeatureList::IsEnabled(features::kSharedArrayBuffer));
GURL main_url =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
GURL iframe_url =
https_server()->GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: require-corp&"
"Cross-Origin-Resource-Policy: cross-origin&"
"Permissions-Policy: cross-origin-isolated=()");
EXPECT_TRUE(NavigateToURL(shell(), main_url));
EXPECT_TRUE(ExecJs(current_frame_host(),
JsReplace("g_iframe = document.createElement('iframe');"
"g_iframe.src = $1;"
"document.body.appendChild(g_iframe);",
iframe_url)));
WaitForLoadStop(web_contents());
RenderFrameHostImpl* main_document = current_frame_host();
RenderFrameHostImpl* sub_document =
current_frame_host()->child_at(0)->current_frame_host();
EXPECT_EQ(true, EvalJs(main_document, "self.crossOriginIsolated"));
EXPECT_EQ(false, EvalJs(sub_document, "self.crossOriginIsolated"));
EXPECT_TRUE(ExecJs(main_document, R"(
g_sab_size = new Promise(resolve => {
addEventListener("message", event => resolve(event.data.byteLength));
});
)",
EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
// TODO(https://crbug.com/1144838): Being able to share SharedArrayBuffer from
// a document with self.crossOriginIsolated == false sounds wrong.
EXPECT_TRUE(ExecJs(sub_document, R"(
let sab = new SharedArrayBuffer(1234);
parent.postMessage(sab, "*");
)"));
EXPECT_EQ(1234, EvalJs(main_document, "g_sab_size"));
}
// Ensure the UnrestrictedSharedArrayBuffer reverse origin trial is correctly
// implemented.
class UnrestrictedSharedArrayBufferOriginTrialBrowserTest
: public ContentBrowserTest {
public:
UnrestrictedSharedArrayBufferOriginTrialBrowserTest() {
feature_list_.InitWithFeatures(
{
// Enabled
},
{
// Disabled
features::kSharedArrayBuffer,
features::kWebAssemblyThreads,
});
}
// Origin Trials key generated with:
//
// tools/origin_trials/generate_token.py --expire-days 5000 --version 3
// https://coop.security:9999 UnrestrictedSharedArrayBuffer
static std::string OriginTrialToken() {
return "A8TH8Ylk6lUuL84RdQ2+FTyupad3leg5sMk+MYEoVlwkURyBtVq1IFncJAc2k"
"Knhh5w3SvIR4XuEtyMzeI2u4wAAAABqeyJvcmlnaW4iOiAiaHR0cHM6Ly9jb2"
"9wLnNlY3VyaXR5Ojk5OTkiLCAiZmVhdHVyZSI6ICJVbnJlc3RyaWN0ZWRTaGF"
"yZWRBcnJheUJ1ZmZlciIsICJleHBpcnkiOiAyMDQ1Njk0NDMyfQ==";
}
// The OriginTrial token is bound to a given origin. Since the
// EmbeddedTestServer's port changes after every test run, it can't be used.
// As a result, response must be served using a URLLoaderInterceptor.
GURL OriginTrialURL() { return GURL("https://coop.security:9999"); }
WebContentsImpl* web_contents() const {
return static_cast<WebContentsImpl*>(shell()->web_contents());
}
RenderFrameHostImpl* current_frame_host() {
return web_contents()->GetMainFrame();
}
net::EmbeddedTestServer* https_server() { return &https_server_; }
private:
void SetUpOnMainThread() final {
ContentBrowserTest::TearDownOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
https_server()->ServeFilesFromSourceDirectory(GetTestDataFilePath());
SetupCrossSiteRedirector(https_server());
net::test_server::RegisterDefaultHandlers(&https_server_);
ASSERT_TRUE(https_server()->Start());
}
void SetUpCommandLine(base::CommandLine* command_line) final {
ContentBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
}
private:
base::test::ScopedFeatureList feature_list_;
net::EmbeddedTestServer https_server_;
};
IN_PROC_BROWSER_TEST_F(UnrestrictedSharedArrayBufferOriginTrialBrowserTest,
HasSharedArrayBuffer) {
URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
[&](URLLoaderInterceptor::RequestParams* params) {
DCHECK_EQ(params->url_request.url, OriginTrialURL());
URLLoaderInterceptor::WriteResponse(
"HTTP/1.1 200 OK\n"
"Content-type: text/html\n"
"Origin-Trial: " +
OriginTrialToken() + "\n\n",
"", params->client.get());
return true;
}));
EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL()));
EXPECT_EQ(false, EvalJs(current_frame_host(), "self.crossOriginIsolated"));
#if !defined(OS_ANDROID)
EXPECT_EQ(true,
EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis"));
#else // defined(OS_ANDROID)
EXPECT_EQ(false,
EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis"));
#endif // defined(OS_ANDROID)
}
IN_PROC_BROWSER_TEST_F(UnrestrictedSharedArrayBufferOriginTrialBrowserTest,
TransferSharedArrayBuffer) {
URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
[&](URLLoaderInterceptor::RequestParams* params) {
DCHECK_EQ(params->url_request.url, OriginTrialURL());
URLLoaderInterceptor::WriteResponse(
"HTTP/1.1 200 OK\n"
"Content-type: text/html\n"
"Origin-Trial: " +
OriginTrialToken() + "\n\n",
"", params->client.get());
return true;
}));
EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL()));
EXPECT_TRUE(ExecJs(current_frame_host(),
"g_iframe = document.createElement('iframe');"
"g_iframe.src = location.href;"
"document.body.appendChild(g_iframe);"));
WaitForLoadStop(web_contents());
RenderFrameHostImpl* main_document = current_frame_host();
RenderFrameHostImpl* sub_document =
current_frame_host()->child_at(0)->current_frame_host();
EXPECT_EQ(false, EvalJs(main_document, "self.crossOriginIsolated"));
EXPECT_EQ(false, EvalJs(sub_document, "self.crossOriginIsolated"));
#if !defined(OS_ANDROID)
EXPECT_TRUE(ExecJs(sub_document, R"(
g_sab_size = new Promise(resolve => {
addEventListener("message", event => resolve(event.data.byteLength));
});
)",
EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
EXPECT_TRUE(ExecJs(main_document, R"(
let sab = new SharedArrayBuffer(1234);
g_iframe.contentWindow.postMessage(sab, "*");
)"));
EXPECT_EQ(1234, EvalJs(sub_document, "g_sab_size"));
#else // defined(OS_ANDROID)
auto postSharedArrayBuffer = EvalJs(main_document, R"(
// Create a WebAssembly Memory to bypass the SAB constructor restriction.
const sab =
new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
g_iframe.contentWindow.postMessage(sab,"*");
)");
EXPECT_THAT(
postSharedArrayBuffer.error,
HasSubstr(
"Failed to execute 'postMessage' on 'Window': ArrayBuffer is not "
"detachable and could not be cloned."));
#endif // defined(OS_ANDROID)
}
// Ensure the SharedArrayBufferOnDesktop kill switch is correctly implemented.
class SharedArrayBufferOnDesktopBrowserTest
: public CrossOriginOpenerPolicyBrowserTest {
public:
SharedArrayBufferOnDesktopBrowserTest() {
feature_list_.InitWithFeatures(
{
// Enabled
features::kSharedArrayBufferOnDesktop,
},
{
// Disabled
features::kSharedArrayBuffer,
features::kWebAssemblyThreads,
});
}
private:
base::test::ScopedFeatureList feature_list_;
};
INSTANTIATE_TEST_SUITE_P(All,
SharedArrayBufferOnDesktopBrowserTest,
kTestParams);
IN_PROC_BROWSER_TEST_P(SharedArrayBufferOnDesktopBrowserTest,
DesktopHasSharedArrayBuffer) {
CHECK(!base::FeatureList::IsEnabled(features::kSharedArrayBuffer));
GURL url = https_server()->GetURL("a.com", "/empty.html");
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_EQ(false, EvalJs(current_frame_host(), "self.crossOriginIsolated"));
#if !defined(OS_ANDROID)
EXPECT_EQ(true,
EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis"));
#else // defined(OS_ANDROID)
EXPECT_EQ(false,
EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis"));
#endif // defined(OS_ANDROID)
}
IN_PROC_BROWSER_TEST_P(SharedArrayBufferOnDesktopBrowserTest,
DesktopTransferSharedArrayBuffer) {
CHECK(!base::FeatureList::IsEnabled(features::kSharedArrayBuffer));
GURL main_url = https_server()->GetURL("a.com", "/empty.html");
GURL iframe_url = https_server()->GetURL("a.com", "/empty.html");
EXPECT_TRUE(NavigateToURL(shell(), main_url));
EXPECT_TRUE(ExecJs(current_frame_host(),
JsReplace("g_iframe = document.createElement('iframe');"
"g_iframe.src = $1;"
"document.body.appendChild(g_iframe);",
iframe_url)));
WaitForLoadStop(web_contents());
RenderFrameHostImpl* main_document = current_frame_host();
RenderFrameHostImpl* sub_document =
current_frame_host()->child_at(0)->current_frame_host();
EXPECT_EQ(false, EvalJs(main_document, "self.crossOriginIsolated"));
EXPECT_EQ(false, EvalJs(sub_document, "self.crossOriginIsolated"));
EXPECT_TRUE(ExecJs(main_document, R"(
g_sab_size = new Promise(resolve => {
addEventListener("message", event => resolve(event.data.byteLength));
});
)",
EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
#if !defined(OS_ANDROID)
EXPECT_TRUE(ExecJs(sub_document, R"(
let sab = new SharedArrayBuffer(1234);
parent.postMessage(sab, "*");
)"));
EXPECT_EQ(1234, EvalJs(main_document, "g_sab_size"));
#else // defined(OS_ANDROID)
EXPECT_FALSE(ExecJs(sub_document, R"(
let sab = new SharedArrayBuffer(1234);
parent.postMessage(sab, "*");
)"));
#endif // defined(OS_ANDROID)
}
} // namespace content