| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/test/bind.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "content/browser/process_lock.h" |
| #include "content/browser/renderer_host/render_frame_host_impl.h" |
| #include "content/browser/site_instance_impl.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| #include "content/common/content_navigation_policy.h" |
| #include "content/public/browser/back_forward_cache.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/commit_message_delayer.h" |
| #include "content/public/test/content_browser_test.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/test_utils.h" |
| #include "content/shell/browser/shell.h" |
| #include "content/test/render_document_feature.h" |
| #include "content/test/test_content_browser_client.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "url/gurl.h" |
| |
| // Unassigned SiteInstances occur when ShouldAssignSiteForURL returns false, |
| // allowing a given SiteInstance to be reused for a future navigation. This test |
| // suite covers how Chrome should behave in these cases, and when SiteInstances |
| // should be reused or replaced. Note that there are some differences between |
| // about:blank and custom embedder-defined cases. |
| // |
| // TODO(crbug.com/1296173): We would like to enforce the fact that unassigned |
| // SiteInstances only ever exist in their own BrowsingInstance. The exact way to |
| // achieve that is still unclear. We might only allow leaving SiteInstances |
| // unassigned for empty schemes, or make the siteless behavior kick in only for |
| // the first navigation in a BrowsingInstance, for example. |
| // |
| // The test suite covers expectations for the following cases: |
| // - Navigation to about:blank, renderer/browser initiated. |
| // - Navigation from about:blank, renderer/browser initiated. |
| // - Navigation to embedder defined url, renderer/browser initiated. |
| // - Navigation from embedder defined url, renderer/browser initiated. |
| // - Initial empty document in popups. |
| // - Navigation in a popup, to about:blank, renderer/browser initiated. |
| // - Navigation in a popup, to embedder defined url, renderer/browser initiated. |
| // - Initial empty document in iframes. |
| // - Navigation in an iframe, to about:blank, renderer initiated. |
| // - Navigation in an iframe, to embedder defined url, renderer initiated. |
| // - Interactions with crossOriginIsolated pages. |
| // - Some bug reproducers, testing things like races and history navigations. |
| |
| namespace content { |
| |
| namespace { |
| |
| const std::string kEmptySchemeForTesting = "empty-scheme"; |
| |
| // ContentBrowserClient that skips assigning a site URL for a given empty |
| // document scheme. Also skips all URLs matching a given URL's scheme and host, |
| // for non-empty cases. |
| // TODO(https://crbug.com/1296173): Remove the non-empty document case. |
| class DontAssignSiteContentBrowserClient : public TestContentBrowserClient { |
| public: |
| // Any visit to |scheme_to_skip| or |url_to_skip| will not cause the site to |
| // be assigned to the SiteInstance. |
| explicit DontAssignSiteContentBrowserClient(const std::string& scheme_to_skip, |
| const GURL& url_to_skip) |
| : scheme_to_skip_(scheme_to_skip), url_to_skip_(url_to_skip) {} |
| |
| DontAssignSiteContentBrowserClient( |
| const DontAssignSiteContentBrowserClient&) = delete; |
| DontAssignSiteContentBrowserClient& operator=( |
| const DontAssignSiteContentBrowserClient&) = delete; |
| |
| bool ShouldAssignSiteForURL(const GURL& url) override { |
| // TODO(https://crbug.com/1296173): Remove url_to_skip_. |
| if (url.host() == url_to_skip_.host() && |
| url.scheme() == url_to_skip_.scheme()) { |
| return false; |
| } |
| return url.scheme() != scheme_to_skip_; |
| } |
| |
| private: |
| std::string scheme_to_skip_; |
| GURL url_to_skip_; |
| }; |
| |
| void InitBackForwardCacheFeature(base::test::ScopedFeatureList* feature_list, |
| bool enable_back_forward_cache) { |
| if (enable_back_forward_cache) { |
| std::vector<base::test::FeatureRefAndParams> features; |
| features.push_back({features::kBackForwardCache, {}}); |
| features.push_back({kBackForwardCacheNoTimeEviction, {}}); |
| features.push_back({features::kBackForwardCacheMemoryControls, {}}); |
| feature_list->InitWithFeaturesAndParameters(features, {}); |
| } else { |
| feature_list->InitAndDisableFeature(features::kBackForwardCache); |
| } |
| } |
| |
| } // namespace |
| |
| // Note that this test suite is parametrized for RenderDocument and |
| // BackForwardCache, like many tests involving navigations, and SiteInstance |
| // picking. This is due to the fact that both features have an important impact |
| // on navigations and are likely to interact. |
| class UnassignedSiteInstanceBrowserTest |
| : public ContentBrowserTest, |
| public ::testing::WithParamInterface<std::tuple<std::string, bool>> { |
| public: |
| UnassignedSiteInstanceBrowserTest() |
| : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) { |
| InitAndEnableRenderDocumentFeature(&feature_list_for_render_document_, |
| std::get<0>(GetParam())); |
| InitBackForwardCacheFeature(&feature_list_for_back_forward_cache_, |
| std::get<1>(GetParam())); |
| url::AddEmptyDocumentScheme(kEmptySchemeForTesting.c_str()); |
| } |
| |
| // 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"); |
| } |
| |
| void SetUpOnMainThread() override { |
| // Allow all hosts on HTTPS. |
| mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); |
| // Support multiple sites on the test server. |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| https_server_.AddDefaultHandlers(GetTestDataFilePath()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE(https_server_.Start()); |
| |
| regular_url_ = https_server_.GetURL("a.test", "/title1.html"); |
| cross_origin_isolated_url_ = |
| https_server_.GetURL("a.test", |
| "/set-header?" |
| "Cross-Origin-Opener-Policy: same-origin&" |
| "Cross-Origin-Embedder-Policy: require-corp"); |
| unassigned_url_ = GURL("about:blank"); |
| embedder_defined_unassigned_url_ = GURL(kEmptySchemeForTesting + "://test"); |
| embedder_defined_nonempty_unassigned_url_ = |
| https_server_.GetURL("b.test", "/title1.html"); |
| |
| regular_http_url_ = |
| embedded_test_server()->GetURL("a.test", "/title1.html"); |
| |
| // Set up a URL for which ShouldAssignSiteForURL will return false. The |
| // corresponding SiteInstance's site will be left unassigned, and its |
| // process won't be locked. Note that this applies to the entire site |
| // instead of the specific url, so we use b.test origin to keep a.test |
| // assigned. |
| content_browser_client_override_ = |
| std::make_unique<DontAssignSiteContentBrowserClient>( |
| kEmptySchemeForTesting, embedder_defined_nonempty_unassigned_url_); |
| old_content_browser_client_ = |
| SetBrowserClientForTesting(content_browser_client_override_.get()); |
| } |
| |
| void TearDownOnMainThread() override { |
| if (old_content_browser_client_) |
| SetBrowserClientForTesting(old_content_browser_client_); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| mock_cert_verifier_.SetUpCommandLine(command_line); |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); |
| } |
| |
| void TearDownInProcessBrowserTestFixture() override { |
| mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); |
| } |
| |
| WebContentsImpl* web_contents() { |
| return static_cast<WebContentsImpl*>(shell()->web_contents()); |
| } |
| |
| net::EmbeddedTestServer* https_server() { return &https_server_; } |
| |
| void DisableBackForwardCache( |
| BackForwardCacheImpl::DisableForTestingReason reason) const { |
| return static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetController() |
| .GetBackForwardCache() |
| .DisableForTesting(reason); |
| } |
| |
| // Returns an url that assigns a site to the SiteInstance it lives in. |
| const GURL& regular_url() const { return regular_url_; } |
| |
| // Returns an url that assigns a site to the SiteInstance it lives in and is |
| // crossOriginIsolated. |
| const GURL& cross_origin_isolated_url() const { |
| return cross_origin_isolated_url_; |
| } |
| |
| // Returns an url that does not assign a site to the SiteInstance it lives in. |
| const GURL& unassigned_url() const { return unassigned_url_; } |
| |
| // Returns an url from an empty document scheme that does not assign a site to |
| // the SiteInstance it lives in, as decided by the content embedder. |
| const GURL& embedder_defined_unassigned_url() const { |
| return embedder_defined_unassigned_url_; |
| } |
| |
| // Returns an url that commits actual content but that does not assign a site |
| // to the SiteInstance it lives in, as decided by the content embedder. |
| // TODO(https://crbug.com/1296173): Remove this ability and require all |
| // unassigned site URLs to be empty document schemes. |
| const GURL& embedder_defined_nonempty_unassigned_url() const { |
| return embedder_defined_nonempty_unassigned_url_; |
| } |
| |
| // Returns an url that assigns a site to the SiteInstance it lives in, and |
| // that is served using HTTP. |
| const GURL& regular_http_url() const { return regular_http_url_; } |
| |
| private: |
| net::EmbeddedTestServer https_server_; |
| content::ContentMockCertVerifier mock_cert_verifier_; |
| |
| GURL regular_url_; |
| GURL cross_origin_isolated_url_; |
| GURL unassigned_url_; |
| GURL embedder_defined_unassigned_url_; |
| GURL embedder_defined_nonempty_unassigned_url_; |
| |
| // This is the only URL that uses HTTP instead of HTTPS, because |
| // IsolateOriginsForTesting() has a hardcoded HTTP filter in it. It should |
| // only be used for that specific purpose. |
| GURL regular_http_url_; |
| |
| std::unique_ptr<DontAssignSiteContentBrowserClient> |
| content_browser_client_override_; |
| raw_ptr<ContentBrowserClient> old_content_browser_client_ = nullptr; |
| |
| base::test::ScopedFeatureList feature_list_for_render_document_; |
| base::test::ScopedFeatureList feature_list_for_back_forward_cache_; |
| url::ScopedSchemeRegistryForTests scheme_registry_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| RendererInitiatedNavigationTo) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(web_contents(), regular_url())); |
| RenderFrameHostImpl* initial_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> initial_si = initial_rfh->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| |
| // Do a renderer-initiated navigation to an unassigned url. |
| EXPECT_TRUE(NavigateToURLFromRenderer(initial_rfh, unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| |
| if (!BackForwardCache::IsBackForwardCacheFeatureEnabled()) { |
| // Normally we reuse the SiteInstance. |
| EXPECT_TRUE(unassigned_si->HasSite()); |
| EXPECT_EQ(unassigned_si, initial_si); |
| } else { |
| // With back/forward cache, we will swap browsing instance for same-site |
| // navigations, not reusing the previous SiteInstance. |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| EXPECT_FALSE(unassigned_si->IsRelatedSiteInstance(initial_si.get())); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| BrowserInitiatedNavigationTo) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(web_contents(), regular_url())); |
| scoped_refptr<SiteInstanceImpl> initial_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| |
| // Do a browser initiated navigation to an unassigned url. |
| EXPECT_TRUE(NavigateToURL(shell(), unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| |
| if (!BackForwardCache::IsBackForwardCacheFeatureEnabled()) { |
| // Normally we reuse the SiteInstance. |
| EXPECT_TRUE(unassigned_si->HasSite()); |
| EXPECT_EQ(unassigned_si, initial_si); |
| } else { |
| // With back/forward cache, we will swap browsing instance for same-site |
| // navigations, not reusing the previous SiteInstance. |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| EXPECT_FALSE(unassigned_si->IsRelatedSiteInstance(initial_si.get())); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| RendererInitiatedNavigationFrom) { |
| // Get a base page without a site. |
| EXPECT_TRUE(NavigateToURL(web_contents(), unassigned_url())); |
| RenderFrameHostImpl* unassigned_url_rfh = |
| web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| unassigned_url_rfh->GetSiteInstance(); |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| |
| // Do a renderer-initiated navigation to an assigned url. We reuse the |
| // unassigned SiteInstance and set its site. |
| EXPECT_TRUE(NavigateToURLFromRenderer(unassigned_url_rfh, regular_url())); |
| scoped_refptr<SiteInstanceImpl> initial_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| EXPECT_EQ(unassigned_si, initial_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| BrowserInitiatedNavigationFrom) { |
| // Get a base page without a site. |
| EXPECT_TRUE(NavigateToURL(web_contents(), unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| |
| // Do a browser-initiated navigation to an assigned url. We reuse the |
| // unassigned SiteInstance and set its site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| scoped_refptr<SiteInstanceImpl> initial_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| EXPECT_EQ(unassigned_si, initial_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| RendererInitiatedNavigationTo_CustomUrl) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| RenderFrameHostImpl* initial_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> initial_si = initial_rfh->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| |
| // Do a renderer-initiated navigation to an embedder-defined unassigned url. |
| // We use a new related or unrelated SiteInstance depending on the |
| // ProactivelySwapBrowsingInstance feature. This differs from the base |
| // unassigned behavior, because about:blank is explicitly considered as same |
| // site with all other site, but custom embedder-defined unassigned urls are |
| // not. |
| EXPECT_TRUE(NavigateToURLFromRenderer(initial_rfh, |
| embedder_defined_unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| if (CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) { |
| EXPECT_FALSE(unassigned_si->IsRelatedSiteInstance(initial_si.get())); |
| } else { |
| EXPECT_TRUE(unassigned_si->IsRelatedSiteInstance(initial_si.get())); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| BrowserInitiatedNavigationTo_CustomUrl) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(web_contents(), regular_url())); |
| scoped_refptr<SiteInstanceImpl> initial_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| |
| // Do a browser initiated navigation to an embedder-defined unassigned url. |
| // We do not reuse the assigned SiteInstance. We use a new SiteInstance in its |
| // own BrowsingInstance. |
| // Note: This differs from the renderer initiated behavior. This comes from |
| // the fact that IsBrowsingInstanceSwapAllowedForPageTransition() explicitly |
| // excludes renderer initiated navigations from generic cross-site |
| // BrowsingInstance swap. |
| EXPECT_TRUE(NavigateToURL(web_contents(), embedder_defined_unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| EXPECT_FALSE(unassigned_si->IsRelatedSiteInstance(initial_si.get())); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| RendererInitiatedNavigationFrom_CustomUrl) { |
| // Get a base page without a site. |
| EXPECT_TRUE(NavigateToURL(web_contents(), embedder_defined_unassigned_url())); |
| RenderFrameHostImpl* unassigned_url_rfh = |
| web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| unassigned_url_rfh->GetSiteInstance(); |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| |
| // Do a renderer-initiated navigation to an assigned url. We reuse the |
| // unassigned SiteInstance and set its site, because the process is unused. |
| EXPECT_TRUE(NavigateToURLFromRenderer(unassigned_url_rfh, regular_url())); |
| scoped_refptr<SiteInstanceImpl> initial_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| EXPECT_EQ(unassigned_si, initial_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| BrowserInitiatedNavigationFrom_CustomUrl) { |
| // Get a base page without a site. |
| EXPECT_TRUE(NavigateToURL(web_contents(), embedder_defined_unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| |
| // Do a browser-initiated navigation to an assigned url. We reuse the |
| // unassigned SiteInstance and set its site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| scoped_refptr<SiteInstanceImpl> initial_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| EXPECT_EQ(unassigned_si, initial_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| RendererInitiatedNavigationFrom_NonEmptyCustomUrl) { |
| // Get a base page without a site that commits content. |
| EXPECT_TRUE(NavigateToURL(web_contents(), |
| embedder_defined_nonempty_unassigned_url())); |
| RenderFrameHostImpl* unassigned_url_rfh = |
| web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| unassigned_url_rfh->GetSiteInstance(); |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| |
| // Do a renderer-initiated navigation to an assigned url. We cannot reuse the |
| // unassigned SiteInstance because it has already been marked as used |
| // (unless Site Isolation is disabled and the regular url does not require a |
| // dedicated process). |
| // TODO(crbug.com/1296173): We also shouldn't let an embedder load arbitrary |
| // content without assigning a site. This should be restricted to empty |
| // schemes that do not load content into a renderer. |
| EXPECT_TRUE(NavigateToURLFromRenderer(unassigned_url_rfh, regular_url())); |
| scoped_refptr<SiteInstanceImpl> initial_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| if (AreAllSitesIsolatedForTesting()) |
| EXPECT_NE(unassigned_si, initial_si); |
| else |
| EXPECT_EQ(unassigned_si, initial_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| BrowserInitiatedNavigationFrom_NonEmptyCustomUrl) { |
| // Get a base page without a site that commits content. |
| EXPECT_TRUE(NavigateToURL(web_contents(), |
| embedder_defined_nonempty_unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| |
| // Do a browser-initiated navigation to an assigned url. We cannot reuse the |
| // unassigned SiteInstance because it has already been marked as used |
| // (unless Site Isolation is disabled and the regular url does not require a |
| // dedicated process). |
| // TODO(crbug.com/1296173): We also shouldn't let an embedder load arbitrary |
| // content without assigning a site. This should be restricted to empty |
| // schemes that do not load content into a renderer. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| scoped_refptr<SiteInstanceImpl> initial_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| if (AreAllSitesIsolatedForTesting()) |
| EXPECT_NE(unassigned_si, initial_si); |
| else |
| EXPECT_EQ(unassigned_si, initial_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| InPopup_InitialAboutBlank) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| RenderFrameHostImpl* original_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> original_si = original_rfh->GetSiteInstance(); |
| EXPECT_TRUE(original_si->HasSite()); |
| |
| // Create a popup. It should reuse the opener SiteInstance. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(original_rfh, "window.open()")); |
| scoped_refptr<SiteInstanceImpl> popup_si = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()) |
| ->GetPrimaryMainFrame() |
| ->GetSiteInstance(); |
| EXPECT_TRUE(popup_si->HasSite()); |
| EXPECT_EQ(popup_si, original_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| InPopup_RendererInitiatedNavigateTo) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| RenderFrameHostImpl* original_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> original_si = original_rfh->GetSiteInstance(); |
| EXPECT_TRUE(original_si->HasSite()); |
| |
| // Create a same-origin popup. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE( |
| ExecJs(original_rfh, JsReplace("window.open($1)", regular_url()))); |
| WebContentsImpl* popup_web_contents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| EXPECT_TRUE(WaitForLoadStop(popup_web_contents)); |
| scoped_refptr<SiteInstanceImpl> popup_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(popup_si->HasSite()); |
| EXPECT_EQ(popup_si, original_si); |
| |
| if (AreDefaultSiteInstancesEnabled()) |
| EXPECT_TRUE(popup_si->IsDefaultSiteInstance()); |
| |
| // In the popup, do a renderer-initiated navigation to an unassigned url. We |
| // should reuse the SiteInstance. |
| EXPECT_TRUE(NavigateToURLFromRenderer( |
| popup_web_contents->GetPrimaryMainFrame(), unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> post_navigation_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(post_navigation_si->HasSite()); |
| EXPECT_EQ(post_navigation_si, original_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| InPopup_BrowserInitiatedNavigateTo) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| RenderFrameHostImpl* original_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> original_si = original_rfh->GetSiteInstance(); |
| EXPECT_TRUE(original_si->HasSite()); |
| |
| // Create a same-origin popup. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE( |
| ExecJs(original_rfh, JsReplace("window.open($1)", regular_url()))); |
| WebContentsImpl* popup_web_contents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| EXPECT_TRUE(WaitForLoadStop(popup_web_contents)); |
| scoped_refptr<SiteInstanceImpl> popup_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(popup_si->HasSite()); |
| EXPECT_EQ(popup_si, original_si); |
| |
| if (AreDefaultSiteInstancesEnabled()) |
| EXPECT_TRUE(popup_si->IsDefaultSiteInstance()); |
| |
| // In the popup, do a browser-initiated navigation to an unassigned url. We |
| // should reuse the SiteInstance. |
| EXPECT_TRUE(NavigateToURL(popup_web_contents, unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> post_navigation_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(post_navigation_si->HasSite()); |
| EXPECT_EQ(post_navigation_si, original_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| InPopup_RendererInitiatedNavigateTo_CustomUrl) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| RenderFrameHostImpl* original_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> original_si = original_rfh->GetSiteInstance(); |
| EXPECT_TRUE(original_si->HasSite()); |
| |
| // Create a same-origin popup. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE( |
| ExecJs(original_rfh, JsReplace("window.open($1)", regular_url()))); |
| WebContentsImpl* popup_web_contents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| EXPECT_TRUE(WaitForLoadStop(popup_web_contents)); |
| scoped_refptr<SiteInstanceImpl> popup_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(popup_si->HasSite()); |
| EXPECT_EQ(popup_si, original_si); |
| |
| if (AreDefaultSiteInstancesEnabled()) |
| EXPECT_TRUE(popup_si->IsDefaultSiteInstance()); |
| |
| // In the popup, do a renderer-initiated navigation to an embedder-defined |
| // unassigned url. We use another related SiteInstance. Note that contrary to |
| // its main window counterpart, here we never swap BrowsingInstance, because |
| // ProactivelySwapBrowsingInstance never applies to popups. |
| EXPECT_TRUE( |
| NavigateToURLFromRenderer(popup_web_contents->GetPrimaryMainFrame(), |
| embedder_defined_unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> post_navigation_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(post_navigation_si->HasSite()); |
| EXPECT_TRUE(post_navigation_si->IsRelatedSiteInstance(original_si.get())); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| InPopup_BrowserInitiatedNavigateTo_CustomUrl) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| RenderFrameHostImpl* original_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> original_si = original_rfh->GetSiteInstance(); |
| EXPECT_TRUE(original_si->HasSite()); |
| |
| // Create a same-origin popup. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE( |
| ExecJs(original_rfh, JsReplace("window.open($1)", regular_url()))); |
| WebContentsImpl* popup_web_contents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| EXPECT_TRUE(WaitForLoadStop(popup_web_contents)); |
| scoped_refptr<SiteInstanceImpl> popup_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(popup_si->HasSite()); |
| EXPECT_EQ(popup_si, original_si); |
| |
| if (AreDefaultSiteInstancesEnabled()) |
| EXPECT_TRUE(popup_si->IsDefaultSiteInstance()); |
| |
| // In the popup, do a browser-initiated navigation to an embedder-defined |
| // unassigned url. We swap browsing instance because the navigation is |
| // considered cross-site. |
| EXPECT_TRUE( |
| NavigateToURL(popup_web_contents, embedder_defined_unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> post_navigation_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(post_navigation_si->HasSite()); |
| EXPECT_FALSE(post_navigation_si->IsRelatedSiteInstance(original_si.get())); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| CrossOriginIsolated_BrowserInitiatedNavigationTo) { |
| // Get a base crossOriginIsolated page with a site. |
| EXPECT_TRUE(NavigateToURL(web_contents(), cross_origin_isolated_url())); |
| scoped_refptr<SiteInstanceImpl> initial_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| EXPECT_TRUE(initial_si->IsCrossOriginIsolated()); |
| |
| // Do a browser initiated navigation to an unassigned url. |
| EXPECT_TRUE(NavigateToURL(shell(), unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| |
| // We explicitly disable SiteInstance reuse for now. Restricting which custom |
| // sites are allowed to be described by the embedder as unassigned might |
| // change that in the future. |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| EXPECT_FALSE(unassigned_si->IsRelatedSiteInstance(initial_si.get())); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| CrossOriginIsolated_BrowserInitiatedNavigationFrom) { |
| // Get a base page without a site. |
| EXPECT_TRUE(NavigateToURL(web_contents(), unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> unassigned_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(unassigned_si->HasSite()); |
| |
| // Do a browser-initiated navigation to a crossOriginIsolated assigned url. We |
| // should not reuse the unassigned SiteInstance currently, because we have no |
| // guarantee that the unassigned page won't load content in the process. |
| // Restricting which custom sites are allowed to be described by the embedder |
| // as unassigned might change that in the future. |
| EXPECT_TRUE(NavigateToURL(shell(), cross_origin_isolated_url())); |
| scoped_refptr<SiteInstanceImpl> initial_si = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_TRUE(initial_si->HasSite()); |
| EXPECT_TRUE(initial_si->IsCrossOriginIsolated()); |
| EXPECT_FALSE(initial_si->IsRelatedSiteInstance(unassigned_si.get())); |
| } |
| |
| // Regression test for https://crbug.com/1324407. |
| // TODO(https://crbug.com/1296173): Require unassigned SiteInstances to have |
| // empty document schemes, making the bug exercised in this test impossible. |
| // This test can then show what's expected for an empty document case. |
| IN_PROC_BROWSER_TEST_P( |
| UnassignedSiteInstanceBrowserTest, |
| InPopup_RendererInitiatedNavigateFrom_NonEmptyCustomUrl) { |
| if (IsIsolatedOriginRequiredToGuaranteeDedicatedProcess()) { |
| // Isolate a.test so that regular_url() is expected to be in a dedicated |
| // process, but also so that embedder_defined_nonempty_unassigned_url |
| // (i.e., b.test) does not expect a dedicated process on Android. |
| IsolateOriginsForTesting(https_server(), web_contents(), {"a.test"}); |
| } |
| |
| // Get a base page with an embedder-defined non-empty unassigned url. |
| EXPECT_TRUE( |
| NavigateToURL(shell(), embedder_defined_nonempty_unassigned_url())); |
| RenderFrameHostImpl* original_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> original_si = original_rfh->GetSiteInstance(); |
| EXPECT_FALSE(original_si->HasSite()); |
| RenderProcessHost* original_process = original_si->GetProcess(); |
| EXPECT_TRUE(original_process->GetProcessLock().allows_any_site()); |
| |
| // Create a same-origin popup. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(original_rfh, "window.open()")); |
| WebContentsImpl* popup_web_contents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| scoped_refptr<SiteInstanceImpl> popup_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(popup_si->HasSite()); |
| EXPECT_EQ(popup_si, original_si); |
| |
| // In the popup, do a renderer-initiated navigation to a regular url. We use |
| // another related SiteInstance. Note that contrary to its main window |
| // counterpart, here we never swap BrowsingInstance, because |
| // ProactivelySwapBrowsingInstance never applies to popups. |
| // |
| // This used to reuse the unassigned SiteInstance, even though the process had |
| // already loaded a real page and the new URL requires a dedicated process. |
| EXPECT_TRUE(NavigateToURLFromRenderer( |
| popup_web_contents->GetPrimaryMainFrame(), regular_http_url())); |
| scoped_refptr<SiteInstanceImpl> post_navigation_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_NE(original_si, post_navigation_si); |
| EXPECT_NE(original_process, post_navigation_si->GetProcess()); |
| EXPECT_FALSE(original_si->HasSite()); |
| EXPECT_TRUE(post_navigation_si->HasSite()); |
| RenderProcessHost* popup_process = post_navigation_si->GetProcess(); |
| EXPECT_TRUE(popup_process->GetProcessLock().is_locked_to_site()); |
| |
| // Try to create a blob URL in the original document. This used to be in the |
| // same process as regular_url(), which is now locked to a different site, |
| // causing a renderer kill. Thus, a second ExecJS call would fail. (Just |
| // calling IsRenderFrameLive immediately after the first call may not fail.) |
| EXPECT_TRUE(ExecJs(original_rfh, "URL.createObjectURL(new Blob())")); |
| EXPECT_TRUE(ExecJs(original_rfh, "true")); |
| EXPECT_TRUE(original_rfh->IsRenderFrameLive()); |
| } |
| |
| // Regression test for https://crbug.com/1324407. |
| // TODO(https://crbug.com/1296173): Require unassigned SiteInstances to have |
| // empty document schemes, making the bug exercised in this test impossible. |
| // This test can then show what's expected for an empty document case. |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| InPopup_BrowserInitiatedNavigateFrom_NonEmptyCustomUrl) { |
| if (IsIsolatedOriginRequiredToGuaranteeDedicatedProcess()) { |
| // Isolate a.test so that regular_url() is expected to be in a dedicated |
| // process, but also so that embedder_defined_nonempty_unassigned_url |
| // (i.e., b.test) does not expect a dedicated process on Android. |
| IsolateOriginsForTesting(embedded_test_server(), web_contents(), |
| {"a.test"}); |
| } |
| |
| // Get a base page with an embedder-defined non-empty unassigned url. |
| EXPECT_TRUE( |
| NavigateToURL(shell(), embedder_defined_nonempty_unassigned_url())); |
| RenderFrameHostImpl* original_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> original_si = original_rfh->GetSiteInstance(); |
| EXPECT_FALSE(original_si->HasSite()); |
| RenderProcessHost* original_process = original_si->GetProcess(); |
| EXPECT_TRUE(original_process->GetProcessLock().allows_any_site()); |
| |
| // Create a same-origin popup. |
| ShellAddedObserver shell_observer; |
| EXPECT_TRUE(ExecJs(original_rfh, "window.open()")); |
| WebContentsImpl* popup_web_contents = |
| static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents()); |
| scoped_refptr<SiteInstanceImpl> popup_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_FALSE(popup_si->HasSite()); |
| EXPECT_EQ(popup_si, original_si); |
| |
| // In the popup, do a browser-initiated navigation to a regular url. This used |
| // to reuse the unassigned SiteInstance, even though the process had already |
| // loaded a real page and the new URL requires a dedicated process. |
| EXPECT_TRUE(NavigateToURL(popup_web_contents, regular_http_url())); |
| scoped_refptr<SiteInstanceImpl> post_navigation_si = |
| popup_web_contents->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_NE(original_si, post_navigation_si); |
| EXPECT_NE(original_process, post_navigation_si->GetProcess()); |
| EXPECT_FALSE(original_si->HasSite()); |
| EXPECT_TRUE(post_navigation_si->HasSite()); |
| RenderProcessHost* popup_process = post_navigation_si->GetProcess(); |
| EXPECT_TRUE(popup_process->GetProcessLock().is_locked_to_site()); |
| |
| // Try to create a blob URL in the original document. This used to be in the |
| // same process as regular_url(), which is now locked to a different site, |
| // causing a renderer kill. Thus, a second ExecJS call would fail. (Just |
| // calling IsRenderFrameLive immediately after the first call may not fail.) |
| EXPECT_TRUE(ExecJs(original_rfh, "URL.createObjectURL(new Blob())")); |
| EXPECT_TRUE(ExecJs(original_rfh, "true")); |
| EXPECT_TRUE(original_rfh->IsRenderFrameLive()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| InIframe_InitialAboutBlank) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| RenderFrameHostImpl* original_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> original_si = original_rfh->GetSiteInstance(); |
| EXPECT_TRUE(original_si->HasSite()); |
| |
| // Create an iframe. The initial empty document should reuse the parent |
| // SiteInstance. |
| ASSERT_TRUE(ExecJs(original_rfh, R"( |
| const frame = document.createElement('iframe'); |
| document.body.appendChild(frame); |
| )")); |
| scoped_refptr<SiteInstanceImpl> iframe_si = |
| original_rfh->child_at(0)->current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(iframe_si->HasSite()); |
| EXPECT_EQ(iframe_si, original_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| InIframe_RendererInitiatedNavigateTo) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| RenderFrameHostImpl* original_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> original_si = original_rfh->GetSiteInstance(); |
| EXPECT_TRUE(original_si->HasSite()); |
| |
| // Create a same-origin iframe. It should reuse the parent SiteInstance. |
| ASSERT_TRUE(ExecJs(original_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_url()))); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* iframe_rfh = |
| original_rfh->child_at(0)->current_frame_host(); |
| scoped_refptr<SiteInstanceImpl> iframe_si = iframe_rfh->GetSiteInstance(); |
| EXPECT_TRUE(iframe_si->HasSite()); |
| EXPECT_EQ(iframe_si, original_si); |
| |
| if (AreDefaultSiteInstancesEnabled()) |
| EXPECT_TRUE(iframe_si->IsDefaultSiteInstance()); |
| |
| // In the iframe, navigate to an unassigned url. We reuse the SiteInstance. |
| EXPECT_TRUE(NavigateToURLFromRenderer(iframe_rfh, unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> post_navigation_si = |
| original_rfh->child_at(0)->current_frame_host()->GetSiteInstance(); |
| EXPECT_TRUE(post_navigation_si->HasSite()); |
| EXPECT_EQ(post_navigation_si, original_si); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| InIframe_RendererInitiatedNavigateTo_CustomUrl) { |
| // Get a base page with a site. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| RenderFrameHostImpl* original_rfh = web_contents()->GetPrimaryMainFrame(); |
| scoped_refptr<SiteInstanceImpl> original_si = original_rfh->GetSiteInstance(); |
| EXPECT_TRUE(original_si->HasSite()); |
| |
| // Create a same-origin iframe. It should reuse the parent SiteInstance. |
| ASSERT_TRUE(ExecJs(original_rfh, JsReplace(R"( |
| const frame = document.createElement('iframe'); |
| frame.src = $1; |
| document.body.appendChild(frame); |
| )", |
| regular_url()))); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| RenderFrameHostImpl* iframe_rfh = |
| original_rfh->child_at(0)->current_frame_host(); |
| scoped_refptr<SiteInstanceImpl> iframe_si = iframe_rfh->GetSiteInstance(); |
| EXPECT_TRUE(iframe_si->HasSite()); |
| EXPECT_EQ(iframe_si, original_si); |
| |
| if (AreDefaultSiteInstancesEnabled()) |
| EXPECT_TRUE(iframe_si->IsDefaultSiteInstance()); |
| |
| // In the iframe, navigate to an embedder defined unassigned url. We use a new |
| // related SiteInstance because the navigation is considered cross-site. |
| EXPECT_TRUE( |
| NavigateToURLFromRenderer(iframe_rfh, embedder_defined_unassigned_url())); |
| scoped_refptr<SiteInstanceImpl> post_navigation_si = |
| original_rfh->child_at(0)->current_frame_host()->GetSiteInstance(); |
| |
| // On Android, we sometimes do not get a related SiteInstance, but the same |
| // default SiteInstance to reduce memory footprint. |
| if (AreDefaultSiteInstancesEnabled()) { |
| EXPECT_TRUE(post_navigation_si->IsDefaultSiteInstance()); |
| EXPECT_TRUE(post_navigation_si->HasSite()); |
| EXPECT_EQ(post_navigation_si, original_si); |
| } else { |
| EXPECT_FALSE(post_navigation_si->HasSite()); |
| EXPECT_TRUE(post_navigation_si->IsRelatedSiteInstance(original_si.get())); |
| } |
| } |
| |
| // Ensure that coming back to a NavigationEntry with a previously unassigned |
| // SiteInstance (which is now used for another site) properly switches processes |
| // and SiteInstances. See https://crbug.com/945399. |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| BackToNowAssignedSiteInstance) { |
| // Navigate to a URL that does not assign site URLs. |
| EXPECT_TRUE(NavigateToURL(shell(), embedder_defined_unassigned_url())); |
| EXPECT_EQ(embedder_defined_unassigned_url(), |
| web_contents()->GetLastCommittedURL()); |
| scoped_refptr<SiteInstanceImpl> instance1( |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); |
| RenderProcessHost* process1 = instance1->GetProcess(); |
| EXPECT_EQ(GURL(), instance1->GetSiteURL()); |
| |
| // Navigate to page that uses up the site. It should reuse the previous |
| // SiteInstance and set its site URL. |
| EXPECT_TRUE(NavigateToURL(shell(), regular_url())); |
| EXPECT_EQ(instance1, |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); |
| EXPECT_TRUE(instance1->HasSite()); |
| if (AreDefaultSiteInstancesEnabled()) { |
| EXPECT_TRUE(instance1->IsDefaultSiteInstance()); |
| } else { |
| EXPECT_EQ(GURL("https://a.test"), instance1->GetSiteURL()); |
| } |
| |
| // The previously committed entry should get a new, related instance to avoid |
| // a SiteInstance mismatch when returning to it. See http://crbug.com/992198 |
| // for further context. |
| scoped_refptr<SiteInstanceImpl> prev_entry_instance = |
| web_contents() |
| ->GetController() |
| .GetEntryAtIndex(0) |
| ->root_node() |
| ->frame_entry->site_instance(); |
| EXPECT_NE(prev_entry_instance, instance1); |
| EXPECT_NE(prev_entry_instance, nullptr); |
| EXPECT_TRUE(prev_entry_instance->IsRelatedSiteInstance(instance1.get())); |
| EXPECT_EQ(GURL(), prev_entry_instance->GetSiteURL()); |
| |
| // Navigate to bar.com, which destroys the previous RenderProcessHost. |
| GURL other_regular_url( |
| https_server()->GetURL("another.test", "/title1.html")); |
| RenderProcessHostWatcher exit_observer( |
| process1, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| |
| // With BackForwardCache, old process won't be deleted on navigation as it is |
| // still in use by the bfcached document, disable back-forward cache to ensure |
| // that the process gets deleted. |
| DisableBackForwardCache(BackForwardCacheImpl::TEST_REQUIRES_NO_CACHING); |
| |
| EXPECT_TRUE(NavigateToURL(shell(), other_regular_url)); |
| exit_observer.Wait(); |
| |
| if (AreDefaultSiteInstancesEnabled()) { |
| // Verify that the new navigation also results in a default SiteInstance, |
| // and verify that it is not related to |instance1| because the navigation |
| // swapped to a new BrowsingInstance. |
| EXPECT_TRUE(web_contents() |
| ->GetPrimaryMainFrame() |
| ->GetSiteInstance() |
| ->IsDefaultSiteInstance()); |
| EXPECT_FALSE(instance1->IsRelatedSiteInstance( |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance())); |
| } else { |
| EXPECT_NE(instance1, |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); |
| } |
| |
| // At this point, process1 is deleted, and the first entry is unfortunately |
| // pointing to instance1, which has been locked to url2 and has no process. |
| EXPECT_FALSE(instance1->HasProcess()); |
| if (AreAllSitesIsolatedForTesting()) { |
| // In site-per-process, we cannot use foo.com's SiteInstance for a.com. |
| EXPECT_FALSE(instance1->IsSuitableForUrlInfo( |
| UrlInfo::CreateForTesting(embedder_defined_unassigned_url()))); |
| } else if (AreDefaultSiteInstancesEnabled()) { |
| // Since |instance1| is a default SiteInstance AND this test explicitly |
| // ensures that ShouldAssignSiteForURL(url1) will return false, |url1| |
| // cannot be placed in the default SiteInstance. This also means that |url1| |
| // cannot be placed in the same process as the default SiteInstance. |
| EXPECT_FALSE(instance1->IsSuitableForUrlInfo( |
| UrlInfo::CreateForTesting(embedder_defined_unassigned_url()))); |
| } else { |
| // If neither foo.com nor a.com require dedicated processes, then we can use |
| // the same process. |
| EXPECT_TRUE(instance1->IsSuitableForUrlInfo( |
| UrlInfo::CreateForTesting(embedder_defined_unassigned_url()))); |
| } |
| |
| // Go back to url1's entry, which should swap to a new SiteInstance with an |
| // unused site URL. |
| TestNavigationObserver observer(web_contents()); |
| web_contents()->GetController().GoToOffset(-2); |
| observer.Wait(); |
| scoped_refptr<SiteInstanceImpl> new_instance = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| EXPECT_EQ(embedder_defined_unassigned_url(), |
| web_contents()->GetLastCommittedURL()); |
| EXPECT_NE(instance1, new_instance); |
| EXPECT_EQ(GURL(), new_instance->GetSiteURL()); |
| EXPECT_TRUE(new_instance->HasProcess()); |
| |
| // Because embedder_defined_unassigned_url does not set a site URL, it should |
| // not lock the new process either, so that it can be used for subsequent |
| // navigations. |
| RenderProcessHost* new_process = new_instance->GetProcess(); |
| auto* policy = ChildProcessSecurityPolicy::GetInstance(); |
| EXPECT_TRUE(policy->CanAccessDataForOrigin( |
| new_process->GetID(), |
| url::Origin::Create(embedder_defined_unassigned_url()))); |
| EXPECT_TRUE(policy->CanAccessDataForOrigin( |
| new_process->GetID(), url::Origin::Create(regular_url()))); |
| } |
| |
| // Check that when a navigation to a URL that doesn't require assigning a site |
| // URL is in progress, another navigation can't reuse the same process in the |
| // meantime. Such reuse previously led to a renderer kill when the unassigned |
| // URL later committed; a real-world example of the unassigned URL was |
| // chrome-native://newtab. See https://crbug.com/970046. |
| IN_PROC_BROWSER_TEST_P(UnassignedSiteInstanceBrowserTest, |
| NavigationRacesWithCommitInunassignedSiteInstance) { |
| // Prepare for a second navigation to a normal URL. Ensure it's isolated so |
| // that it requires a process lock on all platforms. |
| auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); |
| policy->AddFutureIsolatedOrigins( |
| {url::Origin::Create(regular_url())}, |
| ChildProcessSecurityPolicy::IsolatedOriginSource::TEST); |
| |
| // Create a new shell where the normal url origin isolation will take effect. |
| Shell* shell = CreateBrowser(); |
| WebContentsImpl* web_contents = |
| static_cast<WebContentsImpl*>(shell->web_contents()); |
| FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root(); |
| RenderProcessHost* regular_process = nullptr; |
| TestNavigationManager regular_manager(web_contents, regular_url()); |
| auto& current_isolation_context = |
| root->current_frame_host()->GetSiteInstance()->GetIsolationContext(); |
| auto site_info = SiteInfo::CreateForTesting(current_isolation_context, |
| GURL("https://a.test")); |
| EXPECT_TRUE(site_info.RequiresDedicatedProcess(current_isolation_context)); |
| |
| // Set up the work to be done after the renderer is asked to commit |
| // |embedder_defined_unassigned_url|, but before the corresponding |
| // DidCommitProvisionalLoad IPC is processed. This will start a navigation to |
| // |regular_url| and wait for its response. |
| auto did_commit_callback = |
| base::BindLambdaForTesting([&](RenderFrameHost* rfh) { |
| // The navigation should stay in the initial empty SiteInstance, with |
| // the site still unassigned. |
| EXPECT_FALSE( |
| static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance())->HasSite()); |
| EXPECT_FALSE(root->render_manager()->speculative_frame_host()); |
| |
| shell->LoadURL(regular_url()); |
| |
| // The foo.com navigation should swap to a new process, since it is not |
| // safe to reuse |embedder_defined_unassigned_url|'s process before |
| // |embedder_defined_unassigned_url| commits. |
| EXPECT_TRUE(root->render_manager()->speculative_frame_host()); |
| regular_process = |
| root->render_manager()->speculative_frame_host()->GetProcess(); |
| |
| // Wait for response. This will cause |regular_manager| to spin up a |
| // nested message loop while we're blocked in the current message loop |
| // (within DidCommitNavigationInterceptor). Thus, it's important to |
| // allow nestable tasks in |regular_manager|'s message loop, so that it |
| // can process the response before we unblock the |
| // DidCommitNavigationInterceptor's message loop and finish processing |
| // the commit. |
| regular_manager.AllowNestableTasks(); |
| EXPECT_TRUE(regular_manager.WaitForResponse()); |
| |
| regular_manager.ResumeNavigation(); |
| // After returning here, the commit for |
| // |embedder_defined_unassigned_url| will be processed. |
| }); |
| |
| CommitMessageDelayer commit_delayer( |
| web_contents, embedder_defined_unassigned_url() /* deferred_url */, |
| std::move(did_commit_callback)); |
| |
| // Start the first navigation, which does not assign a site URL. |
| shell->LoadURL(embedder_defined_unassigned_url()); |
| |
| // The navigation should stay in the initial empty SiteInstance, so there |
| // shouldn't be a speculative RFH at this point. |
| EXPECT_FALSE(root->render_manager()->speculative_frame_host()); |
| |
| // Wait for the DidCommit IPC for |embedder_defined_unassigned_url|, and |
| // before processing it, trigger a navigation to |regular_url| and wait for |
| // its response. |
| commit_delayer.Wait(); |
| |
| // Check that the renderer hasn't been killed. At this point, it should've |
| // successfully committed the navigation to |embedder_defined_unassigned_url|, |
| // and it shouldn't be locked. |
| EXPECT_TRUE(web_contents->GetPrimaryMainFrame()->IsRenderFrameLive()); |
| EXPECT_EQ(embedder_defined_unassigned_url(), |
| web_contents->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| RenderProcessHost* process1 = |
| web_contents->GetPrimaryMainFrame()->GetProcess(); |
| EXPECT_FALSE( |
| web_contents->GetPrimaryMainFrame()->GetSiteInstance()->HasSite()); |
| auto process1_lock = process1->GetProcessLock(); |
| EXPECT_FALSE(process1_lock.is_invalid()); |
| EXPECT_TRUE(process1_lock.allows_any_site()); |
| |
| // Now wait for second navigation to finish and ensure it also succeeds. |
| regular_manager.WaitForNavigationFinished(); |
| EXPECT_TRUE(regular_manager.was_successful()); |
| EXPECT_TRUE(web_contents->GetPrimaryMainFrame()->IsRenderFrameLive()); |
| EXPECT_EQ(regular_url(), |
| web_contents->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| |
| // The regular url navigation should've used a different process, locked to |
| // a.test. |
| BrowserContext* browser_context = web_contents->GetBrowserContext(); |
| RenderProcessHost* process2 = |
| web_contents->GetPrimaryMainFrame()->GetProcess(); |
| EXPECT_NE(process1, process2); |
| EXPECT_EQ( |
| GURL("https://a.test"), |
| web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL()); |
| EXPECT_EQ( |
| ProcessLock::FromSiteInfo(SiteInfo( |
| GURL("https://a.test"), GURL("https://a.test"), |
| false /* requires_origin_keyed_process */, false /* is_sandboxed */, |
| UrlInfo::kInvalidUniqueSandboxId, |
| StoragePartitionConfig::CreateDefault(browser_context), |
| WebExposedIsolationInfo::CreateNonIsolated(), false /* is_guest */, |
| false /* does_site_request_dedicated_process_for_coop */, |
| false /* is_jit_disabled */, false /* is_pdf */, |
| false /*is_fenced */)), |
| policy->GetProcessLock(process2->GetID())); |
| |
| // Ensure also that the regular url process didn't change midway through the |
| // navigation. |
| EXPECT_EQ(regular_process, process2); |
| } |
| |
| static auto kTestParams = |
| testing::Combine(testing::ValuesIn(RenderDocumentFeatureLevelValues()), |
| testing::Bool()); |
| INSTANTIATE_TEST_SUITE_P(All, |
| UnassignedSiteInstanceBrowserTest, |
| kTestParams, |
| UnassignedSiteInstanceBrowserTest::DescribeParams); |
| |
| } // namespace content |