| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/command_line.h" |
| #include "base/strings/escape.h" |
| #include "base/test/bind.h" |
| #include "base/test/gtest_util.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "content/browser/process_lock.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/browser/site_isolation_policy.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/url_constants.h" |
| #include "content/public/test/back_forward_cache_util.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/content_browser_test.h" |
| #include "content/public/test/content_browser_test_content_browser_client.h" |
| #include "content/public/test/content_browser_test_utils.h" |
| #include "content/public/test/content_mock_cert_verifier.h" |
| #include "content/public/test/test_navigation_observer.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/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_embedder_policy.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( |
| const absl::optional<url::Origin>& origin = absl::nullopt) { |
| network::CrossOriginOpenerPolicy coop; |
| coop.value = network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin; |
| coop.soap_by_default_value = |
| network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin; |
| coop.origin = origin; |
| return coop; |
| } |
| |
| network::CrossOriginOpenerPolicy CoopSameOriginPlusCoep( |
| const absl::optional<url::Origin>& origin = absl::nullopt) { |
| network::CrossOriginOpenerPolicy coop; |
| coop.value = |
| network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep; |
| coop.soap_by_default_value = |
| network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep; |
| coop.origin = origin; |
| return coop; |
| } |
| |
| network::CrossOriginOpenerPolicy CoopSameOriginAllowPopups( |
| const absl::optional<url::Origin>& origin = absl::nullopt) { |
| network::CrossOriginOpenerPolicy coop; |
| coop.value = |
| network::mojom::CrossOriginOpenerPolicyValue::kSameOriginAllowPopups; |
| coop.soap_by_default_value = |
| network::mojom::CrossOriginOpenerPolicyValue::kSameOriginAllowPopups; |
| coop.origin = origin; |
| return coop; |
| } |
| |
| network::CrossOriginOpenerPolicy CoopRestrictProperties( |
| const absl::optional<url::Origin>& origin = absl::nullopt) { |
| network::CrossOriginOpenerPolicy coop; |
| coop.value = |
| network::mojom::CrossOriginOpenerPolicyValue::kRestrictProperties; |
| coop.soap_by_default_value = |
| network::mojom::CrossOriginOpenerPolicyValue::kRestrictProperties; |
| coop.origin = origin; |
| return coop; |
| } |
| |
| network::CrossOriginOpenerPolicy CoopRestrictPropertiesPlusCoep( |
| const absl::optional<url::Origin>& origin = absl::nullopt) { |
| network::CrossOriginOpenerPolicy coop; |
| coop.value = |
| network::mojom::CrossOriginOpenerPolicyValue::kRestrictPropertiesPlusCoep; |
| coop.soap_by_default_value = |
| network::mojom::CrossOriginOpenerPolicyValue::kRestrictPropertiesPlusCoep; |
| coop.origin = origin; |
| return coop; |
| } |
| |
| // This is the value of COOP when navigating to a page without COOP set: |
| // - value is kUnsafeNone |
| // - soap_by_default_value is kSameOriginAllowPopups |
| network::CrossOriginOpenerPolicy CoopUnsafeNoneWithSoapByDefault( |
| const absl::optional<url::Origin>& origin = absl::nullopt) { |
| network::CrossOriginOpenerPolicy coop; |
| coop.soap_by_default_value = |
| network::mojom::CrossOriginOpenerPolicyValue::kSameOriginAllowPopups; |
| coop.origin = origin; |
| return coop; |
| } |
| |
| network::CrossOriginOpenerPolicy CoopUnsafeNone( |
| const absl::optional<url::Origin>& origin = absl::nullopt) { |
| network::CrossOriginOpenerPolicy coop; |
| // Using the default value. |
| coop.origin = origin; |
| 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; |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> |
| CoopAndCspSandboxRedirectHandler(const net::test_server::HttpRequest& request) { |
| std::string dest = |
| base::UnescapeBinaryURLComponent(request.GetURL().query_piece()); |
| net::test_server::RequestQuery query = |
| net::test_server::ParseQuery(request.GetURL()); |
| |
| 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("Content-Security-Policy", "sandbox"); |
| return http_response; |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> |
| RedirectToTargetOnSecondNavigation( |
| unsigned int& navigation_counter, |
| const net::test_server::HttpRequest& request) { |
| ++navigation_counter; |
| if (navigation_counter == 1) { |
| auto http_response = |
| std::make_unique<net::test_server::BasicHttpResponse>(); |
| http_response->set_code(net::HttpStatusCode::HTTP_OK); |
| http_response->AddCustomHeader("Cache-Control", |
| "no-store, must-revalidate"); |
| return http_response; |
| } |
| |
| 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); |
| return http_response; |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> ServeCoopOnSecondNavigation( |
| unsigned int& navigation_counter, |
| const net::test_server::HttpRequest& request) { |
| ++navigation_counter; |
| auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| http_response->set_code(net::HttpStatusCode::HTTP_OK); |
| http_response->AddCustomHeader("Cache-Control", "no-store, must-revalidate"); |
| if (navigation_counter > 1) |
| http_response->AddCustomHeader("Cross-Origin-Opener-Policy", "same-origin"); |
| return http_response; |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> |
| ServeDifferentCoopOnSecondNavigation( |
| unsigned int& navigation_counter, |
| const net::test_server::HttpRequest& request) { |
| ++navigation_counter; |
| auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| http_response->set_code(net::HttpStatusCode::HTTP_OK); |
| http_response->AddCustomHeader("Cache-Control", "no-store, must-revalidate"); |
| if (navigation_counter > 1) { |
| http_response->AddCustomHeader("Cross-Origin-Opener-Policy", "same-origin"); |
| } else { |
| http_response->AddCustomHeader("Cross-Origin-Opener-Policy", |
| "restrict-properties"); |
| } |
| 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_.InitAndEnableFeature( |
| network::features::kCrossOriginOpenerPolicy); |
| |
| // Enable RenderDocument: |
| InitAndEnableRenderDocumentFeature(&feature_list_for_render_document_, |
| std::get<0>(GetParam())); |
| // Enable BackForwardCache: |
| if (IsBackForwardCacheEnabled()) { |
| feature_list_for_back_forward_cache_.InitWithFeaturesAndParameters( |
| GetDefaultEnabledBackForwardCacheFeaturesForTesting( |
| /*ignore_outstanding_network_request=*/false), |
| GetDefaultDisabledBackForwardCacheFeaturesForTesting()); |
| } else { |
| feature_list_for_back_forward_cache_.InitWithFeatures( |
| {}, {features::kBackForwardCache}); |
| } |
| } |
| |
| // Provides meaningful param names instead of /0, /1, ... |
| static std::string DescribeParams( |
| const testing::TestParamInfo<ParamType>& info) { |
| auto [render_document_level, enable_back_forward_cache] = info.param; |
| return base::StringPrintf( |
| "%s_%s", |
| GetRenderDocumentLevelNameForTestParams(render_document_level).c_str(), |
| enable_back_forward_cache ? "BFCacheEnabled" : "BFCacheDisabled"); |
| } |
| |
| bool IsBackForwardCacheEnabled() { return std::get<1>(GetParam()); } |
| |
| 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()->GetPrimaryMainFrame(); |
| } |
| |
| void SetUpOnMainThread() override { |
| ContentBrowserTest::SetUpOnMainThread(); |
| mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); |
| |
| 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))); |
| https_server_.RegisterDefaultHandler(base::BindRepeating( |
| &net::test_server::HandlePrefixedRequest, |
| "/redirect-with-coop-and-csp-headers", |
| base::BindRepeating(CoopAndCspSandboxRedirectHandler))); |
| |
| unsigned int navigation_counter = 0; |
| https_server_.RegisterDefaultHandler(base::BindRepeating( |
| &net::test_server::HandlePrefixedRequest, |
| "/redirect-to-target-on-second-navigation", |
| base::BindRepeating(&RedirectToTargetOnSecondNavigation, |
| base::OwnedRef(navigation_counter)))); |
| https_server_.RegisterDefaultHandler(base::BindRepeating( |
| &net::test_server::HandlePrefixedRequest, |
| "/serve-coop-on-second-navigation", |
| base::BindRepeating(&ServeCoopOnSecondNavigation, |
| base::OwnedRef(navigation_counter)))); |
| https_server_.RegisterDefaultHandler(base::BindRepeating( |
| &net::test_server::HandlePrefixedRequest, |
| "/serve-different-coop-on-second-navigation", |
| base::BindRepeating(&ServeDifferentCoopOnSecondNavigation, |
| base::OwnedRef(navigation_counter)))); |
| |
| ASSERT_TRUE(https_server()->Start()); |
| } |
| |
| private: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| ContentBrowserTest::SetUpCommandLine(command_line); |
| mock_cert_verifier_.SetUpCommandLine(command_line); |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| ContentBrowserTest::SetUpInProcessBrowserTestFixture(); |
| mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); |
| } |
| |
| void TearDownInProcessBrowserTestFixture() override { |
| ContentBrowserTest::TearDownInProcessBrowserTestFixture(); |
| mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); |
| } |
| |
| content::ContentMockCertVerifier mock_cert_verifier_; |
| 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, |
| }); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| // Same as CrossOriginOpenerPolicyBrowserTest, but enables COOP: |
| // restrict-properties. See https://crbug.com/1221127. |
| class CoopRestrictPropertiesBrowserTest |
| : public CrossOriginOpenerPolicyBrowserTest { |
| public: |
| CoopRestrictPropertiesBrowserTest() { |
| feature_list_.InitWithFeatures({network::features::kCoopRestrictProperties}, |
| {}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| // Same as CoopRestrictPropertiesBrowserTest, but skips on platforms not |
| // providing full site isolation, to help test the existence of proxies. Also |
| // provides helper functions to leverage FrameTreeVisualizer. Inherits its |
| // parametrization for RenderDocument and BackForwardCache. |
| class CoopRestrictPropertiesProxiesBrowserTest |
| : public CoopRestrictPropertiesBrowserTest { |
| public: |
| void SetUpOnMainThread() override { |
| // These tests verify what proxies exist using DepictFrameTree and exact |
| // string comparison. Return early if we would not put cross-origin |
| // iframes and popups in their own processes, which would modify the proxy |
| // structure. |
| if (!AreAllSitesIsolatedForTesting()) { |
| GTEST_SKIP(); |
| } |
| CoopRestrictPropertiesBrowserTest::SetUpOnMainThread(); |
| } |
| |
| std::string DepictFrameTree(FrameTreeNode* node) { |
| return visualizer_.DepictFrameTree(node); |
| } |
| |
| WebContentsImpl* OpenPopupAndWaitForInitialRFHDeletion( |
| RenderFrameHostImpl* opener_rfh, |
| const GURL& url, |
| const std::string& name) { |
| // First open a popup to the initial empty document, and then navigate it to |
| // the final url. This allows waiting on the deletion of the initial empty |
| // document proxies and having a clean state for proxy checking. |
| ShellAddedObserver shell_observer; |
| CHECK(ExecJs(opener_rfh, JsReplace("window.open('', $1);", name))); |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| shell_observer.GetShell()->web_contents()); |
| RenderFrameHostWrapper initial_popup_rfh( |
| popup_window->GetPrimaryMainFrame()); |
| CHECK(NavigateToURLFromRenderer(initial_popup_rfh.get(), url)); |
| CHECK(initial_popup_rfh.WaitUntilRenderFrameDeleted()); |
| return popup_window; |
| } |
| |
| private: |
| FrameTreeVisualizer visualizer_; |
| }; |
| |
| // Same as CoopRestrictPropertiesBrowserTest, but uses the new |
| // BrowsingContextState mode that swaps BrowsingContextState when navigating |
| // cross BrowsingInstance. Inherits its parametrization for RenderDocument and |
| // BackForwardCache. |
| class CoopRestrictPropertiesWithNewBrowsingContextStateModeBrowserTest |
| : public CoopRestrictPropertiesBrowserTest { |
| public: |
| CoopRestrictPropertiesWithNewBrowsingContextStateModeBrowserTest() { |
| feature_list_.InitWithFeatures( |
| {features::kNewBrowsingContextStateOnBrowsingContextGroupSwap}, {}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| using CoopRestrictPropertiesAccessBrowserTest = |
| CoopRestrictPropertiesBrowserTest; |
| |
| static constexpr char kCoopRpErrorMessageRegex[] = |
| ".*Cross-Origin-Opener-Policy: 'restrict-properties' blocked the access."; |
| |
| using CoopRestrictPropertiesReportingBrowserTest = |
| CoopRestrictPropertiesBrowserTest; |
| |
| // Certain features are only active when SiteIsolation is off or restricted. |
| // This is the case for example for Default SiteInstances that are used on |
| // Android to limit the number of processes. Testing these particularities of |
| // the process model and their interaction with cross-origin isolation requires |
| // to disable SiteIsolation. |
| class NoSiteIsolationCrossOriginIsolationBrowserTest |
| : public CrossOriginOpenerPolicyBrowserTest { |
| public: |
| NoSiteIsolationCrossOriginIsolationBrowserTest() { |
| // Disable the heuristic to isolate COOP pages from the default |
| // SiteInstance. This is otherwise on by default on Android. |
| feature_list_.InitWithFeatures( |
| {}, {features::kSiteIsolationForCrossOriginOpenerPolicy}); |
| } |
| |
| void SetUpOnMainThread() override { |
| CrossOriginOpenerPolicyBrowserTest::SetUpOnMainThread(); |
| browser_client_ = std::make_unique<NoSiteIsolationContentBrowserClient>(); |
| |
| // The custom ContentBrowserClient above typically ensures that this test |
| // runs without strict site isolation, but it's still possible to |
| // inadvertently override this when running with --site-per-process on the |
| // command line. This might happen on try bots, so these tests take this |
| // into account to prevent failures, but this is not an intended |
| // configuration for these tests. |
| if (AreAllSitesIsolatedForTesting()) { |
| LOG(WARNING) << "This test should be run without --site-per-process, " |
| << "as it's designed to exercise code paths when strict " |
| << "site isolation is turned off."; |
| } |
| } |
| |
| void TearDownOnMainThread() override { |
| CrossOriginOpenerPolicyBrowserTest::TearDownOnMainThread(); |
| browser_client_.reset(); |
| } |
| |
| // A custom ContentBrowserClient to turn off strict site isolation, since |
| // process model differences exist in environments like Android. Note that |
| // kSitePerProcess is a higher-layer feature, so we can't just disable it |
| // here. |
| class NoSiteIsolationContentBrowserClient |
| : public ContentBrowserTestContentBrowserClient { |
| public: |
| bool ShouldEnableStrictSiteIsolation() override { return false; } |
| }; |
| |
| private: |
| std::unique_ptr<NoSiteIsolationContentBrowserClient> browser_client_; |
| |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| using VirtualBrowsingContextGroupTest = CrossOriginOpenerPolicyBrowserTest; |
| using SoapByDefaultVirtualBrowsingContextGroupTest = |
| CrossOriginOpenerPolicyBrowserTest; |
| |
| int VirtualBrowsingContextGroup(WebContents* wc) { |
| return static_cast<WebContentsImpl*>(wc) |
| ->GetPrimaryMainFrame() |
| ->virtual_browsing_context_group(); |
| } |
| |
| int SoapByDefaultVirtualBrowsingContextGroup(WebContents* wc) { |
| return static_cast<WebContentsImpl*>(wc) |
| ->GetPrimaryMainFrame() |
| ->soap_by_default_virtual_browsing_context_group(); |
| } |
| |
| } // namespace |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| NewPopupCOOP_InheritsSameOrigin) { |
| GURL starting_page(https_server()->GetURL( |
| "a.test", "/set-header?cross-origin-opener-policy: same-origin")); |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| RenderFrameHostImpl* main_rfh = current_frame_host(); |
| |
| // Create same origin child frame. |
| ASSERT_TRUE(ExecJs(main_rfh, R"( |
| const frame = document.createElement('iframe'); |
| frame.src = '/empty.html'; |
| document.body.appendChild(frame); |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| |
| ShellAddedObserver shell_observer; |
| RenderFrameHostImpl* iframe_rfh = main_rfh->child_at(0)->current_frame_host(); |
| EXPECT_TRUE(ExecJs(iframe_rfh, "window.open('about:blank')")); |
| |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| EXPECT_EQ(main_rfh->cross_origin_opener_policy(), |
| CoopSameOrigin(url::Origin::Create(starting_page))); |
| EXPECT_EQ(popup_rfh->cross_origin_opener_policy(), |
| CoopSameOrigin(url::Origin::Create(starting_page))); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| NewPopupCOOP_InheritsSameOriginAllowPopups) { |
| GURL starting_page(https_server()->GetURL( |
| "a.test", |
| "/set-header?cross-origin-opener-policy: same-origin-allow-popups")); |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| RenderFrameHostImpl* main_rfh = current_frame_host(); |
| |
| // Create same origin child frame. |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| const frame = document.createElement('iframe'); |
| frame.src = '/empty.html'; |
| document.body.appendChild(frame); |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| |
| ShellAddedObserver shell_observer; |
| RenderFrameHostImpl* iframe_rfh = main_rfh->child_at(0)->current_frame_host(); |
| EXPECT_TRUE(ExecJs(iframe_rfh, "window.open('about:blank')")); |
| |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| EXPECT_EQ(main_rfh->cross_origin_opener_policy(), |
| CoopSameOriginAllowPopups(url::Origin::Create(starting_page))); |
| EXPECT_EQ(popup_rfh->cross_origin_opener_policy(), |
| CoopSameOriginAllowPopups(url::Origin::Create(starting_page))); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| NewPopupCOOP_CrossOriginDoesNotInherit) { |
| GURL starting_page(https_server()->GetURL( |
| "a.test", "/set-header?cross-origin-opener-policy: same-origin")); |
| GURL url_b(https_server()->GetURL("b.test", "/empty.html")); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| RenderFrameHostImpl* main_rfh = current_frame_host(); |
| |
| // Create cross origin child frame. |
| ASSERT_TRUE(ExecJs(main_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| url_b))); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| |
| ShellAddedObserver shell_observer; |
| RenderFrameHostImpl* iframe_rfh = main_rfh->child_at(0)->current_frame_host(); |
| EXPECT_TRUE(ExecJs(iframe_rfh, "window.open('about:blank')")); |
| |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| EXPECT_EQ(main_rfh->cross_origin_opener_policy(), |
| CoopSameOrigin(url::Origin::Create(starting_page))); |
| EXPECT_EQ(popup_rfh->cross_origin_opener_policy(), CoopUnsafeNone()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P( |
| CrossOriginOpenerPolicyBrowserTest, |
| NewPopupCOOP_SameOriginPolicyAndCrossOriginIframeSetsNoopener) { |
| for (const char* header : |
| {"cross-origin-opener-policy: same-origin", |
| "cross-origin-opener-policy: same-origin&cross-origin-embedder-policy: " |
| "require-corp"}) { |
| GURL starting_page( |
| https_server()->GetURL("a.test", std::string("/set-header?") + header)); |
| GURL url_b(https_server()->GetURL("b.test", "/empty.html")); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| RenderFrameHostImpl* main_rfh = current_frame_host(); |
| |
| // Create cross origin child frame. |
| ASSERT_TRUE(ExecJs(main_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| url_b))); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| |
| ShellAddedObserver new_shell_observer; |
| RenderFrameHostImpl* iframe_rfh = |
| main_rfh->child_at(0)->current_frame_host(); |
| EXPECT_TRUE(ExecJs(iframe_rfh, "window.open('about:blank')")); |
| |
| Shell* new_shell = new_shell_observer.GetShell(); |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>(new_shell->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| scoped_refptr<SiteInstance> main_rfh_site_instance( |
| main_rfh->GetSiteInstance()); |
| scoped_refptr<SiteInstance> iframe_site_instance( |
| iframe_rfh->GetSiteInstance()); |
| scoped_refptr<SiteInstance> popup_site_instance( |
| popup_rfh->GetSiteInstance()); |
| |
| ASSERT_TRUE(main_rfh_site_instance); |
| ASSERT_TRUE(iframe_site_instance); |
| ASSERT_TRUE(popup_site_instance); |
| EXPECT_FALSE(main_rfh_site_instance->IsRelatedSiteInstance( |
| popup_site_instance.get())); |
| EXPECT_FALSE( |
| iframe_site_instance->IsRelatedSiteInstance(popup_site_instance.get())); |
| |
| // Check that `window.opener` is not set. |
| EXPECT_EQ(true, EvalJs(new_shell, "window.opener == null;")) |
| << "window.opener is set"; |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| BlobInheritsCreatorSameOrigin) { |
| GURL starting_page(https_server()->GetURL( |
| "a.test", "/set-header?cross-origin-opener-policy: same-origin")); |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| // Create and open blob. |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| const blob = new Blob(['foo'], {type : 'text/html'}); |
| const url = URL.createObjectURL(blob); |
| window.open(url); |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(shell_observer.GetShell()->web_contents())); |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| // COOP and COEP inherited from Blob creator |
| EXPECT_EQ(popup_rfh->cross_origin_opener_policy(), |
| CoopSameOrigin(url::Origin::Create(starting_page))); |
| EXPECT_EQ(popup_rfh->cross_origin_embedder_policy().value, |
| network::mojom::CrossOriginEmbedderPolicyValue::kNone); |
| EXPECT_FALSE(popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| BlobInheritsInitiatorSameOriginPlusCoepCredentialless) { |
| GURL starting_page( |
| https_server()->GetURL("a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: same-origin" |
| "&cross-origin-embedder-policy: credentialless")); |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| // Create and open blob. |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| const blob = new Blob(['foo'], {type : 'text/html'}); |
| const url = URL.createObjectURL(blob); |
| window.open(url); |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(shell_observer.GetShell()->web_contents())); |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| // COOP and COEP inherited from Blob creator |
| EXPECT_EQ(popup_rfh->cross_origin_opener_policy(), |
| CoopSameOriginPlusCoep(url::Origin::Create(starting_page))); |
| EXPECT_EQ(popup_rfh->cross_origin_embedder_policy().value, |
| network::mojom::CrossOriginEmbedderPolicyValue::kCredentialless); |
| EXPECT_TRUE(popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| BlobInheritsInitiatorSameOriginPlusCoep) { |
| GURL starting_page( |
| https_server()->GetURL("a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: same-origin" |
| "&cross-origin-embedder-policy: require-corp")); |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| // Create and open blob. |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| const blob = new Blob(['foo'], {type : 'text/html'}); |
| const url = URL.createObjectURL(blob); |
| window.open(url); |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(shell_observer.GetShell()->web_contents())); |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| // COOP and COEP inherited from Blob creator |
| EXPECT_EQ(popup_rfh->cross_origin_opener_policy(), |
| CoopSameOriginPlusCoep(url::Origin::Create(starting_page))); |
| EXPECT_EQ(popup_rfh->cross_origin_embedder_policy().value, |
| network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp); |
| EXPECT_TRUE(popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| BlobInheritsCreatorSameOriginAllowPopups) { |
| GURL starting_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: same-origin-allow-popups" |
| "&cross-origin-embedder-policy: require-corp")); |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| // Create and open blob. |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| const blob = new Blob(['foo'], {type : 'text/html'}); |
| const url = URL.createObjectURL(blob); |
| window.open(url); |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(shell_observer.GetShell()->web_contents())); |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| // COOP and COEP inherited from Blob creator |
| EXPECT_EQ(popup_rfh->cross_origin_opener_policy(), |
| CoopSameOriginAllowPopups(url::Origin::Create(starting_page))); |
| EXPECT_EQ(popup_rfh->cross_origin_embedder_policy().value, |
| network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp); |
| EXPECT_FALSE(popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| BlobInheritsCreatorTopFrameSameOriginCreatorIframeCOEP) { |
| GURL starting_page(https_server()->GetURL( |
| "a.test", "/set-header?cross-origin-opener-policy: same-origin")); |
| GURL iframe_with_coep_url(https_server()->GetURL( |
| "a.test", "/set-header?cross-origin-embedder-policy: require-corp")); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| // Create same origin child frame with COEP |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| iframe_with_coep_url))); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| |
| RenderFrameHostImpl* child_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| // Create and open blob from iframe. |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(child_rfh, R"( |
| const blob = new Blob(['foo'], {type : 'text/html'}); |
| const url = URL.createObjectURL(blob); |
| window.open(url); |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(shell_observer.GetShell()->web_contents())); |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| // COOP is inherited from creator's top level document, COEP is inherited from |
| // creator. |
| EXPECT_EQ(popup_rfh->cross_origin_opener_policy(), |
| CoopSameOrigin(url::Origin::Create(starting_page))); |
| EXPECT_EQ(popup_rfh->cross_origin_embedder_policy().value, |
| network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp); |
| EXPECT_FALSE(popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| BlobInheritsCreatorNotInitiator) { |
| GURL starting_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: same-origin-allow-popups")); |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| // Create blob url in main page, which will be used later. |
| // Then open a popup on a document that is same-origin without COOP. |
| ShellAddedObserver first_shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| const blob = new Blob(['foo'], {type : 'text/html'}); |
| window.url = URL.createObjectURL(blob); |
| window.open("/empty.html"); |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(first_shell_observer.GetShell()->web_contents())); |
| RenderFrameHostImpl* first_popup_rfh = |
| static_cast<WebContentsImpl*>( |
| first_shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| // Open blob url created in opener. |
| ShellAddedObserver second_shell_observer; |
| ASSERT_TRUE(ExecJs(first_popup_rfh, R"( |
| window.open(opener.url); |
| )")); |
| EXPECT_TRUE( |
| WaitForLoadStop(second_shell_observer.GetShell()->web_contents())); |
| RenderFrameHostImpl* second_popup_rfh = |
| static_cast<WebContentsImpl*>( |
| second_shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| // COOP and COEP inherited from Blob creator (initial window) and not the |
| // initiator (first popup) |
| // TODO(https://crbug.com/1059300) COOP should be inherited from creator and |
| // be same-origin-allow-popups, instead of inheriting from initiator. |
| EXPECT_EQ( |
| second_popup_rfh->cross_origin_opener_policy(), |
| CoopUnsafeNoneWithSoapByDefault(url::Origin::Create(starting_page))); |
| EXPECT_EQ(second_popup_rfh->cross_origin_embedder_policy().value, |
| network::mojom::CrossOriginEmbedderPolicyValue::kNone); |
| EXPECT_FALSE(second_popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| } |
| |
| // Verify that a opening a popup to a COOP page, with sandbox flags inherited |
| // from the initiator ends up as an error page. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| SandboxViaInheritanceWithCoop) { |
| GURL main_page_url = https_server()->GetURL( |
| "a.test", "/cross-origin-opener-policy_sandbox_popup.html"); |
| GURL coop_url = https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin"); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), main_page_url)); |
| ShellAddedObserver shell_observer; |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| ASSERT_TRUE(ExecJs(iframe_rfh, JsReplace("window.open($1);", coop_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); |
| } |
| |
| // Verify that a navigation toward a COOP page, with sandbox flags inherited |
| // from the initiator ends up as an error page. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| SandboxViaInheritanceNavigationsToCoop) { |
| GURL main_page_url = https_server()->GetURL( |
| "a.test", "/cross-origin-opener-policy_sandbox_popup.html"); |
| GURL coop_url = https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin"); |
| GURL non_coop_url = https_server()->GetURL("a.test", "/title1.html"); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), main_page_url)); |
| ShellAddedObserver shell_observer; |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| ASSERT_TRUE(ExecJs(iframe_rfh, JsReplace("window.open($1);", non_coop_url))); |
| |
| auto* popup_webcontents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| WaitForLoadStop(popup_webcontents); |
| ASSERT_NE(popup_webcontents->GetPrimaryMainFrame()->active_sandbox_flags(), |
| network::mojom::WebSandboxFlags::kNone); |
| |
| EXPECT_FALSE(NavigateToURL(popup_webcontents, coop_url)); |
| EXPECT_EQ( |
| popup_webcontents->GetController().GetLastCommittedEntry()->GetPageType(), |
| PAGE_TYPE_ERROR); |
| } |
| |
| // Verify that a document setting COOP can also set sandbox via CSP. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| SandboxViaCspWithCoop) { |
| GURL coop_and_csp_url = |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Content-Security-Policy: sandbox"); |
| EXPECT_TRUE(NavigateToURL(shell(), coop_and_csp_url)); |
| EXPECT_EQ( |
| web_contents()->GetController().GetLastCommittedEntry()->GetPageType(), |
| PAGE_TYPE_NORMAL); |
| ASSERT_EQ(current_frame_host()->active_sandbox_flags(), |
| network::mojom::WebSandboxFlags::kAll); |
| EXPECT_TRUE(web_contents() |
| ->GetPrimaryMainFrame() |
| ->cross_origin_opener_policy() |
| .IsEqualExcludingOrigin(CoopSameOrigin())); |
| EXPECT_TRUE(web_contents() |
| ->GetPrimaryMainFrame() |
| ->cross_origin_opener_policy() |
| .origin->opaque()); |
| } |
| |
| // Verify that navigating from a document sandboxed via CSP to a COOP document, |
| // and vice versa, does not end up as an error page. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| SandboxViaCspNavigationsToCoop) { |
| GURL csp_url(https_server()->GetURL( |
| "a.test", "/set-header?Content-Security-Policy: sandbox")); |
| GURL coop_url = https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin"); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), csp_url)); |
| ASSERT_EQ(current_frame_host()->active_sandbox_flags(), |
| network::mojom::WebSandboxFlags::kAll); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), coop_url)); |
| EXPECT_EQ( |
| web_contents()->GetController().GetLastCommittedEntry()->GetPageType(), |
| PAGE_TYPE_NORMAL); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), csp_url)); |
| EXPECT_EQ( |
| web_contents()->GetController().GetLastCommittedEntry()->GetPageType(), |
| PAGE_TYPE_NORMAL); |
| } |
| |
| // Verify that CSP sandbox, which makes the origin opaque, is taken into account |
| // for the COOP enforcement of the final response. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| SandboxViaCspOpaqueOriginForResponse) { |
| GURL coop_url = https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin"); |
| GURL coop_and_csp_url = |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Content-Security-Policy: sandbox"); |
| |
| // Start on a page that sets COOP: same-origin. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_url)); |
| scoped_refptr<SiteInstance> coop_site_instance = |
| current_frame_host()->GetSiteInstance(); |
| |
| // We want to figure out if a BrowsingInstance swap happens because of COOP. |
| // To prevent some other types of swaps, such as proactive swaps, we do the |
| // navigations in a popup. |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE( |
| ExecJs(current_frame_host(), JsReplace("window.open($1);", coop_url))); |
| auto* popup_webcontents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| WaitForLoadStop(popup_webcontents); |
| RenderFrameHostImpl* popup_rfh = popup_webcontents->GetPrimaryMainFrame(); |
| ASSERT_EQ(popup_rfh->GetSiteInstance(), coop_site_instance.get()); |
| |
| // Navigate to a same-origin COOP page that sets sandboxing via CSP. The popup |
| // should be sandboxed and have an opaque origin. |
| ASSERT_TRUE(NavigateToURL(popup_webcontents, coop_and_csp_url)); |
| scoped_refptr<SiteInstance> coop_and_csp_site_instance = |
| popup_webcontents->GetPrimaryMainFrame()->GetSiteInstance(); |
| ASSERT_EQ(popup_webcontents->GetPrimaryMainFrame()->active_sandbox_flags(), |
| network::mojom::WebSandboxFlags::kAll); |
| EXPECT_FALSE(coop_site_instance->IsRelatedSiteInstance( |
| coop_and_csp_site_instance.get())); |
| |
| // Navigate again to the COOP+CSP page. The same should be true in the other |
| // direction. |
| ASSERT_TRUE(NavigateToURL(popup_webcontents, coop_and_csp_url)); |
| scoped_refptr<SiteInstance> final_coop_site_instance = |
| popup_webcontents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(coop_and_csp_site_instance->IsRelatedSiteInstance( |
| final_coop_site_instance.get())); |
| } |
| |
| // Verify that CSP sandbox, which makes the origin opaque, is not taken into |
| // account for the COOP enforcement of the final response. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| SandboxViaCspNonOpaqueOriginForRedirect) { |
| GURL coop_url = https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin"); |
| GURL coop_and_csp_redirect_url = https_server()->GetURL( |
| "a.test", "/redirect-with-coop-and-csp-headers?" + coop_url.spec()); |
| |
| // Start on a page that sets COOP: same-origin. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_url)); |
| scoped_refptr<SiteInstance> coop_site_instance = |
| current_frame_host()->GetSiteInstance(); |
| |
| // We want to figure out if a BrowsingInstance swap happens because of COOP. |
| // To prevent some other types of swaps, such as proactive swaps, we do the |
| // navigations in a popup. |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE( |
| ExecJs(current_frame_host(), JsReplace("window.open($1);", coop_url))); |
| auto* popup_webcontents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| WaitForLoadStop(popup_webcontents); |
| RenderFrameHostImpl* popup_rfh = popup_webcontents->GetPrimaryMainFrame(); |
| ASSERT_EQ(popup_rfh->GetSiteInstance(), coop_site_instance.get()); |
| |
| // Navigate to a same-origin redirection url, that sets COOP and sandboxing |
| // via CSP. It then redirects to a same-origin COOP page without CSP. |
| ASSERT_TRUE( |
| NavigateToURL(popup_webcontents, coop_and_csp_redirect_url, coop_url)); |
| scoped_refptr<SiteInstance> post_redirect_site_instance = |
| popup_webcontents->GetPrimaryMainFrame()->GetSiteInstance(); |
| ASSERT_EQ(popup_webcontents->GetPrimaryMainFrame()->active_sandbox_flags(), |
| network::mojom::WebSandboxFlags::kNone); |
| |
| // No BrowsingInstance swap should have happened. |
| EXPECT_TRUE(coop_site_instance->IsRelatedSiteInstance( |
| post_redirect_site_instance.get())); |
| } |
| |
| // Verify that a document setting COOP + COEP and CSP: sandbox cannot live in |
| // the same process as a document setting COOP + COEP with the same (non-opaque) |
| // origin. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| SandboxViaCspOpaqueOriginForIsolation) { |
| GURL coi_url = |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"); |
| GURL coi_and_csp_url = |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp&" |
| "Content-Security-Policy: sandbox"); |
| |
| // Start on the non opaque page, that does not set CSP: sandbox. |
| ASSERT_TRUE(NavigateToURL(shell(), coi_url)); |
| RenderFrameHostImpl* main_page_rfh = current_frame_host(); |
| |
| // Open a popup with the same characteristics, but with CSP: sandbox. |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE( |
| ExecJs(main_page_rfh, JsReplace("window.open($1)", coi_and_csp_url))); |
| WebContents* popup_webcontents = shell_observer.GetShell()->web_contents(); |
| WaitForLoadStop(popup_webcontents); |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>(popup_webcontents)->GetPrimaryMainFrame(); |
| ASSERT_EQ(popup_rfh->active_sandbox_flags(), |
| network::mojom::WebSandboxFlags::kAll); |
| ASSERT_NE(main_page_rfh->GetLastCommittedOrigin(), |
| popup_rfh->GetLastCommittedOrigin()); |
| ASSERT_TRUE(main_page_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| ASSERT_TRUE(popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| |
| // They should be in different BrowsingInstances and processes. |
| EXPECT_FALSE(main_page_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| EXPECT_NE(main_page_rfh->GetSiteInstance()->GetProcess(), |
| popup_rfh->GetSiteInstance()->GetProcess()); |
| } |
| |
| 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.IsEqualExcludingOrigin( |
| expected_coop_)); |
| CHECK(!navigation_request->response() |
| ->parsed_headers->cross_origin_opener_policy.origin.has_value()); |
| } |
| |
| 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.test", "/cross-origin-opener-policy_redirect_initial.html")); |
| GURL redirect_final_page(https_server()->GetURL( |
| "a.test", "/cross-origin-opener-policy_redirect_final.html")); |
| |
| CrossOriginPolicyHeadersObserver obs( |
| web_contents(), |
| network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp, |
| CoopSameOriginPlusCoep(url::Origin::Create(redirect_final_page))); |
| |
| 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.test", "/title1.html")); |
| GURL coop_page = embedded_test_server()->GetURL( |
| "a.test", "/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(url::Origin::Create(non_coop_page))); |
| |
| ASSERT_TRUE(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); |
| |
| // The iframe's COOP value is defaulted to unsafe-none since the iframe is |
| // cross origin with its top frame. |
| EXPECT_EQ(iframe_rfh->cross_origin_opener_policy(), CoopUnsafeNone()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| CoopSameOriginIframeInheritance) { |
| GURL coop_url(embedded_test_server()->GetURL( |
| "/set-header?cross-origin-opener-policy: same-origin")); |
| ASSERT_TRUE(NavigateToURL(shell(), coop_url)); |
| |
| // Create same origin child frame. |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| const frame = document.createElement('iframe'); |
| frame.src = '/empty.html'; |
| document.body.appendChild(frame); |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| |
| RenderFrameHostImpl* child_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| // The embedded document has a COOP value that is always inherited from its |
| // top level document if they are same-origin. This has no incidence on the |
| // embeddee but is inherited by the popup opened hereafter. |
| EXPECT_EQ( |
| network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin, |
| child_rfh->policy_container_host()->cross_origin_opener_policy().value); |
| |
| // Create a popup from the iframe. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(child_rfh, R"( |
| w = window.open("about:blank"); |
| )")); |
| WebContentsImpl* popup_webcontents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| RenderFrameHostImpl* popup_rfh = popup_webcontents->GetPrimaryMainFrame(); |
| |
| // Verify inheritance from the opener: |
| // The second about:blank document of the popup, due to the synchronous |
| // re-navigation to about:blank, inherits COOP from its opener. |
| // When the opener is same-origin with its top-level document, the top-level |
| // document's COOP value (same-origin) is used. |
| // In practice policy container handles the inheritance, taking the value |
| // from the opener directly, which was properly set when the document was |
| // committed. |
| EXPECT_EQ( |
| network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin, |
| popup_rfh->policy_container_host()->cross_origin_opener_policy().value); |
| |
| PolicyContainerHost* popup_initial_policy_container = |
| popup_rfh->policy_container_host(); |
| |
| // Navigate the popup from the iframe to about:blank. |
| EXPECT_TRUE(ExecJs(child_rfh, R"( |
| w.location.href = "about:blank"; |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(popup_webcontents)); |
| popup_rfh = popup_webcontents->GetPrimaryMainFrame(); |
| |
| // Verify the policy container changed, highlighting that the popup has |
| // navigated to a different about:blank document. |
| EXPECT_NE(popup_initial_policy_container, popup_rfh->policy_container_host()); |
| |
| // Verify inheritance from the initiator: |
| // The navigation to a local scheme inherits COOP from the initiator. When the |
| // initiator is same-origin with its top-level document, the top-level |
| // document's COOP value (same-origin) is used. |
| // In practice policy container handles the inheritance, taking the value |
| // from the initiator directly, which was properly set when the document was |
| // committed. |
| EXPECT_EQ( |
| network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin, |
| popup_rfh->policy_container_host()->cross_origin_opener_policy().value); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| CoopCrossOriginIframeInheritance) { |
| GURL coop_url(embedded_test_server()->GetURL( |
| "/set-header?cross-origin-opener-policy: same-origin-allow-popups")); |
| GURL url_b(embedded_test_server()->GetURL("b.test", "/empty.html")); |
| ASSERT_TRUE(NavigateToURL(shell(), coop_url)); |
| |
| // Create child frame. |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| url_b))); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| |
| RenderFrameHostImpl* child_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| // The embedded document has a COOP value that is always defaulted when it is |
| // cross origin with its top level document. This has no incidence on the |
| // embeddee but is inherited by the popup opened hereafter. |
| EXPECT_EQ( |
| network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone, |
| child_rfh->policy_container_host()->cross_origin_opener_policy().value); |
| |
| // Create a popup from the iframe. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(child_rfh, R"( |
| w = window.open("about:blank"); |
| )")); |
| WebContentsImpl* popup_webcontents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| RenderFrameHostImpl* popup_rfh = popup_webcontents->GetPrimaryMainFrame(); |
| |
| // The second about:blank document of the popup, due to the synchronous |
| // re-navigation to about:blank, inherits COOP from its opener. |
| // When the opener is cross-origin with its top-level document, the COOP value |
| // is defaulted to unsafe-none. |
| // In practice policy container handles the inheritance, taking the value |
| // from the opener directly, which was properly set when the document was |
| // committed. |
| EXPECT_EQ( |
| network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone, |
| popup_rfh->policy_container_host()->cross_origin_opener_policy().value); |
| |
| PolicyContainerHost* popup_initial_policy_container = |
| popup_rfh->policy_container_host(); |
| |
| // Navigate the popup from the iframe. |
| EXPECT_TRUE(ExecJs(child_rfh, R"( |
| w.location.href = "about:blank"; |
| )")); |
| EXPECT_TRUE(WaitForLoadStop(popup_webcontents)); |
| popup_rfh = popup_webcontents->GetPrimaryMainFrame(); |
| |
| // Verify the policy container changed, highlighting that the popup has |
| // navigated to a different about:blank document. |
| EXPECT_NE(popup_initial_policy_container, popup_rfh->policy_container_host()); |
| |
| // Verify inheritance from the initiator: |
| // The navigation to a local scheme inherits COOP from the initiator. When the |
| // initiator is cross-origin with its top-level document, the COOP value is |
| // defaulted to unsafe-none. |
| // In practice policy container handles the inheritance, taking the value |
| // from the initiator directly, which was properly set when the document was |
| // committed. |
| EXPECT_EQ( |
| network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone, |
| popup_rfh->policy_container_host()->cross_origin_opener_policy().value); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| NonCoopPageCrashIntoCoop) { |
| IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess()); |
| GURL non_coop_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_page = https_server()->GetURL( |
| "a.test", "/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 RenderFrameProxyHost for another cross-site page. |
| GURL non_coop_cross_site_page( |
| https_server()->GetURL("b.test", "/title1.html")); |
| OpenPopup(current_frame_host(), non_coop_cross_site_page, ""); |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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(url::Origin::Create(coop_page))); |
| |
| // The COOP page should no longer have any RenderFrameHostProxies. |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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.test", "/title1.html")); |
| |
| // Ensure it has a RenderFrameProxyHost for another cross-site page. |
| OpenPopup(current_frame_host(), non_coop_cross_site_page, ""); |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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. |
| ASSERT_TRUE(coop_navigation.WaitForNavigationFinished()); |
| |
| // The navigation will fail if we create speculative RFH when the navigation |
| // started (instead of only when the response started), because the renderer |
| // process will crash and trigger deletion of the speculative RFH and the |
| // navigation using that speculative RFH. |
| // TODO(https://crbug.com/1426413): If the final RenderFrameHost picked for |
| // the navigation doesn't use the same process as the crashed process, we |
| // can crash the process after the final RenderFrameHost has been picked |
| // instead, and the navigation will commit normally. |
| if (ShouldCreateNewHostForAllFrames()) { |
| EXPECT_FALSE(coop_navigation.was_committed()); |
| return; |
| } |
| |
| 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(url::Origin::Create(non_coop_page))); |
| |
| // The COOP page should no longer have any RenderFrameHostProxies. |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->GetProxyCount(), |
| 0u); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| CoopPageCrashIntoNonCoop) { |
| IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess()); |
| GURL coop_allow_popups_page(https_server()->GetURL( |
| "a.test", |
| "/set-header?Cross-Origin-Opener-Policy: same-origin-allow-popups")); |
| GURL non_coop_page(https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: unsafe-none")); |
| GURL cross_origin_non_coop_page( |
| https_server()->GetURL("b.test", "/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 RenderFrameProxyHost for another cross-site page. |
| OpenPopup(current_frame_host(), cross_origin_non_coop_page, ""); |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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(url::Origin::Create(non_coop_page))); |
| |
| // The non COOP page should no longer have any RenderFrameHostProxies. |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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 RenderFrameProxyHost for another cross-site page. |
| OpenPopup(current_frame_host(), cross_origin_non_coop_page, ""); |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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. |
| ASSERT_TRUE(non_coop_navigation.WaitForNavigationFinished()); |
| |
| // The navigation will fail if we create speculative RFH when the navigation |
| // started (instead of only when the response started), because the renderer |
| // process will crash and trigger deletion of the speculative RFH and the |
| // navigation using that speculative RFH. |
| // TODO(https://crbug.com/1426413): If the final RenderFrameHost picked for |
| // the navigation doesn't use the same process as the crashed process, we |
| // can crash the process after the final RenderFrameHost has been picked |
| // instead, and the navigation will commit normally. |
| if (ShouldCreateNewHostForAllFrames()) { |
| EXPECT_FALSE(non_coop_navigation.was_committed()); |
| return; |
| } |
| |
| 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(url::Origin::Create(non_coop_page))); |
| |
| // The non COOP page should no longer have any RenderFrameHostProxies. |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->GetProxyCount(), |
| 0u); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| CoopPageCrashIntoCoop) { |
| IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess()); |
| GURL coop_allow_popups_page(https_server()->GetURL( |
| "a.test", |
| "/set-header?Cross-Origin-Opener-Policy: same-origin-allow-popups")); |
| GURL cross_origin_non_coop_page( |
| https_server()->GetURL("b.test", "/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(url::Origin::Create(coop_allow_popups_page))); |
| |
| // Ensure it has a RenderFrameProxyHost for another cross-site page. |
| OpenPopup(current_frame_host(), cross_origin_non_coop_page, ""); |
| |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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(url::Origin::Create(coop_allow_popups_page))); |
| |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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 RenderFrameProxyHost for another cross-site page. |
| OpenPopup(current_frame_host(), cross_origin_non_coop_page, ""); |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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. |
| ASSERT_TRUE(coop_navigation.WaitForNavigationFinished()); |
| |
| // The navigation will fail if we create speculative RFH when the navigation |
| // started (instead of only when the response started), because the renderer |
| // process will crash and trigger deletion of the speculative RFH and the |
| // navigation using that speculative RFH. |
| // TODO(https://crbug.com/1426413): If the final RenderFrameHost picked for |
| // the navigation doesn't use the same process as the crashed process, we |
| // can crash the process after the final RenderFrameHost has been picked |
| // instead, and the navigation will commit normally. |
| if (ShouldCreateNewHostForAllFrames()) { |
| EXPECT_FALSE(coop_navigation.was_committed()); |
| } else { |
| EXPECT_TRUE(coop_navigation.was_committed()); |
| } |
| |
| EXPECT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| initial_site_instance.get())); |
| EXPECT_EQ( |
| current_frame_host()->cross_origin_opener_policy(), |
| CoopSameOriginAllowPopups(url::Origin::Create(coop_allow_popups_page))); |
| |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->GetProxyCount(), |
| 1u); |
| } |
| } |
| |
| // This test is a reproducer for https://crbug.com/1264104. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| BackNavigationCoiToNonCoiAfterCrashReproducer) { |
| GURL isolated_page( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| GURL non_isolated_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| // Put a non isolated page in history. |
| EXPECT_TRUE(NavigateToURL(shell(), non_isolated_page)); |
| scoped_refptr<SiteInstanceImpl> non_isolated_site_instance( |
| current_frame_host()->GetSiteInstance()); |
| RenderFrameHostImplWrapper non_isolated_rfh(current_frame_host()); |
| EXPECT_FALSE(non_isolated_site_instance->IsCrossOriginIsolated()); |
| |
| // Keep this alive, simulating not receiving the UnloadACK from the renderer. |
| current_frame_host()->DoNotDeleteForTesting(); |
| |
| // Navigate to an isolated page. |
| EXPECT_TRUE(NavigateToURL(shell(), isolated_page)); |
| scoped_refptr<SiteInstanceImpl> isolated_site_instance( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_TRUE(isolated_site_instance->IsCrossOriginIsolated()); |
| |
| // Simulate the renderer process crashing. |
| RenderProcessHost* process = isolated_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(); |
| |
| web_contents()->GetController().GoBack(); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| ProxiesAreRemovedWhenCrossingCoopBoundary) { |
| GURL non_coop_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_page = https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin"); |
| |
| RenderFrameHostManager* main_window_rfhm = |
| web_contents()->GetPrimaryFrameTree().root()->render_manager(); |
| EXPECT_TRUE(NavigateToURL(shell(), non_coop_page)); |
| EXPECT_EQ(main_window_rfhm->current_frame_host() |
| ->browsing_context_state() |
| ->GetProxyCount(), |
| 0u); |
| |
| Shell* popup_shell = OpenPopup(shell(), coop_page, ""); |
| |
| // The main frame should not have the popup referencing it. |
| EXPECT_EQ(main_window_rfhm->current_frame_host() |
| ->browsing_context_state() |
| ->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()) |
| ->GetPrimaryFrameTree() |
| .root(); |
| RenderFrameHostManager* popup_rfhm = popup->render_manager(); |
| EXPECT_EQ(popup_rfhm->current_frame_host() |
| ->browsing_context_state() |
| ->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.test", "/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 RenderFrameProxyHost for another cross-site page. |
| Shell* popup_shell = OpenPopup(current_frame_host(), coop_page, ""); |
| GURL cross_site_iframe(https_server()->GetURL("b.test", "/title1.html")); |
| TestNavigationManager iframe_navigation(popup_shell->web_contents(), |
| cross_site_iframe); |
| EXPECT_TRUE( |
| ExecJs(popup_shell->web_contents(), |
| JsReplace("const iframe = document.createElement('iframe');" |
| "iframe.src = $1;" |
| "document.body.appendChild(iframe);", |
| cross_site_iframe))); |
| ASSERT_TRUE(iframe_navigation.WaitForNavigationFinished()); |
| EXPECT_EQ(web_contents() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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() |
| ->GetPrimaryMainFrame() |
| ->browsing_context_state() |
| ->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.test", "/title1.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| // Open a popup with CrossOriginOpenerPolicy and CrossOriginEmbedderPolicy |
| // set. |
| GURL url_openee = |
| https_server()->GetURL("a.test", |
| "/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->GetPrimaryMainFrame()->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.test", |
| "/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->GetPrimaryMainFrame()->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.test", "/title1.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), final_page)); |
| EXPECT_EQ(current_frame_host()->GetProcess(), |
| popup_webcontents->GetPrimaryMainFrame()->GetProcess()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| SpeculativeRfhsAndCoop) { |
| GURL non_coop_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_page = |
| https_server()->GetURL("a.test", |
| "/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()); |
| |
| // A speculative RenderFrameHost will only be created if we always use a new |
| // RenderFrameHost for all cross-document navigations. |
| EXPECT_EQ(ShouldCreateNewHostForAllFrames(), |
| !!web_contents() |
| ->GetPrimaryFrameTree() |
| .root() |
| ->render_manager() |
| ->speculative_frame_host()); |
| |
| ASSERT_TRUE(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() |
| ->GetPrimaryFrameTree() |
| .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); |
| } |
| |
| ASSERT_TRUE(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() |
| ->GetPrimaryFrameTree() |
| .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); |
| } |
| |
| ASSERT_TRUE(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()); |
| |
| // A speculative RenderFrameHost will only be created if we always use a new |
| // RenderFrameHost for all cross-document navigations. |
| EXPECT_EQ(ShouldCreateNewHostForAllFrames(), |
| !!web_contents() |
| ->GetPrimaryFrameTree() |
| .root() |
| ->render_manager() |
| ->speculative_frame_host()); |
| |
| ASSERT_TRUE(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); |
| } |
| } |
| |
| // https://crbug.com/1266819 suggested that navigating to a cross-origin page |
| // from a cross-origin isolated page is a good reproducer for potential |
| // speculative RFHs + crossOriginIsolated issues. Tests from both a regular and |
| // a crashed frame to also verify with the crash optimization commit. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| SpeculativeSiteInstanceAndCrossOriginIsolation) { |
| GURL coop_page_a = |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"); |
| GURL page_b(https_server()->GetURL("b.test", "/title1.html")); |
| |
| // Usual navigation. |
| { |
| // Start on a COI page. |
| EXPECT_TRUE(NavigateToURL(shell(), coop_page_a)); |
| scoped_refptr<SiteInstanceImpl> main_site_instance( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_TRUE(main_site_instance->IsCrossOriginIsolated()); |
| |
| // Popup to a cross-origin page. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.open($1, 'windowName')", page_b))); |
| WebContents* popup = shell_observer.GetShell()->web_contents(); |
| WaitForLoadStop(popup); |
| |
| RenderFrameHostImpl* popup_frame_host = static_cast<WebContentsImpl*>(popup) |
| ->GetPrimaryFrameTree() |
| .root() |
| ->current_frame_host(); |
| scoped_refptr<SiteInstanceImpl> popup_site_instance( |
| popup_frame_host->GetSiteInstance()); |
| EXPECT_FALSE(popup_site_instance->IsCrossOriginIsolated()); |
| |
| // Verify that COOP enforcement was done properly. |
| EXPECT_FALSE( |
| main_site_instance->IsRelatedSiteInstance(popup_site_instance.get())); |
| EXPECT_EQ(true, EvalJs(popup_frame_host, "window.opener == null;")); |
| EXPECT_EQ("", EvalJs(popup_frame_host, "window.name")); |
| popup->Close(); |
| } |
| |
| // Navigation from a crashed page. |
| { |
| // Start on a COI page. |
| EXPECT_TRUE(NavigateToURL(shell(), coop_page_a)); |
| scoped_refptr<SiteInstanceImpl> main_site_instance( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_TRUE(main_site_instance->IsCrossOriginIsolated()); |
| |
| // Open an empty popup. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(current_frame_host(), |
| "window.open('about:blank', 'windowName')")); |
| WebContents* popup = shell_observer.GetShell()->web_contents(); |
| WaitForLoadStop(popup); |
| RenderFrameHostImpl* popup_frame_host = static_cast<WebContentsImpl*>(popup) |
| ->GetPrimaryFrameTree() |
| .root() |
| ->current_frame_host(); |
| scoped_refptr<SiteInstanceImpl> popup_site_instance( |
| popup_frame_host->GetSiteInstance()); |
| |
| // Crash it. |
| { |
| RenderProcessHost* process = popup_site_instance->GetProcess(); |
| ASSERT_TRUE(process); |
| auto crash_observer = std::make_unique<RenderProcessHostWatcher>( |
| process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| process->Shutdown(0); |
| crash_observer->Wait(); |
| } |
| |
| // Navigate it to a cross-origin page. |
| EXPECT_TRUE(NavigateToURL(popup, page_b)); |
| WaitForLoadStop(popup); |
| popup_frame_host = static_cast<WebContentsImpl*>(popup) |
| ->GetPrimaryFrameTree() |
| .root() |
| ->current_frame_host(); |
| popup_site_instance = popup_frame_host->GetSiteInstance(); |
| EXPECT_FALSE(popup_site_instance->IsCrossOriginIsolated()); |
| |
| // Verify that COOP enforcement was done properly. |
| EXPECT_FALSE( |
| main_site_instance->IsRelatedSiteInstance(popup_site_instance.get())); |
| EXPECT_EQ(true, EvalJs(popup_frame_host, "window.opener == null;")); |
| EXPECT_EQ("", EvalJs(popup_frame_host, "window.name")); |
| popup->Close(); |
| } |
| } |
| |
| // 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.test", "/title1.html"), |
| https_server()->GetURL("a.test", "/title2.html"), |
| false, |
| }, |
| { |
| // different-origin => keep. |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| https_server()->GetURL("b.a.test", "/title2.html"), |
| false, |
| }, |
| { |
| // different-site => keep. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL("b.test", "/title2.html"), |
| false, |
| }, |
| |
| // non-coop <-> coop. |
| { |
| // same-origin => change. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-origin => change. |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| https_server()->GetURL("b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-site => change. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL("b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| |
| // coop <-> coop. |
| { |
| // same-origin => keep. |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| false, |
| }, |
| { |
| // different-origin => change. |
| https_server()->GetURL("a.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-site => keep. |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("b.test", |
| "/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.test", "/title1.html"), |
| https_server()->GetURL( |
| "a.test", |
| "/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.test", "/title1.html"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-site => change. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "b.test", |
| "/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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "a.test", |
| "/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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-site => keep. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.test", |
| "/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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "a.test", |
| "/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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-site => change |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.test", |
| "/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.test", "/title1.html"), |
| GURL(), |
| false, |
| }, |
| { |
| // From coop-ro. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| GURL(), |
| false, |
| }, |
| { |
| // From coop. |
| https_server()->GetURL("a.test", |
| "/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.test", "/title1.html"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| false, |
| }, |
| |
| // non-coop opens coop-ro. |
| { |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "a.test", |
| "/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.test", "/title1.html"), |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| |
| // coop opens non-coop. |
| { |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| true, |
| }, |
| |
| // coop-ro opens coop-ro (same-origin). |
| { |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "a.test", |
| "/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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.test", |
| "/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 (unless soap_by_default is true). |
| // |
| // 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. |
| // |
| // If soap_by_default is true, then the test will check the soap by default |
| // virtual browsing context group. |
| struct VirtualBcgAllowPopupTestCase { |
| GURL url_a; |
| GURL url_b; |
| bool expect_different_group_window_open; |
| bool expect_different_group_navigation; |
| int (*get_virtual_browsing_context_group)(WebContents*); |
| }; |
| |
| 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 = |
| test_case.get_virtual_browsing_context_group(shell->web_contents()); |
| |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(shell->web_contents()->GetPrimaryMainFrame(), |
| JsReplace("window.open($1)", test_case.url_b))); |
| WebContents* popup = shell_observer.GetShell()->web_contents(); |
| WaitForLoadStop(popup); |
| int group_openee = test_case.get_virtual_browsing_context_group(popup); |
| |
| ASSERT_TRUE(NavigateToURL(shell, test_case.url_b)); |
| int group_navigate = |
| test_case.get_virtual_browsing_context_group(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.test", "/title1.html"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| }; |
| 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| false, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| false, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| false, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| }; |
| 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| false, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL("b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| false, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL("b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| false, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| }; |
| 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| false, |
| false, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL( |
| "a.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| |
| }; |
| 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| false, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL("a.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| }; |
| 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL("a.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: " |
| "same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| }; |
| |
| 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.test", |
| "/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.test", "/title1.html"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL( |
| "a.a.test", |
| "/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.test", "/title1.html"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL( |
| "a.test", |
| "/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.test", "/title1.html"), |
| true, |
| true, |
| VirtualBrowsingContextGroup, |
| }, |
| }; |
| |
| 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"); |
| GURL url_b = https_server()->GetURL( |
| "b.test", |
| "/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.test", "/title1.html"); |
| GURL url_b = https_server()->GetURL("b.test", "/title1.html"); |
| GURL url_c = https_server()->GetURL( |
| "c.test", |
| "/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); |
| } |
| |
| // A test to make sure that loading a page with COOP/COEP headers doesn't set |
| // is_origin_keyed() on the SiteInstance's SiteInfo. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| CoopCoepNotOriginKeyed) { |
| GURL isolated_page( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), isolated_page)); |
| SiteInstanceImpl* current_si = current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(current_si->IsCrossOriginIsolated()); |
| // Use of COOP/COEP headers should not cause SiteInfo::is_origin_keyed() to |
| // return true. The metrics that track OriginAgentCluster isolation expect |
| // is_origin_keyed() to refer only to the OriginAgentCluster header. |
| EXPECT_FALSE(current_si->GetSiteInfo().requires_origin_keyed_process()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| CrossOriginIsolatedSiteInstance_MainFrame) { |
| GURL isolated_page( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| GURL isolated_page_b( |
| https_server()->GetURL("cdn.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| GURL non_isolated_page(https_server()->GetURL("a.test", "/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->IsCrossOriginIsolated()); |
| } |
| |
| // 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->IsCrossOriginIsolated()); |
| 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->IsCrossOriginIsolated()); |
| 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->IsCrossOriginIsolated()); |
| 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->IsCrossOriginIsolated()); |
| 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->IsCrossOriginIsolated()); |
| 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->IsCrossOriginIsolated()); |
| EXPECT_TRUE(site_instance_2->IsCrossOriginIsolated()); |
| 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| GURL isolated_page_b( |
| https_server()->GetURL("cdn.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| GURL non_isolated_page(https_server()->GetURL("a.test", "/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->IsCrossOriginIsolated()); |
| } |
| |
| // 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->IsCrossOriginIsolated()); |
| 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->IsCrossOriginIsolated()); |
| 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->IsCrossOriginIsolated()); |
| 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->IsCrossOriginIsolated()); |
| EXPECT_TRUE(site_instance_2->IsCrossOriginIsolated()); |
| 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| GURL isolated_page_b( |
| https_server()->GetURL("cdn.a.test", |
| "/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->IsCrossOriginIsolated()); |
| |
| // Same origin iframe. |
| { |
| TestNavigationManager same_origin_iframe_navigation(web_contents(), |
| isolated_page); |
| |
| EXPECT_TRUE( |
| ExecJs(web_contents(), |
| JsReplace("const iframe = document.createElement('iframe'); " |
| "iframe.src = $1; " |
| "document.body.appendChild(iframe);", |
| isolated_page))); |
| |
| ASSERT_TRUE(same_origin_iframe_navigation.WaitForNavigationFinished()); |
| EXPECT_TRUE(same_origin_iframe_navigation.was_successful()); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| SiteInstanceImpl* iframe_si = iframe_rfh->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("const iframe = document.createElement('iframe'); " |
| "iframe.src = $1; " |
| "document.body.appendChild(iframe);", |
| isolated_page_b))); |
| |
| ASSERT_TRUE(cross_origin_iframe_navigation.WaitForNavigationFinished()); |
| EXPECT_TRUE(cross_origin_iframe_navigation.was_successful()); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(1)->current_frame_host(); |
| SiteInstanceImpl* iframe_si = iframe_rfh->GetSiteInstance(); |
| EXPECT_TRUE(iframe_si->IsCrossOriginIsolated()); |
| 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| GURL isolated_page_b( |
| https_server()->GetURL("cdn.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| GURL non_isolated_page( |
| embedded_test_server()->GetURL("a.test", "/title1.html")); |
| |
| // Initial cross-origin isolated page. |
| EXPECT_TRUE(NavigateToURL(shell(), isolated_page)); |
| SiteInstanceImpl* main_si = current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(main_si->IsCrossOriginIsolated()); |
| |
| // Open a non isolated popup. |
| { |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), non_isolated_page, "") |
| ->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| EXPECT_FALSE(popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| EXPECT_FALSE(popup_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| current_frame_host()->GetSiteInstance())); |
| EXPECT_FALSE(popup_rfh->frame_tree_node()->opener()); |
| } |
| |
| // Open an isolated popup. |
| { |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), isolated_page, "")->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| EXPECT_TRUE(popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| EXPECT_EQ(popup_rfh->GetSiteInstance(), |
| current_frame_host()->GetSiteInstance()); |
| } |
| |
| // Open an isolated popup, but cross-origin. |
| { |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), isolated_page_b, "") |
| ->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| EXPECT_TRUE(popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| EXPECT_FALSE(popup_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| current_frame_host()->GetSiteInstance())); |
| EXPECT_FALSE(popup_rfh->frame_tree_node()->opener()); |
| EXPECT_NE(popup_rfh->GetSiteInstance()->GetProcess(), |
| current_frame_host()->GetSiteInstance()->GetProcess()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| CrossOriginIsolatedSiteInstance_ErrorPage) { |
| GURL isolated_page( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| GURL non_coep_page(https_server()->GetURL("b.test", |
| "/set-header?" |
| "Access-Control-Allow-Origin: *")); |
| |
| GURL invalid_url( |
| https_server()->GetURL("a.test", "/this_page_does_not_exist.html")); |
| |
| GURL error_url(https_server()->GetURL("a.test", "/page404.html")); |
| |
| // Initial cross-origin isolated page. |
| EXPECT_TRUE(NavigateToURL(shell(), isolated_page)); |
| SiteInstanceImpl* main_si = current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(main_si->IsCrossOriginIsolated()); |
| |
| // Iframe. |
| { |
| TestNavigationManager iframe_navigation(web_contents(), invalid_url); |
| |
| EXPECT_TRUE( |
| ExecJs(web_contents(), |
| JsReplace("const iframe = document.createElement('iframe'); " |
| "iframe.src = $1; " |
| "document.body.appendChild(iframe);", |
| invalid_url))); |
| |
| ASSERT_TRUE(iframe_navigation.WaitForNavigationFinished()); |
| EXPECT_FALSE(iframe_navigation.was_successful()); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| SiteInstanceImpl* iframe_si = iframe_rfh->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_rfh, "document.location.href;")); |
| EXPECT_TRUE(IsExpectedSubframeErrorTransition(main_si, iframe_si)); |
| EXPECT_TRUE(iframe_si->IsCrossOriginIsolated()); |
| } |
| |
| // Iframe with a body added to the HTTP 404. |
| { |
| TestNavigationManager iframe_navigation(web_contents(), error_url); |
| |
| EXPECT_TRUE( |
| ExecJs(web_contents(), |
| JsReplace("const iframe = document.createElement('iframe'); " |
| "iframe.src = $1; " |
| "document.body.appendChild(iframe);", |
| error_url))); |
| |
| ASSERT_TRUE(iframe_navigation.WaitForNavigationFinished()); |
| EXPECT_FALSE(iframe_navigation.was_successful()); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| SiteInstanceImpl* iframe_si = iframe_rfh->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_rfh, "document.location.href;")); |
| EXPECT_TRUE(iframe_si->IsCrossOriginIsolated()); |
| } |
| |
| // Iframe blocked by coep. |
| { |
| TestNavigationManager iframe_navigation(web_contents(), non_coep_page); |
| |
| EXPECT_TRUE( |
| ExecJs(web_contents(), |
| JsReplace("const iframe = document.createElement('iframe'); " |
| "iframe.src = $1; " |
| "document.body.appendChild(iframe);", |
| non_coep_page))); |
| |
| ASSERT_TRUE(iframe_navigation.WaitForNavigationFinished()); |
| EXPECT_FALSE(iframe_navigation.was_successful()); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| SiteInstanceImpl* iframe_si = iframe_rfh->GetSiteInstance(); |
| EXPECT_TRUE(IsExpectedSubframeErrorTransition(main_si, iframe_si)); |
| EXPECT_TRUE(iframe_si->IsCrossOriginIsolated()); |
| } |
| |
| // 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->IsCrossOriginIsolated()); |
| } |
| } |
| |
| // Regression test for https://crbug.com/1226909. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| NavigatePopupToErrorAndCrash) { |
| GURL isolated_page( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| |
| // Initial cross-origin isolated page. |
| EXPECT_TRUE(NavigateToURL(shell(), isolated_page)); |
| SiteInstanceImpl* main_si = current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(main_si->IsCrossOriginIsolated()); |
| |
| ShellAddedObserver shell_observer; |
| GURL error_url(embedded_test_server()->GetURL("/close-socket")); |
| EXPECT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", error_url))); |
| WebContentsImpl* popup_web_contents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| WaitForLoadStop(popup_web_contents); |
| |
| // The popup should commit an error page with default COOP. |
| EXPECT_EQ(PAGE_TYPE_ERROR, popup_web_contents->GetController() |
| .GetLastCommittedEntry() |
| ->GetPageType()); |
| EXPECT_FALSE(popup_web_contents->GetPrimaryMainFrame() |
| ->GetSiteInstance() |
| ->IsCrossOriginIsolated()); |
| EXPECT_TRUE(CoopUnsafeNone().IsEqualExcludingOrigin( |
| popup_web_contents->GetPrimaryMainFrame()->cross_origin_opener_policy())); |
| |
| EXPECT_TRUE(popup_web_contents->GetPrimaryMainFrame() |
| ->cross_origin_opener_policy() |
| .origin->opaque()); |
| |
| url::Origin error_origin = |
| popup_web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(); |
| |
| // Simulate the popup renderer process crashing. |
| RenderProcessHost* popup_process = |
| popup_web_contents->GetPrimaryMainFrame()->GetProcess(); |
| EXPECT_NE(popup_process, current_frame_host()->GetProcess()); |
| |
| ASSERT_TRUE(popup_process); |
| { |
| RenderProcessHostWatcher crash_observer( |
| popup_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| popup_process->Shutdown(0); |
| crash_observer.Wait(); |
| } |
| |
| // Try to navigate the popup. This should not be possible, since the opener |
| // relationship should be closed. |
| EXPECT_TRUE( |
| ExecJs(current_frame_host(), "window.w.location = 'about:blank';")); |
| WaitForLoadStop(popup_web_contents); |
| |
| // The popup should not have navigated. |
| EXPECT_EQ( |
| error_origin, |
| popup_web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| EXPECT_FALSE(popup_web_contents->GetPrimaryMainFrame() |
| ->GetSiteInstance() |
| ->IsCrossOriginIsolated()); |
| EXPECT_TRUE(CoopUnsafeNone().IsEqualExcludingOrigin( |
| popup_web_contents->GetPrimaryMainFrame()->cross_origin_opener_policy())); |
| |
| EXPECT_TRUE(popup_web_contents->GetPrimaryMainFrame() |
| ->cross_origin_opener_policy() |
| .origin->opaque()); |
| } |
| |
| // Regression test for https://crbug.com/1239540. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| ReloadCrossOriginIsolatedPageWhileOffline) { |
| GURL isolated_page( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| |
| // Initial cross origin isolated page. |
| EXPECT_TRUE(NavigateToURL(shell(), isolated_page)); |
| SiteInstanceImpl* main_si = current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(main_si->IsCrossOriginIsolated()); |
| |
| // Simulate being offline by failing all network requests. |
| auto url_loader_interceptor = |
| std::make_unique<content::URLLoaderInterceptor>(base::BindRepeating( |
| [](content::URLLoaderInterceptor::RequestParams* params) { |
| network::URLLoaderCompletionStatus status; |
| status.error_code = net::Error::ERR_CONNECTION_FAILED; |
| params->client->OnComplete(status); |
| return true; |
| })); |
| |
| // Reload and end up with an error page to verify we do not violate any cross |
| // origin isolation invariant. |
| ReloadBlockUntilNavigationsComplete(shell(), 1); |
| } |
| |
| // Regression test for https://crbug.com/1239540. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| ReloadCoopPageWhileOffline) { |
| GURL isolated_page( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin")); |
| |
| // Initial coop isolated page. |
| EXPECT_TRUE(NavigateToURL(shell(), isolated_page)); |
| RenderFrameHostImpl* main_rfh = current_frame_host(); |
| EXPECT_EQ(main_rfh->cross_origin_opener_policy(), |
| CoopSameOrigin(url::Origin::Create(isolated_page))); |
| |
| // Simulate being offline by failing all network requests. |
| auto url_loader_interceptor = |
| std::make_unique<content::URLLoaderInterceptor>(base::BindRepeating( |
| [](content::URLLoaderInterceptor::RequestParams* params) { |
| network::URLLoaderCompletionStatus status; |
| status.error_code = net::Error::ERR_CONNECTION_FAILED; |
| params->client->OnComplete(status); |
| return true; |
| })); |
| |
| // Reload and end up with an error page to verify we do not violate any cross |
| // origin isolation invariant. |
| ReloadBlockUntilNavigationsComplete(shell(), 1); |
| } |
| |
| // Regression test for https://crbug.com/1239540. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| BackNavigationToCrossOriginIsolatedPageWhileOffline) { |
| GURL isolated_page( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| |
| GURL same_origin_isolated_page( |
| https_server()->GetURL("a.test", "/cross-origin-isolated.html")); |
| |
| // Put the initial isolated page in history. |
| EXPECT_TRUE(NavigateToURL(shell(), isolated_page)); |
| SiteInstanceImpl* main_si = current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(main_si->IsCrossOriginIsolated()); |
| |
| // This test relies on actually doing the back navigation from network. |
| // We disable BFCache on the initial to ensure that happens. |
| DisableBFCacheForRFHForTesting(current_frame_host()->GetGlobalId()); |
| |
| // Navigate to a same origin isolated page, staying in the same |
| // BrowsingInstance. This is also ensured by having the BFCache disabled on |
| // the initial page, avoiding special same-site proactive swaps. |
| EXPECT_TRUE(NavigateToURL(shell(), same_origin_isolated_page)); |
| main_si = current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(main_si->IsCrossOriginIsolated()); |
| |
| // Simulate being offline by failing all network requests. |
| auto url_loader_interceptor = |
| std::make_unique<content::URLLoaderInterceptor>(base::BindRepeating( |
| [](content::URLLoaderInterceptor::RequestParams* params) { |
| network::URLLoaderCompletionStatus status; |
| status.error_code = net::Error::ERR_CONNECTION_FAILED; |
| params->client->OnComplete(status); |
| return true; |
| })); |
| |
| // Go back and end up with an error page to verify we do not violate any cross |
| // origin isolation invariant. |
| web_contents()->GetController().GoBack(); |
| EXPECT_FALSE(WaitForLoadStop(web_contents())); |
| } |
| |
| // Regression test for https://crbug.com/1374705. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| ReloadRedirectsToCoopPage) { |
| GURL coop_page( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin")); |
| GURL redirect_page(https_server()->GetURL( |
| "a.test", |
| "/redirect-to-target-on-second-navigation?" + coop_page.spec())); |
| |
| // Navigate to the redirect page. On the first navigation, this is a simple |
| // empty page with no headers. |
| EXPECT_TRUE(NavigateToURL(shell(), redirect_page)); |
| scoped_refptr<SiteInstanceImpl> main_si = |
| current_frame_host()->GetSiteInstance(); |
| EXPECT_EQ(current_frame_host()->GetLastCommittedURL(), redirect_page); |
| |
| // Reload. This time we should be redirected to a COOP: same-origin page. |
| ReloadBlockUntilNavigationsComplete(shell(), 1); |
| EXPECT_EQ(current_frame_host()->GetLastCommittedURL(), coop_page); |
| |
| // We should have swapped BrowsingInstance. |
| EXPECT_FALSE( |
| main_si->IsRelatedSiteInstance(current_frame_host()->GetSiteInstance())); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| ReloadPageWithUpdatedCoopHeader) { |
| GURL changing_coop_page( |
| https_server()->GetURL("a.test", "/serve-coop-on-second-navigation")); |
| |
| // Navigate to the page. On the first navigation, this is a simple empty page |
| // with no headers. |
| EXPECT_TRUE(NavigateToURL(shell(), changing_coop_page)); |
| scoped_refptr<SiteInstanceImpl> main_si = |
| current_frame_host()->GetSiteInstance(); |
| |
| // Reload. This time the page should be served with COOP: same-origin. |
| ReloadBlockUntilNavigationsComplete(shell(), 1); |
| |
| // We should have swapped BrowsingInstance. |
| EXPECT_FALSE( |
| main_si->IsRelatedSiteInstance(current_frame_host()->GetSiteInstance())); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| CrossOriginRedirectHasProperCrossOriginIsolatedState) { |
| GURL non_isolated_page( |
| embedded_test_server()->GetURL("a.test", "/title1.html")); |
| |
| GURL isolated_page( |
| https_server()->GetURL("c.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| |
| GURL redirect_isolated_page(https_server()->GetURL( |
| "b.test", "/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->IsCrossOriginIsolated()); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), redirect_isolated_page, isolated_page)); |
| current_si = current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(current_si->IsCrossOriginIsolated()); |
| EXPECT_TRUE( |
| current_si->GetWebExposedIsolationInfo().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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp")); |
| GURL isolated_page_b( |
| https_server()->GetURL("cdn.a.test", |
| "/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->IsCrossOriginIsolated()); |
| |
| TestNavigationManager cross_origin_iframe_navigation(web_contents(), |
| isolated_page_b); |
| |
| EXPECT_TRUE( |
| ExecJs(web_contents(), |
| JsReplace("const iframe = document.createElement('iframe'); " |
| "iframe.src = $1; " |
| "document.body.appendChild(iframe);", |
| isolated_page_b))); |
| |
| ASSERT_TRUE(cross_origin_iframe_navigation.WaitForNavigationFinished()); |
| EXPECT_TRUE(cross_origin_iframe_navigation.was_successful()); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| SiteInstanceImpl* iframe_si = iframe_rfh->GetSiteInstance(); |
| EXPECT_TRUE(iframe_si->IsCrossOriginIsolated()); |
| EXPECT_TRUE(iframe_si->IsRelatedSiteInstance(main_si)); |
| EXPECT_EQ(iframe_si->GetProcess(), main_si->GetProcess()); |
| |
| // Open an isolated popup, but cross-origin. |
| { |
| RenderFrameHostImpl* popup_rfh = |
| static_cast<WebContentsImpl*>( |
| OpenPopup(iframe_rfh, isolated_page, "", "", false)->web_contents()) |
| ->GetPrimaryMainFrame(); |
| |
| EXPECT_TRUE(popup_rfh->GetSiteInstance()->IsCrossOriginIsolated()); |
| EXPECT_FALSE(popup_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| current_frame_host()->GetSiteInstance())); |
| EXPECT_FALSE(popup_rfh->frame_tree_node()->opener()); |
| EXPECT_NE(popup_rfh->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.test", |
| "/set-header?Cross-Origin-Opener-Policy-Report-Only: same-origin")); |
| GURL b_url(https_server()->GetURL("b.test", "/empty.html")); |
| GURL c_url(https_server()->GetURL("c.test", "/empty.html")); |
| |
| // 1. Start from COOP-Report-Only:same-origin. (a.test 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.test) |
| 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.test). |
| 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"; |
| )")); |
| } |
| |
| // This test is a reproducer for https://crbug.com/1305394. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| CrossOriginIframeCoopBypass) { |
| // This test requires that a cross-origin iframe be placed in its own |
| // process. It is irrelevant without strict site isolation. |
| if (!SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) |
| return; |
| |
| GURL non_coop_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL cross_origin_non_coop_page( |
| https_server()->GetURL("b.test", "/title1.html")); |
| GURL coop_page(https_server()->GetURL( |
| "a.test", "/set-header?cross-origin-opener-policy: same-origin")); |
| |
| // Get an initial non-COOP page with an empty popup. |
| ASSERT_TRUE(NavigateToURL(shell(), non_coop_page)); |
| RenderFrameHostImplWrapper initial_main_rfh(current_frame_host()); |
| |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(initial_main_rfh.get(), |
| JsReplace("window.open($1)", non_coop_page))); |
| WebContentsImpl* popup = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| RenderFrameHostImpl* popup_rfh = popup->GetPrimaryMainFrame(); |
| |
| // At this stage we have a single SiteInstance used both for the main page and |
| // the same-site popup. |
| SiteInstanceImpl* initial_main_si = initial_main_rfh->GetSiteInstance(); |
| SiteInstanceImpl* popup_si = popup_rfh->GetSiteInstance(); |
| ASSERT_EQ(initial_main_si, popup_si); |
| RenderProcessHost* process_A = initial_main_si->GetProcess(); |
| |
| // The popup then navigates the opener to a COOP page. |
| EXPECT_TRUE(popup_rfh->frame_tree_node()->opener()); |
| ASSERT_TRUE(ExecJs(popup_rfh, JsReplace("opener.location = $1", coop_page))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| ASSERT_TRUE(initial_main_rfh.WaitUntilRenderFrameDeleted()); |
| |
| // This should trigger a BrowsingInstance swap. The main frame gets a new |
| // unrelated BrowsingInstance. |
| RenderFrameHostImpl* main_rfh = current_frame_host(); |
| SiteInstanceImpl* main_si = main_rfh->GetSiteInstance(); |
| RenderProcessHost* process_B = main_si->GetProcess(); |
| ASSERT_FALSE(popup_si->IsRelatedSiteInstance(main_si)); |
| |
| // The popup still uses process A, but the opener link should be cut and no |
| // proxy should remain between the two site instances. |
| EXPECT_EQ(process_A, popup_si->GetProcess()); |
| if (ShouldCreateNewHostForAllFrames()) { |
| // When RenderDocument is enabled, we will create a new RenderFrameHost |
| // using the same SiteInstance from the start of the navigation where we |
| // don't have the COOP information yet. Then when we receive the final |
| // response, we will try to reuse the process used by the speculative RFH, |
| // which is the same process as before. |
| // TODO(https://crbug.com/1426413): This is unexpected. Fix this so that the |
| // process won't be reused. |
| EXPECT_EQ(process_B, process_A); |
| } else { |
| // When RenderDocument is enabled, we will only create a new RenderFrameHost |
| // when the final response for the COOP page is created. In this case, a new |
| // process will be created for the final RenderFrameHost. |
| EXPECT_NE(process_B, process_A); |
| } |
| EXPECT_FALSE(popup_rfh->frame_tree_node()->opener()); |
| EXPECT_TRUE(popup_rfh->frame_tree_node() |
| ->render_manager() |
| ->GetAllProxyHostsForTesting() |
| .empty()); |
| EXPECT_TRUE(main_rfh->frame_tree_node() |
| ->render_manager() |
| ->GetAllProxyHostsForTesting() |
| .empty()); |
| |
| // Load an iframe that is cross-origin to the top frame's opener. |
| ASSERT_TRUE(ExecJs(popup_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| cross_origin_non_coop_page))); |
| EXPECT_TRUE(WaitForLoadStop(popup)); |
| RenderFrameHostImpl* iframe_rfh = |
| popup_rfh->child_at(0)->current_frame_host(); |
| SiteInstanceImpl* iframe_si = iframe_rfh->GetSiteInstance(); |
| |
| // The iframe being cross-origin, it is put in a different but related |
| // SiteInstance. |
| EXPECT_TRUE(iframe_si->IsRelatedSiteInstance(popup_si)); |
| EXPECT_FALSE(iframe_si->IsRelatedSiteInstance(main_si)); |
| |
| // We end up with the main window, the main popup frame and the iframe all |
| // living in their own process. We should only have proxies from the popup |
| // main frame to iframe and vice versa. Opener links should stay severed. |
| RenderProcessHost* process_C = iframe_si->GetProcess(); |
| EXPECT_NE(process_C, process_A); |
| EXPECT_NE(process_C, process_B); |
| EXPECT_EQ(1u, iframe_rfh->frame_tree_node() |
| ->render_manager() |
| ->GetAllProxyHostsForTesting() |
| .size()); |
| EXPECT_EQ(1u, popup_rfh->frame_tree_node() |
| ->render_manager() |
| ->GetAllProxyHostsForTesting() |
| .size()); |
| |
| // The opener should not be reachable from the popup iframe. |
| EXPECT_EQ(true, EvalJs(iframe_rfh, "parent.opener == null")); |
| } |
| |
| // Check whether not using COOP causes a RenderProcessHost change during |
| // same-origin navigations. This is a control test for the subsequent tests. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| Process_CoopUnsafeNone_SameOrigin) { |
| GURL url_1(https_server()->GetURL("a.test", "/empty.html?1")); |
| GURL url_2(https_server()->GetURL("a.test", "/empty.html?2")); |
| GURL url_3(https_server()->GetURL("a.test", "/empty.html?3")); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), url_1)); |
| int rph_id_1 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_TRUE(NavigateToURL(shell(), url_2)); |
| int rph_id_2 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_TRUE(NavigateToURL(shell(), url_3)); |
| int rph_id_3 = current_frame_host()->GetProcess()->GetID(); |
| |
| EXPECT_EQ(rph_id_1, rph_id_2); |
| EXPECT_EQ(rph_id_2, rph_id_3); |
| EXPECT_EQ(rph_id_3, rph_id_1); |
| } |
| |
| // Check whether using COOP causes a RenderProcessHost change during |
| // same-origin navigations. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| Process_CoopSameOrigin_SameOrigin) { |
| GURL url_1(https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin&1")); |
| GURL url_2(https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin&2")); |
| GURL url_3(https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin&3")); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), url_1)); |
| int rph_id_1 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_TRUE(NavigateToURL(shell(), url_2)); |
| int rph_id_2 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_TRUE(NavigateToURL(shell(), url_3)); |
| int rph_id_3 = current_frame_host()->GetProcess()->GetID(); |
| |
| EXPECT_EQ(rph_id_1, rph_id_2); |
| EXPECT_EQ(rph_id_2, rph_id_3); |
| EXPECT_EQ(rph_id_3, rph_id_1); |
| } |
| |
| // Check that a COOP mismatch does not cause a RenderProcessHost change during |
| // same-origin navigations, unless COOP triggers the site isolation heuristic |
| // of requiring a dedicated process, which would force a process swap. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| Process_CoopAlternate_SameOrigin) { |
| GURL url_1(https_server()->GetURL("a.test", "/empty.html")); |
| GURL url_2(https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin")); |
| GURL url_3(https_server()->GetURL("a.test", "/empty.html")); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), url_1)); |
| int rph_id_1 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_TRUE(NavigateToURL(shell(), url_2)); |
| int rph_id_2 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_TRUE(NavigateToURL(shell(), url_3)); |
| int rph_id_3 = current_frame_host()->GetProcess()->GetID(); |
| |
| // If we're using the COOP site isolation heuristic (e.g., on Android), we |
| // have to swap processes since we're going from an unlocked process to a |
| // locked process. |
| if (SiteIsolationPolicy::IsSiteIsolationForCOOPEnabled()) { |
| EXPECT_NE(rph_id_1, rph_id_2); |
| // COOP isolation only applies to the current BrowsingInstance if there was |
| // no user gesture. Since NavigateToURL forced a BrowsingInstance swap, |
| // and since there was no user gesture on url_2, we'll be going from a |
| // locked process back to an unlocked process, and hence require a process |
| // swap. |
| EXPECT_NE(rph_id_2, rph_id_3); |
| } else { |
| EXPECT_EQ(rph_id_1, rph_id_2); |
| EXPECT_EQ(rph_id_2, rph_id_3); |
| } |
| } |
| |
| // Check whether COOP causes a RenderProcessHost change during same-site |
| // navigations. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| Process_CoopAlternate_SameSite) { |
| GURL url_1(https_server()->GetURL("a.a.test", "/empty.html")); |
| GURL url_2(https_server()->GetURL( |
| "b.a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin")); |
| GURL url_3(https_server()->GetURL("c.a.test", "/empty.html")); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), url_1)); |
| int rph_id_1 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_TRUE(NavigateToURL(shell(), url_2)); |
| int rph_id_2 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_TRUE(NavigateToURL(shell(), url_3)); |
| int rph_id_3 = current_frame_host()->GetProcess()->GetID(); |
| |
| // If we're using the COOP site isolation heuristic (e.g., on Android), we |
| // have to swap processes since we're going from an unlocked process to a |
| // locked process. |
| if (SiteIsolationPolicy::IsSiteIsolationForCOOPEnabled()) { |
| EXPECT_NE(rph_id_1, rph_id_2); |
| // COOP isolation only applies to the current BrowsingInstance if there was |
| // no user gesture. Since NavigateToURL forced a BrowsingInstance swap, |
| // and since there was no user gesture on url_2, we'll be going from a |
| // locked process back to an unlocked process, and hence require a process |
| // swap. |
| EXPECT_NE(rph_id_2, rph_id_3); |
| } else { |
| EXPECT_EQ(rph_id_1, rph_id_2); |
| EXPECT_EQ(rph_id_2, rph_id_3); |
| } |
| } |
| |
| // Check whether COOP causes a RenderProcessHost change during cross-origin |
| // navigations. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| Process_CoopSameOrigin_CrossOrigin) { |
| GURL url_1(https_server()->GetURL("a.test", "/empty.html")); |
| GURL url_2(https_server()->GetURL( |
| "b.test", "/set-header?Cross-Origin-Opener-Policy: same-origin")); |
| GURL url_3(https_server()->GetURL("c.test", "/empty.html")); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), url_1)); |
| int rph_id_1 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_TRUE(NavigateToURL(shell(), url_2)); |
| int rph_id_2 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_TRUE(NavigateToURL(shell(), url_3)); |
| int rph_id_3 = current_frame_host()->GetProcess()->GetID(); |
| |
| EXPECT_NE(rph_id_1, rph_id_2); |
| EXPECT_NE(rph_id_2, rph_id_3); |
| EXPECT_NE(rph_id_3, rph_id_1); |
| } |
| |
| // Smoke test for an iframe in a crossOriginIsolated page doing a same-document |
| // history navigation. Added to prevent regression of https://crbug.com/1413081. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| SmokeTest_CoopCoepSameDocumentIframeHistoryNavigation) { |
| GURL main_page_url( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-origin-opener-policy: same-origin&" |
| "Cross-origin-embedder-policy: require-corp")); |
| GURL iframe_url( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Embedder-Policy: require-corp&" |
| "Cross-Origin-Resource-Policy: cross-origin")); |
| |
| // Start with a cross-origin isolated document. |
| ASSERT_TRUE(NavigateToURL(shell(), main_page_url)); |
| |
| // Add an iframe that has the appropriate COEP and CORP headers. |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| iframe_url))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| |
| // Do a pushState/popState in the iframe. This will generate a same-document |
| // history navigation. |
| RenderFrameHostImpl* child_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| ASSERT_TRUE(ExecJs(child_rfh, "history.pushState({}, '', '');")); |
| ASSERT_TRUE(ExecJs(child_rfh, "history.go(-1)")); |
| |
| // We should commit and gracefully finish loading. |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| } |
| |
| // Ensure that when navigating from a non-COOP site to a site with COOP that |
| // also requires a dedicated process, there's only one new process created, and |
| // the BrowsingInstance swap required by COOP doesn't trigger an unneeded |
| // second process swap at response time. In other words, the process created |
| // for the speculative RenderFrameHost at navigation start time ought to be |
| // reused by the speculative RenderFrameHost that's recomputed at |
| // OnResponseStarted response time (where it's recomputed due to the |
| // BrowsingInstance swap required by COOP). |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| NoExtraProcessSwapFromDiscardedSpeculativeRFH) { |
| if (IsIsolatedOriginRequiredToGuaranteeDedicatedProcess()) { |
| IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(), |
| {url::Origin::Create(GURL("https://b.test/"))}); |
| } |
| |
| GURL url_1(https_server()->GetURL("a.test", "/empty.html")); |
| GURL url_2(https_server()->GetURL( |
| "b.test", "/set-header?Cross-Origin-Opener-Policy: same-origin")); |
| |
| // Navigate to a non-COOP URL. Note that on Android this will be in a |
| // default SiteInstance and in a process that's not locked to a specific |
| // site, and on desktop it'll be in a process that's locked to a.test. We're |
| // interested in covering both cases. |
| EXPECT_TRUE(NavigateToURL(shell(), url_1)); |
| int rph_id_1 = current_frame_host()->GetProcess()->GetID(); |
| |
| // Start a navigation to b.test, which will have COOP headers, but this isn't |
| // known until response time. This creates a speculative RFH and process |
| // that's locked to b.test. |
| TestNavigationManager navigation(web_contents(), url_2); |
| EXPECT_TRUE(BeginNavigateToURLFromRenderer(web_contents(), url_2)); |
| EXPECT_TRUE(navigation.WaitForRequestStart()); |
| RenderFrameHostWrapper speculative_rfh(web_contents() |
| ->GetPrimaryFrameTree() |
| .root() |
| ->render_manager() |
| ->speculative_frame_host()); |
| ASSERT_TRUE(speculative_rfh.get()); |
| int rph_id_2 = speculative_rfh->GetProcess()->GetID(); |
| EXPECT_NE(rph_id_1, rph_id_2); |
| |
| // Allow the navigation to receive the response and commit. |
| navigation.ResumeNavigation(); |
| EXPECT_TRUE(navigation.WaitForNavigationFinished()); |
| EXPECT_TRUE(navigation.was_successful()); |
| |
| // When the response for `url_2` was received, we should have learned about |
| // the COOP headers and swapped BrowsingInstances. This should've recreated |
| // the speculative RFH in a new SiteInstance/BrowsingInstance, but note that |
| // since `url_2` only has COOP but no COEP (and hence no process isolation |
| // requirement due to cross-origin isolation), it still just needs a regular |
| // process locked to b.test, which is exactly the process that we created for |
| // the original speculative RFH. Ensure that this process gets reused and not |
| // wasted. |
| int rph_id_3 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_EQ(rph_id_2, rph_id_3); |
| |
| // This test is parameterized on whether the bfcache is enabled. With |
| // bfcache, we force a BrowsingInstance swap at the very beginning when the |
| // navigation to `url_2` starts, so there's no need to create a new |
| // SiteInstance when we learn about COOP at response time, since the |
| // candidate (speculative RFH's) SiteInstance is already in a fresh |
| // BrowsingInstance. Therefore, with bfcache, the original speculative RFH |
| // will be the RFH that eventually commits. Otherwise, the original |
| // speculative RFH should be destroyed and replaced by another RFH. |
| EXPECT_NE(IsBackForwardCacheEnabled(), speculative_rfh.IsDestroyed()); |
| } |
| |
| // Ensure that same-site navigations that result in a COOP mismatch avoid an |
| // unnecessary process swap when those navigations happen in a |
| // BrowsingContextGroup of size 1 (in this case, in the same WebContents). |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| NoExtraProcessSwapFromSameSiteCOOPMismatch) { |
| GURL url_1(https_server()->GetURL("a.test", "/empty.html")); |
| GURL url_2(https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin")); |
| |
| // Navigate to a non-COOP URL. |
| EXPECT_TRUE(NavigateToURL(shell(), url_1)); |
| int rph_id_1 = current_frame_host()->GetProcess()->GetID(); |
| bool rph_1_is_locked = |
| current_frame_host()->GetProcess()->GetProcessLock().is_locked_to_site(); |
| |
| // Start a navigation to a page on a.test that will have COOP headers. |
| TestNavigationManager navigation(web_contents(), url_2); |
| EXPECT_TRUE(BeginNavigateToURLFromRenderer(web_contents(), url_2)); |
| EXPECT_TRUE(navigation.WaitForRequestStart()); |
| RenderFrameHostImpl* speculative_rfh = web_contents() |
| ->GetPrimaryFrameTree() |
| .root() |
| ->render_manager() |
| ->speculative_frame_host(); |
| |
| // When the back-forward cache is enabled, or when RenderDocument is used, we |
| // will get a speculative RenderFrameHost, which should reuse the existing |
| // process because the navigation is same-site. Otherwise, the navigation |
| // should stay in the current RenderFrameHost. |
| int rph_id_2; |
| if (IsBackForwardCacheEnabled() || ShouldCreateNewHostForAllFrames()) { |
| ASSERT_TRUE(speculative_rfh); |
| rph_id_2 = speculative_rfh->GetProcess()->GetID(); |
| EXPECT_EQ(rph_id_1, rph_id_2); |
| } else { |
| ASSERT_FALSE(speculative_rfh); |
| rph_id_2 = rph_id_1; |
| } |
| |
| // Allow the navigation to receive the response and commit. |
| navigation.ResumeNavigation(); |
| EXPECT_TRUE(navigation.WaitForNavigationFinished()); |
| EXPECT_TRUE(navigation.was_successful()); |
| |
| // When the response for `url_2` was received, we should have learned about |
| // the COOP headers and swapped BrowsingInstances. This should've created a |
| // new speculative RFH in a new SiteInstance/BrowsingInstance, but it should |
| // reuse the old a.com process since `url_2` only has COOP but no COEP (and |
| // hence no process isolation requirement due to cross-origin isolation). An |
| // exception to this is if COOP triggers site isolation (e.g., on Android), |
| // and the old process wasn't already locked to a.test. In that case, a |
| // process swap is required, since we are going from an unlocked process to a |
| // locked process. |
| int rph_id_3 = current_frame_host()->GetProcess()->GetID(); |
| if (SiteIsolationPolicy::IsSiteIsolationForCOOPEnabled()) { |
| EXPECT_NE(rph_id_2, rph_id_3); |
| EXPECT_FALSE(rph_1_is_locked); |
| EXPECT_TRUE(current_frame_host() |
| ->GetProcess() |
| ->GetProcessLock() |
| .is_locked_to_site()); |
| } else { |
| EXPECT_EQ(rph_id_2, rph_id_3); |
| } |
| } |
| |
| // Verify that there's no extra process swap during a same-site navigation from |
| // one COOP page to another COOP page. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| NavigatingFromCOOPToCOOPHasNoExtraProcessCreation) { |
| GURL url_1(https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin")); |
| GURL url_2(https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin&2")); |
| |
| // Navigate to a COOP URL. |
| EXPECT_TRUE(NavigateToURL(shell(), url_1)); |
| int rph_id_1 = current_frame_host()->GetProcess()->GetID(); |
| |
| // Start a navigation to another same-site COOP URL. |
| TestNavigationManager navigation(web_contents(), url_2); |
| EXPECT_TRUE(BeginNavigateToURLFromRenderer(web_contents(), url_2)); |
| EXPECT_TRUE(navigation.WaitForRequestStart()); |
| RenderFrameHostImpl* speculative_rfh = web_contents() |
| ->GetPrimaryFrameTree() |
| .root() |
| ->render_manager() |
| ->speculative_frame_host(); |
| |
| // When the back-forward cache is enabled, or when RenderDocument is used, we |
| // will get a speculative RenderFrameHost, which should reuse the existing |
| // process because the navigation is same-site. Otherwise, the navigation |
| // should stay in the current RenderFrameHost. The else path verifies that |
| // we don't assume no COOP when initially making the request to `url_2` and |
| // place the candidate SiteInstance in a new BrowsingInstance, and later come |
| // back to the original BrowsingInstance after realizing at response time |
| // that COOP hasn't changed. |
| int rph_id_2; |
| if (IsBackForwardCacheEnabled() || ShouldCreateNewHostForAllFrames()) { |
| ASSERT_TRUE(speculative_rfh); |
| rph_id_2 = speculative_rfh->GetProcess()->GetID(); |
| EXPECT_EQ(rph_id_1, rph_id_2); |
| } else { |
| ASSERT_FALSE(speculative_rfh); |
| rph_id_2 = rph_id_1; |
| } |
| |
| // Allow the navigation to receive the response and commit. |
| navigation.ResumeNavigation(); |
| EXPECT_TRUE(navigation.WaitForNavigationFinished()); |
| EXPECT_TRUE(navigation.was_successful()); |
| |
| // When the response for `url_2` was received, we should verify that COOP |
| // status hasn't changed, so no BrowsingInstance swap is needed, and we |
| // should stay in the same process. |
| int rph_id_3 = current_frame_host()->GetProcess()->GetID(); |
| EXPECT_EQ(rph_id_2, rph_id_3); |
| } |
| |
| // Ensure that a same-site COOP mismatch that happens in a popup does *not* |
| // reuse the existing process, unlike in the |
| // NoExtraProcessSwapFromSameSiteCOOPMismatch test above. This ensures that |
| // same-site COOP mismatch reuses the old process only in single-window |
| // BrowsingInstances, and noopener-like popups with a COOP mismatch still get a |
| // fresh process. |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| NoProcessReuseForSameSiteCOOPMismatchInPopup) { |
| GURL url_1(https_server()->GetURL("a.test", "/empty.html")); |
| GURL url_2(https_server()->GetURL( |
| "a.test", "/set-header?Cross-Origin-Opener-Policy: same-origin")); |
| |
| // Navigate to a non-COOP URL. |
| EXPECT_TRUE(NavigateToURL(shell(), url_1)); |
| int rph_id_1 = current_frame_host()->GetProcess()->GetID(); |
| |
| // Open a same-site popup with COOP. |
| Shell* new_shell = OpenPopup(web_contents(), url_2, ""); |
| EXPECT_TRUE(new_shell); |
| WebContentsImpl* popup_contents = |
| static_cast<WebContentsImpl*>(new_shell->web_contents()); |
| |
| // When the response for `url_2` was received, we should have learned about |
| // the COOP headers and swapped BrowsingInstances. This should've created a |
| // new speculative RFH in a new SiteInstance/BrowsingInstance, and it should |
| // create a fresh process rather than reuse the old a.com process, since |
| // there was more than one active window in the old BrowsingInstance. |
| int rph_id_2 = popup_contents->GetPrimaryMainFrame()->GetProcess()->GetID(); |
| EXPECT_NE(rph_id_1, rph_id_2); |
| } |
| |
| // 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, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| INSTANTIATE_TEST_SUITE_P(All, |
| VirtualBrowsingContextGroupTest, |
| kTestParams, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| INSTANTIATE_TEST_SUITE_P(All, |
| NoSharedArrayBufferByDefault, |
| kTestParams, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| INSTANTIATE_TEST_SUITE_P(All, |
| SoapByDefaultVirtualBrowsingContextGroupTest, |
| kTestParams, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| INSTANTIATE_TEST_SUITE_P(All, |
| CoopRestrictPropertiesBrowserTest, |
| kTestParams, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| INSTANTIATE_TEST_SUITE_P(All, |
| CoopRestrictPropertiesProxiesBrowserTest, |
| kTestParams, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| CoopRestrictPropertiesWithNewBrowsingContextStateModeBrowserTest, |
| kTestParams, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| INSTANTIATE_TEST_SUITE_P(All, |
| CoopRestrictPropertiesAccessBrowserTest, |
| kTestParams, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| INSTANTIATE_TEST_SUITE_P(All, |
| NoSiteIsolationCrossOriginIsolationBrowserTest, |
| kTestParams, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| INSTANTIATE_TEST_SUITE_P(All, |
| CoopRestrictPropertiesReportingBrowserTest, |
| kTestParams, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| |
| IN_PROC_BROWSER_TEST_P(NoSharedArrayBufferByDefault, BaseCase) { |
| GURL url = https_server()->GetURL("a.test", "/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.test", |
| "/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.test", |
| "/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"( |
| const 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"); |
| GURL iframe_url = |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Embedder-Policy: require-corp&" |
| "Cross-Origin-Resource-Policy: cross-origin&" |
| "Permissions-Policy: cross-origin-isolated%3D()"); |
| 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"( |
| const sab = new SharedArrayBuffer(1234); |
| g_iframe.contentWindow.postMessage(sab,"*"); |
| )"); |
| |
| EXPECT_THAT(postSharedArrayBuffer.error, |
| HasSubstr("Failed to execute 'postMessage' on 'Window':")); |
| } |
| |
| // 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.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"); |
| GURL iframe_url = |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Embedder-Policy: require-corp&" |
| "Cross-Origin-Resource-Policy: cross-origin&" |
| "Permissions-Policy: cross-origin-isolated%3D()"); |
| 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)); |
| |
| EXPECT_EQ(false, EvalJs(sub_document, "'SharedArrayBuffer' in globalThis")); |
| |
| // 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"( |
| // Create a WebAssembly Memory to bypass the SAB constructor restriction. |
| const sab = new (new WebAssembly.Memory( |
| { shared:true, initial:1, maximum:1 }).buffer.constructor)(1234); |
| parent.postMessage(sab, "*"); |
| )")); |
| |
| EXPECT_EQ(1234, EvalJs(main_document, "g_sab_size")); |
| } |
| |
| class OriginTrialBrowserTest : public ContentBrowserTest { |
| public: |
| // 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()->GetPrimaryMainFrame(); |
| } |
| |
| net::EmbeddedTestServer* https_server() { return &https_server_; } |
| |
| private: |
| void SetUpOnMainThread() override { |
| ContentBrowserTest::SetUpOnMainThread(); |
| mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); |
| 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) override { |
| ContentBrowserTest::SetUpCommandLine(command_line); |
| mock_cert_verifier_.SetUpCommandLine(command_line); |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| ContentBrowserTest::SetUpInProcessBrowserTestFixture(); |
| mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); |
| } |
| |
| void TearDownInProcessBrowserTestFixture() override { |
| ContentBrowserTest::TearDownInProcessBrowserTestFixture(); |
| mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); |
| } |
| |
| content::ContentMockCertVerifier mock_cert_verifier_; |
| net::EmbeddedTestServer https_server_; |
| }; |
| |
| // Ensure the UnrestrictedSharedArrayBuffer reverse origin trial is correctly |
| // implemented. |
| class UnrestrictedSharedArrayBufferOriginTrialBrowserTest |
| : public OriginTrialBrowserTest { |
| public: |
| UnrestrictedSharedArrayBufferOriginTrialBrowserTest() { |
| feature_list_.InitWithFeatures( |
| { |
| // Enabled |
| }, |
| { |
| // Disabled |
| features::kSharedArrayBuffer, |
| }); |
| } |
| |
| // 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=="; |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| 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 !BUILDFLAG(IS_ANDROID) |
| EXPECT_EQ(true, |
| EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis")); |
| #else // !BUILDFLAG(IS_ANDROID) |
| EXPECT_EQ(false, |
| EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis")); |
| #endif // !BUILDFLAG(IS_ANDROID) |
| } |
| |
| // Check setting the OriginTrial works, even in popups where the javascript |
| // context of the initial empty document is reused. |
| IN_PROC_BROWSER_TEST_F(UnrestrictedSharedArrayBufferOriginTrialBrowserTest, |
| HasSharedArrayBufferReuseContext) { |
| // Create a document without the origin trial in a renderer process. |
| { |
| 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\n", |
| "", params->client.get()); |
| return true; |
| })); |
| EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL())); |
| EXPECT_EQ(false, EvalJs(current_frame_host(), |
| "'SharedArrayBuffer' in globalThis")); |
| } |
| |
| // In the same process, open a popup. The document loaded defines an |
| // OriginTrial. It will reuse the javascript context created for the initial |
| // empty document. |
| { |
| 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; |
| })); |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.open(location.href)")); |
| |
| auto* popup = static_cast<WebContentsImpl*>( |
| shell_observer.GetShell()->web_contents()); |
| WaitForLoadStop(popup); |
| |
| #if BUILDFLAG(IS_ANDROID) |
| EXPECT_EQ(false, EvalJs(popup, "'SharedArrayBuffer' in globalThis")); |
| #else |
| EXPECT_EQ(true, EvalJs(popup, "'SharedArrayBuffer' in globalThis")); |
| #endif |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UnrestrictedSharedArrayBufferOriginTrialBrowserTest, |
| SupportForMeta) { |
| 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", |
| "<meta http-equiv=\"origin-trial\" content=\"" + |
| OriginTrialToken() + "\">", |
| params->client.get()); |
| return true; |
| })); |
| EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL())); |
| |
| EXPECT_EQ(false, EvalJs(current_frame_host(), "self.crossOriginIsolated")); |
| |
| #if BUILDFLAG(IS_ANDROID) |
| EXPECT_EQ(false, |
| EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis")); |
| #else |
| EXPECT_EQ(true, |
| EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis")); |
| #endif |
| } |
| |
| 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 !BUILDFLAG(IS_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"( |
| const sab = new SharedArrayBuffer(1234); |
| g_iframe.contentWindow.postMessage(sab, "*"); |
| )")); |
| |
| EXPECT_EQ(1234, EvalJs(sub_document, "g_sab_size")); |
| #else // !BUILDFLAG(IS_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'")); |
| #endif // !BUILDFLAG(IS_ANDROID) |
| } |
| |
| // Enable the reverse OriginTrial via a <meta> tag. Then send a Webassembly's |
| // SharedArrayBuffer toward the iframe. |
| // Regression test for https://crbug.com/1201589). |
| // The SAB reverse origin trial only work on Desktop. |
| #if !BUILDFLAG(IS_ANDROID) |
| IN_PROC_BROWSER_TEST_F(UnrestrictedSharedArrayBufferOriginTrialBrowserTest, |
| CrashForBug1201589) { |
| 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", |
| "<meta http-equiv=\"origin-trial\" content=\"" + |
| OriginTrialToken() + "\">", |
| 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")); |
| |
| EXPECT_EQ(true, EvalJs(main_document, "'SharedArrayBuffer' in globalThis")); |
| EXPECT_EQ(true, EvalJs(sub_document, "'SharedArrayBuffer' in globalThis")); |
| |
| 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"( |
| const wasm_shared_memory = new WebAssembly.Memory({ |
| shared:true, initial:0, maximum:0 }); |
| g_iframe.contentWindow.postMessage(wasm_shared_memory.buffer, "*"); |
| )")); |
| EXPECT_EQ(0, EvalJs(sub_document, "g_sab_size")); |
| } |
| #endif |
| |
| // Ensure the SharedArrayBufferOnDesktop kill switch is correctly implemented. |
| class SharedArrayBufferOnDesktopBrowserTest |
| : public CrossOriginOpenerPolicyBrowserTest { |
| public: |
| SharedArrayBufferOnDesktopBrowserTest() { |
| feature_list_.InitWithFeatures( |
| { |
| // Enabled |
| features::kSharedArrayBufferOnDesktop, |
| }, |
| { |
| // Disabled |
| features::kSharedArrayBuffer, |
| }); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| SharedArrayBufferOnDesktopBrowserTest, |
| kTestParams, |
| CrossOriginOpenerPolicyBrowserTest::DescribeParams); |
| |
| IN_PROC_BROWSER_TEST_P(SharedArrayBufferOnDesktopBrowserTest, |
| DesktopHasSharedArrayBuffer) { |
| CHECK(!base::FeatureList::IsEnabled(features::kSharedArrayBuffer)); |
| GURL url = https_server()->GetURL("a.test", "/empty.html"); |
| EXPECT_TRUE(NavigateToURL(shell(), url)); |
| EXPECT_EQ(false, EvalJs(current_frame_host(), "self.crossOriginIsolated")); |
| #if !BUILDFLAG(IS_ANDROID) |
| EXPECT_EQ(true, |
| EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis")); |
| #else // !BUILDFLAG(IS_ANDROID) |
| EXPECT_EQ(false, |
| EvalJs(current_frame_host(), "'SharedArrayBuffer' in globalThis")); |
| #endif // !BUILDFLAG(IS_ANDROID) |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SharedArrayBufferOnDesktopBrowserTest, |
| DesktopTransferSharedArrayBuffer) { |
| CHECK(!base::FeatureList::IsEnabled(features::kSharedArrayBuffer)); |
| GURL main_url = https_server()->GetURL("a.test", "/empty.html"); |
| GURL iframe_url = https_server()->GetURL("a.test", "/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 !BUILDFLAG(IS_ANDROID) |
| EXPECT_TRUE(ExecJs(sub_document, R"( |
| const sab = new SharedArrayBuffer(1234); |
| parent.postMessage(sab, "*"); |
| )")); |
| |
| EXPECT_EQ(1234, EvalJs(main_document, "g_sab_size")); |
| #else // !BUILDFLAG(IS_ANDROID) |
| EXPECT_FALSE(ExecJs(sub_document, R"( |
| const sab = new SharedArrayBuffer(1234); |
| parent.postMessage(sab, "*"); |
| )")); |
| #endif // !BUILDFLAG(IS_ANDROID) |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SoapByDefaultVirtualBrowsingContextGroupTest, NoHeader) { |
| const VirtualBcgAllowPopupTestCase kTestCases[] = { |
| { |
| // same-origin. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| false, |
| false, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| https_server()->GetURL("b.a.test", "/title1.html"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL("b.test", "/title1.html"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| }; |
| for (const auto& test : kTestCases) |
| RunTest(test, shell()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SoapByDefaultVirtualBrowsingContextGroupTest, |
| ToUnsafeNone) { |
| const VirtualBcgAllowPopupTestCase kTestCases[] = { |
| { |
| // same-origin. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: unsafe-none"), |
| false, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| https_server()->GetURL("b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: unsafe-none"), |
| false, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL("b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: unsafe-none"), |
| false, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| }; |
| for (const auto& test : kTestCases) |
| RunTest(test, shell()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SoapByDefaultVirtualBrowsingContextGroupTest, |
| FromUnsafeNone) { |
| const VirtualBcgAllowPopupTestCase kTestCases[] = { |
| { |
| // same-origin. |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: unsafe-none"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: unsafe-none"), |
| https_server()->GetURL("b.a.test", "/title1.html"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: unsafe-none"), |
| https_server()->GetURL("b.test", "/title1.html"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| }; |
| for (const auto& test : kTestCases) |
| RunTest(test, shell()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SoapByDefaultVirtualBrowsingContextGroupTest, |
| ToSameOriginAllowPopups) { |
| const VirtualBcgAllowPopupTestCase kTestCases[] = { |
| { |
| // same-origin. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups"), |
| false, |
| false, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| }; |
| for (const auto& test : kTestCases) |
| RunTest(test, shell()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SoapByDefaultVirtualBrowsingContextGroupTest, |
| FromSameOriginAllowPopus) { |
| const VirtualBcgAllowPopupTestCase kTestCases[] = { |
| { |
| // same-origin. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| false, |
| false, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups"), |
| https_server()->GetURL("b.a.test", "/title1.html"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin-allow-popups"), |
| https_server()->GetURL("b.test", "/title1.html"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| }; |
| for (const auto& test : kTestCases) |
| RunTest(test, shell()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SoapByDefaultVirtualBrowsingContextGroupTest, |
| ToSameOrigin) { |
| const VirtualBcgAllowPopupTestCase kTestCases[] = { |
| { |
| // same-origin. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| https_server()->GetURL("b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL("b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| }; |
| for (const auto& test : kTestCases) |
| RunTest(test, shell()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SoapByDefaultVirtualBrowsingContextGroupTest, |
| FromSameOrigin) { |
| const VirtualBcgAllowPopupTestCase kTestCases[] = { |
| { |
| // same-origin. |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-origin. |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin"), |
| https_server()->GetURL("b.a.test", "/title1.html"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| { |
| // cross-site. |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin"), |
| https_server()->GetURL("b.test", "/title1.html"), |
| true, |
| true, |
| SoapByDefaultVirtualBrowsingContextGroup, |
| }, |
| }; |
| 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(SoapByDefaultVirtualBrowsingContextGroupTest, |
| HistoryNavigation) { |
| GURL url_a = https_server()->GetURL("a.test", "/title1.html"); |
| GURL url_b = https_server()->GetURL("b.test", "/title1.html"); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), url_a)); |
| int group_1 = SoapByDefaultVirtualBrowsingContextGroup(web_contents()); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), url_b)); |
| int group_2 = SoapByDefaultVirtualBrowsingContextGroup(web_contents()); |
| |
| web_contents()->GetController().GoBack(); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| int group_3 = SoapByDefaultVirtualBrowsingContextGroup(web_contents()); |
| |
| web_contents()->GetController().GoForward(); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| int group_4 = SoapByDefaultVirtualBrowsingContextGroup(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 A2 (same virtual browsing context group). |
| // 2. A2 navigates to B3 (different virtual browsing context group). |
| // 3. B3 navigates back to A4 using the history (different virtual browsing |
| // context group). |
| // |
| // A1 and A4 must not be in the same browsing context group. |
| IN_PROC_BROWSER_TEST_P(SoapByDefaultVirtualBrowsingContextGroupTest, |
| HistoryNavigationWithPopup) { |
| GURL url_a = https_server()->GetURL("a.test", "/title1.html"); |
| GURL url_b = https_server()->GetURL("b.test", "/title1.html"); |
| |
| // Navigate to A1. |
| EXPECT_TRUE(NavigateToURL(shell(), url_a)); |
| int group_1 = SoapByDefaultVirtualBrowsingContextGroup(web_contents()); |
| |
| // A1 opens A2. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE( |
| ExecJs(current_frame_host(), JsReplace("window.open($1)", url_a))); |
| WebContents* popup = shell_observer.GetShell()->web_contents(); |
| EXPECT_TRUE(WaitForLoadStop(popup)); |
| int group_2 = SoapByDefaultVirtualBrowsingContextGroup(popup); |
| |
| // A2 navigates to B3. |
| EXPECT_TRUE(ExecJs(popup, JsReplace("location.href = $1;", url_b))); |
| EXPECT_TRUE(WaitForLoadStop(popup)); |
| int group_3 = SoapByDefaultVirtualBrowsingContextGroup(popup); |
| |
| // B3 navigates back to A4. |
| EXPECT_TRUE(ExecJs(popup, JsReplace("history.back()"))); |
| EXPECT_TRUE(WaitForLoadStop(popup)); |
| int group_4 = SoapByDefaultVirtualBrowsingContextGroup(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); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| CoopRestrictPropertiesIsParsed) { |
| GURL starting_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| // Verify that COOP: restrict-properties was parsed. |
| EXPECT_EQ(current_frame_host()->cross_origin_opener_policy(), |
| CoopRestrictProperties(url::Origin::Create(starting_page))); |
| EXPECT_FALSE( |
| current_frame_host()->GetSiteInstance()->IsCrossOriginIsolated()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| CoopRestrictPropertiesPlusCoepIsParsed) { |
| GURL starting_page( |
| https_server()->GetURL("a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties" |
| "&cross-origin-embedder-policy: require-corp")); |
| EXPECT_TRUE(NavigateToURL(shell(), starting_page)); |
| |
| // Verify that COOP: restrict-properties was parsed along COEP, and that it |
| // correctly enabled cross origin isolation. |
| EXPECT_EQ(current_frame_host()->cross_origin_opener_policy(), |
| CoopRestrictPropertiesPlusCoep(url::Origin::Create(starting_page))); |
| EXPECT_TRUE(current_frame_host()->GetSiteInstance()->IsCrossOriginIsolated()); |
| } |
| |
| class CoopRestrictPropertiesOriginTrialBrowserTest |
| : public OriginTrialBrowserTest, |
| public ::testing::WithParamInterface<bool> { |
| public: |
| CoopRestrictPropertiesOriginTrialBrowserTest() |
| : is_origin_trial_enabled(GetParam()) { |
| if (is_origin_trial_enabled) { |
| feature_list_.InitWithFeatures( |
| { |
| network::features::kCoopRestrictPropertiesOriginTrial, |
| }, // Enabled |
| {} // Disabled |
| ); |
| } else { |
| feature_list_.InitWithFeatures( |
| {}, // Enabled |
| { |
| network::features::kCoopRestrictPropertiesOriginTrial, |
| } // Disabled |
| ); |
| } |
| } |
| |
| // Origin Trials key generated with: |
| // |
| // tools/origin_trials/generate_token.py --expire-days 5000 --version 3 |
| // https://coop.security:9999 CoopRestrictProperties |
| static std::string OriginTrialToken() { |
| return "A8Yj3ElroyqJKJPrXAbAcR7e4oZZo978guRoJqwghGM0nnOI8PM8Ay1y1TRlAajef7o" |
| "CHH+lahsRWglSKSy+" |
| "Wg8AAABjeyJvcmlnaW4iOiAiaHR0cHM6Ly9jb29wLnNlY3VyaXR5Ojk5OTkiLCAiZmV" |
| "hdHVyZSI6ICJDb29wUmVzdHJpY3RQcm9wZXJ0aWVzIiwgImV4cGlyeSI6IDIxMTY1MT" |
| "cwMTd9"; |
| } |
| GURL OtherURL() { return GURL("https://a.test"); } |
| bool is_origin_trial_enabled; |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| CoopRestrictPropertiesOriginTrialBrowserTest, |
| testing::Bool()); |
| |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesOriginTrialBrowserTest, |
| CoopRestrictPropertiesValidToken) { |
| 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" |
| "Cross-Origin-Opener-Policy: restrict-properties\n" |
| "Origin-Trial: " + |
| OriginTrialToken() + "\n\n", |
| "", params->client.get()); |
| return true; |
| })); |
| EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL())); |
| EXPECT_EQ( |
| current_frame_host()->cross_origin_opener_policy().value, |
| is_origin_trial_enabled |
| ? network::mojom::CrossOriginOpenerPolicyValue::kRestrictProperties |
| : network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesOriginTrialBrowserTest, |
| CoopRestrictPropertiesTokenOriginMismatched) { |
| URLLoaderInterceptor interceptor(base::BindLambdaForTesting( |
| [&](URLLoaderInterceptor::RequestParams* params) { |
| DCHECK_EQ(params->url_request.url, OtherURL()); |
| URLLoaderInterceptor::WriteResponse( |
| "HTTP/1.1 200 OK\n" |
| "Content-type: text/html\n" |
| "Cross-Origin-Opener-Policy: restrict-properties\n" |
| "Origin-Trial: " + |
| OriginTrialToken() + "\n\n", |
| "", params->client.get()); |
| return true; |
| })); |
| EXPECT_TRUE(NavigateToURL(shell(), OtherURL())); |
| EXPECT_EQ(current_frame_host()->cross_origin_opener_policy().value, |
| network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesOriginTrialBrowserTest, |
| CoopRestrictPropertiesPlusCoepValidToken) { |
| 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" |
| "Cross-Origin-Opener-Policy: restrict-properties\n" |
| "Cross-Origin-Embedder-Policy: require-corp\n" |
| "Origin-Trial: " + |
| OriginTrialToken() + "\n\n", |
| "", params->client.get()); |
| return true; |
| })); |
| EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL())); |
| EXPECT_EQ(current_frame_host()->cross_origin_opener_policy().value, |
| is_origin_trial_enabled |
| ? network::mojom::CrossOriginOpenerPolicyValue:: |
| kRestrictPropertiesPlusCoep |
| : network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesOriginTrialBrowserTest, |
| CoopRestrictPropertiesPlusCoepTokenOriginMismatched) { |
| URLLoaderInterceptor interceptor(base::BindLambdaForTesting( |
| [&](URLLoaderInterceptor::RequestParams* params) { |
| DCHECK_EQ(params->url_request.url, OtherURL()); |
| URLLoaderInterceptor::WriteResponse( |
| "HTTP/1.1 200 OK\n" |
| "Content-type: text/html\n" |
| "Cross-Origin-Opener-Policy: restrict-properties\n" |
| "Cross-Origin-Embedder-Policy: require-corp\n" |
| "Origin-Trial: " + |
| OriginTrialToken() + "\n\n", |
| "", params->client.get()); |
| return true; |
| })); |
| EXPECT_TRUE(NavigateToURL(shell(), OtherURL())); |
| EXPECT_EQ(current_frame_host()->cross_origin_opener_policy().value, |
| network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesOriginTrialBrowserTest, |
| CoopReportOnlyRestrictPropertiesValidToken) { |
| 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" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties\n" |
| "Origin-Trial: " + |
| OriginTrialToken() + "\n\n", |
| "", params->client.get()); |
| return true; |
| })); |
| EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL())); |
| EXPECT_EQ( |
| current_frame_host()->cross_origin_opener_policy().report_only_value, |
| is_origin_trial_enabled |
| ? network::mojom::CrossOriginOpenerPolicyValue::kRestrictProperties |
| : network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesOriginTrialBrowserTest, |
| CoopReportOnlyRestrictPropertiesTokenOriginMismatched) { |
| URLLoaderInterceptor interceptor(base::BindLambdaForTesting( |
| [&](URLLoaderInterceptor::RequestParams* params) { |
| DCHECK_EQ(params->url_request.url, OtherURL()); |
| URLLoaderInterceptor::WriteResponse( |
| "HTTP/1.1 200 OK\n" |
| "Content-type: text/html\n" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties\n" |
| "Origin-Trial: " + |
| OriginTrialToken() + "\n\n", |
| "", params->client.get()); |
| return true; |
| })); |
| EXPECT_TRUE(NavigateToURL(shell(), OtherURL())); |
| EXPECT_EQ( |
| current_frame_host()->cross_origin_opener_policy().report_only_value, |
| network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesOriginTrialBrowserTest, |
| CoopReportOnlyRestrictPropertiesPlusCoepValidToken) { |
| 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" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties\n" |
| "Cross-Origin-Embedder-Policy: require-corp\n" |
| "Origin-Trial: " + |
| OriginTrialToken() + "\n\n", |
| "", params->client.get()); |
| return true; |
| })); |
| EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL())); |
| EXPECT_EQ( |
| current_frame_host()->cross_origin_opener_policy().report_only_value, |
| is_origin_trial_enabled |
| ? network::mojom::CrossOriginOpenerPolicyValue:: |
| kRestrictPropertiesPlusCoep |
| : network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone); |
| } |
| |
| IN_PROC_BROWSER_TEST_P( |
| CoopRestrictPropertiesOriginTrialBrowserTest, |
| CoopReportOnlyRestrictPropertiesPlusCoepTokenOriginMismatched) { |
| URLLoaderInterceptor interceptor(base::BindLambdaForTesting( |
| [&](URLLoaderInterceptor::RequestParams* params) { |
| DCHECK_EQ(params->url_request.url, OtherURL()); |
| URLLoaderInterceptor::WriteResponse( |
| "HTTP/1.1 200 OK\n" |
| "Content-type: text/html\n" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties\n" |
| "Cross-Origin-Embedder-Policy: require-corp\n" |
| "Origin-Trial: " + |
| OriginTrialToken() + "\n\n", |
| "", params->client.get()); |
| return true; |
| })); |
| EXPECT_TRUE(NavigateToURL(shell(), OtherURL())); |
| EXPECT_EQ( |
| current_frame_host()->cross_origin_opener_policy().report_only_value, |
| network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone); |
| } |
| |
| // Verify that a simple navigation from a regular page to a COOP: |
| // restrict-properties page puts the two pages in different BrowsingInstances in |
| // the same CoopRelatedGroup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| NavigateNonCoopToCoopRp) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> final_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(final_si.get())); |
| EXPECT_TRUE(initial_si->IsCoopRelatedSiteInstance(final_si.get())); |
| } |
| |
| // Verify that a simple navigation from a COOP: restrict-properties page to a |
| // regular page puts the two pages in BrowsingInstances in the same |
| // CoopRelatedGroup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| NavigateCoopRpToNonCoop) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| scoped_refptr<SiteInstanceImpl> final_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(final_si.get())); |
| EXPECT_TRUE(initial_si->IsCoopRelatedSiteInstance(final_si.get())); |
| } |
| |
| // Verify that a simple navigation from a COOP: restrict-properties page to |
| // another same-origin COOP: restrict-properties page puts the two pages in the |
| // same SiteInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| NavigateCoopRpToCoopRpSameOrigin) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL coop_rp_page_2(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties&1")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page_2)); |
| scoped_refptr<SiteInstanceImpl> final_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| // BFCache can force a proactive BrowsingInstance swap, since we're not |
| // dealing with popups. |
| if (IsBackForwardCacheEnabled()) { |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(final_si.get())); |
| EXPECT_FALSE(initial_si->IsCoopRelatedSiteInstance(final_si.get())); |
| } else { |
| EXPECT_EQ(initial_si.get(), final_si.get()); |
| } |
| } |
| |
| // Verify that a simple navigation from a COOP: restrict-properties page to |
| // another cross-origin COOP: restrict-properties page puts the two pages in |
| // different SiteInstances and BrowsingInstances in the same CoopRelatedGroup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| NavigateCoopRpToCoopRpCrossOrigin) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL coop_rp_page_2(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page_2)); |
| scoped_refptr<SiteInstanceImpl> final_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| EXPECT_NE(initial_si, final_si); |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(final_si.get())); |
| EXPECT_TRUE(initial_si->IsCoopRelatedSiteInstance(final_si.get())); |
| } |
| |
| // Verify that a simple navigation from a COOP: restrict-properties page to |
| // another same-origin COOP: restrict-properties page that also sets COEP puts |
| // the two pages in different SiteInstances and BrowsingInstances in the same |
| // CoopRelatedGroup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| NavigateCoopRpToCoopRpPlusCoep) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL coop_rp_with_coep_page( |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "cross-origin-opener-policy: restrict-properties&" |
| "cross-origin-embedder-policy: require-corp")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_with_coep_page)); |
| scoped_refptr<SiteInstanceImpl> final_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| EXPECT_NE(initial_si, final_si); |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(final_si.get())); |
| EXPECT_TRUE(initial_si->IsCoopRelatedSiteInstance(final_si.get())); |
| } |
| |
| // Verify that a navigation from a regular page to a COOP: restrict-properties |
| // and then to another regular page reuses the initial BrowsingInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| NavigateNonCoopToCoopRpToNonCoop) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page_2(https_server()->GetURL("a.test", "/title2.html")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page_2)); |
| scoped_refptr<SiteInstanceImpl> final_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| EXPECT_EQ(initial_si.get(), final_si.get()); |
| } |
| |
| // Verify that a navigation from a security sensitive page to a COOP: |
| // restrict-properties changes the CoopRelatedGroup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| NavigateWebUiToCoopRp) { |
| GURL webui_page("chrome://ukm"); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), webui_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> final_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| EXPECT_NE(initial_si.get(), final_si.get()); |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(final_si.get())); |
| EXPECT_FALSE(initial_si->IsCoopRelatedSiteInstance(final_si.get())); |
| } |
| |
| // Verify that a popup opened with matching COOP: restrict-properties value and |
| // origin stays in the same SiteInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| NoSwapForMatchingPopupAndMainPage) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL coop_rp_page_2(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties&1")); |
| |
| // Start with a page that sets COOP: restrict-properties. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> main_page_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| // Open a same-origin page that also sets COOP: restrict-properties. |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_rp_page_2, "")->web_contents()); |
| scoped_refptr<SiteInstanceImpl> popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_EQ(main_page_si.get(), popup_si.get()); |
| EXPECT_TRUE(main_page_si->IsRelatedSiteInstance(popup_si.get())); |
| EXPECT_TRUE(main_page_si->IsCoopRelatedSiteInstance(popup_si.get())); |
| } |
| |
| // Verify that a popup in a different BrowsingInstance within the same |
| // CoopRelatedGroup can come back to the main page SiteInstance if navigating to |
| // a compatible page. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| ReuseBrowsingInstanceInCoopGroupPopupAndMainPage) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // We start with a simple page which opens a COOP: restrict-properties popup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| scoped_refptr<SiteInstanceImpl> main_page_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_rp_page, "")->web_contents()); |
| scoped_refptr<SiteInstanceImpl> initial_popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_NE(main_page_si.get(), initial_popup_si.get()); |
| EXPECT_FALSE(main_page_si->IsRelatedSiteInstance(initial_popup_si.get())); |
| EXPECT_TRUE(main_page_si->IsCoopRelatedSiteInstance(initial_popup_si.get())); |
| |
| // Navigate the popup to the same url as the main page. It should reuse the |
| // main page BrowsingInstance and SiteInstance. |
| ASSERT_TRUE(NavigateToURL(popup_window, regular_page)); |
| scoped_refptr<SiteInstanceImpl> final_popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_EQ(main_page_si.get(), final_popup_si.get()); |
| } |
| |
| // Verify that a popup a in a different BrowsingInstance within the same |
| // CoopRelatedGroup can come back to the main page SiteInstance if navigating to |
| // a compatible page, initiated by the renderer. |
| IN_PROC_BROWSER_TEST_P( |
| CoopRestrictPropertiesBrowserTest, |
| ReuseBrowsingInstanceInCoopGroupPopupAndMainPageRenderInitiated) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // We start with a simple page which opens a COOP: restrict-properties popup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| scoped_refptr<SiteInstanceImpl> main_page_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_rp_page, "")->web_contents()); |
| scoped_refptr<SiteInstanceImpl> initial_popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_NE(main_page_si.get(), initial_popup_si.get()); |
| EXPECT_FALSE(main_page_si->IsRelatedSiteInstance(initial_popup_si.get())); |
| EXPECT_TRUE(main_page_si->IsCoopRelatedSiteInstance(initial_popup_si.get())); |
| |
| // Navigate the popup to the same url as the main page, from the renderer. It |
| // should reuse the main page BrowsingInstance and SiteInstance. |
| ASSERT_TRUE(ExecJs(popup_window->GetPrimaryMainFrame(), |
| JsReplace("location.href = $1", regular_page))); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| scoped_refptr<SiteInstanceImpl> final_popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_EQ(main_page_si.get(), final_popup_si.get()); |
| } |
| |
| // Verify that two pages in different BrowsingInstances within the same |
| // CoopRelatedGroup can both navigate to a third page, and end up in the same |
| // SiteInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| ReuseBrowsingInstanceInCoopGroupTwoPopups) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL coop_rp_page_2(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| // We start with a COOP: restrict-properties page which opens a popup to a |
| // cross-origin COOP: restrict-properties page. They end up in different |
| // BrowsingInstances but in the same CoopRelatedGroup. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> initial_main_page_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_rp_page_2, "")->web_contents()); |
| scoped_refptr<SiteInstanceImpl> initial_popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_NE(initial_main_page_si.get(), initial_popup_si.get()); |
| EXPECT_FALSE( |
| initial_main_page_si->IsRelatedSiteInstance(initial_popup_si.get())); |
| EXPECT_TRUE( |
| initial_main_page_si->IsCoopRelatedSiteInstance(initial_popup_si.get())); |
| |
| // Navigate both COOP: restrict-properties pages to the same unsafe-none page. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ASSERT_TRUE(NavigateToURL(popup_window, regular_page)); |
| |
| // They should both use the same newly created BrowsingInstance and |
| // SiteInstance. |
| scoped_refptr<SiteInstanceImpl> final_main_page_si( |
| current_frame_host()->GetSiteInstance()); |
| scoped_refptr<SiteInstanceImpl> final_popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_EQ(final_main_page_si.get(), final_popup_si.get()); |
| } |
| |
| // Verify that CSP: sandbox is taken into account for the common coop origin |
| // computation. |
| // TODO(https://crbug.com/1385827): This is not currently the case. Enable once |
| // COOP is bundled with the appropriate origin. |
| IN_PROC_BROWSER_TEST_P( |
| CoopRestrictPropertiesBrowserTest, |
| DISABLED_DoNotReuseBrowsingInstanceInCoopGroupOpaqueOrigin) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL coop_rp_and_csp_page( |
| https_server()->GetURL("a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties&" |
| "Content-Security-Policy: sandbox")); |
| |
| // We start with a COOP: restrict-properties page which opens a popup to a |
| // same-origin COOP: restrict-properties page, but which sets CSP, making its |
| // origin opaque. They should end up in different BrowsingInstances in the |
| // same CoopRelatedGroup. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> main_page_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_rp_and_csp_page, "") |
| ->web_contents()); |
| scoped_refptr<SiteInstanceImpl> popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_NE(main_page_si.get(), popup_si.get()); |
| EXPECT_FALSE(main_page_si->IsRelatedSiteInstance(popup_si.get())); |
| EXPECT_TRUE(main_page_si->IsCoopRelatedSiteInstance(popup_si.get())); |
| |
| // The recorded common COOP origin should differ, because CSP forces an opaque |
| // origin. |
| ASSERT_EQ(main_page_si->GetCommonCoopOrigin(), |
| popup_si->GetCommonCoopOrigin()); |
| } |
| |
| // Verify that active WebContents counting works across different |
| // BrowsingInstances in the same CoopRelatedGroup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| ActiveWebContentsCountInCoopRelatedGroup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL coop_so_page( |
| https_server()->GetURL("a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: same-origin")); |
| |
| // We start with a simple page which opens a COOP: restrict-properties popup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| scoped_refptr<SiteInstanceImpl> main_page_si( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_EQ(1u, main_page_si->GetRelatedActiveContentsCount()); |
| |
| // Open a popup in the same BrowsingInstance and SiteInstance. |
| WebContentsImpl* first_popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), regular_page, "")->web_contents()); |
| scoped_refptr<SiteInstanceImpl> first_popup_si( |
| first_popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_EQ(2u, main_page_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(2u, first_popup_si->GetRelatedActiveContentsCount()); |
| |
| // Open a popup in the same CoopRelatedGroup in another BrowsingInstance. |
| WebContentsImpl* second_popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_rp_page, "")->web_contents()); |
| scoped_refptr<SiteInstanceImpl> second_popup_si( |
| second_popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_EQ(3u, main_page_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(3u, first_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(3u, second_popup_si->GetRelatedActiveContentsCount()); |
| |
| // Have each of these popups open a new COOP: restrict-properties popup. |
| WebContentsImpl* third_popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(first_popup_window->GetPrimaryMainFrame(), coop_rp_page, "") |
| ->web_contents()); |
| scoped_refptr<SiteInstanceImpl> third_popup_si( |
| third_popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| WebContentsImpl* fourth_popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(second_popup_window->GetPrimaryMainFrame(), coop_rp_page, "") |
| ->web_contents()); |
| scoped_refptr<SiteInstanceImpl> fourth_popup_si( |
| fourth_popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_EQ(5u, main_page_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, first_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, second_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, third_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, fourth_popup_si->GetRelatedActiveContentsCount()); |
| |
| // Open an extra popup from the root, that does not belong to the COOP group, |
| // and verify that the count is not increased. |
| WebContentsImpl* fifth_popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_so_page, "")->web_contents()); |
| scoped_refptr<SiteInstanceImpl> fifth_popup_si( |
| fifth_popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_EQ(5u, main_page_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, first_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, second_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, third_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, fourth_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(1u, fifth_popup_si->GetRelatedActiveContentsCount()); |
| |
| fifth_popup_window->Close(); |
| EXPECT_EQ(5u, main_page_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, first_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, second_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, third_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(5u, fourth_popup_si->GetRelatedActiveContentsCount()); |
| |
| // Close all the popups one by one and verify that the web contents decreases |
| // accordingly. Purposefully close the middle popups before the leaf popups, |
| // to verify counting works without the root window. |
| first_popup_window->Close(); |
| EXPECT_EQ(4u, main_page_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(4u, second_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(4u, third_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(4u, fourth_popup_si->GetRelatedActiveContentsCount()); |
| |
| second_popup_window->Close(); |
| EXPECT_EQ(3u, main_page_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(3u, third_popup_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(3u, fourth_popup_si->GetRelatedActiveContentsCount()); |
| |
| third_popup_window->Close(); |
| EXPECT_EQ(2u, main_page_si->GetRelatedActiveContentsCount()); |
| EXPECT_EQ(2u, fourth_popup_si->GetRelatedActiveContentsCount()); |
| |
| fourth_popup_window->Close(); |
| EXPECT_EQ(1u, main_page_si->GetRelatedActiveContentsCount()); |
| } |
| |
| // Verify that the COOP: restrict-properties origin is inherited by a subframe. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| CommonCoopOriginInheritedBySubframe) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| ASSERT_EQ(current_frame_host()->cross_origin_opener_policy(), |
| CoopRestrictProperties(url::Origin::Create(coop_rp_page))); |
| |
| // Create a cross origin child frame. |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| EXPECT_EQ(iframe_rfh->GetSiteInstance()->GetCommonCoopOrigin(), |
| current_frame_host()->GetSiteInstance()->GetCommonCoopOrigin()); |
| } |
| |
| // Verify that the COOP: restrict-properties origin is inherited by a subframe |
| // even when it specifies its own COOP header, which should be ignored. |
| IN_PROC_BROWSER_TEST_P( |
| CoopRestrictPropertiesBrowserTest, |
| CommonCoopOriginInheritedBySubframeOverridesIgnoredCoopHeader) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL coop_rp_page_2(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| ASSERT_EQ(current_frame_host()->cross_origin_opener_policy(), |
| CoopRestrictProperties(url::Origin::Create(coop_rp_page))); |
| |
| // Create cross origin child frame. |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| coop_rp_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| EXPECT_EQ(iframe_rfh->GetSiteInstance()->GetCommonCoopOrigin(), |
| current_frame_host()->GetSiteInstance()->GetCommonCoopOrigin()); |
| } |
| |
| // Verify that the COOP: restrict-properties origin is inherited by a subframe |
| // even when it is in a popup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| CommonCoopOriginInheritedBySubframeInPopup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| |
| // Start by opening a popup to a COOP: restrict-properties page from a regular |
| // page. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_rp_page, "")->web_contents()); |
| RenderFrameHostImpl* main_popup_rfh = popup_window->GetPrimaryMainFrame(); |
| EXPECT_NE(current_frame_host()->GetSiteInstance(), |
| main_popup_rfh->GetSiteInstance()); |
| EXPECT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| main_popup_rfh->GetSiteInstance())); |
| EXPECT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| main_popup_rfh->GetSiteInstance())); |
| |
| // Now create a cross origin child frame in the popup. |
| ASSERT_TRUE(ExecJs(main_popup_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* iframe_rfh = |
| main_popup_rfh->child_at(0)->current_frame_host(); |
| EXPECT_EQ(iframe_rfh->GetSiteInstance()->GetCommonCoopOrigin(), |
| main_popup_rfh->GetSiteInstance()->GetCommonCoopOrigin()); |
| } |
| |
| // This test verifies that navigating to a COOP: restrict-properties page and |
| // back uses the appropriate BrowsingInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| HistoryNavigationBackToCoopRpFromNonCoop) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> coop_rp_si( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_NE(initial_si.get(), coop_rp_si.get()); |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(coop_rp_si.get())); |
| EXPECT_TRUE(initial_si->IsCoopRelatedSiteInstance(coop_rp_si.get())); |
| |
| // Navigate back. The correct SiteInstance should be reused. |
| web_contents()->GetController().GoBack(); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| scoped_refptr<SiteInstanceImpl> back_si( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_EQ(initial_si.get(), back_si.get()); |
| } |
| |
| // This test verifies that navigating to a regular page from a COOP: |
| // restrict-properties page and then back, puts the initial page in the |
| // appropriate BrowsingInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| HistoryNavigationBackToNonCoopFromCoopRp) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| scoped_refptr<SiteInstanceImpl> regular_si( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_NE(initial_si.get(), regular_si.get()); |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(regular_si.get())); |
| EXPECT_TRUE(initial_si->IsCoopRelatedSiteInstance(regular_si.get())); |
| |
| // Navigate the popup back. The correct SiteInstance should be reused. |
| web_contents()->GetController().GoBack(); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| scoped_refptr<SiteInstanceImpl> back_si( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_EQ(initial_si.get(), back_si.get()); |
| } |
| |
| // This test verifies that a popup initially on a regular page navigates to a |
| // COOP: restrict-properties page and back gets put in the appropriate |
| // BrowsingInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| HistoryNavigationBackToCoopRpFromNonCoopInPopup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // We start with a simple page which opens a popup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| scoped_refptr<SiteInstanceImpl> main_page_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), regular_page, "")->web_contents()); |
| scoped_refptr<SiteInstanceImpl> initial_popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| ASSERT_EQ(main_page_si.get(), initial_popup_si.get()); |
| |
| // Navigate the popup to a COOP: restrict-properties page and then back. It |
| // should reuse the original SiteInstance. |
| ASSERT_TRUE(NavigateToURL(popup_window, coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> second_popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_NE(second_popup_si.get(), initial_popup_si.get()); |
| EXPECT_FALSE(second_popup_si->IsRelatedSiteInstance(initial_popup_si.get())); |
| EXPECT_TRUE( |
| second_popup_si->IsCoopRelatedSiteInstance(initial_popup_si.get())); |
| |
| popup_window->GetController().GoBack(); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| scoped_refptr<SiteInstanceImpl> back_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_EQ(initial_popup_si.get(), back_si.get()); |
| } |
| |
| // This test verifies that a popup initially on a COOP: restrict-properties page |
| // that navigates to a regular page and then back, gets put in the appropriate |
| // original BrowsingInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| HistoryNavigationBackToNonCoopFromCoopRpInPopup) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| // We start with a COOP: restrict-properties page which opens a popup to a |
| // same-origin COOP: restrict-properties page. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> main_page_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_rp_page, "")->web_contents()); |
| scoped_refptr<SiteInstanceImpl> initial_popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| ASSERT_EQ(main_page_si.get(), initial_popup_si.get()); |
| |
| // Navigate the popup to a regular page and then back. It should reuse the |
| // original SiteInstance. |
| ASSERT_TRUE(NavigateToURL(popup_window, regular_page)); |
| scoped_refptr<SiteInstanceImpl> second_popup_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_NE(second_popup_si.get(), initial_popup_si.get()); |
| EXPECT_FALSE(second_popup_si->IsRelatedSiteInstance(initial_popup_si.get())); |
| EXPECT_TRUE( |
| second_popup_si->IsCoopRelatedSiteInstance(initial_popup_si.get())); |
| |
| popup_window->GetController().GoBack(); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| scoped_refptr<SiteInstanceImpl> back_si( |
| popup_window->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_EQ(initial_popup_si.get(), back_si.get()); |
| } |
| |
| // This test verifies that the reload of a COOP: restrict-properties page ends |
| // up in the appropriate BrowsingInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| HistoryNavigationReloadOfCoopRp) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start on a COOP: restrict-properties page. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| // Reload the page. It should end up in the same SiteInstance. |
| ReloadBlockUntilNavigationsComplete(shell(), 1); |
| scoped_refptr<SiteInstanceImpl> final_si( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_EQ(initial_si.get(), final_si.get()); |
| } |
| |
| // This test verifies that the failed reload of a COOP: restrict-properties page |
| // ends up in the appropriate BrowsingInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| HistoryNavigationFailedReloadOfCoopRp) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start on a COOP: restrict-properties page. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| // Simulate being offline by failing all network requests. |
| auto url_loader_interceptor = |
| std::make_unique<content::URLLoaderInterceptor>(base::BindRepeating( |
| [](content::URLLoaderInterceptor::RequestParams* params) { |
| network::URLLoaderCompletionStatus status; |
| status.error_code = net::Error::ERR_CONNECTION_FAILED; |
| params->client->OnComplete(status); |
| return true; |
| })); |
| |
| // Reload the page. It will end up as an error page. |
| ReloadBlockUntilNavigationsComplete(shell(), 1); |
| scoped_refptr<SiteInstanceImpl> error_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| // Error pages have COOP: unsafe-none, so it should end up in a different |
| // BrowsingInstance in the same CoopRelatedGroup. |
| EXPECT_NE(initial_si.get(), error_si.get()); |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(error_si.get())); |
| EXPECT_TRUE(initial_si->IsCoopRelatedSiteInstance(error_si.get())); |
| } |
| |
| // This test verifies that a back navigation supposed to be in the same |
| // CoopRelatedGroup, but that ends up in a different one due a change in header |
| // is handled properly. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| HistoryNavigationsBackToChangedCoopHeader) { |
| GURL changing_coop_page(https_server()->GetURL( |
| "a.test", "/serve-different-coop-on-second-navigation")); |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| // Start on a changing COOP headers page. It is first served with COOP: |
| // restrict-properties. |
| ASSERT_TRUE(NavigateToURL(shell(), changing_coop_page)); |
| scoped_refptr<SiteInstanceImpl> initial_si( |
| current_frame_host()->GetSiteInstance()); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| scoped_refptr<SiteInstanceImpl> intermediate_si( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(intermediate_si.get())); |
| EXPECT_TRUE(initial_si->IsCoopRelatedSiteInstance(intermediate_si.get())); |
| |
| // When going back, the page is now served with COOP: same-origin. This should |
| // force a different CoopRelatedGroup, and not only a different |
| // BrowsingInstance. |
| web_contents()->GetController().GoBack(); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| scoped_refptr<SiteInstanceImpl> final_si( |
| current_frame_host()->GetSiteInstance()); |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(final_si.get())); |
| EXPECT_FALSE(initial_si->IsCoopRelatedSiteInstance(final_si.get())); |
| } |
| |
| // This test verifies that after a simple page opens a popup to a COOP: |
| // restrict-properties page, we have two cross-BrowsingInstance proxies. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| SimpleCrossBrowsingInstanceProxy) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start by opening a popup to a COOP: rp page from a regular page. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), coop_rp_page, ""); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Verify that the opener and openee frames exist as proxies in each other's |
| // SiteInstanceGroup. Note that the actual sites are the same, but they exist |
| // in different SiteInstanceGroups because they are in different |
| // BrowsingInstances. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(popup_rfh->frame_tree_node())); |
| } |
| |
| // This test verifies that a new iframe in a page that opened a popup in a |
| // different BrowsingInstance in the same CoopRelatedGroup is not visible to |
| // the popup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| SubframeInMainPageCrossBrowsingInstanceProxy) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| |
| // Start with a page that opens a COOP: restrict-properties popup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), coop_rp_page, ""); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Now add a cross-origin iframe in the main page. |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| |
| // The main frame should have proxies in both the popup's and iframe's |
| // SiteInstanceGroup. The iframe should not have a proxy in the popup's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // The popup should only have a proxy in the main frame's SiteInstanceGroup, |
| // but not the iframe's SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site C ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " C = https://a.test/", |
| DepictFrameTree(popup_rfh->frame_tree_node())); |
| } |
| |
| // This test verifies that a new iframe in a popup that lives in a different |
| // BrowsingInstance in the same CoopRelatedGroup has visibility of the opener |
| // frame and of no other frame in the other BrowsingInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| SubframeInPopupCrossBrowsingInstanceProxy) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start with a page with a cross-origin iframe. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* main_page_iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| // Verify that we have simple parent/child proxies. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| |
| // Now open a COOP: restrict-properties popup in another BrowsingInstance in |
| // the same CoopRelatedGroup. |
| WebContentsImpl* popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), coop_rp_page, ""); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // The main frame should have proxies in both the popup's and iframe's |
| // SiteInstanceGroups. The iframe should not have a proxy in the popup's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // The popup should only have a proxy in the main frame's SiteInstanceGroup, |
| // but not the iframe's SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site C ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " C = https://a.test/", |
| DepictFrameTree(popup_rfh->frame_tree_node())); |
| |
| // Now create a cross-origin subframe in the popup. We reuse the same url as |
| // for the main page's iframe, but it should not matter since they are in |
| // different BrowsingInstances. |
| ASSERT_TRUE(ExecJs(popup_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_iframe_rfh = |
| popup_rfh->child_at(0)->current_frame_host(); |
| ASSERT_FALSE(main_page_iframe_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_iframe_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| main_page_iframe_rfh->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_iframe_rfh->GetSiteInstance())); |
| |
| // The popup's iframe should only have a proxy in its parent's |
| // SiteInstanceGroup. The popup's iframe's SiteInstanceGroup should have |
| // proxies for the parent frame and the opener, but not the opener's iframe. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C D\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://a.test/\n" |
| " D = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site C ------------ proxies for A D\n" |
| " +--Site D ------- proxies for C\n" |
| "Where A = https://a.test/\n" |
| " C = https://a.test/\n" |
| " D = https://b.test/", |
| DepictFrameTree(popup_rfh->frame_tree_node())); |
| } |
| |
| // This test verifies that a subframe opening a popup in another |
| // BrowsingInstance in the same CoopRelatedGroup gets the appropriate proxies. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| SubframeOpenerCrossBrowsingInstanceProxy) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start with a page with a cross-origin iframe. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| // Verify that we have simple parent/child proxies. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| |
| // Now open a COOP: restrict-properties popup from the iframe. |
| WebContentsImpl* popup_window = |
| OpenPopupAndWaitForInitialRFHDeletion(iframe_rfh, coop_rp_page, ""); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(iframe_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE(iframe_rfh->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // The main frame should have proxies in the iframe and the popup's |
| // SiteInstanceGroup. The popup cannot reach the main frame, but |
| // we still need a main frame proxy to have the iframe proxy, which cannot |
| // exist by itself. The iframe should have a proxy in the main frame's and the |
| // popup's SiteInstanceGroups. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " +--Site B ------- proxies for A C\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // The popup should have a proxy in the iframe's SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site C ------------ proxies for B\n" |
| "Where B = https://b.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(popup_rfh->frame_tree_node())); |
| } |
| |
| // This test verifies that a popup opened from a popup already in a different |
| // BrowsingInstance but same CoopRelatedGroup as its opener, cannot see its |
| // opener's opener. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| ChainedPopupsCrossBrowsingInstanceProxies) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL coop_rp_page_2(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start with a regular page that opens a COOP: restrict-properties popup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* first_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), coop_rp_page, ""); |
| RenderFrameHostImpl* first_popup_rfh = |
| first_popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| |
| // Verify that the opener and openee frames exist as proxies in each other's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| |
| // Open another popup from the first popup. The three pages live in different |
| // BrowsingInstances. |
| WebContentsImpl* second_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| first_popup_rfh, coop_rp_page_2, ""); |
| RenderFrameHostImpl* second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| ASSERT_FALSE(first_popup_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE(first_popup_rfh->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| |
| // The main frame should not have a proxy in the second popup's |
| // SiteInstanceGroup and vice versa. Only the first popup should have two |
| // proxies, one in the main frame's and one in the second popup's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A C\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| EXPECT_EQ( |
| " Site C ------------ proxies for B\n" |
| "Where B = https://a.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| |
| // Verify that it is not possible for the second popup to reach the main page, |
| // as means of accessing it should be restricted. |
| std::string result = |
| EvalJs(second_popup_rfh, |
| "try { window.opener.opener } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| } |
| |
| // This test verifies that a new popup opened from a popup in the same |
| // BrowsingInstance will have visibility of all its BrowsingInstance frames, but |
| // will only have visibility of the direct opener frame in a different |
| // BrowsingInstance in the same CoopRelatedGroup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| ChainedPopupsMixedBrowsingInstanceProxies) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| |
| // Start with a COOP: restrict-properties page that opens a regular popup. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| WebContentsImpl* first_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), regular_page, ""); |
| RenderFrameHostImpl* first_popup_rfh = |
| first_popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| |
| // Verify that the opener and openee frames exist as proxies in each other's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| |
| // Open a cross-origin popup from the first popup. It should live in a |
| // different SiteInstance in the same BrowsingInstance. |
| WebContentsImpl* second_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| first_popup_rfh, regular_page_2, ""); |
| RenderFrameHostImpl* second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE(first_popup_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| |
| // The original frame should have proxies in the first and second popup's |
| // SiteInstanceGroups, because they can respectively use opener and |
| // opener.opener to reach the original frame. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // The first popup frame should have proxies in the original frame's and the |
| // second popup's SiteInstanceGroups, which can both reach it. |
| EXPECT_EQ( |
| " Site B ------------ proxies for A C\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| // Finally the second popup frame should only have a proxy in the first |
| // popup's SiteInstanceGroup, because the original frame has no way to reach |
| // it. |
| EXPECT_EQ( |
| " Site C ------------ proxies for B\n" |
| "Where B = https://a.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| } |
| |
| // Allows waiting until a frame name change is effective in the Browser process. |
| class FrameNameChangedWaiter : public WebContentsObserver { |
| public: |
| explicit FrameNameChangedWaiter(WebContents* web_contents, |
| RenderFrameHostImpl* frame, |
| const std::string& expected_name) |
| : WebContentsObserver(web_contents), |
| frame_(frame), |
| expected_name_(expected_name) {} |
| |
| // This will wait until the given frame, in the given WebContents, changes its |
| // name to the expected name, all given during construction. |
| void Wait() { run_loop_.Run(); } |
| |
| private: |
| void FrameNameChanged(RenderFrameHost* render_frame_host, |
| const std::string& name) override { |
| if (render_frame_host == frame_.get() && name == expected_name_) { |
| run_loop_.Quit(); |
| } |
| } |
| |
| raw_ptr<RenderFrameHostImpl> frame_; |
| std::string expected_name_; |
| base::RunLoop run_loop_; |
| }; |
| |
| // This test verifies that proxies usually created to support named targeting |
| // are not created for cross-BrowsingInstance frames. |
| // TODO(https://crbug.com/1370357): This test will likely need to change if we |
| // implement per-BrowsingInstance names. In that case, named targeting would be |
| // possible using the per-BrowsingContextGroup names, and proxies should be |
| // created. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| NamedTargetingCrossBrowsingInstanceProxies) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start with a regular page, with a cross-origin subframe. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| |
| // Now open a COOP: restrict-properties popup with a name. The name should be |
| // cleared and trigger no extra proxy creation. |
| WebContentsImpl* popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), coop_rp_page, "test_name"); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Verify that the popup frame is not proxied in the iframe's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site C ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " C = https://a.test/", |
| DepictFrameTree(popup_rfh->frame_tree_node())); |
| |
| // Manually update the popup name. By the time the WebContentsObserver gets |
| // notified of a frame name change, we've run the proxy creation code, so this |
| // should be enough to wait for. |
| FrameNameChangedWaiter frame_name_changed(popup_window, popup_rfh, |
| "another_name"); |
| ASSERT_TRUE(ExecJs(popup_rfh, "window.name = 'another_name';")); |
| frame_name_changed.Wait(); |
| |
| // No extra proxy should be created when a name is set. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site C ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " C = https://a.test/", |
| DepictFrameTree(popup_rfh->frame_tree_node())); |
| } |
| |
| // This test verifies that proxies are created on demand to support postMessage |
| // event.source, even cross-BrowsingInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| PostMessageProxiesCrossBrowsingInstance) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| |
| // Start from a regular page and open a COOP: restrict-properties popup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), coop_rp_page, ""); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Verify that the opener and openee frames exist as proxies in each other's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(popup_rfh->frame_tree_node())); |
| |
| // Add a cross-origin iframe to the popup. |
| ASSERT_TRUE(ExecJs(popup_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* iframe_rfh = |
| popup_rfh->child_at(0)->current_frame_host(); |
| |
| // The iframe can see the original frame via parent.opener, but there should |
| // be no proxy for the iframe in the original frame's SiteInstanceGroup, |
| // because the original frame should not be able to access it at this point. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A C\n" |
| " +--Site C ------- proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(popup_rfh->frame_tree_node())); |
| |
| // Now send a postMessage from the iframe to the main frame, and wait for it |
| // to be received. |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| window.future_message = new Promise(r => { |
| onmessage = (event) => { |
| if (event.data == 'test') { |
| window.post_message_source = event.source; |
| r(); |
| } |
| } |
| }); 0;)")); // This avoids waiting on the promise right now. |
| ASSERT_TRUE(ExecJs(iframe_rfh, "window.top.opener.postMessage('test', '*')")); |
| ASSERT_TRUE(ExecJs(current_frame_host(), "window.future_message")); |
| |
| // Verify that an iframe proxy was created in the main frame's |
| // SiteInstanceGroup to support event.source. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A C\n" |
| " +--Site C ------- proxies for A B\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/\n" |
| " C = https://b.test/", |
| DepictFrameTree(popup_rfh->frame_tree_node())); |
| |
| // Finally postMessage to event.source to make sure the proxy is functional. |
| ASSERT_TRUE(ExecJs(iframe_rfh, R"( |
| window.future_message = new Promise(r => { |
| onmessage = (event) => { |
| if (event.data == 'test') r(); |
| } |
| }); 0;)")); // This avoids waiting on the promise right now. |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| "window.post_message_source.postMessage('test', '*')")); |
| ASSERT_TRUE(ExecJs(iframe_rfh, "window.future_message")); |
| } |
| |
| // This test verifies that proxies are created on demand to support postMessage |
| // event.source, even cross-BrowsingInstance, even when the source is an iframe |
| // for which the target frame's SiteInstanceGroup does not have a main frame |
| // proxy yet. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| SubframePostMessageProxiesCrossBrowsingInstance) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL regular_page_3(https_server()->GetURL("c.test", "/title1.html")); |
| |
| // Start from a COOP: restrict-properties opening a regular popup. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| WebContentsImpl* first_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), regular_page, ""); |
| RenderFrameHostImpl* first_popup_rfh = |
| first_popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| |
| // Verify that the opener and openee frames exist as proxies in each other's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| |
| // Then open a popup from the popup, in the same BrowsingInstance and add |
| // a cross-origin iframe to it. This setup makes sure that we have an iframe |
| // and a main frame that are unknown to the main page. |
| WebContentsImpl* second_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| first_popup_rfh, regular_page_2, ""); |
| RenderFrameHostImpl* second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(first_popup_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| |
| ASSERT_TRUE(ExecJs(second_popup_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_3))); |
| ASSERT_TRUE(WaitForLoadStop(second_popup_window)); |
| RenderFrameHostImpl* iframe_rfh = |
| second_popup_rfh->child_at(0)->current_frame_host(); |
| |
| // The main frame should have proxies in the first popup's, second popup's and |
| // iframe's SiteInstanceGroups. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C D\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/\n" |
| " C = https://b.test/\n" |
| " D = https://c.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // The first popup should have proxies in the main frame's, second popup's and |
| // iframe's SiteInstanceGroups. |
| EXPECT_EQ( |
| " Site B ------------ proxies for A C D\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/\n" |
| " C = https://b.test/\n" |
| " D = https://c.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| // The second popup should have proxies in the first popup's and iframe's |
| // SiteInstanceGroups. The iframe popup should have proxies in the first and |
| // second popup's SiteInstanceGroup. Note that the main frame does not know |
| // about the second popup nor its iframe. |
| EXPECT_EQ( |
| " Site C ------------ proxies for B D\n" |
| " +--Site D ------- proxies for B C\n" |
| "Where B = https://a.test/\n" |
| " C = https://b.test/\n" |
| " D = https://c.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| |
| // Now send a postMessage from the iframe to the main frame, and wait for it |
| // to be received. |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| window.future_message = new Promise(r => { |
| onmessage = (event) => { |
| if (event.data == 'test') r(); |
| } |
| }); 0;)")); // This avoids waiting on the promise right now. |
| ASSERT_TRUE( |
| ExecJs(iframe_rfh, "window.top.opener.opener.postMessage('test', '*')")); |
| ASSERT_TRUE(ExecJs(current_frame_host(), "window.future_message")); |
| |
| // Verify that an iframe proxy and a second popup proxy were created in the |
| // main frame's SiteInstanceGroup to support event.source, and to make sure |
| // the iframe proxy does not float around without a main frame proxy. |
| EXPECT_EQ( |
| " Site C ------------ proxies for A B D\n" |
| " +--Site D ------- proxies for A B C\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/\n" |
| " C = https://b.test/\n" |
| " D = https://c.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| } |
| |
| // Smoke test for the case where a proxy for a given subframe is created before |
| // other subframe proxies, that might be below it in the indexed order. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| SubframesProxiesInWrongOrderSmokeTest) { |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page(https_server()->GetURL("b.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("c.test", "/title1.html")); |
| GURL regular_page_3(https_server()->GetURL("d.test", "/title1.html")); |
| GURL regular_page_4(https_server()->GetURL("e.test", "/title1.html")); |
| |
| // Start from a COOP: restrict-properties opening a regular popup. |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| WebContentsImpl* first_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), regular_page, ""); |
| RenderFrameHostImpl* first_popup_rfh = |
| first_popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| |
| // Verify that the opener and openee frames exist as proxies in each other's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| |
| // Then open a popup from the popup, in the same BrowsingInstance and add |
| // a two cross-origin iframes to it. This setup makes sure that we have two |
| // iframes and a main frame that are unknown to the main page. |
| WebContentsImpl* second_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| first_popup_rfh, regular_page_2, ""); |
| RenderFrameHostImpl* second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(first_popup_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| |
| ASSERT_TRUE( |
| ExecJs(second_popup_rfh, JsReplace(R"( |
| const frame1 = document.createElement('iframe'); |
| const frame2 = document.createElement('iframe'); |
| frame1.src = $1; |
| frame2.src = $2; |
| document.body.appendChild(frame1); |
| document.body.appendChild(frame2); |
| )", |
| regular_page_3, regular_page_4))); |
| ASSERT_TRUE(WaitForLoadStop(second_popup_window)); |
| RenderFrameHostImpl* first_iframe_rfh = |
| second_popup_rfh->child_at(0)->current_frame_host(); |
| RenderFrameHostImpl* second_iframe_rfh = |
| second_popup_rfh->child_at(1)->current_frame_host(); |
| |
| // Both iframes should have proxies in their parent's, parent's opener's and |
| // other subframe's SiteInstanceGroup. The original frame should not know |
| // about them at this stage. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C D E\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://c.test/\n" |
| " D = https://d.test/\n" |
| " E = https://e.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A C D E\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://c.test/\n" |
| " D = https://d.test/\n" |
| " E = https://e.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| EXPECT_EQ( |
| " Site C ------------ proxies for B D E\n" |
| " |--Site D ------- proxies for B C E\n" |
| " +--Site E ------- proxies for B C D\n" |
| "Where B = https://b.test/\n" |
| " C = https://c.test/\n" |
| " D = https://d.test/\n" |
| " E = https://e.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| |
| // Now send a postMessage from the second iframe to the main frame, and wait |
| // for it to be received. |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| window.future_message = new Promise(r => { |
| onmessage = (event) => { |
| if (event.data == 'test') r(); |
| } |
| }); 0;)")); // This avoids waiting on the promise right now. |
| ASSERT_TRUE(ExecJs(second_iframe_rfh, |
| "window.top.opener.opener.postMessage('test', '*')")); |
| ASSERT_TRUE(ExecJs(current_frame_host(), "window.future_message")); |
| |
| // The second iframe should now have a proxy in the main frame's |
| // SiteInstanceGroup, but the first iframe should not yet. |
| EXPECT_EQ( |
| " Site C ------------ proxies for A B D E\n" |
| " |--Site D ------- proxies for B C E\n" |
| " +--Site E ------- proxies for A B C D\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://c.test/\n" |
| " D = https://d.test/\n" |
| " E = https://e.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| |
| // Now send a postMessage from the first iframe to the main frame, and wait |
| // for it to be received. |
| ASSERT_TRUE(ExecJs(current_frame_host(), R"( |
| window.future_message = new Promise(r => { |
| onmessage = (event) => { |
| if (event.data == 'test') r(); |
| } |
| }); 0;)")); // This avoids waiting on the promise right now. |
| ASSERT_TRUE(ExecJs(first_iframe_rfh, |
| "window.top.opener.opener.postMessage('test', '*')")); |
| ASSERT_TRUE(ExecJs(current_frame_host(), "window.future_message")); |
| |
| // The first iframe should now have a proxy in the main frame's |
| // SiteInstanceGroup. Creating proxies in the wrong order should not crash or |
| // cause problems. |
| EXPECT_EQ( |
| " Site C ------------ proxies for A B D E\n" |
| " |--Site D ------- proxies for A B C E\n" |
| " +--Site E ------- proxies for A B C D\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://c.test/\n" |
| " D = https://d.test/\n" |
| " E = https://e.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| } |
| |
| // This test verifies that a BrowsingInstance swap to a different |
| // CoopRelatedGroup clears preexisting proxies to other BrowsingInstances. |
| IN_PROC_BROWSER_TEST_P( |
| CoopRestrictPropertiesProxiesBrowserTest, |
| StrictBrowsingInstanceSwapDeletesCrossBrowsingInstanceProxies) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL coop_so_page( |
| https_server()->GetURL("a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: same-origin")); |
| |
| // Start by opening a popup to a COOP: restrict-properties page from a regular |
| // page. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), coop_rp_page, ""); |
| RenderFrameHostImpl* coop_rp_rfh = popup_window->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> coop_rp_si = coop_rp_rfh->GetSiteInstance(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| coop_rp_si.get())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| coop_rp_si.get())); |
| |
| // Verify that the opener and openee frames exist as proxies in each other's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://a.test/", |
| DepictFrameTree(coop_rp_rfh->frame_tree_node())); |
| |
| // Navigate the popup to a COOP: same-origin page. This should trigger a swap |
| // to a BrowsingInstance in a different CoopRelatedGroup. |
| RenderFrameDeletedObserver popup_deleted_observer_1(coop_rp_rfh); |
| ASSERT_TRUE(NavigateToURL(popup_window, coop_so_page)); |
| RenderFrameHostImpl* coop_so_rfh = popup_window->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> coop_so_si = coop_so_rfh->GetSiteInstance(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| coop_so_si.get())); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| coop_so_si.get())); |
| |
| // Wait for the previous RFH to be deleted so that the proxy count does not |
| // flake. |
| popup_deleted_observer_1.WaitUntilDeleted(); |
| |
| // The cross-BrowsingInstance proxies should be gone. |
| EXPECT_EQ(0u, current_frame_host()->GetProxyCount()); |
| EXPECT_EQ(0u, coop_so_rfh->GetProxyCount()); |
| |
| // Finally go back. The original COOP: restrict-properties SiteInstance will |
| // be reused. |
| RenderFrameDeletedObserver popup_deleted_observer_2(coop_so_rfh); |
| popup_window->GetController().GoBack(); |
| WaitForLoadStop(popup_window); |
| RenderFrameHostImpl* back_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_EQ(back_rfh->GetSiteInstance(), coop_rp_si.get()); |
| EXPECT_FALSE( |
| back_rfh->GetSiteInstance()->IsCoopRelatedSiteInstance(coop_so_si.get())); |
| |
| // BackForwardCache will kick in and store the RenderFrameHost, preventing its |
| // deletion. |
| if (!IsBackForwardCacheEnabled()) { |
| popup_deleted_observer_2.WaitUntilDeleted(); |
| } |
| |
| // Proxies are not re-created, because the opener was removed by going to |
| // COOP: same-origin, and is not restored when going back, despite the |
| // SiteInstance reuse. |
| EXPECT_EQ(0u, current_frame_host()->GetProxyCount()); |
| EXPECT_EQ(0u, back_rfh->GetProxyCount()); |
| } |
| |
| // This test verifies that proxies are as expected after a navigation. Start on |
| // a page with an existing SiteInstance before navigating to a COOP: |
| // restrict-properties page. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| ExistingSiteInstanceNavigationProxies) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "c.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start from a regular page and open a regular cross-origin popup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* first_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), regular_page_2, ""); |
| RenderFrameHostImpl* first_popup_rfh = |
| first_popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| |
| // Verify that the opener and openee frames exist as proxies in each other's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| |
| // Now open from the first popup a second popup with the same url as the main |
| // page. It should reuse its SiteInstance. |
| WebContentsImpl* second_popup_window = |
| OpenPopupAndWaitForInitialRFHDeletion(first_popup_rfh, regular_page, ""); |
| RenderFrameHostImpl* second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| ASSERT_EQ(current_frame_host()->GetSiteInstance(), |
| second_popup_rfh->GetSiteInstance()); |
| |
| // The main frame should have a proxy in the first popup's SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // The first popup should have a proxy in the main frame's and second popup's |
| // SiteInstanceGroup (which are the same). |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| // The second popup should have a proxy in the first popup's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| |
| // Finally, navigate the second popup to a COOP: restrict-properties page. |
| RenderFrameDeletedObserver initial_popup_rfh_observer(second_popup_rfh); |
| ASSERT_TRUE(NavigateToURL(second_popup_window, coop_rp_page)); |
| RenderFrameHostImpl* final_second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| initial_popup_rfh_observer.WaitUntilDeleted(); |
| |
| // The main frame should have a proxy in the first popup's SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // The first popup should have a proxy in the main frame's and second popup's |
| // SiteInstanceGroups (which are now different). |
| EXPECT_EQ( |
| " Site B ------------ proxies for A C\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://c.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| // The second popup should have a proxy in the first popup's |
| // SiteInstanceGroup. |
| // |
| // It also exists as a proxy in the main frame's SiteInstanceGroup, because |
| // the page was initially in the same SiteInstance as the main page. When the |
| // cross-site navigation starts, a proxy of the second popup is created in |
| // its own SiteInstanceGroup, which happens to be the same as another frame. |
| // This proxy is never deleted because there is still a frame using the |
| // SiteInstanceGroup after the navigation is finished. This should be fine |
| // because being in the same SiteInstanceGroup in the first place means that |
| // the frame retaining the proxy knew about this frame's existence. |
| EXPECT_EQ( |
| " Site C ------------ proxies for A B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://c.test/", |
| DepictFrameTree(final_second_popup_rfh->frame_tree_node())); |
| |
| // To confirm that the second popup is not leaking extra information in the |
| // main frame's SiteInstanceGroup, add an iframe in it and check that it does |
| // not have a proxy in the main frame's SiteInstanceGroup. |
| ASSERT_TRUE(ExecJs(final_second_popup_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page))); |
| ASSERT_TRUE(WaitForLoadStop(second_popup_window)); |
| |
| // The iframe should not have a proxy in the main frame's SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site C ------------ proxies for A B D\n" |
| " +--Site D ------- proxies for C\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://c.test/\n" |
| " D = https://a.test/", |
| DepictFrameTree(final_second_popup_rfh->frame_tree_node())); |
| } |
| |
| // This test verifies that proxies are as expected after a navigation. Start on |
| // a page in a related SiteInstance before navigating to a COOP: |
| // restrict-properties page. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| RelatedSiteInstanceNavigationProxies) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL regular_page_3(https_server()->GetURL("c.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "d.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start from a regular page and open a regular cross-origin popup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* first_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), regular_page_2, ""); |
| RenderFrameHostImpl* first_popup_rfh = |
| first_popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| |
| // Verify that the opener and openee frames exist as proxies in each other's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| |
| // Now open from the first popup a second popup with a third origin. It should |
| // use a new related SiteInstance. |
| WebContentsImpl* second_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| first_popup_rfh, regular_page_3, ""); |
| RenderFrameHostImpl* second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(first_popup_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| |
| // The main frame should have a proxy in the first popup's and the second |
| // popup's SiteInstanceGroups. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://c.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // The first popup should have a proxy in the main frame's and second popup's |
| // SiteInstanceGroups. |
| EXPECT_EQ( |
| " Site B ------------ proxies for A C\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " C = https://c.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| // The second popup should have a proxy in the first popup's |
| // SiteInstanceGroup. It does not exist as a proxy in the main frame's |
| // SiteInstanceGroup, because the main frame does not have a way to reference |
| // it. |
| EXPECT_EQ( |
| " Site C ------------ proxies for B\n" |
| "Where B = https://b.test/\n" |
| " C = https://c.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| |
| // Finally, navigate the second popup to a COOP: restrict-properties page. |
| RenderFrameDeletedObserver initial_popup_rfh_observer(second_popup_rfh); |
| ASSERT_TRUE(NavigateToURL(second_popup_window, coop_rp_page)); |
| RenderFrameHostImpl* final_second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| initial_popup_rfh_observer.WaitUntilDeleted(); |
| |
| // The main frame should have a proxy in the first popup's SiteInstanceGroup, |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // The first popup should have a proxy in the main frame's and second popup's |
| // SiteInstanceGroups. |
| EXPECT_EQ( |
| " Site B ------------ proxies for A D\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/\n" |
| " D = https://d.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| // The second popup should have a proxy in the first popup's |
| // SiteInstanceGroup. The main frame's SiteInstanceGroup still does not have a |
| // proxy of the second popup's frame, as opposed to the case where they |
| // initially share the same SiteInstance. |
| EXPECT_EQ( |
| " Site D ------------ proxies for B\n" |
| "Where B = https://b.test/\n" |
| " D = https://d.test/", |
| DepictFrameTree(final_second_popup_rfh->frame_tree_node())); |
| } |
| |
| // This test verifies that proxies are as expected after a navigation. Start on |
| // a page in an unrelated SiteInstance before navigating to a COOP: |
| // restrict-properties page. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| UnrelatedSiteInstanceNavigationProxies) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL coop_so_page( |
| https_server()->GetURL("c.test", |
| "/set-header" |
| "?cross-origin-opener-policy: same-origin")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "d.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start from a regular page and open a regular cross-origin popup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* first_popup_window = OpenPopupAndWaitForInitialRFHDeletion( |
| current_frame_host(), regular_page_2, ""); |
| RenderFrameHostImpl* first_popup_rfh = |
| first_popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| first_popup_rfh->GetSiteInstance())); |
| |
| // Verify that the opener and openee frames exist as proxies in each other's |
| // SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| |
| // Open a second popup to a COOP: same-origin page. This should trigger a swap |
| // to a BrowsingInstance in a different CoopRelatedGroup. |
| WebContentsImpl* second_popup_window = |
| OpenPopupAndWaitForInitialRFHDeletion(first_popup_rfh, coop_so_page, ""); |
| RenderFrameHostImpl* second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(first_popup_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| |
| // The main frame should have a proxy in the first popup's SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // The first popup should have a proxy in the main frame's SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| // The second popup should have no proxies. |
| EXPECT_EQ( |
| " Site C\n" |
| "Where C = https://c.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| |
| // Finally, navigate the second popup to a COOP: restrict-properties page. |
| RenderFrameDeletedObserver initial_popup_rfh_observer(second_popup_rfh); |
| ASSERT_TRUE(NavigateToURL(second_popup_window, coop_rp_page)); |
| RenderFrameHostImpl* final_second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| |
| // BackForwardCache will kick in and store the RenderFrameHost, preventing its |
| // deletion. |
| if (!IsBackForwardCacheEnabled()) { |
| initial_popup_rfh_observer.WaitUntilDeleted(); |
| } |
| |
| // No new proxy should have been created. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site B ------------ proxies for A\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| EXPECT_EQ( |
| " Site D\n" |
| "Where D = https://d.test/", |
| DepictFrameTree(final_second_popup_rfh->frame_tree_node())); |
| } |
| |
| // This test verifies that an opener update does not create extra proxies in |
| // SiteInstanceGroups in other BrowsingInstances. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesProxiesBrowserTest, |
| NoExtraProxyDiscoveredByOpenerUpdate) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Set up a main page with two same-origin popups. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* first_popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), regular_page, "")->web_contents()); |
| RenderFrameHostImpl* first_popup_rfh = |
| first_popup_window->GetPrimaryMainFrame(); |
| ASSERT_EQ(current_frame_host()->GetSiteInstance(), |
| first_popup_rfh->GetSiteInstance()); |
| |
| WebContentsImpl* second_popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), regular_page, "second_popup_name") |
| ->web_contents()); |
| RenderFrameHostImpl* second_popup_rfh = |
| second_popup_window->GetPrimaryMainFrame(); |
| ASSERT_EQ(current_frame_host()->GetSiteInstance(), |
| second_popup_rfh->GetSiteInstance()); |
| |
| // From the second popup, open a final popup to a COOP: restrict-properties |
| // page. |
| WebContentsImpl* third_popup_window = |
| OpenPopupAndWaitForInitialRFHDeletion(second_popup_rfh, coop_rp_page, ""); |
| RenderFrameHostImpl* third_popup_rfh = |
| third_popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(second_popup_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| third_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE(second_popup_rfh->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| third_popup_rfh->GetSiteInstance())); |
| |
| // The main page should not be visible by the third popup's SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A\n" |
| "Where A = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| // Neither should the first popup's SiteInstanceGroup. |
| EXPECT_EQ( |
| " Site A\n" |
| "Where A = https://a.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| // On the other hand, the third popup's SiteInstanceGroup should know about |
| // the second popup. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| |
| // To begin with, window.opener.opener should return null in the second popup, |
| // because its opener is the main page which itself does not have an opener. |
| ASSERT_EQ(true, EvalJs(second_popup_rfh, "window.opener.opener == null;")); |
| |
| // Now update the opener of the second popup using named targeting. The second |
| // popup's opener is now the first popup. |
| ASSERT_TRUE(ExecJs(first_popup_rfh, |
| "window.w = window.open('', 'second_popup_name');")); |
| |
| // Verify the opener was properly updated in the second popup. |
| ASSERT_EQ(true, |
| EvalJs(second_popup_rfh, "window.opener.opener.opener == null;")); |
| |
| // The COOP: restrict-properties SiteInstanceGroup in the third popup should |
| // still be unaware of the main page and the first popup. |
| EXPECT_EQ( |
| " Site A\n" |
| "Where A = https://a.test/", |
| DepictFrameTree(current_frame_host()->frame_tree_node())); |
| EXPECT_EQ( |
| " Site A\n" |
| "Where A = https://a.test/", |
| DepictFrameTree(first_popup_rfh->frame_tree_node())); |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| "Where A = https://a.test/\n" |
| " B = https://b.test/", |
| DepictFrameTree(second_popup_rfh->frame_tree_node())); |
| } |
| |
| // This test verifies that named targeting does not resolve across |
| // BrowsingInstances. |
| // TODO(https://crbug.com/1370357): Named targeting will evolve in the future, |
| // when we're able to have per-BrowsingInstance names. For now, we're simply |
| // blocking all named targeting. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesBrowserTest, |
| NamedTargetingIsBlockedAcrossBrowsingInstances) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| |
| // 1. Verify that the set name gets cleared when opening a popup in a |
| // different BrowsingInstance. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_rp_page, "name1")->web_contents()); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| EXPECT_EQ("", popup_rfh->GetFrameName()); |
| EXPECT_EQ(true, EvalJs(popup_rfh, "window.name == '';")); |
| |
| // 2. Verify that setting a new name to the frame still doesn't make the popup |
| // targetable. |
| FrameNameChangedWaiter frame_name_changed(popup_window, popup_rfh, "name2"); |
| ASSERT_TRUE(ExecJs(popup_rfh, "window.name = 'name2';")); |
| |
| // Note: This waits for the name update to reach the browser, which will send |
| // replication state updates to the renderers processes keeping proxies of |
| // this frame. Because the interfaces are associated, we expect the proxy |
| // update to happen before the script execution below. |
| frame_name_changed.Wait(); |
| |
| ShellAddedObserver main_page_targeting_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), "window.open('', 'name2')")); |
| main_page_targeting_observer.GetShell(); |
| |
| // We should have 3 different windows: the main page, the first popup and the |
| // second popup that was just opened because named targeting did not resolve. |
| EXPECT_EQ(3u, Shell::windows().size()); |
| |
| // 3. Verify that a named subframe is similarly not targetable by the opening |
| // context. |
| ASSERT_TRUE(ExecJs(popup_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.name = 'name3'; |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| |
| // The iframe should not even have a proxy in the main page's process, and no |
| // matching frame should be returned. A new popup is created instead. |
| ShellAddedObserver iframe_targeting_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), "window.open('', 'name3')")); |
| iframe_targeting_observer.GetShell(); |
| |
| // We should have all 3 preceding windows, and another one that was opened |
| // because the subframe targeting did not resolve. |
| EXPECT_EQ(4u, Shell::windows().size()); |
| } |
| |
| // Smoke test with kNewBrowsingContextStateOnBrowsingContextGroupSwap enabled. |
| // Verifies that nothing breaks when we're dealing with proxies across different |
| // BrowsingInstances with COOP: restrict-properties. |
| // TODO(1394669): Enable once BrowsingContextState new mode implementation is |
| // further down the line. Currently this test crashes even with COOP: |
| // same-origin. |
| IN_PROC_BROWSER_TEST_P( |
| CoopRestrictPropertiesWithNewBrowsingContextStateModeBrowserTest, |
| DISABLED_BrowsingContextStateNewModeSmokeTest) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start by opening a popup to a COOP: rp page from a regular page. |
| // Note: This currently causes a crash in the renderer. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| WebContentsImpl* popup_window = static_cast<WebContentsImpl*>( |
| OpenPopup(current_frame_host(), coop_rp_page, "")->web_contents()); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| popup_window->Close(); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(NoSiteIsolationCrossOriginIsolationBrowserTest, |
| COICanLiveInDefaultSI) { |
| GURL isolated_page( |
| https_server()->GetURL("a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: same-origin" |
| "&cross-origin-embedder-policy: require-corp")); |
| GURL non_isolated_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), isolated_page)); |
| SiteInstanceImpl* main_frame_si = current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(main_frame_si->IsCrossOriginIsolated()); |
| EXPECT_TRUE(main_frame_si->IsDefaultSiteInstance()); |
| |
| { |
| // Open a popup to a page with similar isolation. Pages that have compatible |
| // cross origin isolation should be put in the same default SiteInstance. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.open($1);", isolated_page))); |
| WebContentsImpl* popup = static_cast<WebContentsImpl*>( |
| shell_observer.GetShell()->web_contents()); |
| EXPECT_TRUE(WaitForLoadStop(popup)); |
| |
| SiteInstanceImpl* popup_si = |
| popup->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(popup_si->IsCrossOriginIsolated()); |
| EXPECT_TRUE(popup_si->IsDefaultSiteInstance()); |
| EXPECT_EQ(popup_si, main_frame_si); |
| |
| popup->Close(); |
| } |
| |
| { |
| // Open a popup to a same origin non-isolated page. This page should live in |
| // a different BrowsingInstance in the default non-isolated SiteInstance. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.open($1);", non_isolated_page))); |
| WebContentsImpl* popup = static_cast<WebContentsImpl*>( |
| shell_observer.GetShell()->web_contents()); |
| EXPECT_TRUE(WaitForLoadStop(popup)); |
| |
| SiteInstanceImpl* popup_si = |
| popup->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(popup_si->IsCrossOriginIsolated()); |
| EXPECT_TRUE(popup_si->IsDefaultSiteInstance()); |
| EXPECT_NE(popup_si, main_frame_si); |
| |
| popup->Close(); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| ConsoleErrorOnWindowLocationAccess) { |
| const GURL non_coop_page = https_server()->GetURL("a.test", "/title1.html"); |
| const GURL coop_page = https_server()->GetURL( |
| "b.test", |
| "/set-header?Cross-Origin-Opener-Policy-Report-Only: same-origin"); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), non_coop_page)); |
| |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.popup = window.open($1)", coop_page))); |
| EXPECT_TRUE(WaitForLoadStop(shell_observer.GetShell()->web_contents())); |
| |
| WebContentsConsoleObserver console_observer(shell()->web_contents()); |
| console_observer.SetPattern( |
| "Cross-Origin-Opener-Policy policy would block the window.location " |
| "call."); |
| ASSERT_TRUE(ExecJs(current_frame_host(), "window.popup.location")); |
| ASSERT_TRUE(console_observer.Wait()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| ConsoleErrorOnWindowIndexedAccess) { |
| const GURL non_coop_page = https_server()->GetURL("a.test", "/title1.html"); |
| const GURL coop_page = https_server()->GetURL( |
| "b.test", |
| "/set-header?Cross-Origin-Opener-Policy-Report-Only: same-origin"); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), non_coop_page)); |
| |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.popup = window.open($1)", coop_page))); |
| EXPECT_TRUE(WaitForLoadStop(shell_observer.GetShell()->web_contents())); |
| ASSERT_TRUE( |
| ExecJs(shell_observer.GetShell()->web_contents(), |
| JsReplace("const iframe = document.createElement('iframe');" |
| "iframe.src = $1;" |
| "document.body.appendChild(iframe);", |
| non_coop_page))); |
| EXPECT_TRUE(WaitForLoadStop(shell_observer.GetShell()->web_contents())); |
| |
| WebContentsConsoleObserver console_observer(shell()->web_contents()); |
| console_observer.SetPattern( |
| "Cross-Origin-Opener-Policy policy would block the window[i] call."); |
| ASSERT_TRUE(ExecJs(current_frame_host(), "window.popup[0]")); |
| ASSERT_TRUE(console_observer.Wait()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, |
| ConsoleErrorOnWindowNamedAccess) { |
| const GURL non_coop_page = https_server()->GetURL("a.test", "/title1.html"); |
| const GURL coop_page = https_server()->GetURL( |
| "a.test", |
| "/set-header?Cross-Origin-Opener-Policy-Report-Only: same-origin"); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), non_coop_page)); |
| |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.popup = window.open($1)", coop_page))); |
| EXPECT_TRUE(WaitForLoadStop(shell_observer.GetShell()->web_contents())); |
| ASSERT_TRUE(ExecJs(shell_observer.GetShell()->web_contents(), R"( |
| const div = document.createElement("div"); |
| div.id = "divID"; |
| document.body.appendChild(div); |
| )")); |
| |
| WebContentsConsoleObserver console_observer(shell()->web_contents()); |
| console_observer.SetPattern( |
| "Cross-Origin-Opener-Policy policy would block the window[\"name\"] " |
| "call."); |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.popup['divID']")); |
| ASSERT_TRUE(console_observer.Wait()); |
| } |
| |
| // Navigate in between two documents. Check the virtual browsing context group |
| // is properly updated. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesReportingBrowserTest, |
| NavigationVirtualBrowsingContextGroup) { |
| 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.test", "/title1.html"), |
| https_server()->GetURL("a.test", "/title2.html"), |
| false, |
| }, |
| { |
| // different-origin => keep. |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| https_server()->GetURL("b.a.test", "/title2.html"), |
| false, |
| }, |
| { |
| // different-site => keep. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL("b.test", "/title2.html"), |
| false, |
| }, |
| |
| // non-coop <-> coop. |
| { |
| // same-origin => change. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-origin => change. |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-site => change. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| |
| // coop <-> coop. |
| { |
| // same-origin => keep. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| false, |
| }, |
| { |
| // different-origin => change. |
| https_server()->GetURL( |
| "a.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-site => change. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| |
| // non-coop <-> coop-ro. |
| { |
| // same-origin => change. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-origin => change. |
| https_server()->GetURL("a.a.test", "/title1.html"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-site => change. |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| |
| // coop-ro <-> coop-ro. |
| { |
| // same-origin => keep. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| false, |
| }, |
| { |
| // different-origin => change. |
| https_server()->GetURL( |
| "a.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-site => keep. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| |
| // coop <-> coop-ro. |
| { |
| // same-origin => change. |
| https_server()->GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-origin => change. |
| https_server()->GetURL( |
| "a.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| { |
| // different-site => change |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| // TODO(https://crbug.com/1424417): Test with COEP-RO. |
| // TODO(https://crbug.com/1424417): Test interactions with COOP: SO. |
| // TODO(https://crbug.com/1424417): Test interactions with COOP: SOAP. |
| }; |
| |
| 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(CoopRestrictPropertiesReportingBrowserTest, |
| WindowOpenVirtualBrowsingContextGroup) { |
| 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.test", "/title1.html"), |
| GURL(), |
| false, |
| }, |
| { |
| // From coop-ro. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| GURL(), |
| false, |
| }, |
| { |
| // From coop. |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "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.test", "/title1.html"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| false, |
| }, |
| |
| // non-coop opens coop-ro. |
| { |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| |
| // non-coop opens coop. |
| { |
| https_server()->GetURL("a.test", "/title1.html"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| true, |
| }, |
| |
| // coop opens non-coop. |
| { |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL("a.test", "/title1.html"), |
| true, |
| }, |
| |
| // coop-ro opens coop-ro (same-origin). |
| { |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| false, |
| }, |
| |
| // coop-ro opens coop-ro (different-origin). |
| { |
| https_server()->GetURL( |
| "a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "Cross-Origin-Embedder-Policy: require-corp"), |
| https_server()->GetURL( |
| "b.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy-Report-Only: restrict-properties&" |
| "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(); |
| } |
| } |
| |
| // Verify that two documents in different browsing context groups in the same |
| // CoopRelatedGroup only have access to window.closed and window.postMessage(). |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| PropertiesAreBlockedAcrossBrowsingContextGroup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL same_origin_iframe(https_server()->GetURL("a.test", "/title1.html")); |
| |
| // Start from a regular page and open a cross-origin popup. Open it manually |
| // to store the returned popup handle. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = window.open($1)", coop_rp_page))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Try to access always-authorized properties. They should return as usual. |
| EXPECT_EQ(false, EvalJs(current_frame_host(), "window.w.closed")); |
| EXPECT_EQ(nullptr, |
| EvalJs(current_frame_host(), "window.w.postMessage('', '*')")); |
| |
| // Then poke at restricted properties and verify that we return a COOP: |
| // restrict-properties SecurityError. |
| |
| // window.window |
| std::string result = |
| EvalJs(current_frame_host(), |
| "try { window.w.window } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window.self |
| result = EvalJs(current_frame_host(), |
| "try { window.w.self } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window.location |
| result = EvalJs(current_frame_host(), |
| "try { window.w.location } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window.focus() |
| result = EvalJs(current_frame_host(), |
| "try { window.w.focus() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window.blur() |
| result = EvalJs(current_frame_host(), |
| "try { window.w.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window.frames |
| result = EvalJs(current_frame_host(), |
| "try { window.w.frames } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window.length |
| result = EvalJs(current_frame_host(), |
| "try { window.w.length } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window.top |
| result = EvalJs(current_frame_host(), |
| "try { window.w.top } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window.opener |
| result = EvalJs(current_frame_host(), |
| "try { window.w.opener } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window.parent |
| result = EvalJs(current_frame_host(), |
| "try { window.w.parent } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window indexed getter |
| result = EvalJs(current_frame_host(), |
| "try { window.w[0] } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // window named getter |
| result = EvalJs(current_frame_host(), |
| "try { window.w['iframe_name'] } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // Verify that getting window["then"] uses the special cross-origin fallback. |
| // See https://html.spec.whatwg.org/#crossoriginpropertyfallback-(-p-) |
| // This makes sure windowProxy is thenable, see the original discussion here: |
| // https://github.com/whatwg/dom/issues/536. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w['then']")); |
| |
| // window.close() |
| result = EvalJs(current_frame_host(), |
| "try { window.w.close() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| } |
| |
| // Verifies that the BrowsingContextGroupInfo is properly propagated when |
| // opening a popup in the same SiteInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| SimpleLocalPopup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| // Start from a regular page and open a popup in the same SiteInstance. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", regular_page))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_EQ(current_frame_host()->GetSiteInstance(), |
| popup_rfh->GetSiteInstance()); |
| |
| // Because they are in the same SiteInstance, their browsing context group |
| // should match and access should be possible. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w.blur()")); |
| EXPECT_TRUE(ExecJs(popup_rfh, "opener.blur()")); |
| } |
| |
| // Verifies that the BrowsingContextGroupInfo is properly propagated when |
| // opening a popup in the same browsing context group in another SiteInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| SimpleRemotePopup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| |
| // Start from a regular page and open a popup in the same browsing context |
| // group. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", regular_page_2))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Because they are in the same browsing context group access should be |
| // possible. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w.blur()")); |
| EXPECT_TRUE(ExecJs(popup_rfh, "opener.blur()")); |
| } |
| |
| // Verifies that the BrowsingContextGroupInfo is properly propagated when |
| // opening a popup in another browsing context group in the same |
| // CoopRelatedGroup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| SimpleCoopPopup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page_same_document( |
| https_server()->GetURL("a.test", "/title1.html#fragment")); |
| |
| // Start from a regular page and open a popup in another browsing context |
| // group in the same CoopRelatedGroup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", coop_rp_page))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_NE(current_frame_host()->GetSiteInstance(), |
| popup_rfh->GetSiteInstance()); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Because they are in different browsing context groups in the same |
| // CoopRelatedGroup, access to cross-origin properties should be restricted. |
| std::string result = |
| EvalJs(current_frame_host(), |
| "try { window.w.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| result = |
| EvalJs(popup_rfh, "try { opener.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // Always-allowed properties should still be accessible, and trying to access |
| // them should not throw an exception. |
| EXPECT_EQ(true, EvalJs(popup_rfh, "opener.closed == false")); |
| EXPECT_EQ(true, EvalJs(current_frame_host(), "window.w.closed == false")); |
| |
| // Finally, close the popup and verify that window.closed reflects the update. |
| // To make sure the update is propagated, run a quick same-document navigation |
| // which should rely on the same underlying interface pipe. |
| popup_window->Close(); |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page_same_document)); |
| EXPECT_EQ(true, EvalJs(current_frame_host(), "window.w.closed == true")); |
| } |
| |
| // Verifies in more details how the BrowsingContextGroupInfo is propagated when |
| // opening a popup in another browsing context group in the same |
| // CoopRelatedGroup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| SimpleCoopPopupDetailed) { |
| // This test verifies details about RenderViewHosts, so make sure we're using |
| // different processes for different pages. |
| if (!AreAllSitesIsolatedForTesting()) { |
| return; |
| } |
| |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start from a regular page and open a popup in another browsing context |
| // group in the same CoopRelatedGroup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| SiteInstanceImpl* main_page_si = current_frame_host()->GetSiteInstance(); |
| base::UnguessableToken main_page_bi_token = |
| main_page_si->browsing_instance_token(); |
| base::UnguessableToken main_page_coop_token = |
| main_page_si->coop_related_group_token(); |
| |
| // Then open a popup in the same SiteInstance. The popup starts with the same |
| // tokens as the main page since it belong to the same SiteInstance. |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), "window.w = open('');")); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| RenderFrameHostManager* popup_rfhm = |
| popup_rfh->frame_tree_node()->render_manager(); |
| ASSERT_EQ(current_frame_host()->GetSiteInstance(), |
| popup_rfh->GetSiteInstance()); |
| |
| // At this stage, two RenderViewHosts exist, in the same process, one for each |
| // page. In both, the frames are local. |
| RenderViewHostImpl* rvh1 = static_cast<RenderViewHostImpl*>( |
| current_frame_host()->GetRenderViewHost()); |
| RenderViewHostImpl* rvh2 = |
| static_cast<RenderViewHostImpl*>(popup_rfh->GetRenderViewHost()); |
| ASSERT_NE(rvh1, rvh2); |
| ASSERT_NE(rvh1->frame_tree(), rvh2->frame_tree()); |
| ASSERT_EQ(rvh1->site_instance_group(), |
| rvh1->frame_tree()->GetMainFrame()->GetSiteInstance()->group()); |
| ASSERT_EQ(rvh2->site_instance_group(), |
| rvh2->frame_tree()->GetMainFrame()->GetSiteInstance()->group()); |
| |
| // Now, start a navigation to a COOP: restrict-properties page, in another |
| // browsing context group in the same CoopRelatedGroup. |
| TestNavigationManager navigation_manager(popup_window, coop_rp_page); |
| NavigationController::LoadURLParams params(coop_rp_page); |
| popup_window->GetController().LoadURLWithParams(params); |
| |
| // Stop when we've started the request. At this stage, we should have no |
| // speculative frame, because we still think we can reuse the same |
| // RenderFrameHost. |
| ASSERT_TRUE(navigation_manager.WaitForRequestStart()); |
| ASSERT_FALSE(popup_rfhm->speculative_frame_host()); |
| |
| // After receiving the response, we realize that COOP headers do not match. We |
| // should have created a new RenderFrameHost in another browsing context |
| // group. |
| ASSERT_TRUE(navigation_manager.WaitForResponse()); |
| RenderFrameHostImpl* new_rfh = popup_rfhm->speculative_frame_host(); |
| ASSERT_TRUE(new_rfh); |
| ASSERT_FALSE(new_rfh->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE(new_rfh->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| base::UnguessableToken popup_bi_token = |
| new_rfh->GetSiteInstance()->browsing_instance_token(); |
| base::UnguessableToken popup_coop_token = |
| new_rfh->GetSiteInstance()->coop_related_group_token(); |
| EXPECT_NE(main_page_bi_token, popup_bi_token); |
| EXPECT_EQ(main_page_coop_token, popup_coop_token); |
| |
| // At this point, we should have 4 RenderViewHosts, one for each page in each |
| // process. Grab the ones created for the new process. |
| RenderFrameProxyHost* proxy_for_main_page_in_popup = |
| current_frame_host() |
| ->browsing_context_state() |
| ->proxy_hosts()[new_rfh->GetSiteInstance()->group()->GetId()] |
| .get(); |
| RenderViewHostImpl* rvh3 = proxy_for_main_page_in_popup->GetRenderViewHost(); |
| RenderViewHostImpl* rvh4 = |
| static_cast<RenderViewHostImpl*>(new_rfh->GetRenderViewHost()); |
| |
| // The first RenderViewHost represents the main page, in the main page |
| // process. |
| ASSERT_EQ(rvh1->frame_tree(), &(web_contents()->GetPrimaryFrameTree())); |
| EXPECT_EQ(rvh1->site_instance_group(), |
| rvh1->frame_tree()->GetMainFrame()->GetSiteInstance()->group()); |
| EXPECT_EQ(rvh1->site_instance_group()->browsing_instance_token(), |
| main_page_bi_token); |
| EXPECT_EQ(rvh1->site_instance_group()->coop_related_group_token(), |
| main_page_coop_token); |
| |
| // The second RenderViewHost represents the popup, in the main page process. |
| // At this stage, the new popup frame has not yet been committed, and it |
| // should still be for the old popup frame. |
| ASSERT_EQ(rvh2->frame_tree(), &(popup_window->GetPrimaryFrameTree())); |
| EXPECT_EQ(rvh2->site_instance_group(), |
| rvh2->frame_tree()->GetMainFrame()->GetSiteInstance()->group()); |
| EXPECT_EQ(rvh2->site_instance_group()->browsing_instance_token(), |
| main_page_bi_token); |
| EXPECT_EQ(rvh2->site_instance_group()->coop_related_group_token(), |
| main_page_coop_token); |
| EXPECT_EQ(rvh2->frame_tree() |
| ->GetMainFrame() |
| ->GetSiteInstance() |
| ->browsing_instance_token(), |
| main_page_bi_token); |
| EXPECT_EQ(rvh2->frame_tree() |
| ->GetMainFrame() |
| ->GetSiteInstance() |
| ->coop_related_group_token(), |
| main_page_coop_token); |
| |
| // The third RenderViewHost represents the main page, in the popup process. It |
| // should have a proxy as its main frame, with the final BrowsingContextGroup |
| // information. We sent the renderer process that information at RenderView |
| // creation time. |
| ASSERT_EQ(rvh3->frame_tree(), &(web_contents()->GetPrimaryFrameTree())); |
| EXPECT_NE(rvh3->site_instance_group(), |
| rvh3->frame_tree()->GetMainFrame()->GetSiteInstance()->group()); |
| EXPECT_EQ(rvh3->site_instance_group()->browsing_instance_token(), |
| popup_bi_token); |
| EXPECT_EQ(rvh3->site_instance_group()->coop_related_group_token(), |
| popup_coop_token); |
| EXPECT_EQ(rvh3->frame_tree() |
| ->GetMainFrame() |
| ->GetSiteInstance() |
| ->browsing_instance_token(), |
| main_page_bi_token); |
| EXPECT_EQ(rvh3->frame_tree() |
| ->GetMainFrame() |
| ->GetSiteInstance() |
| ->coop_related_group_token(), |
| main_page_coop_token); |
| |
| // The fourth RenderViewHost represents the popup, in the popup process. |
| // Before commit, the main frame should be a proxy. We sent the renderer |
| // process the current frame's BrowsingContextGroup information at RenderView |
| // creation time. |
| ASSERT_EQ(rvh4->frame_tree(), &(popup_window->GetPrimaryFrameTree())); |
| EXPECT_NE(rvh4->site_instance_group(), |
| rvh4->frame_tree()->GetMainFrame()->GetSiteInstance()->group()); |
| EXPECT_EQ(rvh4->site_instance_group()->browsing_instance_token(), |
| popup_bi_token); |
| EXPECT_EQ(rvh4->site_instance_group()->coop_related_group_token(), |
| popup_coop_token); |
| EXPECT_EQ(rvh4->frame_tree() |
| ->GetMainFrame() |
| ->GetSiteInstance() |
| ->browsing_instance_token(), |
| main_page_bi_token); |
| EXPECT_EQ(rvh4->frame_tree() |
| ->GetMainFrame() |
| ->GetSiteInstance() |
| ->coop_related_group_token(), |
| main_page_coop_token); |
| |
| // Commit the navigation. The speculative RenderFrameHost is now the current |
| // RenderFrameHost. |
| ASSERT_TRUE(navigation_manager.WaitForNavigationFinished()); |
| ASSERT_EQ(new_rfh, popup_window->GetPrimaryMainFrame()); |
| |
| // At commit time, two things happened: |
| // (1) We sent the popup's renderer (rvh4) the new RenderFrameHost tokens as |
| // part of the commit. They should be in line with the currently active frame, |
| // which is now local. Note that we cannot verify the information sent to the |
| // renderer, but at least make sure that the browser side holds the correct |
| // information. |
| EXPECT_EQ(rvh4->site_instance_group(), |
| rvh4->frame_tree()->GetMainFrame()->GetSiteInstance()->group()); |
| EXPECT_EQ(rvh4->site_instance_group()->browsing_instance_token(), |
| popup_bi_token); |
| EXPECT_EQ(rvh4->site_instance_group()->coop_related_group_token(), |
| popup_coop_token); |
| |
| // (2) We've broadcasted the BrowsingContextGroupInfo update to |
| // RenderViewHosts that have a proxy of the navigated frame as their main |
| // frame. In this case, rvh2, which now has a proxy of the popup frame as its |
| // main frame. |
| EXPECT_NE(rvh2->site_instance_group(), |
| rvh2->frame_tree()->GetMainFrame()->GetSiteInstance()->group()); |
| EXPECT_EQ(rvh2->site_instance_group()->browsing_instance_token(), |
| main_page_bi_token); |
| EXPECT_EQ(rvh2->site_instance_group()->coop_related_group_token(), |
| main_page_coop_token); |
| EXPECT_EQ(rvh2->frame_tree() |
| ->GetMainFrame() |
| ->GetSiteInstance() |
| ->browsing_instance_token(), |
| popup_bi_token); |
| EXPECT_EQ(rvh2->frame_tree() |
| ->GetMainFrame() |
| ->GetSiteInstance() |
| ->coop_related_group_token(), |
| popup_coop_token); |
| |
| // Finally, make sure the right properties are blocked, and the right |
| // properties can be accessed. |
| std::string result = |
| EvalJs(current_frame_host(), |
| "try { window.w.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| result = EvalJs(new_rfh, "try { opener.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // Always-allowed properties should still be accessible, and trying to access |
| // them should not throw any exception. |
| EXPECT_EQ(true, EvalJs(new_rfh, "opener.closed == false")); |
| EXPECT_EQ(true, EvalJs(current_frame_host(), "window.w.closed == false")); |
| } |
| |
| // Verifies that BrowsingContextGroupInfo is properly propagated to an iframe |
| // in the same SiteInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, LocalSubframe) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| // Navigate to a regular page, with a subframe in the same SiteInstance. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| // The iframe is in the same SiteInstance, and access should be possible. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window[0].blur()")); |
| EXPECT_TRUE(ExecJs(iframe_rfh, "top.blur()")); |
| } |
| |
| // Verifies that BrowsingContextGroupInfo is properly propagated to an iframe |
| // in the same browsing context group in another SiteInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| RemoteSubframe) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| |
| // Navigate to a regular page, with a subframe in another SiteInstance. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| // The iframe is in the same browsing context group, and access should be |
| // possible. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window[0].blur()")); |
| EXPECT_TRUE(ExecJs(iframe_rfh, "top.blur()")); |
| } |
| |
| // Verifies that BrowsingContextGroupInfo is properly propagated to iframes and |
| // iframes in popups, all living in the same SiteInstance. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| LocalSubframesInPopup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| |
| // Start from a regular page with a subframe and open a popup with a subframe, |
| // all in the same SiteInstance. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", regular_page))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_EQ(current_frame_host()->GetSiteInstance(), |
| popup_rfh->GetSiteInstance()); |
| |
| ASSERT_TRUE(ExecJs(popup_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page))); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_iframe_rfh = |
| popup_rfh->child_at(0)->current_frame_host(); |
| |
| // All frames are in the same SiteInstance, and access should be possible. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window[0].blur()")); |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w.blur()")); |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w[0].blur()")); |
| |
| EXPECT_TRUE(ExecJs(iframe_rfh, "top.blur()")); |
| EXPECT_TRUE(ExecJs(iframe_rfh, "top.w.blur()")); |
| EXPECT_TRUE(ExecJs(iframe_rfh, "top.w[0].blur()")); |
| |
| EXPECT_TRUE(ExecJs(popup_rfh, "opener.blur()")); |
| EXPECT_TRUE(ExecJs(popup_rfh, "opener[0].blur()")); |
| EXPECT_TRUE(ExecJs(popup_rfh, "window[0].blur()")); |
| |
| EXPECT_TRUE(ExecJs(popup_iframe_rfh, "top.blur()")); |
| EXPECT_TRUE(ExecJs(popup_iframe_rfh, "top.opener.blur()")); |
| EXPECT_TRUE(ExecJs(popup_iframe_rfh, "top.opener[0].blur()")); |
| } |
| |
| // Verifies that BrowsingContextGroupInfo is properly propagated to iframes and |
| // iframes in popups, all living in the same browsing context group. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| RemoteSubframesInPopup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL regular_page_3(https_server()->GetURL("c.test", "/title1.html")); |
| GURL regular_page_4(https_server()->GetURL("d.test", "/title1.html")); |
| |
| // Start from a regular page with a subframe and open a popup with a subframe, |
| // all in the same browsing context group, but in different SiteInstances. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", regular_page_3))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| ASSERT_TRUE(ExecJs(popup_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_4))); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_iframe_rfh = |
| popup_rfh->child_at(0)->current_frame_host(); |
| |
| // All frames are in the same browsing context group and access should be |
| // possible. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window[0].blur()")); |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w.blur()")); |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w[0].blur()")); |
| |
| // The iframe in the main page can only access its top frame, because it has |
| // no way to grab the window.w handle as a cross-origin frame. |
| EXPECT_TRUE(ExecJs(iframe_rfh, "top.blur()")); |
| |
| EXPECT_TRUE(ExecJs(popup_rfh, "opener.blur()")); |
| EXPECT_TRUE(ExecJs(popup_rfh, "opener[0].blur()")); |
| EXPECT_TRUE(ExecJs(popup_rfh, "window[0].blur()")); |
| |
| EXPECT_TRUE(ExecJs(popup_iframe_rfh, "top.blur()")); |
| EXPECT_TRUE(ExecJs(popup_iframe_rfh, "top.opener.blur()")); |
| EXPECT_TRUE(ExecJs(popup_iframe_rfh, "top.opener[0].blur()")); |
| } |
| |
| // Verifies that BrowsingContextGroupInfo is properly propagated to iframes and |
| // iframes in popups living in a different browsing context group in the same |
| // CoopRelatedGroup. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| SubframesInCoopPopup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "c.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page_3(https_server()->GetURL("d.test", "/title1.html")); |
| |
| // Start from a regular page with a subframe and open a popup in another |
| // browsing context group in the same CoopRelatedGroup, itself with an iframe. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ASSERT_TRUE(ExecJs(current_frame_host(), JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_2))); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* iframe_rfh = |
| current_frame_host()->child_at(0)->current_frame_host(); |
| |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", coop_rp_page))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| ASSERT_TRUE(ExecJs(popup_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_page_3))); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_iframe_rfh = |
| popup_rfh->child_at(0)->current_frame_host(); |
| |
| // Different pages are in different browsing context groups and access should |
| // be restricted. Access within a page should not. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window[0].blur()")); |
| std::string result = |
| EvalJs(current_frame_host(), |
| "try { window.w.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| EXPECT_EQ(true, EvalJs(current_frame_host(), "window.w.closed == false")); |
| |
| EXPECT_TRUE(ExecJs(iframe_rfh, "top.blur()")); |
| |
| EXPECT_TRUE(ExecJs(popup_rfh, "window[0].blur()")); |
| result = |
| EvalJs(popup_rfh, "try { opener.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| EXPECT_EQ(true, EvalJs(popup_rfh, "opener.closed == false")); |
| |
| EXPECT_TRUE(ExecJs(popup_iframe_rfh, "top.blur()")); |
| result = EvalJs(popup_iframe_rfh, |
| "try { top.opener.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| EXPECT_EQ(true, EvalJs(popup_iframe_rfh, "top.opener.closed == false")); |
| } |
| |
| // Verify that navigating to another browsing context group in the same |
| // CoopRelatedGroup and ending up in an error page propagates the |
| // BrowsingContextGroupInfo properly. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| NavigationToError) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL error_page(https_server()->GetURL("b.test", "/page_not_found")); |
| |
| // Start from a regular page and a popup in different browsing context groups |
| // in the same CoopRelatedGroup. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", coop_rp_page))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Navigate the popup to an error page. It should reuse the original browsing |
| // context group. |
| ASSERT_FALSE(NavigateToURL(popup_window, error_page)); |
| RenderFrameHostImpl* error_popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| error_popup_rfh->GetSiteInstance())); |
| |
| // We've come back to the original browsing context group, so access should be |
| // possible. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w.blur()")); |
| EXPECT_TRUE(ExecJs(error_popup_rfh, "opener.blur()")); |
| } |
| |
| // Verify that navigating to another browsing context group in the same |
| // CoopRelatedGroup and going back propagates the BrowsingContextGroupInfo |
| // properly. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| HistoryNavigation) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "c.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start from a regular page and a popup in the same browsing context group. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", regular_page_2))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Navigate the popup to another browsing context group in the same |
| // CoopRelatedGroup. |
| ASSERT_TRUE(NavigateToURL(popup_window, coop_rp_page)); |
| RenderFrameHostImpl* second_popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| |
| // Navigate back. The browsing context group information should properly be |
| // updated. |
| popup_window->GetController().GoBack(); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* back_popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| back_popup_rfh->GetSiteInstance())); |
| |
| // We've come back to the original browsing context group, access should be |
| // possible. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w.blur()")); |
| EXPECT_TRUE(ExecJs(back_popup_rfh, "opener.blur()")); |
| } |
| |
| // Verify that activating a BackForwardCache entry in another browsing context |
| // group in the same CoopRelatedGroup propagates the BrowsingContextGroupInfo |
| // properly. This can only be approximated, because BFCache will not usually |
| // kick in on pages with popups. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| BackForwardCacheNavigation) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "b.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| GURL regular_page_2(https_server()->GetURL("c.test", "/title1.html")); |
| |
| // Start on a first page, then navigate to a page in another browsing context |
| // group in the same CoopRelatedGroup, and finally back. The back navigation |
| // uses the BFCache if enabled. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| SiteInstanceImpl* initial_si = current_frame_host()->GetSiteInstance(); |
| base::UnguessableToken initial_bi_token = |
| initial_si->browsing_instance_token(); |
| base::UnguessableToken initial_coop_token = |
| initial_si->coop_related_group_token(); |
| |
| ASSERT_TRUE(NavigateToURL(shell(), coop_rp_page)); |
| web_contents()->GetController().GoBack(); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| |
| // The back navigation should still have the original tokens. |
| SiteInstanceImpl* bfcache_si = current_frame_host()->GetSiteInstance(); |
| EXPECT_EQ(bfcache_si->browsing_instance_token(), initial_bi_token); |
| EXPECT_EQ(bfcache_si->coop_related_group_token(), initial_coop_token); |
| |
| // Now open a popup in the same browsing context group. |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", regular_page_2))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // If the BrowsingContextGroupInfo was properly propagated to the renderer |
| // upon the BFCache navigation, access to the popup should be unrestricted. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w.blur()")); |
| EXPECT_TRUE(ExecJs(popup_rfh, "opener.blur()")); |
| } |
| |
| // Verify that navigating to another browsing context group in the same |
| // CoopRelatedGroup from a crashed frame propagates the BrowsingContextGroupInfo |
| // properly. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| PostCrashNavigation) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "c.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // To be able to properly test that access is preserved after a crashed |
| // process navigates again, we don't want both the openee and the opener to |
| // live in the same process and to both crash. |
| if (!AreAllSitesIsolatedForTesting()) { |
| return; |
| } |
| |
| // Start from a regular page and a popup in the same browsing context group. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", regular_page_2))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Simulate the renderer process used for the popup crashing. |
| RenderProcessHost* process = popup_rfh->GetSiteInstance()->GetProcess(); |
| ASSERT_TRUE(process); |
| RenderProcessHostWatcher crash_observer( |
| process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| process->Shutdown(0); |
| crash_observer.Wait(); |
| |
| // Navigate the popup to another browsing context group in the same |
| // CoopRelatedGroup. |
| ASSERT_TRUE(NavigateToURL(popup_window, coop_rp_page)); |
| RenderFrameHostImpl* second_popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| second_popup_rfh->GetSiteInstance())); |
| |
| // Because they are in different browsing context groups in the same |
| // CoopRelatedGroup, access should be restricted. |
| std::string result = |
| EvalJs(current_frame_host(), |
| "try { window.w.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| result = EvalJs(second_popup_rfh, |
| "try { opener.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // Access to window.closed should not throw any exception. |
| EXPECT_EQ(true, EvalJs(second_popup_rfh, "opener.closed == false")); |
| EXPECT_EQ(true, EvalJs(current_frame_host(), "window.w.closed == false")); |
| } |
| |
| // Verify that navigating to another browsing context group in another |
| // CoopRelatedGroup, in one of the rare cases that preserve openers (here to a |
| // WebUI), propagates the correct BrowsingContextGroupInfo. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| NavigationToOtherCoopRelatedGroup) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL regular_page_2(https_server()->GetURL("b.test", "/title1.html")); |
| GURL webui_page("chrome://ukm"); |
| |
| // Start from a regular page and a popup in the same browsing context group. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ShellAddedObserver shell_observer; |
| ASSERT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = open($1);", regular_page_2))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_TRUE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Navigate to a WebUI page. It should use another browsing context group in |
| // another CoopRelatedGroup. This WebUI page will not have an opener, but will |
| // NOT clear proxies, keeping the handle in the main page valid. |
| // TODO(https://crbug.com/1366827): This is an unspec'd behavior and might |
| // change in the future. |
| ASSERT_TRUE(NavigateToURL(popup_window, webui_page)); |
| RenderFrameHostImpl* webui_popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| webui_popup_rfh->GetSiteInstance())); |
| ASSERT_EQ(true, EvalJs(webui_popup_rfh, "window.opener == null")); |
| ASSERT_EQ(false, EvalJs(current_frame_host(), "window.w == null")); |
| |
| // Because they are in different browsing context groups in different |
| // CoopRelatedGroups, access to cross-origin properties should conservatively |
| // NOT be restricted. |
| // TODO(https://crbug.com/1370351): This might change in the future, if we |
| // decide to impose restrictions on all accesses from different browsing |
| // context groups. |
| EXPECT_TRUE(ExecJs(current_frame_host(), "window.w.blur()")); |
| |
| // Some actions should be blocked nonetheless, regardless of COOP: |
| // restrict-properties. This is the case for sending postMessages. Set up a |
| // listener in the WebUI page, and send a message from the main page. If we |
| // have not received anything within a second, consider this passed. Receiving |
| // the message would throw an exception. |
| ASSERT_TRUE(ExecJs(webui_popup_rfh, R"( |
| window.future_message = new Promise( |
| (resolve, reject) => { |
| onmessage = (event) => { |
| if (event.data == 'test') { |
| reject('Received message'); |
| } |
| }; |
| setTimeout(resolve, 1000); |
| }); 0;)")); // This avoids waiting on the promise right now. |
| EXPECT_TRUE( |
| ExecJs(current_frame_host(), "window.w.postMessage('test', '*')")); |
| |
| // If we've received the message, this promise would be rejected and an |
| // exception would be thrown. |
| EXPECT_TRUE(ExecJs(webui_popup_rfh, "window.future_message;")); |
| |
| // Navigating frames in other CoopRelatedGroup should also not be permitted. |
| // Try to start a navigation and verify that nothing happened. |
| EXPECT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w.location = $1", regular_page))); |
| EXPECT_TRUE(WaitForLoadStop(popup_window)); |
| EXPECT_EQ(popup_window->GetLastCommittedURL(), webui_page); |
| } |
| |
| // This test verifies that two pages in different browsing context groups with |
| // the same origin trying to access each other does not cause a crash. |
| IN_PROC_BROWSER_TEST_P(CoopRestrictPropertiesAccessBrowserTest, |
| SameOriginInDifferentBrowsingContextGroupAccess) { |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start from a regular page and open a same-origin popup in another browsing |
| // context group in the same CoopRelatedGroup. Although the two pages are |
| // same-origin, they should only be able to reach out to each other using |
| // postMessage() and closed. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = window.open($1)", coop_rp_page))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| |
| // Because they are in different browsing context groups in the same |
| // CoopRelatedGroup, access to cross-origin properties should be restricted. |
| std::string result = |
| EvalJs(current_frame_host(), |
| "try { window.w.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| result = |
| EvalJs(popup_rfh, "try { opener.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // Similarly, same-origin properties access should be blocked. |
| result = EvalJs(current_frame_host(), |
| "try { window.w.name} catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| result = EvalJs(popup_rfh, "try { opener.name} catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // Always-allowed properties should still be accessible. |
| EXPECT_EQ(true, EvalJs(current_frame_host(), "window.w.closed == false")); |
| EXPECT_EQ(true, EvalJs(popup_rfh, "opener.closed == false")); |
| } |
| |
| // Similar to above test, but forces process reuse to have both the popup and |
| // the main page live in the same process. |
| IN_PROC_BROWSER_TEST_P( |
| CoopRestrictPropertiesAccessBrowserTest, |
| SameOriginInDifferentBrowsingContextGroupAccessSameProcess) { |
| // Some platform force COOP pages to be isolated, making this test irrelevant. |
| if (SiteIsolationPolicy::IsSiteIsolationForCOOPEnabled()) { |
| return; |
| } |
| |
| // Set a process limit of 1 for testing. This will force same-origin pages |
| // with different COOP status to share a process. |
| RenderProcessHostImpl::SetMaxRendererProcessCount(1); |
| |
| GURL regular_page(https_server()->GetURL("a.test", "/title1.html")); |
| GURL coop_rp_page(https_server()->GetURL( |
| "a.test", |
| "/set-header" |
| "?cross-origin-opener-policy: restrict-properties")); |
| |
| // Start from a regular page and open a same-origin popup in another browsing |
| // context group in the same CoopRelatedGroup. Although the two pages are |
| // same-origin, they should only be able to reach out to each other using |
| // postMessage() and closed. |
| ASSERT_TRUE(NavigateToURL(shell(), regular_page)); |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(current_frame_host(), |
| JsReplace("window.w = window.open($1)", coop_rp_page))); |
| WebContentsImpl* popup_window = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| |
| ASSERT_TRUE(WaitForLoadStop(popup_window)); |
| RenderFrameHostImpl* popup_rfh = popup_window->GetPrimaryMainFrame(); |
| ASSERT_FALSE(current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_TRUE( |
| current_frame_host()->GetSiteInstance()->IsCoopRelatedSiteInstance( |
| popup_rfh->GetSiteInstance())); |
| ASSERT_EQ(current_frame_host()->GetProcess(), popup_rfh->GetProcess()); |
| |
| // Because they are in different browsing context groups in the same |
| // CoopRelatedGroup, access to cross-origin properties should be restricted. |
| std::string result = |
| EvalJs(current_frame_host(), |
| "try { window.w.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| result = |
| EvalJs(popup_rfh, "try { opener.blur() } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // Similarly, same-origin properties access should also be blocked. |
| result = EvalJs(current_frame_host(), |
| "try { window.w.name } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| result = EvalJs(popup_rfh, "try { opener.name } catch (e) { e.toString(); }") |
| .ExtractString(); |
| EXPECT_THAT(result, ::testing::MatchesRegex(kCoopRpErrorMessageRegex)); |
| |
| // Always-allowed properties should still be accessible. |
| EXPECT_EQ(true, EvalJs(current_frame_host(), "window.w.closed == false")); |
| EXPECT_EQ(true, EvalJs(popup_rfh, "opener.closed == false")); |
| } |
| |
| } // namespace content |