Network Service: All DownloadManager to throttle navigation interception

This CL adds the throttling logic for navigation interception with Network service enabled.
This matches the same logic used by DownloadResourceThrottle.
It will prompt user for storage permission, and limiting the number of download requests per tab.

BUG=715630

Change-Id: I88ad091972739e204ebf9b9720589bc10779eed4
Reviewed-on: https://chromium-review.googlesource.com/773483
Commit-Queue: Min Qin <[email protected]>
Reviewed-by: John Abd-El-Malek <[email protected]>
Reviewed-by: Xing Liu <[email protected]>
Reviewed-by: David Trainor <[email protected]>
Cr-Commit-Position: refs/heads/master@{#519463}
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 370b88f..ae9078e3 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -51,6 +51,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/resource_context.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/common/referrer.h"
@@ -69,6 +70,19 @@
 namespace content {
 namespace {
 
+WebContents* GetWebContents(int render_process_id,
+                            int render_frame_id,
+                            int frame_tree_node_id) {
+  DCHECK(IsBrowserSideNavigationEnabled());
+
+  WebContents* web_contents = WebContents::FromRenderFrameHost(
+      RenderFrameHost::FromID(render_process_id, render_frame_id));
+  if (web_contents)
+    return web_contents;
+
+  return WebContents::FromFrameTreeNodeId(frame_tree_node_id);
+}
+
 StoragePartitionImpl* GetStoragePartition(BrowserContext* context,
                                           int render_process_id,
                                           int render_frame_id) {
@@ -180,18 +194,20 @@
     return nullptr;
   }
 
+  // TODO(qinmin): Check the storage permission before creating the URLLoader.
+  // This is already done for context menu download, but it is missing for
+  // download service and download resumption.
   return DownloadManagerImpl::UniqueUrlDownloadHandlerPtr(
       ResourceDownloader::BeginDownload(
           download_manager, std::move(params), std::move(request),
-          url_loader_factory_getter, file_system_context, download_id, false)
+          url_loader_factory_getter, file_system_context,
+          base::Bind(&GetWebContents, -1, -1, -1), download_id, false)
           .release());
 }
 
 // Creates a ResourceDownloader to own the URLLoader and intercept the response,
 // and passes it back to the DownloadManager.
 void InterceptNavigationResponse(
-    base::OnceCallback<void(DownloadManagerImpl::UniqueUrlDownloadHandlerPtr)>
-        callback,
     base::WeakPtr<DownloadManagerImpl> download_manager,
     const scoped_refptr<ResourceResponse>& response,
     mojo::ScopedDataPipeConsumerHandle consumer_handle,
@@ -202,16 +218,30 @@
     std::vector<GURL> url_chain,
     base::Optional<network::URLLoaderCompletionStatus> status) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DownloadManagerImpl::UniqueUrlDownloadHandlerPtr resource_downloader(
-      ResourceDownloader::InterceptNavigationResponse(
-          download_manager, std::move(resource_request), response,
-          std::move(consumer_handle), ssl_status, frame_tree_node_id,
-          std::move(url_loader), std::move(url_chain), std::move(status))
-          .release());
+  GURL url = resource_request->url;
+  std::string method = resource_request->method;
+  ResourceRequestInfo::WebContentsGetter getter =
+      base::Bind(&GetWebContents, resource_request->origin_pid,
+                 resource_request->render_frame_id, frame_tree_node_id);
+  std::unique_ptr<ResourceDownloader> resource_downloader =
+      ResourceDownloader::CreateWithURLLoader(
+          download_manager, std::move(resource_request), getter,
+          std::move(url_loader), std::move(status));
+
+  // Use Unretained() is safe as |resource_downloader| will be deleted on
+  // the IO thread.
+  base::OnceClosure start_interception_cb = base::BindOnce(
+      &ResourceDownloader::StartNavigationInterception,
+      base::Unretained(resource_downloader.get()), response,
+      std::move(consumer_handle), ssl_status, std::move(url_chain));
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::BindOnce(std::move(callback), std::move(resource_downloader)));
+      base::BindOnce(&DownloadManagerImpl::CheckDownloadAllowed,
+                     download_manager, getter, url, method,
+                     DownloadManagerImpl::UniqueUrlDownloadHandlerPtr(
+                         resource_downloader.release()),
+                     std::move(start_interception_cb)));
 }
 
 class DownloadItemFactoryImpl : public DownloadItemFactory {
@@ -738,12 +768,35 @@
     int frame_tree_node_id) {
   return base::BindOnce(
       &InterceptNavigationResponse,
-      base::BindOnce(&DownloadManagerImpl::AddUrlDownloadHandler,
-                     weak_factory_.GetWeakPtr()),
       weak_factory_.GetWeakPtr(), response, std::move(consumer_handle),
       ssl_status, frame_tree_node_id);
 }
 
+void DownloadManagerImpl::CheckDownloadAllowed(
+    const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
+    const GURL& url,
+    const std::string& request_method,
+    UniqueUrlDownloadHandlerPtr downloader,
+    base::OnceClosure callback) {
+  if (delegate_) {
+    delegate_->CheckDownloadAllowed(
+        web_contents_getter, url, request_method,
+        base::BindOnce(&DownloadManagerImpl::OnDownloadAllowedCheckComplete,
+                       weak_factory_.GetWeakPtr(), std::move(downloader),
+                       std::move(callback)));
+  }
+}
+
+void DownloadManagerImpl::OnDownloadAllowedCheckComplete(
+    UniqueUrlDownloadHandlerPtr downloader,
+    base::OnceClosure callback,
+    bool allow) {
+  if (!allow)
+    return;
+  AddUrlDownloadHandler(std::move(downloader));
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, std::move(callback));
+}
+
 int DownloadManagerImpl::RemoveDownloadsByURLAndTime(
     const base::Callback<bool(const GURL&)>& url_filter,
     base::Time remove_begin,