// 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 "base/command_line.h" #include "base/strings/stringprintf.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/public/browser/notification_observer.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/web_contents_observer.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_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 "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" namespace content { class SitePerProcessWebContentsObserver: public WebContentsObserver { public: explicit SitePerProcessWebContentsObserver(WebContents* web_contents) : WebContentsObserver(web_contents), navigation_succeeded_(false) {} ~SitePerProcessWebContentsObserver() override {} void DidStartProvisionalLoadForFrame(RenderFrameHost* render_frame_host, const GURL& validated_url, bool is_error_page, bool is_iframe_srcdoc) override { navigation_succeeded_ = false; } void DidFailProvisionalLoad( RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code, const base::string16& error_description) override { navigation_url_ = validated_url; navigation_succeeded_ = false; } void DidCommitProvisionalLoadForFrame( RenderFrameHost* render_frame_host, const GURL& url, ui::PageTransition transition_type) override { navigation_url_ = url; navigation_succeeded_ = true; } const GURL& navigation_url() const { return navigation_url_; } int navigation_succeeded() const { return navigation_succeeded_; } private: GURL navigation_url_; bool navigation_succeeded_; DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver); }; 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 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; } // // SitePerProcessBrowserTest // SitePerProcessBrowserTest::SitePerProcessBrowserTest() { }; 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)); } bool SitePerProcessBrowserTest::NavigateIframeToURL(Shell* window, const GURL& url, std::string iframe_id) { // TODO(creis): This should wait for LOAD_STOP, but cross-site subframe // navigations generate extra DidStartLoading and DidStopLoading messages. // Until we replace swappedout:// with frame proxies, we need to listen for // something else. For now, we trigger NEW_SUBFRAME navigations and listen // for commit. std::string script = base::StringPrintf( "setTimeout(\"" "var iframes = document.getElementById('%s');iframes.src='%s';" "\",0)", iframe_id.c_str(), url.spec().c_str()); WindowedNotificationObserver load_observer( NOTIFICATION_NAV_ENTRY_COMMITTED, Source( &window->web_contents()->GetController())); bool result = ExecuteScript(window->web_contents(), script); load_observer.Wait(); return result; } void SitePerProcessBrowserTest::SetUpCommandLine(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()); } // It times out on Android, so disabled while investigating. // http://crbug.com/399775 #if defined(OS_ANDROID) #define MAYBE_CrossSiteIframe DISABLED_CrossSiteIframe #else #define MAYBE_CrossSiteIframe CrossSiteIframe #endif IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, MAYBE_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(shell()->web_contents())-> GetFrameTree()->root(); SitePerProcessWebContentsObserver 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.navigation_url()); EXPECT_TRUE(observer.navigation_succeeded()); { // There should be only one RenderWidgetHost when there are no // cross-process iframes. std::set views_set = static_cast(shell()->web_contents()) ->GetRenderWidgetHostViewsInTree(); EXPECT_EQ(1U, views_set.size()); } RenderFrameProxyHost* proxy_to_parent = child->render_manager()->GetRenderFrameProxyHost( shell()->web_contents()->GetSiteInstance()); EXPECT_FALSE(proxy_to_parent); // Load cross-site page into iframe. NavigateFrameToURL( root->child_at(0), embedded_test_server()->GetURL("/cross-site/foo.com/title2.html")); // Verify that the navigation succeeded and the expected URL was loaded. EXPECT_TRUE(observer.navigation_succeeded()); EXPECT_EQ(embedded_test_server()->base_url().scheme(), observer.navigation_url().scheme()); EXPECT_EQ("foo.com", observer.navigation_url().host()); EXPECT_EQ("/title2.html", observer.navigation_url().path()); // 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 views_set = static_cast(shell()->web_contents()) ->GetRenderWidgetHostViewsInTree(); EXPECT_EQ(2U, views_set.size()); } proxy_to_parent = child->render_manager()->GetProxyToParent(); EXPECT_TRUE(proxy_to_parent); EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector()); EXPECT_EQ( rvh->GetView(), proxy_to_parent->cross_process_frame_connector()->get_view_for_testing()); // Load another cross-site page into the same iframe. NavigateFrameToURL( root->child_at(0), embedded_test_server()->GetURL("/cross-site/bar.com/title3.html")); EXPECT_TRUE(observer.navigation_succeeded()); EXPECT_EQ(embedded_test_server()->base_url().scheme(), observer.navigation_url().scheme()); EXPECT_EQ("bar.com", observer.navigation_url().host()); EXPECT_EQ("/title3.html", observer.navigation_url().path()); // 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 views_set = static_cast(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_EQ( child->current_frame_host()->render_view_host()->GetView(), proxy_to_parent->cross_process_frame_connector()->get_view_for_testing()); } // It times out on Android, so disabled while investigating. // http://crbug.com/399775 #if defined(OS_ANDROID) #define MAYBE_NavigateRemoteFrame DISABLED_NavigateRemoteFrame #else #define MAYBE_NavigateRemoteFrame NavigateRemoteFrame #endif IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, MAYBE_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(shell()->web_contents())-> GetFrameTree()->root(); SitePerProcessWebContentsObserver 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.navigation_url()); EXPECT_TRUE(observer.navigation_succeeded()); // Load cross-site page into iframe. NavigateFrameToURL( root->child_at(0), embedded_test_server()->GetURL("/cross-site/foo.com/title2.html")); EXPECT_TRUE(observer.navigation_succeeded()); EXPECT_EQ("foo.com", observer.navigation_url().host()); EXPECT_EQ("/title2.html", observer.navigation_url().path()); // 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. NavigateIframeToURL( shell(), embedded_test_server()->GetURL("/cross-site/bar.com/title3.html"), "test"); EXPECT_TRUE(observer.navigation_succeeded()); EXPECT_EQ("bar.com", observer.navigation_url().host()); EXPECT_EQ("/title3.html", observer.navigation_url().path()); // 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()); // TODO(japhet): This currently causes an assertion in the renderer process. // Enable when the assertion is fixed. //NavigateFrameToURL(child, http_url); //EXPECT_EQ(http_url, observer.navigation_url()); //EXPECT_TRUE(observer.navigation_succeeded()); //EXPECT_EQ(shell()->web_contents()->GetSiteInstance(), // child->current_frame_host()->GetSiteInstance()); } // 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. // TODO(creis): Enable this on Android when we can kill the process there. 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(), embedded_test_server()->GetURL("/cross-site/foo.com/title2.html"), "test")); // Check the subframe process. FrameTreeNode* root = static_cast(shell()->web_contents())-> GetFrameTree()->root(); ASSERT_EQ(1U, 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); base::KillProcess(child_process->GetHandle(), 0, false); crash_observer.Wait(); } // Ensure that the child frame still exists but has been cleared. EXPECT_EQ(1U, 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); base::KillProcess(root_process->GetHandle(), 0, false); crash_observer.Wait(); } EXPECT_EQ(0U, root->child_count()); EXPECT_EQ(GURL(), root->current_url()); } // 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); SitePerProcessWebContentsObserver 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(), client_redirect_https_url, "test")); // DidFailProvisionalLoad when navigating to client_redirect_https_url. EXPECT_EQ(observer.navigation_url(), client_redirect_https_url); EXPECT_FALSE(observer.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(), server_redirect_http_url, "test")); EXPECT_EQ(observer.navigation_url(), http_url); EXPECT_TRUE(observer.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(), server_redirect_http_url, "test")); // DidFailProvisionalLoad when navigating to https_url. EXPECT_EQ(observer.navigation_url(), https_url); EXPECT_FALSE(observer.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(), server_redirect_http_url, "test")); EXPECT_EQ(observer.navigation_url(), https_url); EXPECT_FALSE(observer.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( &shell()->web_contents()->GetController())); EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test")); // Same-site Client-Redirect Page should be loaded successfully. EXPECT_EQ(observer.navigation_url(), client_redirect_http_url); EXPECT_TRUE(observer.navigation_succeeded()); // Redirecting to Cross-site Page should be blocked. load_observer2.Wait(); EXPECT_EQ(observer.navigation_url(), https_url); EXPECT_FALSE(observer.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(), server_redirect_http_url, "test")); EXPECT_EQ(observer.navigation_url(), http_url); EXPECT_TRUE(observer.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( &shell()->web_contents()->GetController())); EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test")); // Same-site Client-Redirect Page should be loaded successfully. EXPECT_EQ(observer.navigation_url(), client_redirect_http_url); EXPECT_TRUE(observer.navigation_succeeded()); // Redirecting to Same-site Page should be loaded successfully. load_observer2.Wait(); EXPECT_EQ(observer.navigation_url(), http_url); EXPECT_TRUE(observer.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); SitePerProcessWebContentsObserver 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( &shell()->web_contents()->GetController())); EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test")); // DidFailProvisionalLoad when navigating to client_redirect_https_url. load_observer2.Wait(); EXPECT_EQ(observer.navigation_url(), client_redirect_https_url); EXPECT_FALSE(observer.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(), server_redirect_http_url, "test")); EXPECT_EQ(observer.navigation_url(), http_url); EXPECT_TRUE(observer.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(), server_redirect_http_url, "test")); // DidFailProvisionalLoad when navigating to https_url. EXPECT_EQ(observer.navigation_url(), https_url); EXPECT_FALSE(observer.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(), server_redirect_http_url, "test")); // DidFailProvisionalLoad when navigating to client_redirect_http_url. EXPECT_EQ(observer.navigation_url(), client_redirect_http_url); EXPECT_FALSE(observer.navigation_succeeded()); } } // Ensure that when navigating a frame cross-process RenderFrameProxyHosts are // created in the FrameTree skipping the subtree of the navigating frame. // TODO(nasko): Test is disabled on Android, because it times out. It should // be fixed together with CrossSiteIframe on that platform. // // Disabled on Mac due to flakiness on ASAN. http://crbug.com/425248 #if defined(OS_ANDROID) || defined(OS_MACOSX) #define MAYBE_ProxyCreationSkipsSubtree DISABLED_ProxyCreationSkipsSubtree #else #define MAYBE_ProxyCreationSkipsSubtree ProxyCreationSkipsSubtree #endif IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, MAYBE_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(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. SitePerProcessWebContentsObserver 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.navigation_url()); EXPECT_TRUE(observer.navigation_succeeded()); RenderFrameProxyHost* proxy_to_parent = root->child_at(0)->render_manager()->GetRenderFrameProxyHost( shell()->web_contents()->GetSiteInstance()); EXPECT_FALSE(proxy_to_parent); } // Create the cross-site URL to navigate to. GURL::Replacements replace_host; std::string foo_com("foo.com"); GURL cross_site_url(embedded_test_server()->GetURL("/frame_tree/1-1.html")); replace_host.SetHostStr(foo_com); cross_site_url = cross_site_url.ReplaceComponents(replace_host); // 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; { SitePerProcessWebContentsObserver 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); EXPECT_TRUE(child->render_manager()->pending_frame_host()); site = child->render_manager()->pending_frame_host()->GetSiteInstance(); EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site); EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(site)); EXPECT_TRUE( root->child_at(0)->render_manager()->GetRenderFrameProxyHost(site)); EXPECT_FALSE(child->render_manager()->GetRenderFrameProxyHost(site)); for (size_t i = 0; i < child->child_count(); ++i) { EXPECT_FALSE( child->child_at(i)->render_manager()->GetRenderFrameProxyHost(site)); } // 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_EQ(cross_site_url, observer.navigation_url()); EXPECT_TRUE(observer.navigation_succeeded()); } // Load another cross-site page into the same iframe. cross_site_url = embedded_test_server()->GetURL("/title2.html"); std::string bar_com("bar.com"); replace_host.SetHostStr(bar_com); cross_site_url = cross_site_url.ReplaceComponents(replace_host); { // 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. SitePerProcessWebContentsObserver 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); EXPECT_TRUE(child->render_manager()->pending_frame_host() != NULL); SiteInstance* site2 = child->render_manager()->pending_frame_host()->GetSiteInstance(); EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site2); EXPECT_NE(site, site2); EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(site2)); EXPECT_TRUE( root->child_at(0)->render_manager()->GetRenderFrameProxyHost(site2)); EXPECT_FALSE(child->render_manager()->GetRenderFrameProxyHost(site2)); for (size_t i = 0; i < child->child_count(); ++i) { EXPECT_FALSE( child->child_at(i)->render_manager()->GetRenderFrameProxyHost(site2)); } navigation_observer.Wait(); EXPECT_EQ(cross_site_url, observer.navigation_url()); EXPECT_TRUE(observer.navigation_succeeded()); EXPECT_EQ(0U, child->child_count()); } } } // namespace content