Fix DisownOpener and related tests.
BUG=397185
TEST=RenderFrameHostManagerTest.DisownOpener*.
TBR=nasko (just for DisownSameSiteOpener test)
Review URL: https://codereview.chromium.org/497183003
Cr-Commit-Position: refs/heads/master@{#291556}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@291556 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 9863277..c6393a6 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -456,7 +456,8 @@
// TODO(creis): Take in RenderFrameHost instead, since frames can have openers.
void RenderFrameHostManager::DidDisownOpener(RenderViewHost* render_view_host) {
- // Notify all swapped out hosts, including the pending RVH.
+ // Notify all RenderViewHosts but the one that notified us. This is necessary
+ // in case a process swap has occurred while the message was in flight.
for (RenderFrameProxyHostMap::iterator iter = proxy_hosts_.begin();
iter != proxy_hosts_.end();
++iter) {
@@ -464,6 +465,14 @@
current_frame_host()->GetSiteInstance());
iter->second->GetRenderViewHost()->DisownOpener();
}
+
+ if (render_frame_host_->render_view_host() != render_view_host)
+ render_frame_host_->render_view_host()->DisownOpener();
+
+ if (pending_render_frame_host_ &&
+ pending_render_frame_host_->render_view_host() != render_view_host) {
+ pending_render_frame_host_->render_view_host()->DisownOpener();
+ }
}
void RenderFrameHostManager::RendererProcessClosing(
@@ -1052,7 +1061,7 @@
// Check if we've already created an RFH for this SiteInstance. If so, try
// to re-use the existing one, which has already been initialized. We'll
- // remove it from the list of swapped out hosts if it commits.
+ // remove it from the list of proxy hosts below if it will be active.
RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(instance);
if (proxy) {
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index 3f96c31..5b7f1912 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -411,10 +411,8 @@
// Test that setting the opener to null in a window affects cross-process
// navigations, including those to existing entries. http://crbug.com/156669.
-// Flaky on android: http://crbug.com/397185
-// Flaky on windows and mac: http://crbug.com/291249
-// This test also crashes under ThreadSanitizer, http://crbug.com/356758.
-#if defined(OS_ANDROID) || defined(OS_WIN) || defined(THREAD_SANITIZER) || defined(OS_MACOSX)
+// This test crashes under ThreadSanitizer, http://crbug.com/356758.
+#if defined(THREAD_SANITIZER)
#define MAYBE_DisownOpener DISABLED_DisownOpener
#else
#define MAYBE_DisownOpener DisownOpener
@@ -444,6 +442,7 @@
&success));
EXPECT_TRUE(success);
Shell* new_shell = new_shell_observer.GetShell();
+ EXPECT_TRUE(new_shell->web_contents()->HasOpener());
// Wait for the navigation in the new tab to finish, if it hasn't.
WaitForLoadStop(new_shell->web_contents());
@@ -460,10 +459,12 @@
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
+ EXPECT_TRUE(new_shell->web_contents()->HasOpener());
// Now disown the opener.
EXPECT_TRUE(ExecuteScript(new_shell->web_contents(),
"window.opener = null;"));
+ EXPECT_FALSE(new_shell->web_contents()->HasOpener());
// Go back and ensure the opener is still null.
{
@@ -477,6 +478,7 @@
"window.domAutomationController.send(window.opener == null);",
&success));
EXPECT_TRUE(success);
+ EXPECT_FALSE(new_shell->web_contents()->HasOpener());
// Now navigate forward again (creating a new process) and check opener.
NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
@@ -486,6 +488,7 @@
"window.domAutomationController.send(window.opener == null);",
&success));
EXPECT_TRUE(success);
+ EXPECT_FALSE(new_shell->web_contents()->HasOpener());
}
// Test that subframes can disown their openers. http://crbug.com/225528.
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 7cc3cb1..925ecd7 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -1159,6 +1159,130 @@
rvh3->GetSiteInstance()));
}
+// Test that a page can disown the opener of the WebContents.
+TEST_F(RenderFrameHostManagerTest, DisownOpener) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+
+ // Navigate to an initial URL.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderFrameHost* rfh1 = main_test_rfh();
+
+ // Create a new tab and simulate having it be the opener for the main tab.
+ scoped_ptr<TestWebContents> opener1(
+ TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
+ contents()->SetOpener(opener1.get());
+ EXPECT_TRUE(contents()->HasOpener());
+
+ // Navigate to a cross-site URL (different SiteInstance but same
+ // BrowsingInstance).
+ contents()->NavigateAndCommit(kUrl2);
+ TestRenderFrameHost* rfh2 = main_test_rfh();
+ EXPECT_NE(rfh1->GetSiteInstance(), rfh2->GetSiteInstance());
+
+ // Disown the opener from rfh2.
+ rfh2->DidDisownOpener();
+
+ // Ensure the opener is cleared.
+ EXPECT_FALSE(contents()->HasOpener());
+}
+
+// Test that a page can disown a same-site opener of the WebContents.
+TEST_F(RenderFrameHostManagerTest, DisownSameSiteOpener) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+
+ // Navigate to an initial URL.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderFrameHost* rfh1 = main_test_rfh();
+
+ // Create a new tab and simulate having it be the opener for the main tab.
+ scoped_ptr<TestWebContents> opener1(
+ TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
+ contents()->SetOpener(opener1.get());
+ EXPECT_TRUE(contents()->HasOpener());
+
+ // Disown the opener from rfh1.
+ rfh1->DidDisownOpener();
+
+ // Ensure the opener is cleared even if it is in the same process.
+ EXPECT_FALSE(contents()->HasOpener());
+}
+
+// Test that a page can disown the opener just as a cross-process navigation is
+// in progress.
+TEST_F(RenderFrameHostManagerTest, DisownOpenerDuringNavigation) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+
+ // Navigate to an initial URL.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderFrameHost* rfh1 = main_test_rfh();
+
+ // Create a new tab and simulate having it be the opener for the main tab.
+ scoped_ptr<TestWebContents> opener1(
+ TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
+ contents()->SetOpener(opener1.get());
+ EXPECT_TRUE(contents()->HasOpener());
+
+ // Navigate to a cross-site URL (different SiteInstance but same
+ // BrowsingInstance).
+ contents()->NavigateAndCommit(kUrl2);
+ TestRenderFrameHost* rfh2 = main_test_rfh();
+ EXPECT_NE(rfh1->GetSiteInstance(), rfh2->GetSiteInstance());
+
+ // Start a back navigation so that rfh1 becomes the pending RFH.
+ contents()->GetController().GoBack();
+ contents()->ProceedWithCrossSiteNavigation();
+
+ // Disown the opener from rfh2.
+ rfh2->DidDisownOpener();
+
+ // Ensure the opener is cleared.
+ EXPECT_FALSE(contents()->HasOpener());
+
+ // The back navigation commits.
+ const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
+ rfh1->SendNavigate(entry1->GetPageID(), entry1->GetURL());
+
+ // Ensure the opener is still cleared.
+ EXPECT_FALSE(contents()->HasOpener());
+}
+
+// Test that a page can disown the opener just after a cross-process navigation
+// commits.
+TEST_F(RenderFrameHostManagerTest, DisownOpenerAfterNavigation) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+
+ // Navigate to an initial URL.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderFrameHost* rfh1 = main_test_rfh();
+
+ // Create a new tab and simulate having it be the opener for the main tab.
+ scoped_ptr<TestWebContents> opener1(
+ TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
+ contents()->SetOpener(opener1.get());
+ EXPECT_TRUE(contents()->HasOpener());
+
+ // Navigate to a cross-site URL (different SiteInstance but same
+ // BrowsingInstance).
+ contents()->NavigateAndCommit(kUrl2);
+ TestRenderFrameHost* rfh2 = main_test_rfh();
+ EXPECT_NE(rfh1->GetSiteInstance(), rfh2->GetSiteInstance());
+
+ // Commit a back navigation before the DidDisownOpener message arrives.
+ // rfh1 will be kept alive because of the opener tab.
+ contents()->GetController().GoBack();
+ contents()->ProceedWithCrossSiteNavigation();
+ const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
+ rfh1->SendNavigate(entry1->GetPageID(), entry1->GetURL());
+
+ // Disown the opener from rfh2.
+ rfh2->DidDisownOpener();
+ EXPECT_FALSE(contents()->HasOpener());
+}
+
// Test that we clean up swapped out RenderViewHosts when a process hosting
// those associated RenderViews crashes. http://crbug.com/258993
TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 5de3388..a3b0d2d6 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -1351,9 +1351,6 @@
}
void RenderViewHostImpl::DisownOpener() {
- // This should only be called when swapped out.
- DCHECK(IsSwappedOut());
-
Send(new ViewMsg_DisownOpener(GetRoutingID()));
}
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 51b9177..623cbf6 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3670,12 +3670,14 @@
}
void WebContentsImpl::DidDisownOpener(RenderFrameHost* render_frame_host) {
- if (opener_) {
- // Clear our opener so that future cross-process navigations don't have an
- // opener assigned.
- RemoveDestructionObserver(opener_);
- opener_ = NULL;
- }
+ // No action is necessary if the opener has already been cleared.
+ if (!opener_)
+ return;
+
+ // Clear our opener so that future cross-process navigations don't have an
+ // opener assigned.
+ RemoveDestructionObserver(opener_);
+ opener_ = NULL;
// Notify all swapped out RenderViewHosts for this tab. This is important
// in case we go back to them, or if another window in those processes tries