| // 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 "base/command_line.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "content/browser/frame_host/frame_tree.h" |
| #include "content/browser/renderer_host/render_view_host_impl.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| #include "content/public/browser/navigation_entry.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/common/url_constants.h" |
| #include "content/public/test/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/shell/browser/shell_content_browser_client.h" |
| #include "content/test/content_browser_test.h" |
| #include "content/test/content_browser_test_utils.h" |
| #include "net/base/escape.h" |
| #include "net/dns/mock_host_resolver.h" |
| |
| namespace content { |
| |
| class SitePerProcessWebContentsObserver: public WebContentsObserver { |
| public: |
| explicit SitePerProcessWebContentsObserver(WebContents* web_contents) |
| : WebContentsObserver(web_contents), |
| navigation_succeeded_(true) {} |
| virtual ~SitePerProcessWebContentsObserver() {} |
| |
| virtual void DidFailProvisionalLoad( |
| int64 frame_id, |
| const base::string16& frame_unique_name, |
| bool is_main_frame, |
| const GURL& validated_url, |
| int error_code, |
| const base::string16& error_description, |
| RenderViewHost* render_view_host) OVERRIDE { |
| navigation_url_ = validated_url; |
| navigation_succeeded_ = false; |
| } |
| |
| virtual void DidCommitProvisionalLoadForFrame( |
| int64 frame_id, |
| const base::string16& frame_unique_name, |
| bool is_main_frame, |
| const GURL& url, |
| PageTransition transition_type, |
| RenderViewHost* render_view_host) 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); |
| virtual ~RedirectNotificationObserver(); |
| |
| // 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: |
| virtual 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; |
| } |
| |
| class SitePerProcessBrowserTest : public ContentBrowserTest { |
| protected: |
| bool NavigateIframeToURL(Shell* window, |
| const GURL& url, |
| std::string iframe_id) { |
| std::string script = base::StringPrintf( |
| "var iframes = document.getElementById('%s');iframes.src='%s';", |
| iframe_id.c_str(), url.spec().c_str()); |
| WindowedNotificationObserver load_observer( |
| NOTIFICATION_LOAD_STOP, |
| Source<NavigationController>( |
| &shell()->web_contents()->GetController())); |
| bool result = ExecuteScript(window->web_contents(), script); |
| load_observer.Wait(); |
| return result; |
| } |
| |
| void NavigateToURLContentInitiated(Shell* window, |
| const GURL& url, |
| bool should_replace_current_entry) { |
| std::string script; |
| if (should_replace_current_entry) |
| script = base::StringPrintf("location.replace('%s')", url.spec().c_str()); |
| else |
| script = base::StringPrintf("location.href = '%s'", url.spec().c_str()); |
| TestNavigationObserver load_observer(shell()->web_contents(), 1); |
| bool result = ExecuteScript(window->web_contents(), script); |
| EXPECT_TRUE(result); |
| load_observer.Wait(); |
| } |
| |
| virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { |
| command_line->AppendSwitch(switches::kSitePerProcess); |
| } |
| }; |
| |
| // TODO(nasko): Disable this test until out-of-process iframes is ready and the |
| // security checks are back in place. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DISABLED_CrossSiteIframe) { |
| 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")); |
| |
| NavigateToURL(shell(), main_url); |
| |
| SitePerProcessWebContentsObserver observer(shell()->web_contents()); |
| { |
| // Load same-site page into Iframe. |
| GURL http_url(test_server()->GetURL("files/title1.html")); |
| EXPECT_TRUE(NavigateIframeToURL(shell(), http_url, "test")); |
| EXPECT_EQ(observer.navigation_url(), http_url); |
| EXPECT_TRUE(observer.navigation_succeeded()); |
| } |
| |
| { |
| // Load cross-site page into Iframe. |
| GURL https_url(https_server.GetURL("files/title1.html")); |
| EXPECT_TRUE(NavigateIframeToURL(shell(), https_url, "test")); |
| EXPECT_EQ(observer.navigation_url(), https_url); |
| EXPECT_FALSE(observer.navigation_succeeded()); |
| } |
| } |
| |
| // TODO(nasko): Disable this test until out-of-process iframes is ready and the |
| // security checks are back in place. |
| 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<NavigationController>( |
| &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<NavigationController>( |
| &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. |
| 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<NavigationController>( |
| &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()); |
| } |
| } |
| |
| // Ensures FrameTree correctly reflects page structure during navigations. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| FrameTreeShape) { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(test_server()->Start()); |
| |
| GURL base_url = test_server()->GetURL("files/site_isolation/"); |
| GURL::Replacements replace_host; |
| std::string host_str("A.com"); // Must stay in scope with replace_host. |
| replace_host.SetHostStr(host_str); |
| base_url = base_url.ReplaceComponents(replace_host); |
| |
| // Load doc without iframes. Verify FrameTree just has root. |
| // Frame tree: |
| // Site-A Root |
| NavigateToURL(shell(), base_url.Resolve("blank.html")); |
| FrameTreeNode* root = |
| static_cast<WebContentsImpl*>(shell()->web_contents())-> |
| GetFrameTree()->root(); |
| EXPECT_EQ(0U, root->child_count()); |
| |
| // Add 2 same-site frames. Verify 3 nodes in tree with proper names. |
| // Frame tree: |
| // Site-A Root -- Site-A frame1 |
| // \-- Site-A frame2 |
| WindowedNotificationObserver observer1( |
| content::NOTIFICATION_LOAD_STOP, |
| content::Source<NavigationController>( |
| &shell()->web_contents()->GetController())); |
| NavigateToURL(shell(), base_url.Resolve("frames-X-X.html")); |
| observer1.Wait(); |
| ASSERT_EQ(2U, root->child_count()); |
| EXPECT_EQ(0U, root->child_at(0)->child_count()); |
| EXPECT_EQ(0U, root->child_at(1)->child_count()); |
| } |
| |
| // TODO(ajwong): Talk with nasko and merge this functionality with |
| // FrameTreeShape. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| FrameTreeShape2) { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(test_server()->Start()); |
| |
| NavigateToURL(shell(), |
| test_server()->GetURL("files/frame_tree/top.html")); |
| |
| WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); |
| RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( |
| wc->GetRenderViewHost()); |
| FrameTreeNode* root = wc->GetFrameTree()->root(); |
| |
| // Check that the root node is properly created with the frame id of the |
| // initial navigation. |
| ASSERT_EQ(3UL, root->child_count()); |
| EXPECT_EQ(std::string(), root->frame_name()); |
| EXPECT_EQ(rvh->main_frame_id(), root->frame_id()); |
| |
| ASSERT_EQ(2UL, root->child_at(0)->child_count()); |
| EXPECT_STREQ("1-1-name", root->child_at(0)->frame_name().c_str()); |
| |
| // Verify the deepest node exists and has the right name. |
| ASSERT_EQ(2UL, root->child_at(2)->child_count()); |
| EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count()); |
| EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count()); |
| EXPECT_STREQ("3-1-id", |
| root->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str()); |
| |
| // Navigate to about:blank, which should leave only the root node of the frame |
| // tree in the browser process. |
| NavigateToURL(shell(), test_server()->GetURL("files/title1.html")); |
| |
| root = wc->GetFrameTree()->root(); |
| EXPECT_EQ(0UL, root->child_count()); |
| EXPECT_EQ(std::string(), root->frame_name()); |
| EXPECT_EQ(rvh->main_frame_id(), root->frame_id()); |
| } |
| |
| // Test that we can navigate away if the previous renderer doesn't clean up its |
| // child frames. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FrameTreeAfterCrash) { |
| ASSERT_TRUE(test_server()->Start()); |
| NavigateToURL(shell(), |
| test_server()->GetURL("files/frame_tree/top.html")); |
| |
| // Crash the renderer so that it doesn't send any FrameDetached messages. |
| WindowedNotificationObserver crash_observer( |
| NOTIFICATION_RENDERER_PROCESS_CLOSED, |
| NotificationService::AllSources()); |
| NavigateToURL(shell(), GURL(kChromeUICrashURL)); |
| crash_observer.Wait(); |
| |
| // The frame tree should be cleared, and the frame ID should be reset. |
| WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); |
| RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( |
| wc->GetRenderViewHost()); |
| FrameTreeNode* root = wc->GetFrameTree()->root(); |
| EXPECT_EQ(0UL, root->child_count()); |
| EXPECT_EQ(FrameTreeNode::kInvalidFrameId, root->frame_id()); |
| EXPECT_EQ(rvh->main_frame_id(), root->frame_id()); |
| |
| // Navigate to a new URL. |
| NavigateToURL(shell(), test_server()->GetURL("files/title1.html")); |
| |
| // The frame ID should now be set. |
| EXPECT_EQ(0UL, root->child_count()); |
| EXPECT_NE(FrameTreeNode::kInvalidFrameId, root->frame_id()); |
| EXPECT_EQ(rvh->main_frame_id(), root->frame_id()); |
| } |
| |
| // Test that we can navigate away if the previous renderer doesn't clean up its |
| // child frames. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigateWithLeftoverFrames) { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(test_server()->Start()); |
| |
| GURL base_url = test_server()->GetURL("files/site_isolation/"); |
| GURL::Replacements replace_host; |
| std::string host_str("A.com"); // Must stay in scope with replace_host. |
| replace_host.SetHostStr(host_str); |
| base_url = base_url.ReplaceComponents(replace_host); |
| |
| NavigateToURL(shell(), |
| test_server()->GetURL("files/frame_tree/top.html")); |
| |
| // Hang the renderer so that it doesn't send any FrameDetached messages. |
| // (This navigation will never complete, so don't wait for it.) |
| shell()->LoadURL(GURL(kChromeUIHangURL)); |
| |
| // Check that the frame tree still has children. |
| WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); |
| FrameTreeNode* root = wc->GetFrameTree()->root(); |
| ASSERT_EQ(3UL, root->child_count()); |
| |
| // Navigate to a new URL. We use LoadURL because NavigateToURL will try to |
| // wait for the previous navigation to stop. |
| TestNavigationObserver tab_observer(wc, 1); |
| shell()->LoadURL(base_url.Resolve("blank.html")); |
| tab_observer.Wait(); |
| |
| // The frame tree should now be cleared, and the frame ID should be valid. |
| EXPECT_EQ(0UL, root->child_count()); |
| EXPECT_NE(FrameTreeNode::kInvalidFrameId, root->frame_id()); |
| } |
| |
| // Tests that the |should_replace_current_entry| flag persists correctly across |
| // request transfers that began with a cross-process navigation. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| ReplaceEntryCrossProcessThenTranfers) { |
| const NavigationController& controller = |
| shell()->web_contents()->GetController(); |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(test_server()->Start()); |
| |
| // These must all stay in scope with replace_host. |
| GURL::Replacements replace_host; |
| std::string a_com("A.com"); |
| std::string b_com("B.com"); |
| |
| // Navigate to a starting URL, so there is a history entry to replace. |
| GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1"); |
| NavigateToURL(shell(), url1); |
| |
| // Force all future navigations to transfer. Note that this includes same-site |
| // navigiations which may cause double process swaps (via OpenURL and then via |
| // transfer). This test intentionally exercises that case. |
| ShellContentBrowserClient::SetSwapProcessesForRedirect(true); |
| |
| // Navigate to a page on A.com with entry replacement. This navigation is |
| // cross-site, so the renderer will send it to the browser via OpenURL to give |
| // to a new process. It will then be transferred into yet another process due |
| // to the call above. |
| GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2"); |
| replace_host.SetHostStr(a_com); |
| url2 = url2.ReplaceComponents(replace_host); |
| NavigateToURLContentInitiated(shell(), url2, true); |
| |
| // There should be one history entry. url2 should have replaced url1. |
| EXPECT_TRUE(controller.GetPendingEntry() == NULL); |
| EXPECT_EQ(1, controller.GetEntryCount()); |
| EXPECT_EQ(0, controller.GetCurrentEntryIndex()); |
| EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL()); |
| |
| // Now navigate as before to a page on B.com, but normally (without |
| // replacement). This will still perform a double process-swap as above, via |
| // OpenURL and then transfer. |
| GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3"); |
| replace_host.SetHostStr(b_com); |
| url3 = url3.ReplaceComponents(replace_host); |
| NavigateToURLContentInitiated(shell(), url3, false); |
| |
| // There should be two history entries. url2 should have replaced url1. url2 |
| // should not have replaced url3. |
| EXPECT_TRUE(controller.GetPendingEntry() == NULL); |
| EXPECT_EQ(2, controller.GetEntryCount()); |
| EXPECT_EQ(1, controller.GetCurrentEntryIndex()); |
| EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL()); |
| EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL()); |
| } |
| |
| // Tests that the |should_replace_current_entry| flag persists correctly across |
| // request transfers that began with a content-initiated in-process |
| // navigation. This test is the same as the test above, except transfering from |
| // in-process. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| ReplaceEntryInProcessThenTranfers) { |
| const NavigationController& controller = |
| shell()->web_contents()->GetController(); |
| ASSERT_TRUE(test_server()->Start()); |
| |
| // Navigate to a starting URL, so there is a history entry to replace. |
| GURL url = test_server()->GetURL("files/site_isolation/blank.html?1"); |
| NavigateToURL(shell(), url); |
| |
| // Force all future navigations to transfer. Note that this includes same-site |
| // navigiations which may cause double process swaps (via OpenURL and then via |
| // transfer). All navigations in this test are same-site, so it only swaps |
| // processes via request transfer. |
| ShellContentBrowserClient::SetSwapProcessesForRedirect(true); |
| |
| // Navigate in-process with entry replacement. It will then be transferred |
| // into a new one due to the call above. |
| GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2"); |
| NavigateToURLContentInitiated(shell(), url2, true); |
| |
| // There should be one history entry. url2 should have replaced url1. |
| EXPECT_TRUE(controller.GetPendingEntry() == NULL); |
| EXPECT_EQ(1, controller.GetEntryCount()); |
| EXPECT_EQ(0, controller.GetCurrentEntryIndex()); |
| EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL()); |
| |
| // Now navigate as before, but without replacement. |
| GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3"); |
| NavigateToURLContentInitiated(shell(), url3, false); |
| |
| // There should be two history entries. url2 should have replaced url1. url2 |
| // should not have replaced url3. |
| EXPECT_TRUE(controller.GetPendingEntry() == NULL); |
| EXPECT_EQ(2, controller.GetEntryCount()); |
| EXPECT_EQ(1, controller.GetCurrentEntryIndex()); |
| EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL()); |
| EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL()); |
| } |
| |
| // Tests that the |should_replace_current_entry| flag persists correctly across |
| // request transfers that cross processes twice from renderer policy. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
| ReplaceEntryCrossProcessTwice) { |
| const NavigationController& controller = |
| shell()->web_contents()->GetController(); |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(test_server()->Start()); |
| |
| // These must all stay in scope with replace_host. |
| GURL::Replacements replace_host; |
| std::string a_com("A.com"); |
| std::string b_com("B.com"); |
| |
| // Navigate to a starting URL, so there is a history entry to replace. |
| GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1"); |
| NavigateToURL(shell(), url1); |
| |
| // Navigate to a page on A.com which redirects to B.com with entry |
| // replacement. This will switch processes via OpenURL twice. First to A.com, |
| // and second in response to the server redirect to B.com. The second swap is |
| // also renderer-initiated via OpenURL because decidePolicyForNavigation is |
| // currently applied on redirects. |
| GURL url2b = test_server()->GetURL("files/site_isolation/blank.html?2"); |
| replace_host.SetHostStr(b_com); |
| url2b = url2b.ReplaceComponents(replace_host); |
| GURL url2a = test_server()->GetURL( |
| "server-redirect?" + net::EscapeQueryParamValue(url2b.spec(), false)); |
| replace_host.SetHostStr(a_com); |
| url2a = url2a.ReplaceComponents(replace_host); |
| NavigateToURLContentInitiated(shell(), url2a, true); |
| |
| // There should be one history entry. url2b should have replaced url1. |
| EXPECT_TRUE(controller.GetPendingEntry() == NULL); |
| EXPECT_EQ(1, controller.GetEntryCount()); |
| EXPECT_EQ(0, controller.GetCurrentEntryIndex()); |
| EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL()); |
| |
| // Now repeat without replacement. |
| GURL url3b = test_server()->GetURL("files/site_isolation/blank.html?3"); |
| replace_host.SetHostStr(b_com); |
| url3b = url3b.ReplaceComponents(replace_host); |
| GURL url3a = test_server()->GetURL( |
| "server-redirect?" + net::EscapeQueryParamValue(url3b.spec(), false)); |
| replace_host.SetHostStr(a_com); |
| url3a = url3a.ReplaceComponents(replace_host); |
| NavigateToURLContentInitiated(shell(), url3a, false); |
| |
| // There should be two history entries. url2b should have replaced url1. url2b |
| // should not have replaced url3b. |
| EXPECT_TRUE(controller.GetPendingEntry() == NULL); |
| EXPECT_EQ(2, controller.GetEntryCount()); |
| EXPECT_EQ(1, controller.GetCurrentEntryIndex()); |
| EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL()); |
| EXPECT_EQ(url3b, controller.GetEntryAtIndex(1)->GetURL()); |
| } |
| |
| } // namespace content |