Hide navigations aborted due to downloading from ntp

If a navigation was started by an HTMLAnchorElement with a download
attribute (<a download>), the navigation will end up being aborted,
and turned into a download, so leaving the URL around for the user to
edit is not useful.

BUG=808966
[email protected],[email protected]

Change-Id: Idcfa5c952cba7933fdf62e75a67ec4a2e2087695
Reviewed-on: https://chromium-review.googlesource.com/908451
Reviewed-by: Camille Lamy <[email protected]>
Reviewed-by: Charlie Reis <[email protected]>
Commit-Queue: Jochen Eisinger <[email protected]>
Cr-Commit-Position: refs/heads/master@{#541471}
diff --git a/chrome/browser/download/download_ui_controller.cc b/chrome/browser/download/download_ui_controller.cc
index 15279d7..28d98a2 100644
--- a/chrome/browser/download/download_ui_controller.cc
+++ b/chrome/browser/download/download_ui_controller.cc
@@ -143,7 +143,8 @@
   content::WebContents* web_contents =
       content::DownloadItemUtils::GetWebContents(item);
   if (web_contents && (item->IsSavePackageDownload() ||
-                       (web_contents->GetURL() != item->GetOriginalUrl() &&
+                       (!web_contents->GetURL().is_empty() &&
+                        web_contents->GetURL() != item->GetOriginalUrl() &&
                         web_contents->GetURL() != item->GetURL()))) {
     auto* security_state_tab_helper =
         SecurityStateTabHelper::FromWebContents(web_contents);
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index 10796e8e..9eaa382 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -297,8 +297,9 @@
   ui_test_utils::NavigateToURL(browser(), download_url);
   download_observer.WaitForFinished();
 
-  // This should have changed the visible URL, but not the last committed one.
-  ASSERT_EQ(download_url, active_tab->GetVisibleURL());
+  // This should neither have changed the visible URL, nor the last committed
+  // one.
+  ASSERT_EQ(GURL(chrome::kChromeUINewTabURL), active_tab->GetVisibleURL());
   ASSERT_EQ(GURL(chrome::kChromeUINewTabURL),
             active_tab->GetLastCommittedURL());
 
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc
index 5de5bfb..f33998c 100644
--- a/content/browser/frame_host/frame_tree_node.cc
+++ b/content/browser/frame_host/frame_tree_node.cc
@@ -603,7 +603,8 @@
         expected_pending_nav_entry_id =
             navigation_request_->navigation_handle()->pending_nav_entry_id();
       }
-      navigator_->DiscardPendingEntryIfNeeded(expected_pending_nav_entry_id);
+      navigator_->DiscardPendingEntryIfNeeded(expected_pending_nav_entry_id,
+                                              false /* is_download */);
     }
     ResetNavigationRequest(false, true);
   }
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index 0cb359a..7add424f 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -8024,4 +8024,22 @@
   EXPECT_TRUE(capturer.is_same_document());
 }
 
+IN_PROC_BROWSER_TEST_F(ContentBrowserTest, HideDownloadFromUnmodifiedNewTab) {
+  GURL url("data:application/octet-stream,");
+
+  const NavigationControllerImpl& controller =
+      static_cast<const NavigationControllerImpl&>(
+          shell()->web_contents()->GetController());
+
+  OpenURLParams params(url, Referrer(), WindowOpenDisposition::CURRENT_TAB,
+                       ui::PAGE_TRANSITION_LINK, true);
+  params.suggested_filename = std::string("foo");
+
+  shell()->web_contents()->OpenURL(params);
+  WaitForLoadStop(shell()->web_contents());
+
+  EXPECT_FALSE(controller.GetPendingEntry());
+  EXPECT_FALSE(controller.GetVisibleEntry());
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 2b5446b5..25c58f38 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -995,11 +995,15 @@
   if (navigation_handle_.get())
     navigation_handle_->set_net_error_code(static_cast<net::Error>(net_error));
 
-  int expected_pending_entry_id =
-      navigation_handle_.get() ? navigation_handle_->pending_nav_entry_id()
-                               : nav_entry_id_;
+  int expected_pending_entry_id = nav_entry_id_;
+  bool is_download = false;
+  if (navigation_handle_.get()) {
+    expected_pending_entry_id = navigation_handle_->pending_nav_entry_id();
+    is_download = navigation_handle_->IsDownload();
+  }
+
   frame_tree_node_->navigator()->DiscardPendingEntryIfNeeded(
-      expected_pending_entry_id);
+      expected_pending_entry_id, is_download);
 
   // If the request was canceled by the user do not show an error page.
   if (net_error == net::ERR_ABORTED) {
diff --git a/content/browser/frame_host/navigator.h b/content/browser/frame_host/navigator.h
index 771a0b9..e93b664 100644
--- a/content/browser/frame_host/navigator.h
+++ b/content/browser/frame_host/navigator.h
@@ -199,7 +199,8 @@
   // With sufficiently bad interleaving of IPCs, this may no longer be the
   // pending NavigationEntry, in which case the pending NavigationEntry will not
   // be discarded.
-  virtual void DiscardPendingEntryIfNeeded(int expected_pending_entry_id) {}
+  virtual void DiscardPendingEntryIfNeeded(int expected_pending_entry_id,
+                                           bool is_download) {}
 
  protected:
   friend class base::RefCounted<Navigator>;
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index f48d0f2..194b79b 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -237,11 +237,13 @@
   }
 
   // Discard the pending navigation entry if needed.
