| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string> |
| |
| #include "base/command_line.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "content/browser/frame_host/frame_tree_node.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/content_browser_test.h" |
| #include "content/public/test/content_browser_test_utils.h" |
| #include "content/public/test/test_frame_navigation_observer.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/shell/browser/shell.h" |
| #include "content/test/content_browser_test_utils_internal.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| |
| class TopDocumentIsolationTest : public ContentBrowserTest { |
| public: |
| TopDocumentIsolationTest() {} |
| |
| protected: |
| std::string DepictFrameTree(FrameTreeNode* node) { |
| return visualizer_.DepictFrameTree(node); |
| } |
| |
| void SetUp() override { |
| scoped_feature_list_.InitAndEnableFeature(features::kTopDocumentIsolation); |
| ContentBrowserTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| SetupCrossSiteRedirector(embedded_test_server()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| |
| FrameTreeNode* root() { |
| return static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| } |
| |
| void GoBack() { |
| TestNavigationObserver back_load_observer(shell()->web_contents()); |
| shell()->web_contents()->GetController().GoBack(); |
| back_load_observer.Wait(); |
| } |
| |
| Shell* OpenPopup(FrameTreeNode* opener, const std::string& url) { |
| GURL gurl = |
| opener->current_frame_host()->GetLastCommittedURL().Resolve(url); |
| return content::OpenPopup(opener, gurl, "_blank"); |
| } |
| |
| void RendererInitiatedNavigateToURL(FrameTreeNode* node, const GURL& url) { |
| TestFrameNavigationObserver nav_observer(node); |
| ASSERT_TRUE( |
| ExecuteScript(node, "window.location.href='" + url.spec() + "'")); |
| nav_observer.Wait(); |
| } |
| |
| private: |
| FrameTreeVisualizer visualizer_; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TopDocumentIsolationTest); |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, SameSiteDeeplyNested) { |
| if (content::AreAllSitesIsolatedForTesting()) |
| return; // Top Document Isolation is disabled in this mode. |
| |
| GURL main_url(embedded_test_server()->GetURL( |
| "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))")); |
| |
| NavigateToURL(shell(), main_url); |
| |
| EXPECT_EQ( |
| " Site A\n" |
| " |--Site A\n" |
| " +--Site A\n" |
| " |--Site A\n" |
| " +--Site A\n" |
| " +--Site A\n" |
| "Where A = http://a.com/", |
| DepictFrameTree(root())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, CrossSiteDeeplyNested) { |
| if (content::AreAllSitesIsolatedForTesting()) |
| return; // Top Document Isolation is disabled in this mode. |
| |
| GURL main_url(embedded_test_server()->GetURL( |
| "a.com", "/cross_site_iframe_factory.html?a(b(c(d(b))))")); |
| |
| NavigateToURL(shell(), main_url); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| " +--Site B -- proxies for A\n" |
| " +--Site B -- proxies for A\n" |
| " +--Site B -- proxies for A\n" |
| "Where A = http://a.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, ReturnToTopSite) { |
| if (content::AreAllSitesIsolatedForTesting()) |
| return; // Top Document Isolation is disabled in this mode. |
| |
| GURL main_url(embedded_test_server()->GetURL( |
| "a.com", "/cross_site_iframe_factory.html?a(b(a(c)))")); |
| |
| NavigateToURL(shell(), main_url); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| " +--Site A -- proxies for B\n" |
| " +--Site B -- proxies for A\n" |
| "Where A = http://a.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, NavigateSubframeToTopSite) { |
| if (content::AreAllSitesIsolatedForTesting()) |
| return; // Top Document Isolation is disabled in this mode. |
| |
| GURL main_url(embedded_test_server()->GetURL( |
| "a.com", "/cross_site_iframe_factory.html?a(b(c(d)))")); |
| |
| NavigateToURL(shell(), main_url); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| " +--Site B -- proxies for A\n" |
| " +--Site B -- proxies for A\n" |
| "Where A = http://a.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| |
| GURL ada_url(embedded_test_server()->GetURL( |
| "a.com", "/cross_site_iframe_factory.html?a(d(a))")); |
| RendererInitiatedNavigateToURL(root()->child_at(0)->child_at(0), ada_url); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| " +--Site A -- proxies for B\n" |
| " +--Site B -- proxies for A\n" |
| " +--Site A -- proxies for B\n" |
| "Where A = http://a.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, NavigateToSubframeSite) { |
| if (content::AreAllSitesIsolatedForTesting()) |
| return; // Top Document Isolation is disabled in this mode. |
| |
| GURL ab_url(embedded_test_server()->GetURL( |
| "a.com", "/cross_site_iframe_factory.html?a(b)")); |
| GURL ba_url(embedded_test_server()->GetURL( |
| "b.com", "/cross_site_iframe_factory.html?b(a, c)")); |
| |
| NavigateToURL(shell(), ab_url); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = http://a.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| |
| NavigateToURL(shell(), ba_url); |
| |
| EXPECT_EQ( |
| " Site C ------------ proxies for D\n" |
| " |--Site D ------- proxies for C\n" |
| " +--Site D ------- proxies for C\n" |
| "Where C = http://b.com/\n" |
| " D = default subframe process", |
| DepictFrameTree(root())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, |
| NavigateToSubframeSiteWithPopup) { |
| if (content::AreAllSitesIsolatedForTesting()) |
| return; // Top Document Isolation is disabled in this mode. |
| |
| // A(B) -> B(A), but while a separate B(A) popup exists. |
| GURL ab_url(embedded_test_server()->GetURL( |
| "a.com", "/cross_site_iframe_factory.html?a(b)")); |
| |
| NavigateToURL(shell(), ab_url); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = http://a.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| |
| Shell* popup = |
| OpenPopup(root()->child_at(0), "/cross_site_iframe_factory.html?b(a)"); |
| FrameTreeNode* popup_root = |
| static_cast<WebContentsImpl*>(popup->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| // This popup's main frame must stay in the default subframe siteinstance, |
| // since its opener (the b.com subframe) may synchronously script it. Note |
| // that the popup's subframe is same-site with window.top.opener.top, the |
| // a.com main frame of the tab. But --top-document-isolation does not |
| // currently place the popup subframe in the a.com process in this case. |
| EXPECT_EQ( |
| " Site B\n" |
| " +--Site B\n" |
| "Where B = default subframe process", |
| DepictFrameTree(popup_root)); |
| |
| GURL ba_url(embedded_test_server()->GetURL( |
| "b.com", "/cross_site_iframe_factory.html?b(a, c)")); |
| NavigateToURL(shell(), ba_url); |
| |
| // This navigation destroys the popup's opener, so we allow the main frame to |
| // commit in a top level process for b.com, in spite of the b.com popup in the |
| // default subframe process. |
| EXPECT_EQ( |
| " Site C ------------ proxies for B\n" |
| " |--Site B ------- proxies for C\n" |
| " +--Site B ------- proxies for C\n" |
| "Where B = default subframe process\n" |
| " C = http://b.com/", |
| DepictFrameTree(root())); |
| EXPECT_EQ( |
| " Site B\n" |
| " +--Site B\n" |
| "Where B = default subframe process", |
| DepictFrameTree(popup_root)); |
| |
| // Navigate the popup to a new site. |
| GURL c_url(embedded_test_server()->GetURL( |
| "c.com", "/cross_site_iframe_factory.html?c(c, c, c, c)")); |
| NavigateToURL(popup, c_url); |
| EXPECT_EQ( |
| " Site D ------------ proxies for B\n" |
| " |--Site D ------- proxies for B\n" |
| " |--Site D ------- proxies for B\n" |
| " |--Site D ------- proxies for B\n" |
| " +--Site D ------- proxies for B\n" |
| "Where B = default subframe process\n" |
| " D = http://c.com/", |
| DepictFrameTree(popup_root)); |
| NavigateToURL(shell(), c_url); |
| EXPECT_EQ( |
| " Site D\n" |
| " |--Site D\n" |
| " |--Site D\n" |
| " |--Site D\n" |
| " +--Site D\n" |
| "Where D = http://c.com/", |
| DepictFrameTree(popup_root)); |
| EXPECT_EQ( |
| " Site D\n" |
| " |--Site D\n" |
| " |--Site D\n" |
| " |--Site D\n" |
| " +--Site D\n" |
| "Where D = http://c.com/", |
| DepictFrameTree(root())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, |
| NavigateToSubframeSiteWithPopup2) { |
| if (content::AreAllSitesIsolatedForTesting()) |
| return; // Top Document Isolation is disabled in this mode. |
| |
| // A(B, C) -> C(A, B), but while a separate C(A) popup exists. |
| // |
| // This test is constructed so that c.com is the second site to commit in the |
| // default subframe SiteInstance, so the default subframe SiteInstance does |
| // not have a "c.com" as the value of GetSiteURL(). |
| GURL abb_url(embedded_test_server()->GetURL( |
| "a.com", "/cross_site_iframe_factory.html?a(b, b)")); |
| |
| NavigateToURL(shell(), abb_url); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " |--Site B ------- proxies for A\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = http://a.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| |
| // A(B, B) -> A(B, C) |
| GURL c_url(embedded_test_server()->GetURL( |
| "c.com", "/cross_site_iframe_factory.html?c")); |
| NavigateFrameToURL(root()->child_at(1), c_url); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " |--Site B ------- proxies for A\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = http://a.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| |
| // This test exercises what happens when the SiteURL of the default subframe |
| // siteinstance doesn't match the subframe site. |
| EXPECT_NE("c.com", root() |
| ->child_at(1) |
| ->current_frame_host() |
| ->GetSiteInstance() |
| ->GetSiteURL() |
| .host()); |
| |
| // Subframe C creates C(A) popup. |
| Shell* popup = |
| OpenPopup(root()->child_at(1), "/cross_site_iframe_factory.html?c(a)"); |
| |
| FrameTreeNode* popup_root = |
| static_cast<WebContentsImpl*>(popup->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| // The popup must stay with its opener, in the default subframe process. |
| EXPECT_EQ( |
| " Site B\n" |
| " +--Site B\n" |
| "Where B = default subframe process", |
| DepictFrameTree(popup_root)); |
| |
| GURL cab_url(embedded_test_server()->GetURL( |
| "c.com", "/cross_site_iframe_factory.html?c(a, b)")); |
| { |
| RenderFrameDeletedObserver deleted_observer(root()->current_frame_host()); |
| NavigateToURL(shell(), cab_url); |
| deleted_observer.WaitUntilDeleted(); |
| } |
| |
| // This c.com navigation currently breaks out of the default subframe process, |
| // even though that process houses a c.com pop-up. |
| EXPECT_EQ( |
| " Site C ------------ proxies for B\n" |
| " |--Site B ------- proxies for C\n" |
| " +--Site B ------- proxies for C\n" |
| "Where B = default subframe process\n" |
| " C = http://c.com/", |
| DepictFrameTree(root())); |
| |
| // c.com popup should remain where it was, in the subframe process. |
| EXPECT_EQ( |
| " Site B\n" |
| " +--Site B\n" |
| "Where B = default subframe process", |
| DepictFrameTree(popup_root)); |
| EXPECT_EQ(nullptr, popup_root->opener()); |
| |
| // If we navigate the popup to a new site, it ought to transfer processes. |
| GURL d_url(embedded_test_server()->GetURL( |
| "d.com", "/cross_site_iframe_factory.html?d")); |
| { |
| RenderFrameDeletedObserver deleted_observer( |
| popup_root->current_frame_host()); |
| NavigateToURL(popup, d_url); |
| deleted_observer.WaitUntilDeleted(); |
| } |
| EXPECT_EQ( |
| " Site D ------------ proxies for B\n" |
| "Where B = default subframe process\n" |
| " D = http://d.com/", |
| DepictFrameTree(popup_root)); |
| { |
| RenderFrameDeletedObserver deleted_observer(root()->current_frame_host()); |
| NavigateToURL(shell(), d_url); |
| deleted_observer.WaitUntilDeleted(); |
| } |
| EXPECT_EQ( |
| " Site D\n" |
| "Where D = http://d.com/", |
| DepictFrameTree(popup_root)); |
| EXPECT_EQ( |
| " Site D\n" |
| "Where D = http://d.com/", |
| DepictFrameTree(root())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, FramesForSitesInHistory) { |
| if (content::AreAllSitesIsolatedForTesting()) |
| return; // Top Document Isolation is disabled in this mode. |
| |
| // First, do a series of navigations. |
| GURL a_url = embedded_test_server()->GetURL( |
| "a.com", "/cross_site_iframe_factory.html?a"); |
| GURL b_url = embedded_test_server()->GetURL( |
| "b.com", "/cross_site_iframe_factory.html?b"); |
| GURL c_url = embedded_test_server()->GetURL( |
| "c.com", "/cross_site_iframe_factory.html?c"); |
| |
| // Browser-initiated navigation to a.com. |
| NavigateToURL(shell(), a_url); |
| EXPECT_EQ( |
| " Site A\n" |
| "Where A = http://a.com/", |
| DepictFrameTree(root())); |
| |
| // Browser-initiated navigation to b.com. |
| { |
| // For any cross-process navigations, we must wait for the old RenderFrame |
| // to be deleted before calling DepictFrameTree, or else there's a chance |
| // the old SiteInstance could be listed while pending deletion. |
| RenderFrameDeletedObserver deleted_observer(root()->current_frame_host()); |
| NavigateToURL(shell(), b_url); |
| deleted_observer.WaitUntilDeleted(); |
| } |
| EXPECT_EQ( |
| " Site B\n" |
| "Where B = http://b.com/", |
| DepictFrameTree(root())); |
| |
| // Renderer-initiated navigation back to a.com. This shouldn't swap processes. |
| RendererInitiatedNavigateToURL(root(), a_url); |
| EXPECT_EQ( |
| " Site B\n" |
| "Where B = http://b.com/", |
| DepictFrameTree(root())); |
| |
| // Browser-initiated navigation to c.com. |
| { |
| RenderFrameDeletedObserver deleted_observer(root()->current_frame_host()); |
| NavigateToURL(shell(), c_url); |
| deleted_observer.WaitUntilDeleted(); |
| } |
| EXPECT_EQ( |
| " Site C\n" |
| "Where C = http://c.com/", |
| DepictFrameTree(root())); |
| |
| // Now, navigate to a fourth site with iframes to the sites in the history. |
| { |
| RenderFrameDeletedObserver deleted_observer(root()->current_frame_host()); |
| NavigateToURL(shell(), |
| embedded_test_server()->GetURL( |
| "d.com", "/cross_site_iframe_factory.html?d(a,b,c)")); |
| deleted_observer.WaitUntilDeleted(); |
| } |
| |
| EXPECT_EQ( |
| " Site D ------------ proxies for E\n" |
| " |--Site E ------- proxies for D\n" |
| " |--Site E ------- proxies for D\n" |
| " +--Site E ------- proxies for D\n" |
| "Where D = http://d.com/\n" |
| " E = default subframe process", |
| DepictFrameTree(root())); |
| |
| // Now try going back. |
| { |
| RenderFrameDeletedObserver deleted_observer(root()->current_frame_host()); |
| GoBack(); |
| deleted_observer.WaitUntilDeleted(); |
| } |
| EXPECT_EQ( |
| " Site C\n" |
| "Where C = http://c.com/", |
| DepictFrameTree(root())); |
| { |
| RenderFrameDeletedObserver deleted_observer(root()->current_frame_host()); |
| GoBack(); |
| deleted_observer.WaitUntilDeleted(); |
| } |
| EXPECT_EQ( |
| " Site B\n" |
| "Where B = http://b.com/", |
| DepictFrameTree(root())); |
| GoBack(); |
| EXPECT_EQ( |
| " Site B\n" |
| "Where B = http://b.com/", |
| DepictFrameTree(root())); |
| { |
| RenderFrameDeletedObserver deleted_observer(root()->current_frame_host()); |
| GoBack(); |
| deleted_observer.WaitUntilDeleted(); |
| } |
| EXPECT_EQ( |
| " Site A\n" |
| "Where A = http://a.com/", |
| DepictFrameTree(root())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, CrossSiteAtLevelTwo) { |
| if (content::AreAllSitesIsolatedForTesting()) |
| return; // Top Document Isolation is disabled in this mode. |
| |
| GURL main_url(embedded_test_server()->GetURL( |
| "a.com", "/cross_site_iframe_factory.html?a(a(b, a))")); |
| |
| NavigateToURL(shell(), main_url); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site A ------- proxies for B\n" |
| " |--Site B -- proxies for A\n" |
| " +--Site A -- proxies for B\n" |
| "Where A = http://a.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| |
| GURL c_url(embedded_test_server()->GetURL( |
| "c.com", "/cross_site_iframe_factory.html?c")); |
| NavigateFrameToURL(root()->child_at(0)->child_at(1), c_url); |
| |
| // This navigation should complete in the default subframe siteinstance. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site A ------- proxies for B\n" |
| " |--Site B -- proxies for A\n" |
| " +--Site B -- proxies for A\n" |
| "Where A = http://a.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, PopupAndRedirection) { |
| if (content::AreAllSitesIsolatedForTesting()) |
| return; // Top Document Isolation is disabled in this mode. |
| |
| GURL main_url(embedded_test_server()->GetURL( |
| "page.com", "/cross_site_iframe_factory.html?page(adnetwork)")); |
| |
| // User opens page on page.com which contains a subframe from adnetwork.com. |
| NavigateToURL(shell(), main_url); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = http://page.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| |
| GURL ad_url(embedded_test_server()->GetURL( |
| "ad.com", "/cross_site_iframe_factory.html?ad")); |
| |
| // adnetwork.com retrieves an ad from advertiser (ad.com) and redirects the |
| // subframe to ad.com. |
| RendererInitiatedNavigateToURL(root()->child_at(0), ad_url); |
| |
| // The subframe still uses the default subframe SiteInstance after navigation. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = http://page.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| |
| // User clicks the ad in the subframe, which opens a popup on the ad |
| // network's domain. |
| GURL popup_url(embedded_test_server()->GetURL( |
| "adnetwork.com", "/cross_site_iframe_factory.html?adnetwork")); |
| Shell* popup = OpenPopup(root()->child_at(0), popup_url.spec()); |
| |
| FrameTreeNode* popup_root = |
| static_cast<WebContentsImpl*>(popup->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| // It's ok for the popup to break out of the subframe process because it's |
| // currently cross-site from its opener frame. |
| EXPECT_EQ( |
| " Site C ------------ proxies for B\n" |
| "Where B = default subframe process\n" |
| " C = http://adnetwork.com/", |
| DepictFrameTree(popup_root)); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " +--Site B ------- proxies for A C\n" |
| "Where A = http://page.com/\n" |
| " B = default subframe process\n" |
| " C = http://adnetwork.com/", |
| DepictFrameTree(root())); |
| |
| // The popup redirects itself to the advertiser's website (ad.com). |
| RenderFrameDeletedObserver deleted_observer(popup_root->current_frame_host()); |
| RendererInitiatedNavigateToURL(popup_root, ad_url); |
| deleted_observer.WaitUntilDeleted(); |
| |
| // This must join its same-site opener, in the default subframe SiteInstance. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = http://page.com/\n" |
| " B = default subframe process", |
| DepictFrameTree(root())); |
| EXPECT_EQ( |
| " Site B\n" |
| "Where B = default subframe process", |
| DepictFrameTree(popup_root)); |
| } |
| |
| } // namespace content |