| // Copyright (c) 2012 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 "content/browser/site_per_process_browsertest.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "content/browser/frame_host/cross_process_frame_connector.h" |
| #include "content/browser/frame_host/frame_tree.h" |
| #include "content/browser/frame_host/navigator.h" |
| #include "content/browser/frame_host/render_frame_proxy_host.h" |
| #include "content/browser/frame_host/render_widget_host_view_child_frame.h" |
| #include "content/browser/renderer_host/render_view_host_impl.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| #include "content/common/frame_messages.h" |
| #include "content/public/browser/notification_observer.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/content_browser_test_utils.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/content_browser_test_utils_internal.h" |
| #include "content/test/test_frame_navigation_observer.h" |
| #include "ipc/ipc_security_test_util.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| |
| namespace content { |
| |
| class RedirectNotificationObserver : public NotificationObserver { |
| public: |
| // Register to listen for notifications of the given type from either a |
| // specific source, or from all sources if |source| is |
| // NotificationService::AllSources(). |
| RedirectNotificationObserver(int notification_type, |
| const NotificationSource& source); |
| ~RedirectNotificationObserver() override; |
| |
| // Wait until the specified notification occurs. If the notification was |
| // emitted between the construction of this object and this call then it |
| // returns immediately. |
| void Wait(); |
| |
| // Returns NotificationService::AllSources() if we haven't observed a |
| // notification yet. |
| const NotificationSource& source() const { |
| return source_; |
| } |
| |
| const NotificationDetails& details() const { |
| return details_; |
| } |
| |
| // NotificationObserver: |
| void Observe(int type, |
| const NotificationSource& source, |
| const NotificationDetails& details) override; |
| |
| private: |
| bool seen_; |
| bool seen_twice_; |
| bool running_; |
| NotificationRegistrar registrar_; |
| |
| NotificationSource source_; |
| NotificationDetails details_; |
| scoped_refptr<MessageLoopRunner> message_loop_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver); |
| }; |
| |
| RedirectNotificationObserver::RedirectNotificationObserver( |
| int notification_type, |
| const NotificationSource& source) |
| : seen_(false), |
| running_(false), |
| source_(NotificationService::AllSources()) { |
| registrar_.Add(this, notification_type, source); |
| } |
| |
| RedirectNotificationObserver::~RedirectNotificationObserver() {} |
| |
| void RedirectNotificationObserver::Wait() { |
| if (seen_ && seen_twice_) |
| return; |
| |
| running_ = true; |
| message_loop_runner_ = new MessageLoopRunner; |
| message_loop_runner_->Run(); |
| EXPECT_TRUE(seen_); |
| } |
| |
| void RedirectNotificationObserver::Observe( |
| int type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| source_ = source; |
| details_ = details; |
| seen_twice_ = seen_; |
| seen_ = true; |
| if (!running_) |
| return; |
| |
| message_loop_runner_->Quit(); |
| running_ = false; |
| } |
| |
| // This observer keeps track of the number of created RenderFrameHosts. Tests |
| // can use this to ensure that a certain number of child frames has been |
| // created after navigating. |
| class RenderFrameHostCreatedObserver : public WebContentsObserver { |
| public: |
| RenderFrameHostCreatedObserver(WebContents* web_contents, |
| int expected_frame_count) |
| : WebContentsObserver(web_contents), |
| expected_frame_count_(expected_frame_count), |
| frames_created_(0), |
| message_loop_runner_(new MessageLoopRunner) {} |
| |
| ~RenderFrameHostCreatedObserver() override; |
| |
| // Runs a nested message loop and blocks until the expected number of |
| // RenderFrameHosts is created. |
| void Wait(); |
| |
| private: |
| // WebContentsObserver |
| void RenderFrameCreated(RenderFrameHost* render_frame_host) override; |
| |
| // The number of RenderFrameHosts to wait for. |
| int expected_frame_count_; |
| |
| // The number of RenderFrameHosts that have been created. |
| int frames_created_; |
| |
| // The MessageLoopRunner used to spin the message loop. |
| scoped_refptr<MessageLoopRunner> message_loop_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver); |
| }; |
| |
| RenderFrameHostCreatedObserver::~RenderFrameHostCreatedObserver() { |
| } |
| |
| void RenderFrameHostCreatedObserver::Wait() { |
| message_loop_runner_->Run(); |
| } |
| |
| void RenderFrameHostCreatedObserver::RenderFrameCreated( |
| RenderFrameHost* render_frame_host) { |
| frames_created_++; |
| if (frames_created_ == expected_frame_count_) { |
| message_loop_runner_->Quit(); |
| } |
| } |
| |
| // A WebContentsDelegate that catches messages sent to the console. |
| class ConsoleObserverDelegate : public WebContentsDelegate { |
| public: |
| ConsoleObserverDelegate(WebContents* web_contents, const std::string& filter) |
| : web_contents_(web_contents), |
| filter_(filter), |
| message_(""), |
| message_loop_runner_(new MessageLoopRunner) {} |
| |
| ~ConsoleObserverDelegate() override {} |
| |
| bool AddMessageToConsole(WebContents* source, |
| int32 level, |
| const base::string16& message, |
| int32 line_no, |
| const base::string16& source_id) override; |
| |
| std::string message() { return message_; } |
| |
| void Wait(); |
| |
| private: |
| WebContents* web_contents_; |
| std::string filter_; |
| std::string message_; |
| |
| // The MessageLoopRunner used to spin the message loop. |
| scoped_refptr<MessageLoopRunner> message_loop_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ConsoleObserverDelegate); |
| }; |
| |
| void ConsoleObserverDelegate::Wait() { |
| message_loop_runner_->Run(); |
| } |
| |
| bool ConsoleObserverDelegate::AddMessageToConsole( |
| WebContents* source, |
| int32 level, |
| const base::string16& message, |
| int32 line_no, |
| const base::string16& source_id) { |
| DCHECK(source == web_contents_); |
| |
| std::string ascii_message = base::UTF16ToASCII(message); |
| if (MatchPattern(ascii_message, filter_)) { |
| message_ = ascii_message; |
| message_loop_runner_->Quit(); |
| } |
| return false; |
| } |
| |
| // |
| // SitePerProcessBrowserTest |
| // |
| |
| SitePerProcessBrowserTest::SitePerProcessBrowserTest() { |
| }; |
| |
| std::string SitePerProcessBrowserTest::DepictFrameTree(FrameTreeNode* node) { |
| return visualizer_.DepictFrameTree(node); |
| } |
| |
| void SitePerProcessBrowserTest::StartFrameAtDataURL() { |
| std::string data_url_script = |
| "var iframes = document.getElementById('test');iframes.src=" |
| "'data:text/html,dataurl';"; |
| ASSERT_TRUE(ExecuteScript(shell()->web_contents(), data_url_script)); |
| } |
| |
| void SitePerProcessBrowserTest::SetUpCommandLine( |
| base::CommandLine* command_line) { |
| command_line->AppendSwitch(switches::kSitePerProcess); |
| }; |
| |
| void SitePerProcessBrowserTest::SetUpOnMainThread() { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
| SetupCrossSiteRedirector(embedded_test_server()); |
| } |
| |
| // Ensure that navigating subframes in --site-per-process mode works and the |
| // correct documents are committed. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteIframe) { |
| GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Load same-site page into iframe. |
| FrameTreeNode* child = root->child_at(0); |
| GURL http_url(embedded_test_server()->GetURL("/title1.html")); |
| NavigateFrameToURL(child, http_url); |
| EXPECT_EQ(http_url, observer.last_navigation_url()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| { |
| // There should be only one RenderWidgetHost when there are no |
| // cross-process iframes. |
| std::set<RenderWidgetHostView*> views_set = |
| static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetRenderWidgetHostViewsInTree(); |
| EXPECT_EQ(1U, views_set.size()); |
| } |
| |
| EXPECT_EQ( |
| " Site A\n" |
| " |--Site A\n" |
| " +--Site A\n" |
| " |--Site A\n" |
| " +--Site A\n" |
| " +--Site A\n" |
| "Where A = http://127.0.0.1/", |
| DepictFrameTree(root)); |
| |
| // Load cross-site page into iframe. |
| GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html"); |
| NavigateFrameToURL(root->child_at(0), url); |
| // Verify that the navigation succeeded and the expected URL was loaded. |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| |
| // Ensure that we have created a new process for the subframe. |
| ASSERT_EQ(2U, root->child_count()); |
| SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance(); |
| RenderViewHost* rvh = child->current_frame_host()->render_view_host(); |
| RenderProcessHost* rph = child->current_frame_host()->GetProcess(); |
| EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance); |
| EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph); |
| { |
| // There should be now two RenderWidgetHosts, one for each process |
| // rendering a frame. |
| std::set<RenderWidgetHostView*> views_set = |
| static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetRenderWidgetHostViewsInTree(); |
| EXPECT_EQ(2U, views_set.size()); |
| } |
| RenderFrameProxyHost* proxy_to_parent = |
| child->render_manager()->GetProxyToParent(); |
| EXPECT_TRUE(proxy_to_parent); |
| EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector()); |
| // The out-of-process iframe should have its own RenderWidgetHost, |
| // independent of any RenderViewHost. |
| EXPECT_NE( |
| rvh->GetView(), |
| proxy_to_parent->cross_process_frame_connector()->get_view_for_testing()); |
| EXPECT_TRUE(child->current_frame_host()->GetRenderWidgetHost()); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " |--Site B ------- proxies for A\n" |
| " +--Site A ------- proxies for B\n" |
| " |--Site A -- proxies for B\n" |
| " +--Site A -- proxies for B\n" |
| " +--Site A -- proxies for B\n" |
| "Where A = http://127.0.0.1/\n" |
| " B = http://foo.com/", |
| DepictFrameTree(root)); |
| |
| // Load another cross-site page into the same iframe. |
| url = embedded_test_server()->GetURL("bar.com", "/title3.html"); |
| NavigateFrameToURL(root->child_at(0), url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| |
| // Check again that a new process is created and is different from the |
| // top level one and the previous one. |
| ASSERT_EQ(2U, root->child_count()); |
| child = root->child_at(0); |
| EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), |
| child->current_frame_host()->render_view_host()); |
| EXPECT_NE(rvh, child->current_frame_host()->render_view_host()); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| child->current_frame_host()->GetSiteInstance()); |
| EXPECT_NE(site_instance, |
| child->current_frame_host()->GetSiteInstance()); |
| EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), |
| child->current_frame_host()->GetProcess()); |
| EXPECT_NE(rph, child->current_frame_host()->GetProcess()); |
| { |
| std::set<RenderWidgetHostView*> views_set = |
| static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetRenderWidgetHostViewsInTree(); |
| EXPECT_EQ(2U, views_set.size()); |
| } |
| EXPECT_EQ(proxy_to_parent, child->render_manager()->GetProxyToParent()); |
| EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector()); |
| EXPECT_NE( |
| child->current_frame_host()->render_view_host()->GetView(), |
| proxy_to_parent->cross_process_frame_connector()->get_view_for_testing()); |
| EXPECT_TRUE(child->current_frame_host()->GetRenderWidgetHost()); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for C\n" |
| " |--Site C ------- proxies for A\n" |
| " +--Site A ------- proxies for C\n" |
| " |--Site A -- proxies for C\n" |
| " +--Site A -- proxies for C\n" |
| " +--Site A -- proxies for C\n" |
| "Where A = http://127.0.0.1/\n" |
| " C = http://bar.com/", |
| DepictFrameTree(root)); |
| } |
| |
| // Tests OOPIF rendering by checking that the RWH of the iframe generates |
| // OnSwapCompositorFrame message. |
| #if defined(OS_ANDROID) |
| // http://crbug.com/471850 |
| #define MAYBE_CompositorFrameSwapped DISABLED_CompositorFrameSwapped |
| #else |
| #define MAYBE_CompositorFrameSwapped CompositorFrameSwapped |
| #endif |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| MAYBE_CompositorFrameSwapped) { |
| GURL main_url( |
| embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| ASSERT_EQ(1U, root->child_count()); |
| |
| FrameTreeNode* child_node = root->child_at(0); |
| GURL site_url(embedded_test_server()->GetURL("baz.com", "/title1.html")); |
| EXPECT_EQ(site_url, child_node->current_url()); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| child_node->current_frame_host()->GetSiteInstance()); |
| RenderWidgetHostViewBase* rwhv_base = static_cast<RenderWidgetHostViewBase*>( |
| child_node->current_frame_host()->GetRenderWidgetHost()->GetView()); |
| |
| // Wait for OnSwapCompositorFrame message. |
| while (rwhv_base->RendererFrameNumber() <= 0) { |
| // TODO(lazyboy): Find a better way to avoid sleeping like this. See |
| // http://crbug.com/405282 for details. |
| base::RunLoop run_loop; |
| base::MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, run_loop.QuitClosure(), |
| base::TimeDelta::FromMilliseconds(10)); |
| run_loop.Run(); |
| } |
| } |
| |
| // Ensure that OOPIFs are deleted after navigating to a new main frame. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CleanupCrossSiteIframe) { |
| GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Load a cross-site page into both iframes. |
| GURL foo_url = embedded_test_server()->GetURL("foo.com", "/title2.html"); |
| NavigateFrameToURL(root->child_at(0), foo_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(foo_url, observer.last_navigation_url()); |
| NavigateFrameToURL(root->child_at(1), foo_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(foo_url, observer.last_navigation_url()); |
| |
| // Ensure that we have created a new process for the subframes. |
| ASSERT_EQ(2U, root->child_count()); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(0)->current_frame_host()->GetSiteInstance()); |
| EXPECT_EQ(root->child_at(0)->current_frame_host()->GetSiteInstance(), |
| root->child_at(1)->current_frame_host()->GetSiteInstance()); |
| |
| // Use Javascript in the parent to remove one of the frames and ensure that |
| // the subframe goes away. |
| EXPECT_TRUE(ExecuteScript(shell()->web_contents(), |
| "document.body.removeChild(" |
| "document.querySelectorAll('iframe')[0])")); |
| ASSERT_EQ(1U, root->child_count()); |
| |
| // Load a new same-site page in the top-level frame and ensure the other |
| // subframe goes away. |
| GURL new_url(embedded_test_server()->GetURL("/title1.html")); |
| NavigateToURL(shell(), new_url); |
| ASSERT_EQ(0U, root->child_count()); |
| } |
| |
| // Ensure that root frames cannot be detached. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, RestrictFrameDetach) { |
| GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Load cross-site pages into both iframes. |
| GURL foo_url = embedded_test_server()->GetURL("foo.com", "/title2.html"); |
| NavigateFrameToURL(root->child_at(0), foo_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(foo_url, observer.last_navigation_url()); |
| GURL bar_url = embedded_test_server()->GetURL("bar.com", "/title2.html"); |
| NavigateFrameToURL(root->child_at(1), bar_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(bar_url, observer.last_navigation_url()); |
| |
| // Ensure that we have created new processes for the subframes. |
| ASSERT_EQ(2U, root->child_count()); |
| FrameTreeNode* foo_child = root->child_at(0); |
| SiteInstance* foo_site_instance = |
| foo_child->current_frame_host()->GetSiteInstance(); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), foo_site_instance); |
| FrameTreeNode* bar_child = root->child_at(1); |
| SiteInstance* bar_site_instance = |
| bar_child->current_frame_host()->GetSiteInstance(); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), bar_site_instance); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " |--Site B ------- proxies for A C\n" |
| " +--Site C ------- proxies for A B\n" |
| "Where A = http://127.0.0.1/\n" |
| " B = http://foo.com/\n" |
| " C = http://bar.com/", |
| DepictFrameTree(root)); |
| |
| // Simulate an attempt to detach the root frame from foo_site_instance. This |
| // should kill foo_site_instance's process. |
| RenderFrameProxyHost* foo_mainframe_rfph = |
| root->render_manager()->GetRenderFrameProxyHost(foo_site_instance); |
| content::RenderProcessHostWatcher foo_terminated( |
| foo_mainframe_rfph->GetProcess(), |
| content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| FrameHostMsg_Detach evil_msg2(foo_mainframe_rfph->GetRoutingID()); |
| IPC::IpcSecurityTestUtil::PwnMessageReceived( |
| foo_mainframe_rfph->GetProcess()->GetChannel(), evil_msg2); |
| foo_terminated.Wait(); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " |--Site B ------- proxies for A C\n" |
| " +--Site C ------- proxies for A B\n" |
| "Where A = http://127.0.0.1/\n" |
| " B = http://foo.com/ (no process)\n" |
| " C = http://bar.com/", |
| DepictFrameTree(root)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| NavigateRemoteFrame) { |
| GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Load same-site page into iframe. |
| FrameTreeNode* child = root->child_at(0); |
| GURL http_url(embedded_test_server()->GetURL("/title1.html")); |
| NavigateFrameToURL(child, http_url); |
| EXPECT_EQ(http_url, observer.last_navigation_url()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| |
| // Load cross-site page into iframe. |
| GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html"); |
| NavigateFrameToURL(root->child_at(0), url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| |
| // Ensure that we have created a new process for the subframe. |
| ASSERT_EQ(2U, root->child_count()); |
| SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance(); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance); |
| |
| // Emulate the main frame changing the src of the iframe such that it |
| // navigates cross-site. |
| url = embedded_test_server()->GetURL("bar.com", "/title3.html"); |
| NavigateIframeToURL(shell()->web_contents(), "test", url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| |
| // Check again that a new process is created and is different from the |
| // top level one and the previous one. |
| ASSERT_EQ(2U, root->child_count()); |
| child = root->child_at(0); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| child->current_frame_host()->GetSiteInstance()); |
| EXPECT_NE(site_instance, |
| child->current_frame_host()->GetSiteInstance()); |
| |
| // Navigate back to the parent's origin and ensure we return to the |
| // parent's process. |
| NavigateFrameToURL(child, http_url); |
| EXPECT_EQ(http_url, observer.last_navigation_url()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(shell()->web_contents()->GetSiteInstance(), |
| child->current_frame_host()->GetSiteInstance()); |
| } |
| |
| #if defined(OS_WIN) |
| // http://crbug.com/465722 |
| #define MAYBE_NavigateRemoteFrameToBlankAndDataURLs \ |
| DISABLED_NavigateRemoteFrameToBlankAndDataURLs |
| #else |
| #define MAYBE_NavigateRemoteFrameToBlankAndDataURLs \ |
| NavigateRemoteFrameToBlankAndDataURLs |
| #endif |
| |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| MAYBE_NavigateRemoteFrameToBlankAndDataURLs) { |
| GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Load same-site page into iframe. |
| FrameTreeNode* child = root->child_at(0); |
| GURL http_url(embedded_test_server()->GetURL("/title1.html")); |
| NavigateFrameToURL(child, http_url); |
| EXPECT_EQ(http_url, observer.last_navigation_url()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| |
| // Load cross-site page into iframe. |
| GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html"); |
| NavigateFrameToURL(root->child_at(0), url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| ASSERT_EQ(2U, root->child_count()); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(0)->current_frame_host()->GetSiteInstance()); |
| |
| // Navigate iframe to a data URL. The navigation happens from a script in the |
| // parent frame, so the data URL should be committed in the same SiteInstance |
| // as the parent frame. |
| GURL data_url("data:text/html,dataurl"); |
| NavigateIframeToURL(shell()->web_contents(), "test", data_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(data_url, observer.last_navigation_url()); |
| |
| // Ensure that we have navigated using the top level process. |
| EXPECT_EQ(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(0)->current_frame_host()->GetSiteInstance()); |
| |
| // Load cross-site page into iframe. |
| url = embedded_test_server()->GetURL("bar.com", "/title2.html"); |
| NavigateFrameToURL(root->child_at(0), url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| ASSERT_EQ(2U, root->child_count()); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(0)->current_frame_host()->GetSiteInstance()); |
| |
| // Navigate iframe to about:blank. The navigation happens from a script in the |
| // parent frame, so it should be committed in the same SiteInstance as the |
| // parent frame. |
| GURL about_blank_url("about:blank"); |
| NavigateIframeToURL(shell()->web_contents(), "test", about_blank_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(about_blank_url, observer.last_navigation_url()); |
| |
| // Ensure that we have navigated using the top level process. |
| EXPECT_EQ(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(0)->current_frame_host()->GetSiteInstance()); |
| } |
| |
| // This test checks that killing a renderer process of a remote frame |
| // and then navigating some other frame to the same SiteInstance of the killed |
| // process works properly. |
| // This can be illustrated as follows, |
| // where 1/2/3 are FrameTreeNode-s and A/B are processes and B* is the killed |
| // B process: |
| // |
| // 1 A A A |
| // / \ -> / \ -> Kill B -> / \ -> Navigate 3 to B -> / \ . |
| // 2 3 B A B* A B* B |
| // |
| // Initially, node1.proxy_hosts_ = {B} |
| // After we kill B, we make sure B stays in node1.proxy_hosts_, then we navigate |
| // 3 to B and we expect that to complete normally. |
| // See http://crbug.com/432107. |
| // |
| // Note that due to http://crbug.com/450681, node2 cannot be re-navigated to |
| // site B and stays in not rendered state. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| NavigateRemoteFrameToKilledProcess) { |
| GURL main_url(embedded_test_server()->GetURL( |
| "/frame_tree/page_with_two_frames.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| ASSERT_EQ(2U, root->child_count()); |
| |
| // Make sure node2 points to the correct cross-site page. |
| GURL site_b_url = embedded_test_server()->GetURL("bar.com", "/title1.html"); |
| FrameTreeNode* node2 = root->child_at(0); |
| EXPECT_EQ(site_b_url, node2->current_url()); |
| |
| // Kill that cross-site renderer. |
| RenderProcessHost* child_process = |
| node2->current_frame_host()->GetProcess(); |
| RenderProcessHostWatcher crash_observer( |
| child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| child_process->Shutdown(0, false); |
| crash_observer.Wait(); |
| |
| // Now navigate the second iframe (node3) to the same site as the node2. |
| FrameTreeNode* node3 = root->child_at(1); |
| NavigateFrameToURL(node3, site_b_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(site_b_url, observer.last_navigation_url()); |
| } |
| |
| // This test is similar to |
| // SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcess with |
| // addition that node2 also has a cross-origin frame to site C. |
| // |
| // 1 A A A |
| // / \ / \ / \ / \ . |
| // 2 3 -> B A -> Kill B -> B* A -> Navigate 3 -> B* B |
| // / / |
| // 4 C |
| // |
| // Initially, node1.proxy_hosts_ = {B, C} |
| // After we kill B, we make sure B stays in node1.proxy_hosts_, but |
| // C gets cleared from node1.proxy_hosts_. |
| // |
| // Note that due to http://crbug.com/450681, node2 cannot be re-navigated to |
| // site B and stays in not rendered state. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| NavigateRemoteFrameToKilledProcessWithSubtree) { |
| GURL main_url(embedded_test_server()->GetURL( |
| "a.com", "/frame_tree/page_with_two_frames_nested.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| ASSERT_EQ(2U, root->child_count()); |
| |
| GURL site_b_url( |
| embedded_test_server()->GetURL( |
| "bar.com", "/frame_tree/page_with_one_frame.html")); |
| // We can't use a TestNavigationObserver to verify the URL here, |
| // since the frame has children that may have clobbered it in the observer. |
| EXPECT_EQ(site_b_url, root->child_at(0)->current_url()); |
| |
| // Ensure that a new process is created for node2. |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(0)->current_frame_host()->GetSiteInstance()); |
| // Ensure that a new process is *not* created for node3. |
| EXPECT_EQ(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(1)->current_frame_host()->GetSiteInstance()); |
| |
| ASSERT_EQ(1U, root->child_at(0)->child_count()); |
| |
| // Make sure node4 points to the correct cross-site page. |
| FrameTreeNode* node4 = root->child_at(0)->child_at(0); |
| GURL site_c_url(embedded_test_server()->GetURL("baz.com", "/title1.html")); |
| EXPECT_EQ(site_c_url, node4->current_url()); |
| |
| // |site_instance_c| is expected to go away once we kill |child_process_b| |
| // below, so create a local scope so we can extend the lifetime of |
| // |site_instance_c| with a refptr. |
| { |
| // Initially each frame has proxies for the other sites. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " |--Site B ------- proxies for A C\n" |
| " | +--Site C -- proxies for A B\n" |
| " +--Site A ------- proxies for B C\n" |
| "Where A = http://a.com/\n" |
| " B = http://bar.com/\n" |
| " C = http://baz.com/", |
| DepictFrameTree(root)); |
| |
| // Kill the render process for Site B. |
| RenderProcessHost* child_process_b = |
| root->child_at(0)->current_frame_host()->GetProcess(); |
| RenderProcessHostWatcher crash_observer( |
| child_process_b, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| child_process_b->Shutdown(0, false); |
| crash_observer.Wait(); |
| |
| // The Site C frame (a child of the crashed Site B frame) should go away, |
| // and there should be no remaining proxies for site C anywhere. |
| EXPECT_EQ( |
| " 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 = http://bar.com/ (no process)", |
| DepictFrameTree(root)); |
| } |
| |
| // Now navigate the second iframe (node3) to Site B also. |
| FrameTreeNode* node3 = root->child_at(1); |
| GURL url = embedded_test_server()->GetURL("bar.com", "/title1.html"); |
| NavigateFrameToURL(node3, url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(url, observer.last_navigation_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 = http://bar.com/", |
| DepictFrameTree(root)); |
| } |
| |
| // In A-embed-B-embed-C scenario, verify that killing process B clears proxies |
| // of C from the tree. |
| // |
| // 1 A A |
| // / \ / \ / \ . |
| // 2 3 -> B A -> Kill B -> B* A |
| // / / |
| // 4 C |
| // |
| // node1 is the root. |
| // Initially, both node1.proxy_hosts_ and node3.proxy_hosts_ contain C. |
| // After we kill B, make sure proxies for C are cleared. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| KillingRendererClearsDescendantProxies) { |
| GURL main_url(embedded_test_server()->GetURL( |
| "a.com", "/frame_tree/page_with_two_frames_nested.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| ASSERT_EQ(2U, root->child_count()); |
| |
| GURL site_b_url( |
| embedded_test_server()->GetURL( |
| "bar.com", "/frame_tree/page_with_one_frame.html")); |
| // We can't use a TestNavigationObserver to verify the URL here, |
| // since the frame has children that may have clobbered it in the observer. |
| EXPECT_EQ(site_b_url, root->child_at(0)->current_url()); |
| |
| // Ensure that a new process is created for node2. |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(0)->current_frame_host()->GetSiteInstance()); |
| // Ensure that a new process is *not* created for node3. |
| EXPECT_EQ(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(1)->current_frame_host()->GetSiteInstance()); |
| |
| ASSERT_EQ(1U, root->child_at(0)->child_count()); |
| |
| // Make sure node4 points to the correct cross-site-page. |
| FrameTreeNode* node4 = root->child_at(0)->child_at(0); |
| GURL site_c_url(embedded_test_server()->GetURL("baz.com", "/title1.html")); |
| EXPECT_EQ(site_c_url, node4->current_url()); |
| |
| // |site_instance_c| is expected to go away once we kill |child_process_b| |
| // below; refcount it to extend the lifetime. |
| scoped_refptr<SiteInstanceImpl> site_instance_c = |
| node4->current_frame_host()->GetSiteInstance(); |
| |
| // Initially proxies for both B and C will be present in the root. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " |--Site B ------- proxies for A C\n" |
| " | +--Site C -- proxies for A B\n" |
| " +--Site A ------- proxies for B C\n" |
| "Where A = http://a.com/\n" |
| " B = http://bar.com/\n" |
| " C = http://baz.com/", |
| DepictFrameTree(root)); |
| // Kill process B. |
| RenderProcessHost* child_process_b = |
| root->child_at(0)->current_frame_host()->GetProcess(); |
| RenderProcessHostWatcher crash_observer( |
| child_process_b, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| child_process_b->Shutdown(0, false); |
| crash_observer.Wait(); |
| |
| // Make sure proxy C has gone from root. |
| // Make sure proxy C has gone from node3 as well. |
| // Make sure proxy B stays around in root and node3. |
| EXPECT_EQ( |
| " 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 = http://bar.com/ (no process)", |
| DepictFrameTree(root)); |
| |
| EXPECT_TRUE(site_instance_c->HasOneRef()); |
| } |
| |
| // Crash a subframe and ensures its children are cleared from the FrameTree. |
| // See http://crbug.com/338508. |
| // TODO(creis): Disabled for flakiness; see http://crbug.com/405582. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DISABLED_CrashSubframe) { |
| GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
| NavigateToURL(shell(), main_url); |
| |
| StartFrameAtDataURL(); |
| |
| // These must stay in scope with replace_host. |
| GURL::Replacements replace_host; |
| std::string foo_com("foo.com"); |
| |
| // Load cross-site page into iframe. |
| EXPECT_TRUE(NavigateIframeToURL( |
| shell()->web_contents(), "test", |
| embedded_test_server()->GetURL("/cross-site/foo.com/title2.html"))); |
| |
| // Check the subframe process. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| ASSERT_EQ(2U, root->child_count()); |
| FrameTreeNode* child = root->child_at(0); |
| EXPECT_EQ(main_url, root->current_url()); |
| EXPECT_EQ("foo.com", child->current_url().host()); |
| EXPECT_EQ("/title2.html", child->current_url().path()); |
| |
| EXPECT_TRUE( |
| child->current_frame_host()->render_view_host()->IsRenderViewLive()); |
| EXPECT_TRUE(child->current_frame_host()->IsRenderFrameLive()); |
| |
| // Crash the subframe process. |
| RenderProcessHost* root_process = root->current_frame_host()->GetProcess(); |
| RenderProcessHost* child_process = child->current_frame_host()->GetProcess(); |
| { |
| RenderProcessHostWatcher crash_observer( |
| child_process, |
| RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| child_process->Shutdown(0, false); |
| crash_observer.Wait(); |
| } |
| |
| // Ensure that the child frame still exists but has been cleared. |
| EXPECT_EQ(2U, root->child_count()); |
| EXPECT_EQ(main_url, root->current_url()); |
| EXPECT_EQ(GURL(), child->current_url()); |
| |
| EXPECT_FALSE( |
| child->current_frame_host()->render_view_host()->IsRenderViewLive()); |
| EXPECT_FALSE(child->current_frame_host()->IsRenderFrameLive()); |
| EXPECT_FALSE(child->current_frame_host()->render_frame_created_); |
| |
| // Now crash the top-level page to clear the child frame. |
| { |
| RenderProcessHostWatcher crash_observer( |
| root_process, |
| RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| root_process->Shutdown(0, false); |
| crash_observer.Wait(); |
| } |
| EXPECT_EQ(0U, root->child_count()); |
| EXPECT_EQ(GURL(), root->current_url()); |
| } |
| |
| // When a new subframe is added, related SiteInstances that can reach the |
| // subframe should create proxies for it (https://crbug.com/423587). This test |
| // checks that if A embeds B and later adds a new subframe A2, A2 gets a proxy |
| // in B's process. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CreateProxiesForNewFrames) { |
| GURL main_url(embedded_test_server()->GetURL( |
| "b.com", "/frame_tree/page_with_one_frame.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| ASSERT_EQ(1U, root->child_count()); |
| |
| // Make sure the frame starts out at the correct cross-site URL. |
| EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title1.html"), |
| root->child_at(0)->current_url()); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = http://b.com/\n" |
| " B = http://baz.com/", |
| DepictFrameTree(root)); |
| |
| // Add a new child frame to the top-level frame. |
| RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1); |
| EXPECT_TRUE(ExecuteScript(shell()->web_contents(), |
| "window.domAutomationController.send(" |
| " addFrame('data:text/html,foo'));")); |
| frame_observer.Wait(); |
| |
| // The new frame should have a proxy in Site B, for use by the old frame. |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " |--Site B ------- proxies for A\n" |
| " +--Site A ------- proxies for B\n" |
| "Where A = http://b.com/\n" |
| " B = http://baz.com/", |
| DepictFrameTree(root)); |
| } |
| |
| // TODO(nasko): Disable this test until out-of-process iframes is ready and the |
| // security checks are back in place. |
| // TODO(creis): Replace SpawnedTestServer with host_resolver to get test to run |
| // on Android (http://crbug.com/187570). |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| DISABLED_CrossSiteIframeRedirectOnce) { |
| ASSERT_TRUE(test_server()->Start()); |
| net::SpawnedTestServer https_server( |
| net::SpawnedTestServer::TYPE_HTTPS, |
| net::SpawnedTestServer::kLocalhost, |
| base::FilePath(FILE_PATH_LITERAL("content/test/data"))); |
| ASSERT_TRUE(https_server.Start()); |
| |
| GURL main_url(test_server()->GetURL("files/site_per_process_main.html")); |
| GURL http_url(test_server()->GetURL("files/title1.html")); |
| GURL https_url(https_server.GetURL("files/title1.html")); |
| |
| NavigateToURL(shell(), main_url); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| { |
| // Load cross-site client-redirect page into Iframe. |
| // Should be blocked. |
| GURL client_redirect_https_url(https_server.GetURL( |
| "client-redirect?files/title1.html")); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| client_redirect_https_url)); |
| // DidFailProvisionalLoad when navigating to client_redirect_https_url. |
| EXPECT_EQ(observer.last_navigation_url(), client_redirect_https_url); |
| EXPECT_FALSE(observer.last_navigation_succeeded()); |
| } |
| |
| { |
| // Load cross-site server-redirect page into Iframe, |
| // which redirects to same-site page. |
| GURL server_redirect_http_url(https_server.GetURL( |
| "server-redirect?" + http_url.spec())); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| server_redirect_http_url)); |
| EXPECT_EQ(observer.last_navigation_url(), http_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| } |
| |
| { |
| // Load cross-site server-redirect page into Iframe, |
| // which redirects to cross-site page. |
| GURL server_redirect_http_url(https_server.GetURL( |
| "server-redirect?files/title1.html")); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| server_redirect_http_url)); |
| // DidFailProvisionalLoad when navigating to https_url. |
| EXPECT_EQ(observer.last_navigation_url(), https_url); |
| EXPECT_FALSE(observer.last_navigation_succeeded()); |
| } |
| |
| { |
| // Load same-site server-redirect page into Iframe, |
| // which redirects to cross-site page. |
| GURL server_redirect_http_url(test_server()->GetURL( |
| "server-redirect?" + https_url.spec())); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| server_redirect_http_url)); |
| |
| EXPECT_EQ(observer.last_navigation_url(), https_url); |
| EXPECT_FALSE(observer.last_navigation_succeeded()); |
| } |
| |
| { |
| // Load same-site client-redirect page into Iframe, |
| // which redirects to cross-site page. |
| GURL client_redirect_http_url(test_server()->GetURL( |
| "client-redirect?" + https_url.spec())); |
| |
| RedirectNotificationObserver load_observer2( |
| NOTIFICATION_LOAD_STOP, |
| Source<NavigationController>( |
| &shell()->web_contents()->GetController())); |
| |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| client_redirect_http_url)); |
| |
| // Same-site Client-Redirect Page should be loaded successfully. |
| EXPECT_EQ(observer.last_navigation_url(), client_redirect_http_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| |
| // Redirecting to Cross-site Page should be blocked. |
| load_observer2.Wait(); |
| EXPECT_EQ(observer.last_navigation_url(), https_url); |
| EXPECT_FALSE(observer.last_navigation_succeeded()); |
| } |
| |
| { |
| // Load same-site server-redirect page into Iframe, |
| // which redirects to same-site page. |
| GURL server_redirect_http_url(test_server()->GetURL( |
| "server-redirect?files/title1.html")); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| server_redirect_http_url)); |
| EXPECT_EQ(observer.last_navigation_url(), http_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| } |
| |
| { |
| // Load same-site client-redirect page into Iframe, |
| // which redirects to same-site page. |
| GURL client_redirect_http_url(test_server()->GetURL( |
| "client-redirect?" + http_url.spec())); |
| RedirectNotificationObserver load_observer2( |
| NOTIFICATION_LOAD_STOP, |
| Source<NavigationController>( |
| &shell()->web_contents()->GetController())); |
| |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| client_redirect_http_url)); |
| |
| // Same-site Client-Redirect Page should be loaded successfully. |
| EXPECT_EQ(observer.last_navigation_url(), client_redirect_http_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| |
| // Redirecting to Same-site Page should be loaded successfully. |
| load_observer2.Wait(); |
| EXPECT_EQ(observer.last_navigation_url(), http_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| } |
| } |
| |
| // TODO(nasko): Disable this test until out-of-process iframes is ready and the |
| // security checks are back in place. |
| // TODO(creis): Replace SpawnedTestServer with host_resolver to get test to run |
| // on Android (http://crbug.com/187570). |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| DISABLED_CrossSiteIframeRedirectTwice) { |
| ASSERT_TRUE(test_server()->Start()); |
| net::SpawnedTestServer https_server( |
| net::SpawnedTestServer::TYPE_HTTPS, |
| net::SpawnedTestServer::kLocalhost, |
| base::FilePath(FILE_PATH_LITERAL("content/test/data"))); |
| ASSERT_TRUE(https_server.Start()); |
| |
| GURL main_url(test_server()->GetURL("files/site_per_process_main.html")); |
| GURL http_url(test_server()->GetURL("files/title1.html")); |
| GURL https_url(https_server.GetURL("files/title1.html")); |
| |
| NavigateToURL(shell(), main_url); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| { |
| // Load client-redirect page pointing to a cross-site client-redirect page, |
| // which eventually redirects back to same-site page. |
| GURL client_redirect_https_url(https_server.GetURL( |
| "client-redirect?" + http_url.spec())); |
| GURL client_redirect_http_url(test_server()->GetURL( |
| "client-redirect?" + client_redirect_https_url.spec())); |
| |
| // We should wait until second client redirect get cancelled. |
| RedirectNotificationObserver load_observer2( |
| NOTIFICATION_LOAD_STOP, |
| Source<NavigationController>( |
| &shell()->web_contents()->GetController())); |
| |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| client_redirect_http_url)); |
| |
| // DidFailProvisionalLoad when navigating to client_redirect_https_url. |
| load_observer2.Wait(); |
| EXPECT_EQ(observer.last_navigation_url(), client_redirect_https_url); |
| EXPECT_FALSE(observer.last_navigation_succeeded()); |
| } |
| |
| { |
| // Load server-redirect page pointing to a cross-site server-redirect page, |
| // which eventually redirect back to same-site page. |
| GURL server_redirect_https_url(https_server.GetURL( |
| "server-redirect?" + http_url.spec())); |
| GURL server_redirect_http_url(test_server()->GetURL( |
| "server-redirect?" + server_redirect_https_url.spec())); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| server_redirect_http_url)); |
| EXPECT_EQ(observer.last_navigation_url(), http_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| } |
| |
| { |
| // Load server-redirect page pointing to a cross-site server-redirect page, |
| // which eventually redirects back to cross-site page. |
| GURL server_redirect_https_url(https_server.GetURL( |
| "server-redirect?" + https_url.spec())); |
| GURL server_redirect_http_url(test_server()->GetURL( |
| "server-redirect?" + server_redirect_https_url.spec())); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| server_redirect_http_url)); |
| |
| // DidFailProvisionalLoad when navigating to https_url. |
| EXPECT_EQ(observer.last_navigation_url(), https_url); |
| EXPECT_FALSE(observer.last_navigation_succeeded()); |
| } |
| |
| { |
| // Load server-redirect page pointing to a cross-site client-redirect page, |
| // which eventually redirects back to same-site page. |
| GURL client_redirect_http_url(https_server.GetURL( |
| "client-redirect?" + http_url.spec())); |
| GURL server_redirect_http_url(test_server()->GetURL( |
| "server-redirect?" + client_redirect_http_url.spec())); |
| EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test", |
| server_redirect_http_url)); |
| |
| // DidFailProvisionalLoad when navigating to client_redirect_http_url. |
| EXPECT_EQ(observer.last_navigation_url(), client_redirect_http_url); |
| EXPECT_FALSE(observer.last_navigation_succeeded()); |
| } |
| } |
| |
| // Ensure that when navigating a frame cross-process RenderFrameProxyHosts are |
| // created in the FrameTree skipping the subtree of the navigating frame. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| ProxyCreationSkipsSubtree) { |
| GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| |
| EXPECT_TRUE(root->child_at(1) != NULL); |
| EXPECT_EQ(2U, root->child_at(1)->child_count()); |
| |
| { |
| // Load same-site page into iframe. |
| TestNavigationObserver observer(shell()->web_contents()); |
| GURL http_url(embedded_test_server()->GetURL("/title1.html")); |
| NavigateFrameToURL(root->child_at(0), http_url); |
| EXPECT_EQ(http_url, observer.last_navigation_url()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ( |
| " Site A\n" |
| " |--Site A\n" |
| " +--Site A\n" |
| " |--Site A\n" |
| " +--Site A\n" |
| " +--Site A\n" |
| "Where A = http://127.0.0.1/", |
| DepictFrameTree(root)); |
| } |
| |
| // Create the cross-site URL to navigate to. |
| GURL cross_site_url = |
| embedded_test_server()->GetURL("foo.com", "/frame_tree/1-1.html"); |
| |
| // Load cross-site page into the second iframe without waiting for the |
| // navigation to complete. Once LoadURLWithParams returns, we would expect |
| // proxies to have been created in the frame tree, but children of the |
| // navigating frame to still be present. The reason is that we don't run the |
| // message loop, so no IPCs that alter the frame tree can be processed. |
| FrameTreeNode* child = root->child_at(1); |
| SiteInstance* site = NULL; |
| { |
| TestNavigationObserver observer(shell()->web_contents()); |
| TestFrameNavigationObserver navigation_observer(child); |
| NavigationController::LoadURLParams params(cross_site_url); |
| params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK); |
| params.frame_tree_node_id = child->frame_tree_node_id(); |
| child->navigator()->GetController()->LoadURLWithParams(params); |
| |
| site = child->render_manager()->pending_frame_host()->GetSiteInstance(); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " |--Site A ------- proxies for B\n" |
| " +--Site A (B pending)\n" |
| " |--Site A\n" |
| " +--Site A\n" |
| " +--Site A\n" |
| "Where A = http://127.0.0.1/\n" |
| " B = http://foo.com/", |
| DepictFrameTree(root)); |
| |
| // Now that the verification is done, run the message loop and wait for the |
| // navigation to complete. |
| navigation_observer.Wait(); |
| EXPECT_FALSE(child->render_manager()->pending_frame_host()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(cross_site_url, observer.last_navigation_url()); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " |--Site A ------- proxies for B\n" |
| " +--Site B ------- proxies for A\n" |
| "Where A = http://127.0.0.1/\n" |
| " B = http://foo.com/", |
| DepictFrameTree(root)); |
| } |
| |
| // Load another cross-site page into the same iframe. |
| cross_site_url = embedded_test_server()->GetURL("bar.com", "/title2.html"); |
| { |
| // Perform the same checks as the first cross-site navigation, since |
| // there have been issues in subsequent cross-site navigations. Also ensure |
| // that the SiteInstance has properly changed. |
| // TODO(nasko): Once we have proper cleanup of resources, add code to |
| // verify that the intermediate SiteInstance/RenderFrameHost have been |
| // properly cleaned up. |
| TestNavigationObserver observer(shell()->web_contents()); |
| TestFrameNavigationObserver navigation_observer(child); |
| NavigationController::LoadURLParams params(cross_site_url); |
| params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK); |
| params.frame_tree_node_id = child->frame_tree_node_id(); |
| child->navigator()->GetController()->LoadURLWithParams(params); |
| |
| SiteInstance* site2 = |
| child->render_manager()->pending_frame_host()->GetSiteInstance(); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site2); |
| EXPECT_NE(site, site2); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B C\n" |
| " |--Site A ------- proxies for B C\n" |
| " +--Site B (C pending) -- proxies for A\n" |
| "Where A = http://127.0.0.1/\n" |
| " B = http://foo.com/\n" |
| " C = http://bar.com/", |
| DepictFrameTree(root)); |
| |
| navigation_observer.Wait(); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(cross_site_url, observer.last_navigation_url()); |
| EXPECT_EQ(0U, child->child_count()); |
| } |
| } |
| |
| // Verify that origin replication works for an A-embed-B-embed-C hierarchy. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, OriginReplication) { |
| GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Navigate the first subframe to a cross-site page with two subframes. |
| // NavigateFrameToURL can't be used here because it doesn't guarantee that |
| // FrameTreeNodes will have been created for child frames when it returns. |
| RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 4); |
| GURL foo_url( |
| embedded_test_server()->GetURL("foo.com", "/frame_tree/1-1.html")); |
| NavigationController::LoadURLParams params(foo_url); |
| params.transition_type = ui::PAGE_TRANSITION_LINK; |
| params.frame_tree_node_id = root->child_at(0)->frame_tree_node_id(); |
| root->child_at(0)->navigator()->GetController()->LoadURLWithParams(params); |
| frame_observer.Wait(); |
| |
| // We can't use a TestNavigationObserver to verify the URL here, |
| // since the frame has children that may have clobbered it in the observer. |
| EXPECT_EQ(foo_url, root->child_at(0)->current_url()); |
| |
| // Ensure that a new process is created for the subframe. |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(0)->current_frame_host()->GetSiteInstance()); |
| |
| // Load cross-site page into subframe's subframe. |
| ASSERT_EQ(2U, root->child_at(0)->child_count()); |
| GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html")); |
| NavigateFrameToURL(root->child_at(0)->child_at(0), bar_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(bar_url, observer.last_navigation_url()); |
| |
| // Check that a new process is created and is different from the top one and |
| // the middle one. |
| FrameTreeNode* bottom_child = root->child_at(0)->child_at(0); |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| bottom_child->current_frame_host()->GetSiteInstance()); |
| EXPECT_NE(root->child_at(0)->current_frame_host()->GetSiteInstance(), |
| bottom_child->current_frame_host()->GetSiteInstance()); |
| |
| // Check that foo.com frame's location.ancestorOrigins contains the correct |
| // origin for the parent. The origin should have been replicated as part of |
| // the ViewMsg_New message that created the parent's RenderFrameProxy in |
| // foo.com's process. |
| int ancestor_origins_length = 0; |
| EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| root->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(location.ancestorOrigins.length);", |
| &ancestor_origins_length)); |
| EXPECT_EQ(1, ancestor_origins_length); |
| std::string result; |
| EXPECT_TRUE(ExecuteScriptAndExtractString( |
| root->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(location.ancestorOrigins[0]);", |
| &result)); |
| EXPECT_EQ(result + "/", main_url.GetOrigin().spec()); |
| |
| // Check that bar.com frame's location.ancestorOrigins contains the correct |
| // origin for its two ancestors. The topmost parent origin should be |
| // replicated as part of ViewMsg_New, and the middle frame (foo.com's) origin |
| // should be replicated as part of FrameMsg_NewFrameProxy sent for foo.com's |
| // frame in bar.com's process. |
| EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| bottom_child->current_frame_host(), |
| "window.domAutomationController.send(location.ancestorOrigins.length);", |
| &ancestor_origins_length)); |
| EXPECT_EQ(2, ancestor_origins_length); |
| EXPECT_TRUE(ExecuteScriptAndExtractString( |
| bottom_child->current_frame_host(), |
| "window.domAutomationController.send(location.ancestorOrigins[0]);", |
| &result)); |
| EXPECT_EQ(result + "/", foo_url.GetOrigin().spec()); |
| EXPECT_TRUE(ExecuteScriptAndExtractString( |
| bottom_child->current_frame_host(), |
| "window.domAutomationController.send(location.ancestorOrigins[1]);", |
| &result)); |
| EXPECT_EQ(result + "/", main_url.GetOrigin().spec()); |
| } |
| |
| // Check that iframe sandbox flags are replicated correctly. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, SandboxFlagsReplication) { |
| GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Navigate the second (sandboxed) subframe to a cross-site page with a |
| // subframe. Use RenderFrameHostCreatedObserver to guarantee that all |
| // FrameTreeNodes are created for child frames. |
| RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 4); |
| GURL foo_url( |
| embedded_test_server()->GetURL("foo.com", "/frame_tree/1-1.html")); |
| NavigateFrameToURL(root->child_at(1), foo_url); |
| frame_observer.Wait(); |
| |
| // We can't use a TestNavigationObserver to verify the URL here, |
| // since the frame has children that may have clobbered it in the observer. |
| EXPECT_EQ(foo_url, root->child_at(1)->current_url()); |
| |
| // Load cross-site page into subframe's subframe. |
| ASSERT_EQ(2U, root->child_at(1)->child_count()); |
| GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html")); |
| NavigateFrameToURL(root->child_at(1)->child_at(0), bar_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(bar_url, observer.last_navigation_url()); |
| |
| // Opening a popup in the sandboxed foo.com iframe should fail. |
| bool success = false; |
| EXPECT_TRUE( |
| ExecuteScriptAndExtractBool(root->child_at(1)->current_frame_host(), |
| "window.domAutomationController.send(" |
| "!window.open('data:text/html,dataurl'));", |
| &success)); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(1u, Shell::windows().size()); |
| |
| // Opening a popup in a frame whose parent is sandboxed should also fail. |
| // Here, bar.com frame's sandboxed parent frame is a remote frame in |
| // bar.com's process. |
| success = false; |
| EXPECT_TRUE(ExecuteScriptAndExtractBool( |
| root->child_at(1)->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(" |
| "!window.open('data:text/html,dataurl'));", |
| &success)); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(1u, Shell::windows().size()); |
| |
| // Same, but now try the case where bar.com frame's sandboxed parent is a |
| // local frame in bar.com's process. |
| success = false; |
| EXPECT_TRUE(ExecuteScriptAndExtractBool( |
| root->child_at(2)->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(" |
| "!window.open('data:text/html,dataurl'));", |
| &success)); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(1u, Shell::windows().size()); |
| |
| // Check that foo.com frame's location.ancestorOrigins contains the correct |
| // origin for the parent, which should be unaffected by sandboxing. |
| int ancestor_origins_length = 0; |
| EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| root->child_at(1)->current_frame_host(), |
| "window.domAutomationController.send(location.ancestorOrigins.length);", |
| &ancestor_origins_length)); |
| EXPECT_EQ(1, ancestor_origins_length); |
| std::string result; |
| EXPECT_TRUE(ExecuteScriptAndExtractString( |
| root->child_at(1)->current_frame_host(), |
| "window.domAutomationController.send(location.ancestorOrigins[0]);", |
| &result)); |
| EXPECT_EQ(result + "/", main_url.GetOrigin().spec()); |
| |
| // Now check location.ancestorOrigins for the bar.com frame. The middle frame |
| // (foo.com's) origin should be unique, since that frame is sandboxed, and |
| // the top frame should match |main_url|. |
| FrameTreeNode* bottom_child = root->child_at(1)->child_at(0); |
| EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| bottom_child->current_frame_host(), |
| "window.domAutomationController.send(location.ancestorOrigins.length);", |
| &ancestor_origins_length)); |
| EXPECT_EQ(2, ancestor_origins_length); |
| EXPECT_TRUE(ExecuteScriptAndExtractString( |
| bottom_child->current_frame_host(), |
| "window.domAutomationController.send(location.ancestorOrigins[0]);", |
| &result)); |
| EXPECT_EQ("null", result); |
| EXPECT_TRUE(ExecuteScriptAndExtractString( |
| bottom_child->current_frame_host(), |
| "window.domAutomationController.send(location.ancestorOrigins[1]);", |
| &result)); |
| EXPECT_EQ(main_url.GetOrigin().spec(), result + "/"); |
| } |
| |
| // Check that dynamic updates to iframe sandbox flags are propagated correctly. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DynamicSandboxFlags) { |
| GURL main_url( |
| embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| ASSERT_EQ(2U, root->child_count()); |
| |
| // Make sure first frame starts out at the correct cross-site page. |
| EXPECT_EQ(embedded_test_server()->GetURL("bar.com", "/title1.html"), |
| root->child_at(0)->current_url()); |
| |
| // Navigate second frame to another cross-site page. |
| GURL baz_url(embedded_test_server()->GetURL("baz.com", "/title1.html")); |
| NavigateFrameToURL(root->child_at(1), baz_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(baz_url, observer.last_navigation_url()); |
| |
| // Both frames should not be sandboxed to start with. |
| EXPECT_EQ(SandboxFlags::NONE, |
| root->child_at(0)->current_replication_state().sandbox_flags); |
| EXPECT_EQ(SandboxFlags::NONE, root->child_at(0)->effective_sandbox_flags()); |
| EXPECT_EQ(SandboxFlags::NONE, |
| root->child_at(1)->current_replication_state().sandbox_flags); |
| EXPECT_EQ(SandboxFlags::NONE, root->child_at(1)->effective_sandbox_flags()); |
| |
| // Dynamically update sandbox flags for the first frame. |
| EXPECT_TRUE(ExecuteScript(shell()->web_contents(), |
| "window.domAutomationController.send(" |
| "document.querySelector('iframe').sandbox=" |
| "'allow-scripts');")); |
| |
| // Check that updated sandbox flags are propagated to browser process. |
| // The new flags should be set in current_replication_state(), while |
| // effective_sandbox_flags() should still reflect the old flags, because |
| // sandbox flag updates take place only after navigations. "allow-scripts" |
| // resets both SandboxFlags::Scripts and SandboxFlags::AutomaticFeatures bits |
| // per blink::parseSandboxPolicy(). |
| SandboxFlags expected_flags = SandboxFlags::ALL & ~SandboxFlags::SCRIPTS & |
| ~SandboxFlags::AUTOMATIC_FEATURES; |
| EXPECT_EQ(expected_flags, |
| root->child_at(0)->current_replication_state().sandbox_flags); |
| EXPECT_EQ(SandboxFlags::NONE, root->child_at(0)->effective_sandbox_flags()); |
| |
| // Navigate the first frame to a page on the same site. The new sandbox |
| // flags should take effect. The new page has a child frame, so use |
| // TestFrameNavigationObserver to wait for it to be loaded. |
| TestFrameNavigationObserver frame_observer(root->child_at(0), 2); |
| GURL bar_url( |
| embedded_test_server()->GetURL("bar.com", "/frame_tree/2-4.html")); |
| NavigateFrameToURL(root->child_at(0), bar_url); |
| frame_observer.Wait(); |
| EXPECT_EQ(bar_url, root->child_at(0)->current_url()); |
| ASSERT_EQ(1U, root->child_at(0)->child_count()); |
| |
| // Confirm that the browser process has updated the frame's current sandbox |
| // flags. |
| EXPECT_EQ(expected_flags, |
| root->child_at(0)->current_replication_state().sandbox_flags); |
| EXPECT_EQ(expected_flags, root->child_at(0)->effective_sandbox_flags()); |
| |
| // Opening a popup in the now-sandboxed frame should fail. |
| bool success = false; |
| EXPECT_TRUE( |
| ExecuteScriptAndExtractBool(root->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(" |
| "!window.open('data:text/html,dataurl'));", |
| &success)); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(1u, Shell::windows().size()); |
| |
| // Navigate the child of the now-sandboxed frame to a page on baz.com. The |
| // child should inherit the latest sandbox flags from its parent frame, which |
| // is currently a proxy in baz.com's renderer process. This checks that the |
| // proxies of |root->child_at(0)| were also updated with the latest sandbox |
| // flags. |
| GURL baz_child_url(embedded_test_server()->GetURL("baz.com", "/title2.html")); |
| NavigateFrameToURL(root->child_at(0)->child_at(0), baz_child_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(baz_child_url, observer.last_navigation_url()); |
| |
| // Opening a popup in the child of a sandboxed frame should fail. |
| success = false; |
| EXPECT_TRUE(ExecuteScriptAndExtractBool( |
| root->child_at(0)->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(" |
| "!window.open('data:text/html,dataurl'));", |
| &success)); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(1u, Shell::windows().size()); |
| } |
| |
| // Check that dynamic updates to iframe sandbox flags are propagated correctly. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| DynamicSandboxFlagsRemoteToLocal) { |
| GURL main_url( |
| embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| ASSERT_EQ(2U, root->child_count()); |
| |
| // Make sure the two frames starts out at correct URLs. |
| EXPECT_EQ(embedded_test_server()->GetURL("bar.com", "/title1.html"), |
| root->child_at(0)->current_url()); |
| EXPECT_EQ(embedded_test_server()->GetURL("/title1.html"), |
| root->child_at(1)->current_url()); |
| |
| // Update the second frame's sandbox flags. |
| EXPECT_TRUE(ExecuteScript(shell()->web_contents(), |
| "window.domAutomationController.send(" |
| "document.querySelectorAll('iframe')[1].sandbox=" |
| "'allow-scripts');")); |
| |
| // Check that the current sandbox flags are updated but the effective |
| // sandbox flags are not. |
| SandboxFlags expected_flags = SandboxFlags::ALL & ~SandboxFlags::SCRIPTS & |
| ~SandboxFlags::AUTOMATIC_FEATURES; |
| EXPECT_EQ(expected_flags, |
| root->child_at(1)->current_replication_state().sandbox_flags); |
| EXPECT_EQ(SandboxFlags::NONE, root->child_at(1)->effective_sandbox_flags()); |
| |
| // Navigate the second subframe to a page on bar.com. This will trigger a |
| // remote-to-local frame swap in bar.com's process. The target page has |
| // another frame, so use TestFrameNavigationObserver to wait for all frames |
| // to be loaded. |
| TestFrameNavigationObserver frame_observer(root->child_at(1), 2); |
| GURL bar_url(embedded_test_server()->GetURL( |
| "bar.com", "/frame_tree/page_with_one_frame.html")); |
| NavigateFrameToURL(root->child_at(1), bar_url); |
| frame_observer.Wait(); |
| EXPECT_EQ(bar_url, root->child_at(1)->current_url()); |
| ASSERT_EQ(1U, root->child_at(1)->child_count()); |
| |
| // Confirm that the browser process has updated the current sandbox flags. |
| EXPECT_EQ(expected_flags, |
| root->child_at(1)->current_replication_state().sandbox_flags); |
| EXPECT_EQ(expected_flags, root->child_at(1)->effective_sandbox_flags()); |
| |
| // Opening a popup in the sandboxed second frame should fail. |
| bool success = false; |
| EXPECT_TRUE( |
| ExecuteScriptAndExtractBool(root->child_at(1)->current_frame_host(), |
| "window.domAutomationController.send(" |
| "!window.open('data:text/html,dataurl'));", |
| &success)); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(1u, Shell::windows().size()); |
| |
| // Make sure that the child frame inherits the sandbox flags of its |
| // now-sandboxed parent frame. |
| success = false; |
| EXPECT_TRUE(ExecuteScriptAndExtractBool( |
| root->child_at(1)->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(" |
| "!window.open('data:text/html,dataurl'));", |
| &success)); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(1u, Shell::windows().size()); |
| } |
| |
| // Check that dynamic updates to iframe sandbox flags are propagated correctly. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| DynamicSandboxFlagsRendererInitiatedNavigation) { |
| GURL main_url( |
| embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| ASSERT_EQ(1U, root->child_count()); |
| |
| // Make sure the frame starts out at the correct cross-site page. |
| EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title1.html"), |
| root->child_at(0)->current_url()); |
| |
| // The frame should not be sandboxed to start with. |
| EXPECT_EQ(SandboxFlags::NONE, |
| root->child_at(0)->current_replication_state().sandbox_flags); |
| EXPECT_EQ(SandboxFlags::NONE, root->child_at(0)->effective_sandbox_flags()); |
| |
| // Dynamically update the frame's sandbox flags. |
| EXPECT_TRUE(ExecuteScript(shell()->web_contents(), |
| "window.domAutomationController.send(" |
| "document.querySelector('iframe').sandbox=" |
| "'allow-scripts');")); |
| |
| // Check that updated sandbox flags are propagated to browser process. |
| // The new flags should be set in current_replication_state(), while |
| // effective_sandbox_flags() should still reflect the old flags, because |
| // sandbox flag updates take place only after navigations. "allow-scripts" |
| // resets both SandboxFlags::Scripts and SandboxFlags::AutomaticFeatures bits |
| // per blink::parseSandboxPolicy(). |
| SandboxFlags expected_flags = SandboxFlags::ALL & ~SandboxFlags::SCRIPTS & |
| ~SandboxFlags::AUTOMATIC_FEATURES; |
| EXPECT_EQ(expected_flags, |
| root->child_at(0)->current_replication_state().sandbox_flags); |
| EXPECT_EQ(SandboxFlags::NONE, root->child_at(0)->effective_sandbox_flags()); |
| |
| // Perform a renderer-initiated same-site navigation in the first frame. The |
| // new sandbox flags should take effect. |
| TestFrameNavigationObserver frame_observer(root->child_at(0)); |
| ASSERT_TRUE(ExecuteScript(root->child_at(0)->current_frame_host(), |
| "window.location.href='/title2.html'")); |
| frame_observer.Wait(); |
| EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title2.html"), |
| root->child_at(0)->current_url()); |
| |
| // Confirm that the browser process has updated the frame's current sandbox |
| // flags. |
| EXPECT_EQ(expected_flags, |
| root->child_at(0)->current_replication_state().sandbox_flags); |
| EXPECT_EQ(expected_flags, root->child_at(0)->effective_sandbox_flags()); |
| |
| // Opening a popup in the now-sandboxed frame should fail. |
| bool success = false; |
| EXPECT_TRUE( |
| ExecuteScriptAndExtractBool(root->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(" |
| "!window.open('data:text/html,dataurl'));", |
| &success)); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(1u, Shell::windows().size()); |
| } |
| |
| // Verify that a child frame can retrieve the name property set by its parent. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, WindowNameReplication) { |
| GURL main_url(embedded_test_server()->GetURL("/frame_tree/2-4.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Load cross-site page into iframe. |
| GURL frame_url = |
| embedded_test_server()->GetURL("foo.com", "/frame_tree/3-1.html"); |
| NavigateFrameToURL(root->child_at(0), frame_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(frame_url, observer.last_navigation_url()); |
| |
| // Ensure that a new process is created for the subframe. |
| EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| root->child_at(0)->current_frame_host()->GetSiteInstance()); |
| |
| // Check that the window.name seen by the frame matches the name attribute |
| // specified by its parent in the iframe tag. |
| std::string result; |
| EXPECT_TRUE(ExecuteScriptAndExtractString( |
| root->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(window.name);", &result)); |
| EXPECT_EQ("3-1-name", result); |
| } |
| |
| // Verify that dynamic updates to a frame's window.name propagate to the |
| // frame's proxies, so that the latest frame names can be used in navigations. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DynamicWindowName) { |
| GURL main_url(embedded_test_server()->GetURL("/frame_tree/2-4.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Load cross-site page into iframe. |
| GURL frame_url = |
| embedded_test_server()->GetURL("foo.com", "/frame_tree/3-1.html"); |
| NavigateFrameToURL(root->child_at(0), frame_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(frame_url, observer.last_navigation_url()); |
| |
| // Browser process should know the child frame's original window.name |
| // specified in the iframe element. |
| EXPECT_EQ(root->child_at(0)->frame_name(), "3-1-name"); |
| |
| // Update the child frame's window.name. |
| EXPECT_TRUE(ExecuteScript(root->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(" |
| "window.name = 'updated-name');")); |
| |
| // The change should propagate to the browser process. |
| EXPECT_EQ(root->child_at(0)->frame_name(), "updated-name"); |
| |
| // The proxy in the parent process should also receive the updated name. |
| // Check that it can reference the child frame by its new name. |
| bool success = false; |
| EXPECT_TRUE( |
| ExecuteScriptAndExtractBool(shell()->web_contents(), |
| "window.domAutomationController.send(" |
| "frames['updated-name'] == frames[0]);", |
| &success)); |
| EXPECT_TRUE(success); |
| |
| // Issue a renderer-initiated navigation from the root frame to the child |
| // frame using the frame's name. Make sure correct frame is navigated. |
| // |
| // TODO(alexmos): When blink::createWindow is refactored to handle |
| // RemoteFrames, this should also be tested via window.open(url, frame_name) |
| // and a more complicated frame hierarchy (https://crbug.com/463742) |
| TestFrameNavigationObserver frame_observer(root->child_at(0)); |
| GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html")); |
| std::string script = base::StringPrintf( |
| "window.domAutomationController.send(" |
| "frames['updated-name'].location.href = '%s');", |
| foo_url.spec().c_str()); |
| EXPECT_TRUE(ExecuteScript(shell()->web_contents(), script)); |
| frame_observer.Wait(); |
| EXPECT_EQ(foo_url, root->child_at(0)->current_url()); |
| } |
| |
| // Verify that when a frame is navigated to a new origin, the origin update |
| // propagates to the frame's proxies. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, OriginUpdatesReachProxies) { |
| GURL main_url( |
| embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " |--Site B ------- proxies for A\n" |
| " +--Site A ------- proxies for B\n" |
| "Where A = http://127.0.0.1/\n" |
| " B = http://bar.com/", |
| DepictFrameTree(root)); |
| |
| // Navigate second subframe to a baz.com. This should send an origin update |
| // to the frame's proxy in the bar.com (first frame's) process. |
| GURL frame_url = embedded_test_server()->GetURL("baz.com", "/title2.html"); |
| NavigateFrameToURL(root->child_at(1), frame_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(frame_url, observer.last_navigation_url()); |
| |
| // The first frame can't directly observe the second frame's origin with |
| // JavaScript. Instead, try to navigate the second frame from the first |
| // frame. This should fail with a console error message, which should |
| // contain the second frame's updated origin (see blink::Frame::canNavigate). |
| scoped_ptr<ConsoleObserverDelegate> console_delegate( |
| new ConsoleObserverDelegate( |
| shell()->web_contents(), |
| "Unsafe JavaScript attempt to initiate navigation*")); |
| shell()->web_contents()->SetDelegate(console_delegate.get()); |
| |
| // frames[1] can't be used due to a bug where RemoteFrames are created out of |
| // order (https://crbug.com/478792). Instead, target second frame by name. |
| EXPECT_TRUE(ExecuteScript( |
| root->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(" |
| " parent.frames['frame2'].location.href = 'data:text/html,foo');")); |
| console_delegate->Wait(); |
| |
| std::string frame_origin = |
| root->child_at(1)->current_replication_state().origin.string(); |
| EXPECT_EQ(frame_origin + "/", frame_url.GetOrigin().spec()); |
| EXPECT_TRUE( |
| MatchPattern(console_delegate->message(), "*" + frame_origin + "*")) |
| << "Error message does not contain the frame's latest origin (" |
| << frame_origin << ")"; |
| } |
| |
| // Ensure that navigating subframes in --site-per-process mode properly fires |
| // the DidStopLoading event on WebContentsObserver. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteDidStopLoading) { |
| GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Load same-site page into iframe. |
| FrameTreeNode* child = root->child_at(0); |
| GURL http_url(embedded_test_server()->GetURL("/title1.html")); |
| NavigateFrameToURL(child, http_url); |
| EXPECT_EQ(http_url, observer.last_navigation_url()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| |
| // Load cross-site page into iframe. |
| TestNavigationObserver nav_observer(shell()->web_contents(), 1); |
| GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html"); |
| NavigationController::LoadURLParams params(url); |
| params.transition_type = ui::PAGE_TRANSITION_LINK; |
| params.frame_tree_node_id = child->frame_tree_node_id(); |
| child->navigator()->GetController()->LoadURLWithParams(params); |
| nav_observer.Wait(); |
| |
| // Verify that the navigation succeeded and the expected URL was loaded. |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| } |
| |
| // Ensure that the renderer does not crash when navigating a frame that has a |
| // sibling RemoteFrame. See https://crbug.com/426953. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| NavigateWithSiblingRemoteFrame) { |
| GURL main_url( |
| embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Make sure the first frame is out of process. |
| ASSERT_EQ(2U, root->child_count()); |
| FrameTreeNode* node2 = root->child_at(0); |
| EXPECT_NE(root->current_frame_host()->GetSiteInstance(), |
| node2->current_frame_host()->GetSiteInstance()); |
| |
| // Make sure the second frame is in the parent's process. |
| FrameTreeNode* node3 = root->child_at(1); |
| EXPECT_EQ(root->current_frame_host()->GetSiteInstance(), |
| node3->current_frame_host()->GetSiteInstance()); |
| |
| // Navigate the second iframe (node3) to a URL in its own process. |
| GURL title_url = embedded_test_server()->GetURL("/title2.html"); |
| NavigateFrameToURL(node3, title_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(title_url, observer.last_navigation_url()); |
| EXPECT_EQ(root->current_frame_host()->GetSiteInstance(), |
| node3->current_frame_host()->GetSiteInstance()); |
| EXPECT_TRUE(node3->current_frame_host()->IsRenderFrameLive()); |
| } |
| |
| // Verify that load events for iframe elements work when the child frame is |
| // out-of-process. In such cases, the load event is forwarded from the child |
| // frame to the parent frame via the browser process. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, LoadEventForwarding) { |
| // Load a page with a cross-site frame. The parent page has an onload |
| // handler in the iframe element that appends "LOADED" to the document title. |
| { |
| GURL main_url( |
| embedded_test_server()->GetURL("/frame_with_load_event.html")); |
| base::string16 expected_title(base::UTF8ToUTF16("LOADED")); |
| TitleWatcher title_watcher(shell()->web_contents(), expected_title); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| EXPECT_EQ(title_watcher.WaitAndGetTitle(), expected_title); |
| } |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| // Load another cross-site page into the iframe and check that the load event |
| // is fired. |
| { |
| GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html")); |
| base::string16 expected_title(base::UTF8ToUTF16("LOADEDLOADED")); |
| TitleWatcher title_watcher(shell()->web_contents(), expected_title); |
| TestNavigationObserver observer(shell()->web_contents()); |
| NavigateFrameToURL(root->child_at(0), foo_url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(foo_url, observer.last_navigation_url()); |
| EXPECT_EQ(title_watcher.WaitAndGetTitle(), expected_title); |
| } |
| } |
| |
| // Check that postMessage can be routed between cross-site iframes. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, SubframePostMessage) { |
| GURL main_url(embedded_test_server()->GetURL( |
| "/frame_tree/page_with_post_message_frames.html")); |
| EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| ASSERT_EQ(2U, root->child_count()); |
| |
| // Verify the frames start at correct URLs. First frame should be |
| // same-site; second frame should be cross-site. |
| GURL same_site_url(embedded_test_server()->GetURL("/post_message.html")); |
| EXPECT_EQ(same_site_url, root->child_at(0)->current_url()); |
| GURL foo_url(embedded_test_server()->GetURL("foo.com", |
| "/post_message.html")); |
| EXPECT_EQ(foo_url, root->child_at(1)->current_url()); |
| EXPECT_NE(root->child_at(0)->current_frame_host()->GetSiteInstance(), |
| root->child_at(1)->current_frame_host()->GetSiteInstance()); |
| |
| // Send a message from first, same-site frame to second, cross-site frame. |
| // Expect the second frame to reply back to the first frame. |
| // |
| // TODO(alexmos): Also try sending from second to first frame. Currently, |
| // this fails due to https://crbug.com/473518, which prevents |
| // parent.frames[x] from working when "parent" is a remote frame. |
| bool success = false; |
| EXPECT_TRUE(ExecuteScriptAndExtractBool( |
| root->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(" |
| " postToSibling('subframe-msg','subframe2'));", |
| &success)); |
| EXPECT_TRUE(success); |
| |
| // Wait for first frame to receive a reply from the second frame. It will |
| // send "done-subframe1" from the DOMAutomationController when the reply |
| // arrives. |
| content::DOMMessageQueue msg_queue; |
| std::string status; |
| while (msg_queue.WaitForMessage(&status)) { |
| if (status == "\"done-subframe1\"") |
| break; |
| } |
| |
| // Send a postMessage from second, cross-site frame to its parent. Expect |
| // parent to send a reply to the frame. |
| base::string16 expected_title(base::ASCIIToUTF16("subframe-msg")); |
| TitleWatcher title_watcher(shell()->web_contents(), expected_title); |
| success = false; |
| EXPECT_TRUE(ExecuteScriptAndExtractBool( |
| root->child_at(1)->current_frame_host(), |
| "window.domAutomationController.send(postToParent('subframe-msg'));", |
| &success)); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); |
| |
| // Wait for second frame to receive a reply from the parent. The frame will |
| // return "done-subframe2" from the DOMAutomationController when the reply |
| // arrives. |
| while (msg_queue.WaitForMessage(&status)) { |
| if (status == "\"done-subframe2\"") |
| break; |
| } |
| |
| // Verify the total number of received messages for each subframe. First |
| // frame should have one message (reply from second frame), and second frame |
| // should have two messages (message from first frame and reply from parent). |
| int subframe1_received_messages = 0; |
| int subframe2_received_messages = 0; |
| EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| root->child_at(0)->current_frame_host(), |
| "window.domAutomationController.send(window.receivedMessages);", |
| &subframe1_received_messages)); |
| EXPECT_EQ(1, subframe1_received_messages); |
| EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| root->child_at(1)->current_frame_host(), |
| "window.domAutomationController.send(window.receivedMessages);", |
| &subframe2_received_messages)); |
| EXPECT_EQ(2, subframe2_received_messages); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, RFPHDestruction) { |
| GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
| NavigateToURL(shell(), main_url); |
| |
| // It is safe to obtain the root frame tree node here, as it doesn't change. |
| FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| ->GetFrameTree() |
| ->root(); |
| |
| TestNavigationObserver observer(shell()->web_contents()); |
| |
| // Load cross-site page into iframe. |
| FrameTreeNode* child = root->child_at(0); |
| GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html"); |
| NavigateFrameToURL(root->child_at(0), url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| EXPECT_EQ( |
| " Site A ------------ proxies for B\n" |
| " |--Site B ------- proxies for A\n" |
| " +--Site A ------- proxies for B\n" |
| " |--Site A -- proxies for B\n" |
| " +--Site A -- proxies for B\n" |
| " +--Site A -- proxies for B\n" |
| "Where A = http://127.0.0.1/\n" |
| " B = http://foo.com/", |
| DepictFrameTree(root)); |
| |
| // Load another cross-site page. |
| url = embedded_test_server()->GetURL("bar.com", "/title3.html"); |
| NavigateIframeToURL(shell()->web_contents(), "test", url); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| EXPECT_EQ( |
| " Site A ------------ proxies for C\n" |
| " |--Site C ------- proxies for A\n" |
| " +--Site A ------- proxies for C\n" |
| " |--Site A -- proxies for C\n" |
| " +--Site A -- proxies for C\n" |
| " +--Site A -- proxies for C\n" |
| "Where A = http://127.0.0.1/\n" |
| " C = http://bar.com/", |
| DepictFrameTree(root)); |
| |
| // Navigate back to the parent's origin. |
| url = embedded_test_server()->GetURL("/title1.html"); |
| NavigateFrameToURL(child, url); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ( |
| " Site A\n" |
| " |--Site A\n" |
| " +--Site A\n" |
| " |--Site A\n" |
| " +--Site A\n" |
| " +--Site A\n" |
| "Where A = http://127.0.0.1/", |
| DepictFrameTree(root)); |
| } |
| |
| } // namespace content |