| // 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 "content/browser/frame_host/frame_tree_node.h" | 
 | #include "content/browser/web_contents/web_contents_impl.h" | 
 | #include "content/public/common/content_switches.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_navigation_observer.h" | 
 | #include "content/shell/browser/shell.h" | 
 | #include "content/test/content_browser_test_utils_internal.h" | 
 | #include "content/test/test_frame_navigation_observer.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 SetUpCommandLine(base::CommandLine* command_line) override { | 
 |     command_line->AppendSwitch(switches::kTopDocumentIsolation); | 
 |   } | 
 |  | 
 |   void SetUpOnMainThread() override { | 
 |     host_resolver()->AddRule("*", "127.0.0.1"); | 
 |     ASSERT_TRUE(embedded_test_server()->Start()); | 
 |     SetupCrossSiteRedirector(embedded_test_server()); | 
 |   } | 
 |  | 
 |   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_; | 
 | }; | 
 |  | 
 | 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 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())); | 
 | } | 
 |  | 
 | 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())); | 
 | } | 
 |  | 
 | // Flaky. See http://crbug.com/611300. | 
 | IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, | 
 |                        DISABLED_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)")); | 
 |   NavigateToURL(shell(), cab_url); | 
 |  | 
 |   // 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")); | 
 |   NavigateToURL(popup, d_url); | 
 |   EXPECT_EQ( | 
 |       " Site D ------------ proxies for B\n" | 
 |       "Where B = default subframe process\n" | 
 |       "      D = http://d.com/", | 
 |       DepictFrameTree(popup_root)); | 
 |   NavigateToURL(shell(), d_url); | 
 |   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())); | 
 | } | 
 |  | 
 | // Flaky on Mac. See https://crbug.com/611344. | 
 | #if defined(OS_MACOSX) | 
 | #define MAYBE_FramesForSitesInHistory DISABLED_FramesForSitesInHistory | 
 | #else | 
 | #define MAYBE_FramesForSitesInHistory FramesForSitesInHistory | 
 | #endif | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, | 
 |                        MAYBE_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. | 
 |   NavigateToURL(shell(), b_url); | 
 |   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. | 
 |   NavigateToURL(shell(), c_url); | 
 |   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. | 
 |   NavigateToURL(shell(), | 
 |                 embedded_test_server()->GetURL( | 
 |                     "d.com", "/cross_site_iframe_factory.html?d(a,b,c)")); | 
 |  | 
 |   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. | 
 |   GoBack(); | 
 |   EXPECT_EQ( | 
 |       " Site C\n" | 
 |       "Where C = http://c.com/", | 
 |       DepictFrameTree(root())); | 
 |   GoBack(); | 
 |   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())); | 
 |   GoBack(); | 
 |   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). | 
 |   RendererInitiatedNavigateToURL(popup_root, ad_url); | 
 |  | 
 |   // This must join its same-site opener, in the default subframe SiteInstance. | 
 |   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())); | 
 |   EXPECT_EQ( | 
 |       " Site C ------------ proxies for B\n" | 
 |       "Where B = default subframe process\n" | 
 |       "      C = http://adnetwork.com/", | 
 |       DepictFrameTree(popup_root)); | 
 | } | 
 |  | 
 | }  // namespace content |