Index: content/browser/tab_contents/render_view_host_manager_unittest.cc |
diff --git a/content/browser/tab_contents/render_view_host_manager_unittest.cc b/content/browser/tab_contents/render_view_host_manager_unittest.cc |
index c2d7117f20aaf2e258f675d7530327dfd57c18a6..dc914df5a2f66bc70043e57fab0e8cd41c938661 100644 |
--- a/content/browser/tab_contents/render_view_host_manager_unittest.cc |
+++ b/content/browser/tab_contents/render_view_host_manager_unittest.cc |
@@ -272,6 +272,141 @@ TEST_F(RenderViewHostManagerTest, Navigate) { |
content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); |
} |
+// Tests the Navigate function. In this unit test we verify that the Navigate |
+// function can handle a new navigation event before the previous navigation |
+// has been committed. This is also a regression test for |
+// http://crbug.com/104600. |
+TEST_F(RenderViewHostManagerTest, NavigateWithEarlyReNavigation) { |
+ TestNotificationTracker notifications; |
+ |
+ SiteInstance* instance = SiteInstance::CreateSiteInstance(profile()); |
+ |
+ TestTabContents tab_contents(profile(), instance); |
+ notifications.ListenFor( |
+ content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, |
+ content::Source<NavigationController>(&tab_contents.controller())); |
+ |
+ // Create. |
+ RenderViewHostManager manager(&tab_contents, &tab_contents); |
+ |
+ manager.Init(profile(), instance, MSG_ROUTING_NONE); |
+ |
+ // 1) The first navigation. -------------------------- |
+ const GURL kUrl1("http://www.google.com/"); |
+ NavigationEntry entry1(NULL /* instance */, -1 /* page_id */, kUrl1, |
+ GURL() /* referrer */, string16() /* title */, |
+ content::PAGE_TRANSITION_TYPED, |
+ false /* is_renderer_init */); |
+ RenderViewHost* host = manager.Navigate(entry1); |
+ |
+ // The RenderViewHost created in Init will be reused. |
+ EXPECT_TRUE(host == manager.current_host()); |
+ EXPECT_FALSE(manager.pending_render_view_host()); |
+ |
+ // We should observe a notification. |
+ EXPECT_TRUE(notifications.Check1AndReset( |
+ content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); |
+ notifications.Reset(); |
+ |
+ // Commit. |
+ manager.DidNavigateMainFrame(host); |
+ |
+ // Commit to SiteInstance should be delayed until RenderView commit. |
+ EXPECT_TRUE(host == manager.current_host()); |
+ ASSERT_TRUE(host); |
+ EXPECT_FALSE(host->site_instance()->has_site()); |
+ host->site_instance()->SetSite(kUrl1); |
+ |
+ // 2) Cross-site navigate to next site. ------------------------- |
+ const GURL kUrl2("http://www.example.com"); |
+ NavigationEntry entry2(NULL /* instance */, -1 /* page_id */, kUrl2, |
+ GURL() /* referrer */, string16() /* title */, |
+ content::PAGE_TRANSITION_TYPED, |
+ false /* is_renderer_init */); |
+ RenderViewHost* host2 = manager.Navigate(entry2); |
+ |
+ // A new RenderViewHost should be created. |
+ EXPECT_TRUE(manager.pending_render_view_host()); |
+ ASSERT_EQ(host2, manager.pending_render_view_host()); |
+ |
+ // Check that the navigation is still suspended because the old RVH |
+ // is not swapped out, yet. |
+ MockRenderProcessHost* test_process_host2 = |
+ static_cast<MockRenderProcessHost*>(host2->process()); |
+ test_process_host2->sink().ClearMessages(); |
+ host2->NavigateToURL(kUrl2); |
+ EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching( |
+ ViewMsg_Navigate::ID)); |
+ |
+ // Allow closing the current Render View (precondition for swapping out |
+ // the RVH): Simulate response from RenderView for ViewMsg_ShouldClose sent by |
+ // FirePageBeforeUnload |
+ TestRenderViewHost* test_host = static_cast<TestRenderViewHost*>(host); |
+ MockRenderProcessHost* test_process_host = |
+ static_cast<MockRenderProcessHost*>(test_host->process()); |
+ EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( |
+ ViewMsg_ShouldClose::ID)); |
+ test_host->SendShouldCloseACK(true); |
+ |
+ // CrossSiteResourceHandler::StartCrossSiteTransition can trigger a |
+ // call of RenderViewHostManager::OnCrossSiteResponse before |
+ // RenderViewHostManager::DidNavigateMainFrame is called. In this case the |
+ // RVH is swapped out. |
+ manager.OnCrossSiteResponse(host2->process()->GetID(), |
+ host2->GetPendingRequestId()); |
+ EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( |
+ ViewMsg_SwapOut::ID)); |
+ test_host->OnSwapOutACK(); |
+ |
+ EXPECT_EQ(host, manager.current_host()); |
+ EXPECT_TRUE(manager.current_host()->is_swapped_out()); |
+ EXPECT_EQ(host2, manager.pending_render_view_host()); |
+ // There should be still no navigation messages being sent. |
+ EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching( |
+ ViewMsg_Navigate::ID)); |
+ |
+ // 3) Cross-site navigate to next site before 2) has committed. -------------- |
+ const GURL kUrl3("http://webkit.org/"); |
+ NavigationEntry entry3(NULL /* instance */, -1 /* page_id */, kUrl3, |
+ GURL() /* referrer */, string16() /* title */, |
+ content::PAGE_TRANSITION_TYPED, |
+ false /* is_renderer_init */); |
+ RenderViewHost* host3 = manager.Navigate(entry3); |
+ |
+ // A new RenderViewHost should be created. |
+ EXPECT_TRUE(manager.pending_render_view_host()); |
+ ASSERT_EQ(host3, manager.pending_render_view_host()); |
+ |
+ EXPECT_EQ(host, manager.current_host()); |
+ EXPECT_TRUE(manager.current_host()->is_swapped_out()); |
+ |
+ // The navigation should not be suspended because the RVH |host| has been |
+ // swapped out already. Therefore, the RVH should send a navigation event |
+ // immediately. |
+ MockRenderProcessHost* test_process_host3 = |
+ static_cast<MockRenderProcessHost*>(host3->process()); |
+ test_process_host3->sink().ClearMessages(); |
+ |
+ // Usually TabContents::NavigateToEntry would call |
+ // RenderViewHostManager::Navigate followed by RenderViewHost::Navigate. |
+ // Here we need to call the latter ourselves. |
+ host3->NavigateToURL(kUrl3); |
+ EXPECT_TRUE(test_process_host3->sink().GetUniqueMessageMatching( |
+ ViewMsg_Navigate::ID)); |
+ |
+ // Commit. |
+ manager.DidNavigateMainFrame(host3); |
+ EXPECT_TRUE(host3 == manager.current_host()); |
+ ASSERT_TRUE(host3); |
+ EXPECT_TRUE(host3->site_instance()->has_site()); |
+ // Check the pending RenderViewHost has been committed. |
+ EXPECT_FALSE(manager.pending_render_view_host()); |
+ |
+ // We should observe a notification. |
+ EXPECT_TRUE(notifications.Check1AndReset( |
+ content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); |
+} |
+ |
// Tests WebUI creation. |
TEST_F(RenderViewHostManagerTest, WebUI) { |
BrowserThreadImpl ui_thread(BrowserThread::UI, MessageLoop::current()); |