| Index: content/browser/frame_host/render_frame_host_manager_unittest.cc
|
| 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 5114a2b03dbf0700ede3beb254e4a93500c54ac6..2f0ece60fe71525184ab306a57638b7bf5dd2fa9 100644
|
| --- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
|
| +++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
|
| @@ -401,6 +401,24 @@ class RenderFrameHostManagerTest : public RenderViewHostImplTestHarness {
|
| return manager->pending_frame_host();
|
| }
|
|
|
| + void CrashFrame(TestRenderFrameHost* frame) {
|
| + // TODO(nick): Standardize how we simulate crashes in content unittests. The
|
| + // test method |set_render_view_created| should probably be eliminated
|
| + // altogether, and maybe there should be a CrashProcess() method on
|
| + // MockRenderProcessHost.
|
| + ASSERT_TRUE(frame->IsRenderFrameLive());
|
| + frame->OnMessageReceived(FrameHostMsg_RenderProcessGone(
|
| + 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
|
| + RenderProcessHost::RendererClosedDetails details(
|
| + base::TERMINATION_STATUS_PROCESS_CRASHED, 0);
|
| + NotificationService::current()->Notify(
|
| + NOTIFICATION_RENDERER_PROCESS_CLOSED,
|
| + Source<RenderProcessHost>(frame->GetProcess()),
|
| + Details<RenderProcessHost::RendererClosedDetails>(&details));
|
| + frame->GetRenderViewHost()->set_render_view_created(false);
|
| + ASSERT_FALSE(frame->IsRenderFrameLive());
|
| + }
|
| +
|
| private:
|
| RenderFrameHostManagerTestWebUIControllerFactory factory_;
|
| };
|
| @@ -963,7 +981,7 @@ TEST_F(RenderFrameHostManagerTest, WebUI) {
|
| EXPECT_EQ(host, manager->current_frame_host());
|
| EXPECT_FALSE(GetPendingFrameHost(manager));
|
|
|
| - // It's important that the site instance get set on the Web UI page as soon
|
| + // It's important that the SiteInstance get set on the Web UI page as soon
|
| // as the navigation starts, rather than lazily after it commits, so we don't
|
| // try to re-use the SiteInstance/process for non Web UI things that may
|
| // get loaded in between.
|
| @@ -1401,11 +1419,11 @@ TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
|
|
|
| // Navigate to an initial URL.
|
| contents()->NavigateAndCommit(kUrl1);
|
| - TestRenderViewHost* rvh1 = test_rvh();
|
| + TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
|
|
|
| // Create a new tab as an opener for the main tab.
|
| scoped_ptr<TestWebContents> opener1(
|
| - TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
|
| + TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
|
| RenderFrameHostManager* opener1_manager =
|
| opener1->GetRenderManagerForTesting();
|
| contents()->SetOpener(opener1.get());
|
| @@ -1417,43 +1435,33 @@ TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
|
| EXPECT_TRUE(opener1_manager->current_frame_host()->IsRenderFrameLive());
|
|
|
| // Use a cross-process navigation in the opener to swap out the old RVH.
|
| - EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
|
| - rvh1->GetSiteInstance()));
|
| + EXPECT_FALSE(
|
| + opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
|
| opener1->NavigateAndCommit(kUrl2);
|
| - EXPECT_TRUE(opener1_manager->GetSwappedOutRenderViewHost(
|
| - rvh1->GetSiteInstance()));
|
| + EXPECT_TRUE(
|
| + opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
|
|
|
| // Fake a process crash.
|
| - RenderProcessHost::RendererClosedDetails details(
|
| - base::TERMINATION_STATUS_PROCESS_CRASHED,
|
| - 0);
|
| - // TODO(nasko): Investigate whether this test can be made more realistic by
|
| - // not faking the notification and just doing the RenderProcessGone. This
|
| - // should also get rid of faking |set_render_view_created()| call below.
|
| - NotificationService::current()->Notify(
|
| - NOTIFICATION_RENDERER_PROCESS_CLOSED,
|
| - Source<RenderProcessHost>(rvh1->GetProcess()),
|
| - Details<RenderProcessHost::RendererClosedDetails>(&details));
|
| - rvh1->set_render_view_created(false);
|
| + CrashFrame(rfh1);
|
|
|
| // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
|
| // is deleted.
|
| RenderFrameProxyHost* render_frame_proxy_host =
|
| - opener1_manager->GetRenderFrameProxyHost(rvh1->GetSiteInstance());
|
| + opener1_manager->GetRenderFrameProxyHost(rfh1->GetSiteInstance());
|
| EXPECT_TRUE(render_frame_proxy_host);
|
| EXPECT_FALSE(render_frame_proxy_host->is_render_frame_proxy_live());
|
|
|
| // Expect the swapped out RVH to exist.
|
| - EXPECT_TRUE(opener1_manager->GetSwappedOutRenderViewHost(
|
| - rvh1->GetSiteInstance()));
|
| + EXPECT_TRUE(
|
| + opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
|
|
|
| // Reload the initial tab. This should recreate the opener's swapped out RVH
|
| // in the original SiteInstance.
|
| contents()->GetController().Reload(true);
|
| contents()->GetMainFrame()->PrepareForCommit();
|
| EXPECT_EQ(opener1_manager->GetSwappedOutRenderViewHost(
|
| - rvh1->GetSiteInstance())->GetRoutingID(),
|
| - test_rvh()->opener_route_id());
|
| + rfh1->GetSiteInstance())->GetRoutingID(),
|
| + contents()->GetMainFrame()->GetRenderViewHost()->opener_route_id());
|
| }
|
|
|
| // Test that RenderViewHosts created for WebUI navigations are properly
|
| @@ -1833,85 +1841,187 @@ TEST_F(RenderFrameHostManagerTest,
|
| }
|
|
|
| // Test that a pending RenderFrameHost in a non-root frame tree node is properly
|
| -// deleted when the node is detached. Motivated by http://crbug.com/441357
|
| +// deleted when the node is detached. Motivated by http://crbug.com/441357 and
|
| +// http://crbug.com/444955.
|
| TEST_F(RenderFrameHostManagerTest, DetachPendingChild) {
|
| base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
| switches::kSitePerProcess);
|
|
|
| - const GURL kUrl1("http://www.google.com/");
|
| - const GURL kUrl2("http://webkit.org/");
|
| -
|
| - RenderFrameHostImpl* host = NULL;
|
| + const GURL kUrlA("http://www.google.com/");
|
| + const GURL kUrlB("http://webkit.org/");
|
|
|
| - contents()->NavigateAndCommit(kUrl1);
|
| + // Create a page with two child frames.
|
| + contents()->NavigateAndCommit(kUrlA);
|
| + contents()->GetMainFrame()->OnCreateChildFrame(
|
| + contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
|
| + std::string("frame_name"), SandboxFlags::NONE);
|
| contents()->GetMainFrame()->OnCreateChildFrame(
|
| contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
|
| std::string("frame_name"), SandboxFlags::NONE);
|
| - RenderFrameHostManager* manager =
|
| + RenderFrameHostManager* root_manager =
|
| + contents()->GetFrameTree()->root()->render_manager();
|
| + RenderFrameHostManager* iframe1 =
|
| contents()->GetFrameTree()->root()->child_at(0)->render_manager();
|
| + RenderFrameHostManager* iframe2 =
|
| + contents()->GetFrameTree()->root()->child_at(1)->render_manager();
|
|
|
| - // 1) The first navigation. --------------------------
|
| - NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
|
| + // 1) The first navigation.
|
| + NavigationEntryImpl entryA(NULL /* instance */, -1 /* page_id */, kUrlA,
|
| Referrer(), base::string16() /* title */,
|
| ui::PAGE_TRANSITION_TYPED,
|
| false /* is_renderer_init */);
|
| - host = GetFrameHostForNavigation(manager, entry1);
|
| + RenderFrameHostImpl* host1 = GetFrameHostForNavigation(iframe1, entryA);
|
|
|
| // The RenderFrameHost created in Init will be reused.
|
| - EXPECT_TRUE(host == manager->current_frame_host());
|
| - EXPECT_FALSE(GetPendingFrameHost(manager));
|
| + EXPECT_TRUE(host1 == iframe1->current_frame_host());
|
| + EXPECT_FALSE(GetPendingFrameHost(iframe1));
|
|
|
| // Commit.
|
| - manager->DidNavigateFrame(host, true);
|
| + iframe1->DidNavigateFrame(host1, true);
|
| // Commit to SiteInstance should be delayed until RenderFrame commit.
|
| - EXPECT_TRUE(host == manager->current_frame_host());
|
| - ASSERT_TRUE(host);
|
| - EXPECT_TRUE(host->GetSiteInstance()->HasSite());
|
| + EXPECT_TRUE(host1 == iframe1->current_frame_host());
|
| + ASSERT_TRUE(host1);
|
| + EXPECT_TRUE(host1->GetSiteInstance()->HasSite());
|
|
|
| - // 2) Cross-site navigate to next site. --------------
|
| - NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
|
| - Referrer(kUrl1, blink::WebReferrerPolicyDefault),
|
| + // 2) Cross-site navigate both frames to next site.
|
| + NavigationEntryImpl entryB(NULL /* instance */, -1 /* page_id */, kUrlB,
|
| + Referrer(kUrlA, blink::WebReferrerPolicyDefault),
|
| base::string16() /* title */,
|
| ui::PAGE_TRANSITION_LINK,
|
| false /* is_renderer_init */);
|
| - host = GetFrameHostForNavigation(manager, entry2);
|
| -
|
| - // A new RenderFrameHost should be created.
|
| - EXPECT_TRUE(GetPendingFrameHost(manager));
|
| - ASSERT_EQ(host, GetPendingFrameHost(manager));
|
| - ASSERT_NE(manager->current_frame_host(), GetPendingFrameHost(manager));
|
| + host1 = GetFrameHostForNavigation(iframe1, entryB);
|
| + RenderFrameHostImpl* host2 = GetFrameHostForNavigation(iframe2, entryB);
|
| +
|
| + // A new, pending RenderFrameHost should be created in each FrameTreeNode.
|
| + EXPECT_TRUE(GetPendingFrameHost(iframe1));
|
| + EXPECT_TRUE(GetPendingFrameHost(iframe2));
|
| + EXPECT_EQ(host1, GetPendingFrameHost(iframe1));
|
| + EXPECT_EQ(host2, GetPendingFrameHost(iframe2));
|
| + EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
|
| + GetPendingFrameHost(iframe1)->rfh_state()));
|
| + EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
|
| + GetPendingFrameHost(iframe2)->rfh_state()));
|
| + EXPECT_NE(GetPendingFrameHost(iframe1), GetPendingFrameHost(iframe2));
|
| + EXPECT_EQ(GetPendingFrameHost(iframe1)->GetSiteInstance(),
|
| + GetPendingFrameHost(iframe2)->GetSiteInstance());
|
| + EXPECT_NE(iframe1->current_frame_host(), GetPendingFrameHost(iframe1));
|
| + EXPECT_NE(iframe2->current_frame_host(), GetPendingFrameHost(iframe2));
|
| EXPECT_FALSE(contents()->cross_navigation_pending())
|
| << "There should be no top-level pending navigation.";
|
|
|
| - RenderFrameHostDeletedObserver delete_watcher(GetPendingFrameHost(manager));
|
| - EXPECT_FALSE(delete_watcher.deleted());
|
| + RenderFrameHostDeletedObserver delete_watcher1(GetPendingFrameHost(iframe1));
|
| + RenderFrameHostDeletedObserver delete_watcher2(GetPendingFrameHost(iframe2));
|
| + EXPECT_FALSE(delete_watcher1.deleted());
|
| + EXPECT_FALSE(delete_watcher2.deleted());
|
|
|
| - // Extend the lifetime of the child frame's SiteInstance, pretending
|
| - // that there is another reference to it.
|
| + // Keep the SiteInstance alive for testing.
|
| scoped_refptr<SiteInstanceImpl> site_instance =
|
| - GetPendingFrameHost(manager)->GetSiteInstance();
|
| + GetPendingFrameHost(iframe1)->GetSiteInstance();
|
| EXPECT_TRUE(site_instance->HasSite());
|
| EXPECT_NE(site_instance, contents()->GetSiteInstance());
|
| - EXPECT_EQ(1U, site_instance->active_frame_count());
|
| - site_instance->increment_active_frame_count();
|
| EXPECT_EQ(2U, site_instance->active_frame_count());
|
|
|
| - // Now detach the child FrameTreeNode. This should kill the pending host.
|
| - manager->current_frame_host()->OnMessageReceived(
|
| - FrameHostMsg_Detach(manager->current_frame_host()->GetRoutingID()));
|
| -
|
| - EXPECT_TRUE(delete_watcher.deleted());
|
| -
|
| + // Proxies should exist.
|
| + EXPECT_NE(nullptr,
|
| + root_manager->GetRenderFrameProxyHost(site_instance.get()));
|
| + EXPECT_NE(nullptr,
|
| + iframe1->GetRenderFrameProxyHost(site_instance.get()));
|
| + EXPECT_NE(nullptr,
|
| + iframe2->GetRenderFrameProxyHost(site_instance.get()));
|
| +
|
| + // Detach the first child FrameTreeNode. This should kill the pending host but
|
| + // not yet destroy proxies in |site_instance| since the other child remains.
|
| + iframe1->current_frame_host()->OnMessageReceived(
|
| + FrameHostMsg_Detach(iframe1->current_frame_host()->GetRoutingID()));
|
| + iframe1 = NULL; // Was just destroyed.
|
| +
|
| + EXPECT_TRUE(delete_watcher1.deleted());
|
| + EXPECT_FALSE(delete_watcher2.deleted());
|
| EXPECT_EQ(1U, site_instance->active_frame_count());
|
| - site_instance->decrement_active_frame_count();
|
|
|
| -#if 0
|
| - // TODO(nick): Currently a proxy to the removed frame lingers in the parent.
|
| - // Enable this assert below once the proxies to the subframe are correctly
|
| - // cleaned up after detach. http://crbug.com/444955.
|
| - ASSERT_TRUE(site_instance->HasOneRef())
|
| + // Proxies should still exist.
|
| + EXPECT_NE(nullptr,
|
| + root_manager->GetRenderFrameProxyHost(site_instance.get()));
|
| + EXPECT_NE(nullptr,
|
| + iframe2->GetRenderFrameProxyHost(site_instance.get()));
|
| +
|
| + // Detach the second child FrameTreeNode. This should trigger cleanup of
|
| + // RenderFrameProxyHosts in |site_instance|.
|
| + iframe2->current_frame_host()->OnMessageReceived(
|
| + FrameHostMsg_Detach(iframe2->current_frame_host()->GetRoutingID()));
|
| + iframe2 = NULL; // Was just destroyed.
|
| +
|
| + EXPECT_TRUE(delete_watcher1.deleted());
|
| + EXPECT_TRUE(delete_watcher2.deleted());
|
| +
|
| + EXPECT_EQ(0U, site_instance->active_frame_count());
|
| + EXPECT_EQ(nullptr,
|
| + root_manager->GetRenderFrameProxyHost(site_instance.get()))
|
| + << "Proxies should have been cleaned up";
|
| + EXPECT_TRUE(site_instance->HasOneRef())
|
| << "This SiteInstance should be destroyable now.";
|
| -#endif
|
| +}
|
| +
|
| +// Two tabs in the same process crash. The first tab is reloaded, and the second
|
| +// tab navigates away without reloading. The second tab's navigation shouldn't
|
| +// mess with the first tab's content. Motivated by http://crbug.com/473714.
|
| +TEST_F(RenderFrameHostManagerTest, TwoTabsCrashOneReloadsOneLeaves) {
|
| + base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
| + switches::kSitePerProcess);
|
| +
|
| + const GURL kUrl1("http://www.google.com/");
|
| + const GURL kUrl2("http://webkit.org/");
|
| + const GURL kUrl3("http://whatwg.org/");
|
| +
|
| + // |contents1| and |contents2| navigate to the same page and then crash.
|
| + TestWebContents* contents1 = contents();
|
| + scoped_ptr<TestWebContents> contents2(
|
| + TestWebContents::Create(browser_context(), contents1->GetSiteInstance()));
|
| + contents1->NavigateAndCommit(kUrl1);
|
| + contents2->NavigateAndCommit(kUrl1);
|
| + CrashFrame(contents1->GetMainFrame());
|
| + CrashFrame(contents2->GetMainFrame());
|
| +
|
| + EXPECT_EQ(contents1->GetSiteInstance(), contents2->GetSiteInstance());
|
| +
|
| + // Reload |contents1|.
|
| + contents1->NavigateAndCommit(kUrl1);
|
| + EXPECT_TRUE(contents1->GetMainFrame()->IsRenderFrameLive());
|
| + EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
|
| + EXPECT_EQ(contents1->GetSiteInstance(), contents2->GetSiteInstance());
|
| +
|
| + // |contents1| creates an out of process iframe.
|
| + contents1->GetMainFrame()->OnCreateChildFrame(
|
| + contents1->GetMainFrame()->GetProcess()->GetNextRoutingID(),
|
| + std::string("frame_name"), SandboxFlags::NONE);
|
| + RenderFrameHostManager* iframe =
|
| + contents()->GetFrameTree()->root()->child_at(0)->render_manager();
|
| + NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl2,
|
| + Referrer(kUrl1, blink::WebReferrerPolicyDefault),
|
| + base::string16() /* title */,
|
| + ui::PAGE_TRANSITION_LINK,
|
| + false /* is_renderer_init */);
|
| + RenderFrameHostImpl* cross_site = GetFrameHostForNavigation(iframe, entry);
|
| + iframe->DidNavigateFrame(cross_site, true);
|
| +
|
| + // A proxy to the iframe should now exist in the SiteInstance of the main
|
| + // frames.
|
| + EXPECT_NE(cross_site->GetSiteInstance(), contents1->GetSiteInstance());
|
| + EXPECT_NE(nullptr,
|
| + iframe->GetRenderFrameProxyHost(contents1->GetSiteInstance()));
|
| + EXPECT_NE(nullptr,
|
| + iframe->GetRenderFrameProxyHost(contents2->GetSiteInstance()));
|
| +
|
| + // Navigate |contents2| away from the sad tab (and thus away from the
|
| + // SiteInstance of |contents1|). This should not destroy the proxies needed by
|
| + // |contents1| -- that was http://crbug.com/473714.
|
| + EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
|
| + contents2->NavigateAndCommit(kUrl3);
|
| + EXPECT_TRUE(contents2->GetMainFrame()->IsRenderFrameLive());
|
| + EXPECT_NE(nullptr,
|
| + iframe->GetRenderFrameProxyHost(contents1->GetSiteInstance()));
|
| + EXPECT_EQ(nullptr,
|
| + iframe->GetRenderFrameProxyHost(contents2->GetSiteInstance()));
|
| }
|
|
|
| } // namespace content
|
|
|