Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / frame_host / render_frame_host_manager_unittest.cc
index fab23b3..e71ee01 100644 (file)
@@ -125,6 +125,37 @@ class RenderViewHostDeletedObserver : public WebContentsObserver {
 };
 
 
+// This observer keeps track of the last deleted RenderViewHost to avoid
+// accessing it and causing use-after-free condition.
+class RenderFrameHostDeletedObserver : public WebContentsObserver {
+ public:
+  RenderFrameHostDeletedObserver(RenderFrameHost* rfh)
+      : WebContentsObserver(WebContents::FromRenderFrameHost(rfh)),
+        process_id_(rfh->GetProcess()->GetID()),
+        routing_id_(rfh->GetRoutingID()),
+        deleted_(false) {
+  }
+
+  virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE {
+    if (render_frame_host->GetProcess()->GetID() == process_id_ &&
+        render_frame_host->GetRoutingID() == routing_id_) {
+      deleted_ = true;
+    }
+  }
+
+  bool deleted() {
+    return deleted_;
+  }
+
+ private:
+  int process_id_;
+  int routing_id_;
+  bool deleted_;
+
+  DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver);
+};
+
+
 // This observer is used to check whether IPC messages are being filtered for
 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
 // update events, which the FilterMessagesWhileSwappedOut test simulates being
@@ -143,7 +174,6 @@ class PluginFaviconMessageObserver : public WebContentsObserver {
   }
 
   virtual void DidUpdateFaviconURL(
-      int32 page_id,
       const std::vector<FaviconURL>& candidates) OVERRIDE {
     favicon_received_ = true;
   }
@@ -368,10 +398,10 @@ TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
     PluginFaviconMessageObserver observer(contents());
     EXPECT_TRUE(ntp_rvh->OnMessageReceived(
                     ViewHostMsg_UpdateFaviconURL(
-                        rvh()->GetRoutingID(), 0, icons)));
+                        rvh()->GetRoutingID(), icons)));
     EXPECT_TRUE(observer.favicon_received());
   }
-  // Create one more view in the same SiteInstance where dest_rvh2
+  // Create one more view in the same SiteInstance where ntp_rvh
   // exists so that it doesn't get deleted on navigation to another
   // site.
   static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())->
@@ -391,7 +421,7 @@ TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
     PluginFaviconMessageObserver observer(contents());
     EXPECT_TRUE(
         dest_rvh->OnMessageReceived(
-            ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), 101, icons)));
+            ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), icons)));
     EXPECT_TRUE(observer.favicon_received());
   }
 
@@ -402,7 +432,7 @@ TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
     PluginFaviconMessageObserver observer(contents());
     EXPECT_TRUE(
         ntp_rvh->OnMessageReceived(
-            ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), 101, icons)));
+            ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), icons)));
     EXPECT_FALSE(observer.favicon_received());
   }
 
@@ -426,23 +456,24 @@ TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
   MockRenderProcessHost* ntp_process_host =
       static_cast<MockRenderProcessHost*>(ntp_rvh->GetProcess());
   ntp_process_host->sink().ClearMessages();
+  RenderFrameHost* ntp_rfh = ntp_rvh->GetMainFrame();
   const base::string16 msg = base::ASCIIToUTF16("Message");
   bool result = false;
   base::string16 unused;