-  int expected_pending_entry_id =
-      render_frame_host->GetNavigationHandle()
-          ? render_frame_host->GetNavigationHandle()->pending_nav_entry_id()
-          : 0;
-  DiscardPendingEntryIfNeeded(expected_pending_entry_id);
+  int expected_pending_entry_id = 0;
+  if (render_frame_host->GetNavigationHandle()) {
+    expected_pending_entry_id =
+        render_frame_host->GetNavigationHandle()->pending_nav_entry_id();
+    DCHECK(!render_frame_host->GetNavigationHandle()->IsDownload());
+  }
+  DiscardPendingEntryIfNeeded(expected_pending_entry_id, false);
 }
 
 void NavigatorImpl::DidFailLoadWithError(
@@ -982,14 +984,15 @@
   }
 }
 
-void NavigatorImpl::DiscardPendingEntryIfNeeded(int expected_pending_entry_id) {
+void NavigatorImpl::DiscardPendingEntryIfNeeded(int expected_pending_entry_id,
+                                                bool is_download) {
   // Racy conditions can cause a fail message to arrive after its corresponding
   // pending entry has been replaced by another navigation. If
   // |DiscardPendingEntry| is called in this case, then the completely valid
   // entry for the new navigation would be discarded. See crbug.com/513742. To
   // catch this case, the current pending entry is compared against the current
   // navigation handle's entry id, which should correspond to the failed load.
-  NavigationEntry* pending_entry = controller_->GetPendingEntry();
+  NavigationEntryImpl* pending_entry = controller_->GetPendingEntry();
   bool pending_matches_fail_msg =
       pending_entry &&
       expected_pending_entry_id == pending_entry->GetUniqueID();
@@ -1010,9 +1013,13 @@
   // allow the view to clear the pending entry and typed URL if the user
   // requests (e.g., hitting Escape with focus in the address bar).
   //
+  // Note that the pending entry does not need to be preserved for downloads,
+  // since the user is unlikely to try again.
+  //
   // Note: don't touch the transient entry, since an interstitial may exist.
-  bool should_preserve_entry = controller_->IsUnmodifiedBlankTab() ||
-                               delegate_->ShouldPreserveAbortedURLs();
+  bool should_preserve_entry = (controller_->IsUnmodifiedBlankTab() ||
+                                delegate_->ShouldPreserveAbortedURLs()) &&
+                               !is_download;
   if (pending_entry != controller_->GetVisibleEntry() ||
       !should_preserve_entry) {
     controller_->DiscardPendingEntry(true);
diff --git a/content/browser/frame_host/navigator_impl.h b/content/browser/frame_host/navigator_impl.h
index a1bedd3..07a1a12 100644
--- a/content/browser/frame_host/navigator_impl.h
+++ b/content/browser/frame_host/navigator_impl.h
@@ -106,7 +106,8 @@
       const base::TimeTicks& renderer_before_unload_end_time) override;
   void CancelNavigation(FrameTreeNode* frame_tree_node,
                         bool inform_renderer) override;
-  void DiscardPendingEntryIfNeeded(int expected_pending_entry_id) override;
+  void DiscardPendingEntryIfNeeded(int expected_pending_entry_id,
+                                   bool is_download) override;
 
  private:
   // Holds data used to track browser side navigation metrics.
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 283d3ba..7ee614e7 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -508,9 +508,12 @@
       // already updated its state properly, and doesn't need to be notified.
       if (speculative_render_frame_host_->GetNavigationHandle() &&
           request.from_begin_navigation()) {
+        DCHECK(!speculative_render_frame_host_->GetNavigationHandle()
+                    ->IsDownload());
         frame_tree_node_->navigator()->DiscardPendingEntryIfNeeded(
             speculative_render_frame_host_->GetNavigationHandle()
-                ->pending_nav_entry_id());
+                ->pending_nav_entry_id(),
+            false /* is_download */);
       }
       DiscardUnusedFrame(UnsetSpeculativeRenderFrameHost());
     }
@@ -547,9 +550,12 @@
       if (speculative_render_frame_host_ &&
           speculative_render_frame_host_->GetNavigationHandle() &&
           request.from_begin_navigation()) {
+        DCHECK(!speculative_render_frame_host_->GetNavigationHandle()
+                    ->IsDownload());
         frame_tree_node_->navigator()->DiscardPendingEntryIfNeeded(
             speculative_render_frame_host_->GetNavigationHandle()
-                ->pending_nav_entry_id());
+                ->pending_nav_entry_id(),
+            false /* is_download */);
       }
 
       // If a previous speculative RenderFrameHost didn't exist or if its
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc
index 20c1331..ef47bd27 100644
--- a/content/shell/browser/shell.cc
+++ b/content/shell/browser/shell.cc
@@ -392,6 +392,7 @@
   load_url_params.is_renderer_initiated = params.is_renderer_initiated;
   load_url_params.should_replace_current_entry =
       params.should_replace_current_entry;
+  load_url_params.suggested_filename = params.suggested_filename;
 
   if (params.uses_post) {
     load_url_params.load_type = NavigationController::LOAD_TYPE_HTTP_POST;