Chromium Code Reviews| Index: content/browser/top_document_isolation_browsertest.cc |
| diff --git a/content/browser/top_document_isolation_browsertest.cc b/content/browser/top_document_isolation_browsertest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..70a88fc6db16fcc43dc2ad148e3868742c879d63 |
| --- /dev/null |
| +++ b/content/browser/top_document_isolation_browsertest.cc |
| @@ -0,0 +1,540 @@ |
| +// 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->current_frame_host(), gurl, "_blank"); |
| + } |
| + |
| + void RendererInitiatedNavigateToURL(FrameTreeNode* node, const GURL& url) { |
| + TestFrameNavigationObserver nav_observer(node); |
| + ASSERT_TRUE(ExecuteScript(node->current_frame_host(), |
| + "window.location.href='" + url.spec() + "'")); |
| + nav_observer.Wait(); |
| + } |
| + |
| + private: |
| + FrameTreeVisualizer visualizer_; |
| +}; |
| + |
| +IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, SameSiteDeeplyNested) { |
| + 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) { |
| + 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) { |
| + 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) { |
| + 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) { |
| + 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) { |
| + // 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) { |
| + // 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())); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(TopDocumentIsolationTest, FramesForSitesInHistory) { |
| + // 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. |
|
Charlie Reis
2016/03/29 17:17:13
nit: Blank line before.
ncarter (slow)
2016/03/29 23:30:57
Done.
|
| + 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) { |
| + 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) { |
| + 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 |