-  ViewHostMsg_RunBeforeUnloadConfirm before_unload_msg(
-      rvh()->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
+  FrameHostMsg_RunBeforeUnloadConfirm before_unload_msg(
+      ntp_rfh->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
   // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
   before_unload_msg.EnableMessagePumping();
-  EXPECT_TRUE(ntp_rvh->OnMessageReceived(before_unload_msg));
+  EXPECT_TRUE(ntp_rfh->OnMessageReceived(before_unload_msg));
   EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
 
   // Also test RunJavaScriptMessage.
   ntp_process_host->sink().ClearMessages();
-  ViewHostMsg_RunJavaScriptMessage js_msg(
-      rvh()->GetRoutingID(), msg, msg, kChromeURL,
+  FrameHostMsg_RunJavaScriptMessage js_msg(
+      ntp_rfh->GetRoutingID(), msg, msg, kChromeURL,
       JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
   js_msg.EnableMessagePumping();
-  EXPECT_TRUE(ntp_rvh->OnMessageReceived(js_msg));
+  EXPECT_TRUE(ntp_rfh->OnMessageReceived(js_msg));
   EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
 }
 
@@ -950,6 +981,50 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) {
       notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
 }
 
+// Test that navigation is not blocked when we make new navigation before
+// previous one has been committed. This is also a regression test for
+// http://crbug.com/104600.
+TEST_F(RenderFrameHostManagerTest, NewCrossNavigationBetweenSwapOutAndCommit) {
+  const GURL kUrl1("http://www.google.com/");
+  const GURL kUrl2("http://www.chromium.org/");
+  const GURL kUrl3("http://www.youtube.com/");
+
+  contents()->NavigateAndCommit(kUrl1);
+  TestRenderViewHost* rvh1 = test_rvh();
+
+  // Keep active_view_count nonzero so that no swapped out views in
+  // this SiteInstance get forcefully deleted.
+  static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())->
+      increment_active_view_count();
+
+  // Navigate but don't commit.
+  contents()->GetController().LoadURL(
+      kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+  EXPECT_TRUE(rvh1->is_waiting_for_beforeunload_ack());
+  contents()->ProceedWithCrossSiteNavigation();
+  EXPECT_FALSE(rvh1->is_waiting_for_beforeunload_ack());
+  StartCrossSiteTransition(contents());
+  EXPECT_TRUE(rvh1->IsWaitingForUnloadACK());
+
+  rvh1->OnSwappedOut(false);
+  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
+
+  TestRenderViewHost* rvh2 = pending_test_rvh();
+  EXPECT_TRUE(rvh2);
+  static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())->
+      increment_active_view_count();
+
+  contents()->GetController().LoadURL(
+      kUrl3, Referrer(), PAGE_TRANSITION_LINK, std::string());
+  // Pending rvh2 is already deleted.
+  contents()->ProceedWithCrossSiteNavigation();
+
+  TestRenderViewHost* rvh3 = pending_test_rvh();
+  EXPECT_TRUE(rvh3);
+  // Navigation should be already unblocked by rvh1.
+  EXPECT_FALSE(rvh3->are_navigations_suspended());
+}
+
 // Tests WebUI creation.
 TEST_F(RenderFrameHostManagerTest, WebUI) {
   set_should_create_webui(true);
@@ -989,7 +1064,7 @@ TEST_F(RenderFrameHostManagerTest, WebUI) {
   EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL());
 
   // The Web UI is committed immediately because the RenderViewHost has not been
-  // used yet. UpdateRendererStateForNavigate() took the short cut path.
+  // used yet. UpdateStateForNavigate() took the short cut path.
   EXPECT_FALSE(manager->pending_web_ui());
   EXPECT_TRUE(manager->web_ui());
 
@@ -1015,7 +1090,7 @@ TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
   manager1->Init(
       browser_context(), blank_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
   // Test the case that new RVH is considered live.
-  manager1->current_host()->CreateRenderView(base::string16(), -1, -1);
+  manager1->current_host()->CreateRenderView(base::string16(), -1, -1, false);
 
   // Navigate to a WebUI page.
   const GURL kUrl1("chrome://foo");
@@ -1050,7 +1125,7 @@ TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
       browser_context(), webui_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
   // Make sure the new RVH is considered live.  This is usually done in
   // RenderWidgetHost::Init when opening a new tab from a link.
-  manager2->current_host()->CreateRenderView(base::string16(), -1, -1);
+  manager2->current_host()->CreateRenderView(base::string16(), -1, -1, false);
 
   const GURL kUrl2("chrome://foo/bar");
   NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
@@ -1252,7 +1327,8 @@ TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
   contents()->SetOpener(opener1.get());
 
   // Make sure the new opener RVH is considered live.
-  opener1_manager->current_host()->CreateRenderView(base::string16(), -1, -1);
+  opener1_manager->current_host()->CreateRenderView(
+      base::string16(), -1, -1, false);
 
   // Use a cross-process navigation in the opener to swap out the old RVH.
   EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
@@ -1720,4 +1796,54 @@ TEST_F(RenderFrameHostManagerTest,
   EXPECT_TRUE(rvh1->IsSwappedOut());
 }
 
+// Test that a RenderFrameHost is properly deleted or swapped out when a
+// cross-site navigation is cancelled.
+TEST_F(RenderFrameHostManagerTest,
+       CancelPendingProperlyDeletesOrSwaps) {
+  const GURL kUrl1("http://www.google.com/");
+  const GURL kUrl2("http://www.chromium.org/");
+  RenderFrameHostImpl* pending_rfh = NULL;
+  base::TimeTicks now = base::TimeTicks::Now();
+
+  // Navigate to the first page.
+  contents()->NavigateAndCommit(kUrl1);
+  TestRenderViewHost* rvh1 = test_rvh();
+  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+
+  // Navigate to a new site, starting a cross-site navigation.
+  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+  {
+    pending_rfh = contents()->GetFrameTree()->root()->render_manager()
+        ->pending_frame_host();
+    RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
+
+    // Cancel the navigation by simulating a declined beforeunload dialog.
+    main_test_rfh()->OnMessageReceived(
+        FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
+    EXPECT_FALSE(contents()->cross_navigation_pending());
+
+    // Since the pending RFH is the only one for the new SiteInstance, it should
+    // be deleted.
+    EXPECT_TRUE(rvh_deleted_observer.deleted());
+  }
+
+  // Start another cross-site navigation.
+  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+  {
+    pending_rfh = contents()->GetFrameTree()->root()->render_manager()
+        ->pending_frame_host();
+    RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
+
+    // Increment the number of active views in the new SiteInstance, which will
+    // cause the pending RFH to be swapped out instead of deleted.
+    static_cast<SiteInstanceImpl*>(
+        pending_rfh->GetSiteInstance())->increment_active_view_count();
+
+    main_test_rfh()->OnMessageReceived(
+        FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
+    EXPECT_FALSE(contents()->cross_navigation_pending());
+    EXPECT_FALSE(rvh_deleted_observer.deleted());
+  }
+}
+
 }  // namespace content