summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/browser/cross_site_transfer_browsertest.cc454
-rw-r--r--content/browser/site_per_process_browsertest.cc430
-rw-r--r--content/content_tests.gypi1
3 files changed, 457 insertions, 428 deletions
diff --git a/content/browser/cross_site_transfer_browsertest.cc b/content/browser/cross_site_transfer_browsertest.cc
new file mode 100644
index 0000000..c5ff7da
--- /dev/null
+++ b/content/browser/cross_site_transfer_browsertest.cc
@@ -0,0 +1,454 @@
+// 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 "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/resource_dispatcher_host_delegate.h"
+#include "content/public/browser/resource_throttle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/shell/browser/shell.h"
+#include "content/shell/browser/shell_content_browser_client.h"
+#include "content/shell/browser/shell_resource_dispatcher_host_delegate.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"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
+
+namespace content {
+
+// Tracks a single request for a specified URL, and allows waiting until the
+// request is destroyed, and then inspecting whether it completed successfully.
+class TrackingResourceDispatcherHostDelegate
+ : public ShellResourceDispatcherHostDelegate {
+ public:
+ TrackingResourceDispatcherHostDelegate() : throttle_created_(false) {
+ }
+
+ virtual void RequestBeginning(
+ net::URLRequest* request,
+ ResourceContext* resource_context,
+ appcache::AppCacheService* appcache_service,
+ ResourceType::Type resource_type,
+ int child_id,
+ int route_id,
+ ScopedVector<ResourceThrottle>* throttles) OVERRIDE {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ ShellResourceDispatcherHostDelegate::RequestBeginning(
+ request, resource_context, appcache_service, resource_type, child_id,
+ route_id, throttles);
+ // Expect only a single request for the tracked url.
+ ASSERT_FALSE(throttle_created_);
+ // If this is a request for the tracked URL, add a throttle to track it.
+ if (request->url() == tracked_url_)
+ throttles->push_back(new TrackingThrottle(request, this));
+ }
+
+ // Starts tracking a URL. The request for previously tracked URL, if any,
+ // must have been made and deleted before calling this function.
+ void SetTrackedURL(const GURL& tracked_url) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Should not currently be tracking any URL.
+ ASSERT_FALSE(run_loop_);
+
+ // Create a RunLoop that will be stopped once the request for the tracked
+ // URL has been destroyed, to allow tracking the URL while also waiting for
+ // other events.
+ run_loop_.reset(new base::RunLoop());
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(
+ &TrackingResourceDispatcherHostDelegate::SetTrackedURLOnIOThread,
+ base::Unretained(this),
+ tracked_url));
+ }
+
+ // Waits until the tracked URL has been requests, and the request for it has
+ // been destroyed.
+ bool WaitForTrackedURLAndGetCompleted() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ run_loop_->Run();
+ run_loop_.reset();
+ return tracked_request_completed_;
+ }
+
+ private:
+ // ResourceThrottle attached to request for the tracked URL. On destruction,
+ // passes the final URLRequestStatus back to the delegate.
+ class TrackingThrottle : public ResourceThrottle {
+ public:
+ TrackingThrottle(net::URLRequest* request,
+ TrackingResourceDispatcherHostDelegate* tracker)
+ : request_(request), tracker_(tracker) {
+ }
+
+ virtual ~TrackingThrottle() {
+ // If the request is deleted without being cancelled, its status will
+ // indicate it succeeded, so have to check if the request is still pending
+ // as well.
+ tracker_->OnTrackedRequestDestroyed(
+ !request_->is_pending() && request_->status().is_success());
+ }
+
+ // ResourceThrottle implementation:
+ virtual const char* GetNameForLogging() const OVERRIDE {
+ return "TrackingThrottle";
+ }
+
+ private:
+ net::URLRequest* request_;
+ TrackingResourceDispatcherHostDelegate* tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrackingThrottle);
+ };
+
+ void SetTrackedURLOnIOThread(const GURL& tracked_url) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ throttle_created_ = false;
+ tracked_url_ = tracked_url;
+ }
+
+ void OnTrackedRequestDestroyed(bool completed) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ tracked_request_completed_ = completed;
+ tracked_url_ = GURL();
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE, run_loop_->QuitClosure());
+ }
+
+ // These live on the IO thread.
+ GURL tracked_url_;
+ bool throttle_created_;
+
+ // This is created and destroyed on the UI thread, but stopped on the IO
+ // thread.
+ scoped_ptr<base::RunLoop> run_loop_;
+
+ // Set on the IO thread while |run_loop_| is non-NULL, read on the UI thread
+ // after deleting run_loop_.
+ bool tracked_request_completed_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrackingResourceDispatcherHostDelegate);
+};
+
+// WebContentsDelegate that fails to open a URL when there's a request that
+// needs to be transferred between renderers.
+class NoTransferRequestDelegate : public WebContentsDelegate {
+ public:
+ NoTransferRequestDelegate() {}
+
+ virtual WebContents* OpenURLFromTab(WebContents* source,
+ const OpenURLParams& params) OVERRIDE {
+ bool is_transfer =
+ (params.transferred_global_request_id != GlobalRequestID());
+ if (is_transfer)
+ return NULL;
+ NavigationController::LoadURLParams load_url_params(params.url);
+ load_url_params.referrer = params.referrer;
+ load_url_params.frame_tree_node_id = params.frame_tree_node_id;
+ load_url_params.transition_type = params.transition;
+ load_url_params.extra_headers = params.extra_headers;
+ load_url_params.should_replace_current_entry =
+ params.should_replace_current_entry;
+ load_url_params.is_renderer_initiated = true;
+ source->GetController().LoadURLWithParams(load_url_params);
+ return source;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NoTransferRequestDelegate);
+};
+
+class CrossSiteTransferTest : public ContentBrowserTest {
+ public:
+ CrossSiteTransferTest() : old_delegate_(NULL) {
+ }
+
+ // ContentBrowserTest implementation:
+ virtual void SetUpOnMainThread() OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(
+ &CrossSiteTransferTest::InjectResourceDisptcherHostDelegate,
+ base::Unretained(this)));
+ }
+
+ virtual void TearDownOnMainThread() OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(
+ &CrossSiteTransferTest::RestoreResourceDisptcherHostDelegate,
+ base::Unretained(this)));
+ }
+
+ protected:
+ void NavigateToURLContentInitiated(Shell* window,
+ const GURL& url,
+ bool should_replace_current_entry,
+ bool should_wait_for_navigation) {
+ 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);
+ if (should_wait_for_navigation)
+ load_observer.Wait();
+ }
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ // Use --site-per-process to force process swaps for cross-site transfers.
+ command_line->AppendSwitch(switches::kSitePerProcess);
+ }
+
+ void InjectResourceDisptcherHostDelegate() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ old_delegate_ = ResourceDispatcherHostImpl::Get()->delegate();
+ ResourceDispatcherHostImpl::Get()->SetDelegate(&tracking_delegate_);
+ }
+
+ void RestoreResourceDisptcherHostDelegate() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ ResourceDispatcherHostImpl::Get()->SetDelegate(old_delegate_);
+ old_delegate_ = NULL;
+ }
+
+ TrackingResourceDispatcherHostDelegate& tracking_delegate() {
+ return tracking_delegate_;
+ }
+
+ private:
+ TrackingResourceDispatcherHostDelegate tracking_delegate_;
+ ResourceDispatcherHostDelegate* old_delegate_;
+};
+
+// 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(CrossSiteTransferTest,
+ ReplaceEntryCrossProcessThenTransfer) {
+ 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);
+ // Used to make sure the request for url2 succeeds, and there was only one of
+ // them.
+ tracking_delegate().SetTrackedURL(url2);
+ NavigateToURLContentInitiated(shell(), url2, true, 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());
+ // Make sure the request succeeded.
+ EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
+
+ // 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);
+ // Used to make sure the request for url3 succeeds, and there was only one of
+ // them.
+ tracking_delegate().SetTrackedURL(url3);
+ NavigateToURLContentInitiated(shell(), url3, false, true);
+
+ // 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());
+
+ // Make sure the request succeeded.
+ EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
+}
+
+// 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(CrossSiteTransferTest,
+ 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, 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, true);
+
+ // 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(CrossSiteTransferTest,
+ 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, 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, true);
+
+ // 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());
+}
+
+// Tests that the request is destroyed when a cross process navigation is
+// cancelled.
+IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest, NoLeakOnCrossSiteCancel) {
+ 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.
+ ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
+
+ NoTransferRequestDelegate no_transfer_request_delegate;
+ WebContentsDelegate* old_delegate = shell()->web_contents()->GetDelegate();
+ shell()->web_contents()->SetDelegate(&no_transfer_request_delegate);
+
+ // 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);
+ // Used to make sure the second request is cancelled, and there is only one
+ // request for url2.
+ tracking_delegate().SetTrackedURL(url2);
+
+ // Don't wait for the navigation to complete, since that never happens in
+ // this case.
+ NavigateToURLContentInitiated(shell(), url2, false, false);
+
+ // There should be one history entry, with url1.
+ EXPECT_EQ(1, controller.GetEntryCount());
+ EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url1, controller.GetEntryAtIndex(0)->GetURL());
+
+ // Make sure the request for url2 did not complete.
+ EXPECT_FALSE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
+
+ shell()->web_contents()->SetDelegate(old_delegate);
+}
+
+} // namespace content
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index f800b10..416b0dc 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -4,32 +4,21 @@
#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/loader/resource_dispatcher_host_impl.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/resource_dispatcher_host_delegate.h"
-#include "content/public/browser/resource_throttle.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/shell/browser/shell_resource_dispatcher_host_delegate.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"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
namespace content {
@@ -165,171 +154,9 @@ void RedirectNotificationObserver::Observe(
running_ = false;
}
-// Tracks a single request for a specified URL, and allows waiting until the
-// request is destroyed, and then inspecting whether it completed successfully.
-class TrackingResourceDispatcherHostDelegate
- : public ShellResourceDispatcherHostDelegate {
- public:
- TrackingResourceDispatcherHostDelegate() : throttle_created_(false) {
- }
-
- virtual void RequestBeginning(
- net::URLRequest* request,
- ResourceContext* resource_context,
- appcache::AppCacheService* appcache_service,
- ResourceType::Type resource_type,
- int child_id,
- int route_id,
- ScopedVector<ResourceThrottle>* throttles) OVERRIDE {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- ShellResourceDispatcherHostDelegate::RequestBeginning(
- request, resource_context, appcache_service, resource_type, child_id,
- route_id, throttles);
- // Expect only a single request for the tracked url.
- ASSERT_FALSE(throttle_created_);
- // If this is a request for the tracked URL, add a throttle to track it.
- if (request->url() == tracked_url_)
- throttles->push_back(new TrackingThrottle(request, this));
- }
-
- // Starts tracking a URL. The request for previously tracked URL, if any,
- // must have been made and deleted before calling this function.
- void SetTrackedURL(const GURL& tracked_url) {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- // Should not currently be tracking any URL.
- ASSERT_FALSE(run_loop_);
-
- // Create a RunLoop that will be stopped once the request for the tracked
- // URL has been destroyed, to allow tracking the URL while also waiting for
- // other events.
- run_loop_.reset(new base::RunLoop());
-
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(
- &TrackingResourceDispatcherHostDelegate::SetTrackedURLOnIOThread,
- base::Unretained(this),
- tracked_url));
- }
-
- // Waits until the tracked URL has been requests, and the request for it has
- // been destroyed.
- bool WaitForTrackedURLAndGetCompleted() {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- run_loop_->Run();
- run_loop_.reset();
- return tracked_request_completed_;
- }
-
- private:
- // ResourceThrottle attached to request for the tracked URL. On destruction,
- // passes the final URLRequestStatus back to the delegate.
- class TrackingThrottle : public ResourceThrottle {
- public:
- TrackingThrottle(net::URLRequest* request,
- TrackingResourceDispatcherHostDelegate* tracker)
- : request_(request), tracker_(tracker) {
- }
-
- virtual ~TrackingThrottle() {
- // If the request is deleted without being cancelled, its status will
- // indicate it succeeded, so have to check if the request is still pending
- // as well.
- tracker_->OnTrackedRequestDestroyed(
- !request_->is_pending() && request_->status().is_success());
- }
-
- // ResourceThrottle implementation:
- virtual const char* GetNameForLogging() const OVERRIDE {
- return "TrackingThrottle";
- }
-
- private:
- net::URLRequest* request_;
- TrackingResourceDispatcherHostDelegate* tracker_;
-
- DISALLOW_COPY_AND_ASSIGN(TrackingThrottle);
- };
-
- void SetTrackedURLOnIOThread(const GURL& tracked_url) {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- throttle_created_ = false;
- tracked_url_ = tracked_url;
- }
-
- void OnTrackedRequestDestroyed(bool completed) {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- tracked_request_completed_ = completed;
- tracked_url_ = GURL();
-
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE, run_loop_->QuitClosure());
- }
-
- // These live on the IO thread.
- GURL tracked_url_;
- bool throttle_created_;
-
- // This is created and destroyed on the UI thread, but stopped on the IO
- // thread.
- scoped_ptr<base::RunLoop> run_loop_;
-
- // Set on the IO thread while |run_loop_| is non-NULL, read on the UI thread
- // after deleting run_loop_.
- bool tracked_request_completed_;
-
- DISALLOW_COPY_AND_ASSIGN(TrackingResourceDispatcherHostDelegate);
-};
-
-// WebContentsDelegate that fails to open a URL when there's a request that
-// needs to be transferred between renderers.
-class NoTransferRequestDelegate : public WebContentsDelegate {
- public:
- NoTransferRequestDelegate() {}
-
- virtual WebContents* OpenURLFromTab(WebContents* source,
- const OpenURLParams& params) OVERRIDE {
- bool is_transfer =
- (params.transferred_global_request_id != GlobalRequestID());
- if (is_transfer)
- return NULL;
- NavigationController::LoadURLParams load_url_params(params.url);
- load_url_params.referrer = params.referrer;
- load_url_params.frame_tree_node_id = params.frame_tree_node_id;
- load_url_params.transition_type = params.transition;
- load_url_params.extra_headers = params.extra_headers;
- load_url_params.should_replace_current_entry =
- params.should_replace_current_entry;
- load_url_params.is_renderer_initiated = true;
- source->GetController().LoadURLWithParams(load_url_params);
- return source;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(NoTransferRequestDelegate);
-};
-
class SitePerProcessBrowserTest : public ContentBrowserTest {
public:
- SitePerProcessBrowserTest() : old_delegate_(NULL) {
- }
-
- // ContentBrowserTest implementation:
- virtual void SetUpOnMainThread() OVERRIDE {
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(
- &SitePerProcessBrowserTest::InjectResourceDisptcherHostDelegate,
- base::Unretained(this)));
- }
-
- virtual void TearDownOnMainThread() OVERRIDE {
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(
- &SitePerProcessBrowserTest::RestoreResourceDisptcherHostDelegate,
- base::Unretained(this)));
- }
+ SitePerProcessBrowserTest() {}
protected:
// Start at a data URL so each extra navigation creates a navigation entry.
@@ -364,48 +191,12 @@ class SitePerProcessBrowserTest : public ContentBrowserTest {
return result;
}
- void NavigateToURLContentInitiated(Shell* window,
- const GURL& url,
- bool should_replace_current_entry,
- bool should_wait_for_navigation) {
- 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);
- if (should_wait_for_navigation)
- load_observer.Wait();
- }
-
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
command_line->AppendSwitch(switches::kSitePerProcess);
// TODO(creis): Remove this when GTK is no longer a supported platform.
command_line->AppendSwitch(switches::kForceCompositingMode);
}
-
- void InjectResourceDisptcherHostDelegate() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- old_delegate_ = ResourceDispatcherHostImpl::Get()->delegate();
- ResourceDispatcherHostImpl::Get()->SetDelegate(&tracking_delegate_);
- }
-
- void RestoreResourceDisptcherHostDelegate() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- ResourceDispatcherHostImpl::Get()->SetDelegate(old_delegate_);
- old_delegate_ = NULL;
- }
-
- TrackingResourceDispatcherHostDelegate& tracking_delegate() {
- return tracking_delegate_;
- }
-
- private:
- TrackingResourceDispatcherHostDelegate tracking_delegate_;
- ResourceDispatcherHostDelegate* old_delegate_;
};
// Ensure that we can complete a cross-process subframe navigation.
@@ -724,221 +515,4 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
}
}
-// 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,
- ReplaceEntryCrossProcessThenTransfer) {
- 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);
- // Used to make sure the request for url2 succeeds, and there was only one of
- // them.
- tracking_delegate().SetTrackedURL(url2);
- NavigateToURLContentInitiated(shell(), url2, true, 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());
- // Make sure the request succeeded.
- EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
-
- // 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);
- // Used to make sure the request for url3 succeeds, and there was only one of
- // them.
- tracking_delegate().SetTrackedURL(url3);
- NavigateToURLContentInitiated(shell(), url3, false, true);
-
- // 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());
-
- // Make sure the request succeeded.
- EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
-}
-
-// 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, 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, true);
-
- // 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, 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, true);
-
- // 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());
-}
-
-// Tests that the request is destroyed when a cross process navigation is
-// cancelled.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NoLeakOnCrossSiteCancel) {
- 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.
- ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
-
- NoTransferRequestDelegate no_transfer_request_delegate;
- WebContentsDelegate* old_delegate = shell()->web_contents()->GetDelegate();
- shell()->web_contents()->SetDelegate(&no_transfer_request_delegate);
-
- // 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);
- // Used to make sure the second request is cancelled, and there is only one
- // request for url2.
- tracking_delegate().SetTrackedURL(url2);
-
- // Don't wait for the navigation to complete, since that never happens in
- // this case.
- NavigateToURLContentInitiated(shell(), url2, false, false);
-
- // There should be one history entry, with url1.
- EXPECT_EQ(1, controller.GetEntryCount());
- EXPECT_EQ(0, controller.GetCurrentEntryIndex());
- EXPECT_EQ(url1, controller.GetEntryAtIndex(0)->GetURL());
-
- // Make sure the request for url2 did not complete.
- EXPECT_FALSE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
-
- shell()->web_contents()->SetDelegate(old_delegate);
-}
-
} // namespace content
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 033eceb..1d5692c 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -982,6 +982,7 @@
'browser/browser_plugin/test_browser_plugin_guest_manager.cc',
'browser/browser_plugin/test_browser_plugin_guest_manager.h',
'browser/child_process_security_policy_browsertest.cc',
+ 'browser/cross_site_transfer_browsertest.cc',
'browser/database_browsertest.cc',
'browser/device_orientation/device_inertial_sensor_browsertest.cc',
'browser/devtools/renderer_overrides_handler_browsertest.cc',