summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/browser/renderer_host/render_view_host.cc17
-rw-r--r--content/browser/renderer_host/render_view_host.h3
-rw-r--r--content/browser/renderer_host/render_view_host_manager_browsertest.cc58
-rw-r--r--content/browser/renderer_host/resource_dispatcher_host.cc10
-rwxr-xr-xnet/tools/testserver/testserver.py9
5 files changed, 91 insertions, 6 deletions
diff --git a/content/browser/renderer_host/render_view_host.cc b/content/browser/renderer_host/render_view_host.cc
index 40792b1..df5f1e1 100644
--- a/content/browser/renderer_host/render_view_host.cc
+++ b/content/browser/renderer_host/render_view_host.cc
@@ -293,8 +293,9 @@ void RenderViewHost::FirePageBeforeUnload(bool for_cross_site_transition) {
void RenderViewHost::ClosePage(bool for_cross_site_transition,
int new_render_process_host_id,
int new_request_id) {
- // In most cases, this will not be set to false afterward. Either the tab
- // will be closed, or a pending RenderViewHost will replace this one.
+ // This will be set back to false in OnClosePageACK, just before we close the
+ // tab or replace it with a pending RVH. There are some cases (such as 204
+ // errors) where we'll continue to show this RVH.
is_waiting_for_unload_ack_ = true;
// Start the hang monitor in case the renderer hangs in the unload handler.
StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
@@ -320,6 +321,18 @@ void RenderViewHost::ClosePage(bool for_cross_site_transition,
}
}
+void RenderViewHost::OnClosePageACK(bool for_cross_site_transition) {
+ StopHangMonitorTimeout();
+ is_waiting_for_unload_ack_ = false;
+
+ // If this ClosePageACK is not for a cross-site transition, then it is for an
+ // attempt to close the tab. We have now finished the unload handler and can
+ // proceed with closing the tab.
+ if (!for_cross_site_transition) {
+ ClosePageIgnoringUnloadEvents();
+ }
+}
+
void RenderViewHost::ClosePageIgnoringUnloadEvents() {
StopHangMonitorTimeout();
is_waiting_for_beforeunload_ack_ = false;
diff --git a/content/browser/renderer_host/render_view_host.h b/content/browser/renderer_host/render_view_host.h
index 114ad41..2e1e564 100644
--- a/content/browser/renderer_host/render_view_host.h
+++ b/content/browser/renderer_host/render_view_host.h
@@ -179,6 +179,9 @@ class RenderViewHost : public RenderWidgetHost {
int new_render_process_host_id,
int new_request_id);
+ // Called by ResourceDispatcherHost after the ClosePageACK is received.
+ void OnClosePageACK(bool for_cross_site_transition);
+
// Close the page ignoring whether it has unload events registers.
// This is called after the beforeunload and unload events have fired
// and the user has agreed to continue with closing the page.
diff --git a/content/browser/renderer_host/render_view_host_manager_browsertest.cc b/content/browser/renderer_host/render_view_host_manager_browsertest.cc
index 20e14a6..d4efbf5 100644
--- a/content/browser/renderer_host/render_view_host_manager_browsertest.cc
+++ b/content/browser/renderer_host/render_view_host_manager_browsertest.cc
@@ -287,3 +287,61 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest,
browser()->CloseWindow();
BrowserClosedObserver wait_for_close(browser());
}
+
+// Test for crbug.com/76666. A cross-site navigation that fails with a 204
+// error should not make us ignore future renderer-initiated navigations.
+IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, ClickLinkAfter204Error) {
+ // Start two servers with different sites.
+ ASSERT_TRUE(test_server()->Start());
+ net::TestServer https_server(
+ net::TestServer::TYPE_HTTPS,
+ FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+ ASSERT_TRUE(https_server.Start());
+
+ // Load a page with links that open in a new window.
+ // The links will point to the HTTPS server.
+ std::string replacement_path;
+ ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
+ "files/click-noreferrer-links.html",
+ https_server.host_port_pair(),
+ &replacement_path));
+ ui_test_utils::NavigateToURL(browser(),
+ test_server()->GetURL(replacement_path));
+
+ // Get the original SiteInstance for later comparison.
+ scoped_refptr<SiteInstance> orig_site_instance(
+ browser()->GetSelectedTabContents()->GetSiteInstance());
+ EXPECT_TRUE(orig_site_instance != NULL);
+
+ // Load a cross-site page that fails with a 204 error.
+ ui_test_utils::NavigateToURL(browser(), https_server.GetURL("nocontent"));
+
+ // We should still be looking at the normal page.
+ scoped_refptr<SiteInstance> post_nav_site_instance(
+ browser()->GetSelectedTabContents()->GetSiteInstance());
+ EXPECT_EQ(orig_site_instance, post_nav_site_instance);
+ EXPECT_EQ("/files/click-noreferrer-links.html",
+ browser()->GetSelectedTabContents()->GetURL().path());
+
+ // Renderer-initiated navigations should work.
+ bool success = false;
+ EXPECT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ browser()->GetSelectedTabContents()->render_view_host(), L"",
+ L"window.domAutomationController.send(clickNoRefLink());",
+ &success));
+ EXPECT_TRUE(success);
+
+ // Wait for the cross-site transition in the current tab to finish.
+ ui_test_utils::WaitForLoadStop(browser()->GetSelectedTabContents());
+
+ // Opens in same tab.
+ EXPECT_EQ(1, browser()->tab_count());
+ EXPECT_EQ(0, browser()->selected_index());
+ EXPECT_EQ("/files/title2.html",
+ browser()->GetSelectedTabContents()->GetURL().path());
+
+ // Should have the same SiteInstance.
+ scoped_refptr<SiteInstance> noref_site_instance(
+ browser()->GetSelectedTabContents()->GetSiteInstance());
+ EXPECT_EQ(orig_site_instance, noref_site_instance);
+}
diff --git a/content/browser/renderer_host/resource_dispatcher_host.cc b/content/browser/renderer_host/resource_dispatcher_host.cc
index 8c4ff73..9d5e429 100644
--- a/content/browser/renderer_host/resource_dispatcher_host.cc
+++ b/content/browser/renderer_host/resource_dispatcher_host.cc
@@ -659,13 +659,15 @@ void ResourceDispatcherHost::OnClosePageACK(
info->cross_site_handler()->ResumeResponse();
}
} else {
- // This is a tab close, so just forward the message to close it.
+ // This is a tab close, so we will close the tab in OnClosePageACK.
DCHECK(params.new_render_process_host_id == -1);
DCHECK(params.new_request_id == -1);
- CallRenderViewHost(params.closing_process_id,
- params.closing_route_id,
- &RenderViewHost::ClosePageIgnoringUnloadEvents);
}
+ // Update the RenderViewHost's internal state after the ACK.
+ CallRenderViewHost(params.closing_process_id,
+ params.closing_route_id,
+ &RenderViewHost::OnClosePageACK,
+ params.for_cross_site_transition);
}
// We are explicitly forcing the download of 'url'.
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
index a222558..36e0a89 100755
--- a/net/tools/testserver/testserver.py
+++ b/net/tools/testserver/testserver.py
@@ -286,6 +286,7 @@ class TestPageHandler(BasePageHandler):
self.AuthDigestHandler,
self.SlowServerHandler,
self.ContentTypeHandler,
+ self.NoContentHandler,
self.ServerRedirectHandler,
self.ClientRedirectHandler,
self.MultipartHandler,
@@ -1070,6 +1071,14 @@ class TestPageHandler(BasePageHandler):
self.wfile.write("<html>\n<body>\n<p>HTML text</p>\n</body>\n</html>\n");
return True
+ def NoContentHandler(self):
+ """Returns a 204 No Content response."""
+ if not self._ShouldHandleRequest("/nocontent"):
+ return False
+ self.send_response(204)
+ self.end_headers()
+ return True
+
def ServerRedirectHandler(self):
"""Sends a server redirect to the given URL. The syntax is
'/server-redirect?http://foo.bar/asdf' to redirect to