| // 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_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/test_utils.h" | 
 | #include "content/shell/browser/shell.h" | 
 | #include "content/test/render_document_feature.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 ContentBrowserTestContentBrowserClient { | 
 |  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_); | 
 |   } | 
 |  | 
 |   void TearDownOnMainThread() override { | 
 |     content_browser_client_override_.reset(); | 
 |   } | 
 |  | 
 |   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_; | 
 |  | 
 |   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. | 
 |   ASSERT_TRUE(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 |