If the renderer drops a navigation, make sure the spinner doesn't spin endlessly.

BUG=431853
TEST=none

Review URL: https://codereview.chromium.org/790843002

Cr-Commit-Position: refs/heads/master@{#307567}
diff --git a/content/browser/frame_host/render_frame_host_delegate.h b/content/browser/frame_host/render_frame_host_delegate.h
index 6c22d94..57b2ad42 100644
--- a/content/browser/frame_host/render_frame_host_delegate.h
+++ b/content/browser/frame_host/render_frame_host_delegate.h
@@ -57,13 +57,17 @@
   // Informs the delegate whenever a RenderFrameHost is deleted.
   virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) {}
 
-  // The top-level RenderFrame began loading a new page. This corresponds to
+  // The specified RenderFrame began loading a new page. This corresponds to
   // Blink's notion of the throbber starting.
   // |to_different_document| will be true unless the load is a fragment
   // navigation, or triggered by history.pushState/replaceState.
   virtual void DidStartLoading(RenderFrameHost* render_frame_host,
                                bool to_different_document) {}
 
+  // The specified RenderFrame stopped loading a page. This corresponds to
+  // Blink's notion of the throbber stopping.
+  virtual void DidStopLoading(RenderFrameHost* render_frame_host) {}
+
   // The RenderFrameHost has been swapped out.
   virtual void SwappedOut(RenderFrameHost* render_frame_host) {}
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 72d5324..13bb3160 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -303,6 +303,7 @@
                         OnDidFailLoadWithError)
     IPC_MESSAGE_HANDLER_GENERIC(FrameHostMsg_DidCommitProvisionalLoad,
                                 OnDidCommitProvisionalLoad(msg))
+    IPC_MESSAGE_HANDLER(FrameHostMsg_DidDropNavigation, OnDidDropNavigation)
     IPC_MESSAGE_HANDLER(FrameHostMsg_OpenURL, OnOpenURL)
     IPC_MESSAGE_HANDLER(FrameHostMsg_DocumentOnLoadCompleted,
                         OnDocumentOnLoadCompleted)
@@ -703,6 +704,14 @@
   frame_tree_node()->navigator()->DidNavigate(this, validated_params);
 }
 
+void RenderFrameHostImpl::OnDidDropNavigation() {
+  // At the end of Navigate(), the delegate's DidStartLoading is called to force
+  // the spinner to start, even if the renderer didn't yet begin the load. If it
+  // turns out that the renderer dropped the navigation, we need to turn off the
+  // spinner.
+  delegate_->DidStopLoading(this);
+}
+
 RenderWidgetHostImpl* RenderFrameHostImpl::GetRenderWidgetHost() {
   return static_cast<RenderWidgetHostImpl*>(render_view_host_);
 }
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 052da3b..3769ce20 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -421,6 +421,7 @@
       int error_code,
       const base::string16& error_description);
   void OnDidCommitProvisionalLoad(const IPC::Message& msg);
+  void OnDidDropNavigation();
   void OnBeforeUnloadACK(
       bool proceed,
       const base::TimeTicks& renderer_before_unload_start_time,
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 8e1c657..a747fe8 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -352,6 +352,7 @@
   void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
   void DidStartLoading(RenderFrameHost* render_frame_host,
                        bool to_different_document) override;
+  void DidStopLoading(RenderFrameHost* render_frame_host) override;
   void SwappedOut(RenderFrameHost* render_frame_host) override;
   void DidDeferAfterResponseStarted(
       const TransitionLayerData& transition_data) override;
@@ -883,9 +884,6 @@
   // Calculates the progress of the current load and notifies the delegate.
   void SendLoadProgressChanged();
 
-  // Called once when the last frame on the page has stopped loading.
-  void DidStopLoading(RenderFrameHost* render_frame_host);
-
   // Misc non-view stuff -------------------------------------------------------
 
   // Helper functions for sending notifications.
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 0b8b72b7..af8289f 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -578,6 +578,9 @@
                     int /* error_code */,
                     base::string16 /* error_description */)
 
+// Sent when the renderer decides to ignore a navigation.
+IPC_MESSAGE_ROUTED0(FrameHostMsg_DidDropNavigation)
+
 // Sent when the renderer starts loading the page. |to_different_document| will
 // be true unless the load is a fragment navigation, or triggered by
 // history.pushState/replaceState.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 28ff2b4..f16b132 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -956,6 +956,7 @@
       params.common_params.url, params.common_params.navigation_type,
       params.commit_params.page_state, true, params.pending_history_list_offset,
       params.page_id, &is_reload, &cache_policy)) {
+    Send(new FrameHostMsg_DidDropNavigation(routing_id_));
     return;
   }
 
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 0e7a0c2..c4665ff 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -864,6 +864,13 @@
   EXPECT_EQ(2, view()->history_list_length_);
   EXPECT_EQ(1, view()->history_list_offset_);
   EXPECT_EQ(3, view()->history_page_ids_[1]);
+
+  // Check for a valid DidDropNavigation message.
+  ProcessPendingMessages();
+  const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
+      FrameHostMsg_DidDropNavigation::ID);
+  ASSERT_TRUE(msg);
+  render_thread_->sink().ClearMessages();
 }
 
 // Test that we do not ignore navigations after the entry limit is reached,