Transfer the URLLoaderClient on navigation commit

This CL has effect on the code only when the NetworkService or the
NavigationMojoResponse features is enabled.

During a navigation, after the headers are received, this CL unbinds the
browser-side URLLoaderClient implementation. It passes the pair of
URLLoaderPtr/URLLoaderClientRequest to the renderer. Then it binds it to
the renderer-side URLLoaderClient implementation that will be used to
continue the navigation (it will receive OnStartLoadingResponseBody()
and OnComplete()).

As a side effect, several additionnal things had to be done:
1) When the navigation is intercepted by the download manager, the same
   kind of thing as above must be done. Instead of the renderer, the
   download manager should get the URLLoaderPtr/URLLoaderClientRequest
   pair and continue the loading.
2) When the load is handled by the URLFileLoader, there was a deadlock,
   the URLLoader and the URLLoaderClient were waiting for each other to
   closing their datapipe endpoints. This is solved by closing the
   URLFileLoader's one first when it has written its last byte.
3) Same as in 2), but with the URLFileDirectoryLoader.

See the others CLs in this series:
[1/3] https://chromium-review.googlesource.com/c/739502
[2/3] https://chromium-review.googlesource.com/c/741237
[3/3] this CL.

This is part 3 of the implementation plan.
Design doc: https://goo.gl/Rrrc7n

Bug: 705744
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_mojo;master.tryserver.chromium.linux:linux_site_isolation
Change-Id: I9df447e28afe119dc678adac943a000de6787bf4
Reviewed-on: https://chromium-review.googlesource.com/753738
Commit-Queue: Arthur Sonzogni <[email protected]>
Reviewed-by: Nasko Oskov <[email protected]>
Reviewed-by: Camille Lamy <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Kinuko Yasuda <[email protected]>
Reviewed-by: Min Qin(OOO 12/7-1/10) <[email protected]>
Reviewed-by: Yutaka Hirano <[email protected]>
Cr-Commit-Position: refs/heads/master@{#522470}
diff --git a/content/browser/browser_side_navigation_browsertest.cc b/content/browser/browser_side_navigation_browsertest.cc
index 4a3e30e..d33c5e9 100644
--- a/content/browser/browser_side_navigation_browsertest.cc
+++ b/content/browser/browser_side_navigation_browsertest.cc
@@ -11,6 +11,7 @@
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/frame_host/navigation_handle_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
+#include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
 #include "content/common/site_isolation_policy.h"
@@ -23,6 +24,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/controllable_http_response.h"
 #include "content/public/test/navigation_handle_observer.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/shell/browser/shell.h"
@@ -38,10 +40,11 @@
 
 namespace content {
 
-class BrowserSideNavigationBrowserTest : public ContentBrowserTest {
- public:
-  BrowserSideNavigationBrowserTest() {}
-
+// Test with BrowserSideNavigation enabled (aka PlzNavigate).
+// If you don't need a custom embedded test server, please use the next class
+// below (BrowserSideNavigationBrowserTest), it will automatically start the
+// default server.
+class BrowserSideNavigationBaseBrowserTest : public ContentBrowserTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitch(switches::kEnableBrowserSideNavigation);
@@ -49,6 +52,14 @@
 
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
+  }
+};
+
+class BrowserSideNavigationBrowserTest
+    : public BrowserSideNavigationBaseBrowserTest {
+ protected:
+  void SetUpOnMainThread() override {
+    BrowserSideNavigationBaseBrowserTest::SetUpOnMainThread();
     ASSERT_TRUE(embedded_test_server()->Start());
   }
 };
@@ -514,4 +525,61 @@
   EXPECT_EQ(url2, shell()->web_contents()->GetLastCommittedURL());
 }
 
+// Navigation are started in the browser process. After the headers are
+// received, the URLLoaderClient is transfered from the browser process to the
+// renderer process. This test ensures that when the the URLLoader is deleted
+// (in the browser process), the URLLoaderClient (in the renderer process) stops
+// properly.
+IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBaseBrowserTest,
+                       CancelRequestAfterReadyToCommit) {
+  // This test cancels the request using the ResourceDispatchHost. With the
+  // NetworkService, it is not used so the request is not canceled.
+  // TODO(arthursonzogni): Find a way to cancel a request from the browser
+  // with the NetworkService.
+  if (base::FeatureList::IsEnabled(features::kNetworkService))
+    return;
+
+  ControllableHttpResponse response(embedded_test_server(), "/main_document");
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // 1) Load a new document. Commit the navigation but do not send the full
+  //    response's body.
+  GURL url(embedded_test_server()->GetURL("/main_document"));
+  TestNavigationManager navigation_manager(shell()->web_contents(), url);
+  shell()->LoadURL(url);
+
+  // Let the navigation start.
+  EXPECT_TRUE(navigation_manager.WaitForRequestStart());
+  navigation_manager.ResumeNavigation();
+
+  // The server sends the first part of the response and waits.
+  response.WaitForRequest();
+  response.Send(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Type: text/html; charset=utf-8\r\n"
+      "\r\n"
+      "<html><body> ... ");
+
+  EXPECT_TRUE(navigation_manager.WaitForResponse());
+  GlobalRequestID global_id =
+      navigation_manager.GetNavigationHandle()->GetGlobalRequestID();
+  navigation_manager.ResumeNavigation();
+
+  // The navigation commits successfully. The renderer is waiting for the
+  // response's body.
+  navigation_manager.WaitForNavigationFinished();
+
+  // 2) The ResourceDispatcherHost cancels the request.
+  auto cancel_request = [](GlobalRequestID global_id) {
+    ResourceDispatcherHostImpl* rdh =
+        static_cast<ResourceDispatcherHostImpl*>(ResourceDispatcherHost::Get());
+    rdh->CancelRequest(global_id.child_id, global_id.request_id);
+  };
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                          base::BindOnce(cancel_request, global_id));
+
+  // 3) Check that the load stops properly.
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+}
+
 }  // namespace content
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index fb1b0aa..0f5c706 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -208,45 +208,6 @@
           .release());
 }
 
-// Creates a ResourceDownloader to own the URLLoader and intercept the response,
-// and passes it back to the DownloadManager.
-void InterceptNavigationResponse(
-    base::WeakPtr<DownloadManagerImpl> download_manager,
-    const scoped_refptr<ResourceResponse>& response,
-    mojo::ScopedDataPipeConsumerHandle consumer_handle,
-    net::CertStatus cert_status,
-    int frame_tree_node_id,
-    std::unique_ptr<ResourceRequest> resource_request,
-    std::unique_ptr<ThrottlingURLLoader> url_loader,
-    std::vector<GURL> url_chain,
-    base::Optional<network::URLLoaderCompletionStatus> status) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  GURL url = resource_request->url;
-  std::string method = resource_request->method;
-  ResourceRequestInfo::WebContentsGetter getter =
-      base::Bind(&GetWebContents, ChildProcessHost::kInvalidUniqueID,
-                 MSG_ROUTING_NONE, 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), cert_status, std::move(url_chain));
-
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&DownloadManagerImpl::CheckDownloadAllowed,
-                     download_manager, getter, url, method,
-                     DownloadManagerImpl::UniqueUrlDownloadHandlerPtr(
-                         resource_downloader.release()),
-                     std::move(start_interception_cb)));
-}
-
 class DownloadItemFactoryImpl : public DownloadItemFactory {
  public:
   DownloadItemFactoryImpl() {}
@@ -731,6 +692,7 @@
 
 void DownloadManagerImpl::AddUrlDownloadHandler(
     UniqueUrlDownloadHandlerPtr downloader) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (downloader)
     url_download_handlers_.push_back(std::move(downloader));
 }
@@ -791,40 +753,33 @@
   return DOWNLOAD_INTERRUPT_REASON_NONE;
 }
 
-NavigationURLLoader::NavigationInterceptionCB
-DownloadManagerImpl::GetNavigationInterceptionCB(
-    const scoped_refptr<ResourceResponse>& response,
-    mojo::ScopedDataPipeConsumerHandle consumer_handle,
+void DownloadManagerImpl::InterceptNavigation(
+    std::unique_ptr<ResourceRequest> resource_request,
+    std::vector<GURL> url_chain,
+    scoped_refptr<ResourceResponse> response,
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
     net::CertStatus cert_status,
     int frame_tree_node_id) {
-  return base::BindOnce(
-      &InterceptNavigationResponse, weak_factory_.GetWeakPtr(), response,
-      std::move(consumer_handle), cert_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)
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!delegate_)
     return;
-  AddUrlDownloadHandler(std::move(downloader));
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, std::move(callback));
+
+  const GURL& url = resource_request->url;
+  const std::string& method = resource_request->method;
+  ResourceRequestInfo::WebContentsGetter web_contents_getter =
+      base::BindRepeating(&GetWebContents, ChildProcessHost::kInvalidUniqueID,
+                          MSG_ROUTING_NONE, frame_tree_node_id);
+
+  base::OnceCallback<void(bool /* download allowed */)>
+      on_download_checks_done = base::BindOnce(
+          &DownloadManagerImpl::InterceptNavigationOnChecksComplete,
+          weak_factory_.GetWeakPtr(), web_contents_getter,
+          std::move(resource_request), std::move(url_chain),
+          std::move(response), cert_status,
+          std::move(url_loader_client_endpoints));
+
+  delegate_->CheckDownloadAllowed(web_contents_getter, url, method,
+                                  std::move(on_download_checks_done));
 }
 
 int DownloadManagerImpl::RemoveDownloadsByURLAndTime(
@@ -1024,6 +979,52 @@
     delegate_->ShowDownloadInShell(download);
 }
 
+void DownloadManagerImpl::InterceptNavigationOnChecksComplete(
+    ResourceRequestInfo::WebContentsGetter web_contents_getter,
+    std::unique_ptr<ResourceRequest> resource_request,
+    std::vector<GURL> url_chain,
+    scoped_refptr<ResourceResponse> response,
+    net::CertStatus cert_status,
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
+    bool is_download_allowed) {
+  if (!is_download_allowed)
+    return;
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&DownloadManagerImpl::CreateDownloadHandlerForNavigation,
+                     weak_factory_.GetWeakPtr(), web_contents_getter,
+                     std::move(resource_request), std::move(url_chain),
+                     std::move(response), std::move(cert_status),
+                     std::move(url_loader_client_endpoints)));
+}
+
+// static
+void DownloadManagerImpl::CreateDownloadHandlerForNavigation(
+    base::WeakPtr<DownloadManagerImpl> download_manager,
+    ResourceRequestInfo::WebContentsGetter web_contents_getter,
+    std::unique_ptr<ResourceRequest> resource_request,
+    std::vector<GURL> url_chain,
+    scoped_refptr<ResourceResponse> response,
+    net::CertStatus cert_status,
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  std::unique_ptr<ResourceDownloader> resource_downloader =
+      ResourceDownloader::InterceptNavigationResponse(
+          download_manager, std::move(resource_request),
+          std::move(web_contents_getter), std::move(url_chain),
+          std::move(response), std::move(cert_status),
+          std::move(url_loader_client_endpoints));
+
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(&DownloadManagerImpl::AddUrlDownloadHandler,
+                     download_manager,
+                     DownloadManagerImpl::UniqueUrlDownloadHandlerPtr(
+                         resource_downloader.release())));
+}
+
 void DownloadManagerImpl::BeginDownloadInternal(
     std::unique_ptr<content::DownloadUrlParameters> params,
     uint32_t id) {
diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h
index 776f92d..6007ada 100644
--- a/content/browser/download/download_manager_impl.h
+++ b/content/browser/download/download_manager_impl.h
@@ -143,22 +143,16 @@
       int render_frame_route_id,
       bool do_not_prompt_for_login);
 
-  // Returns the callback to intercept the navigation response.
-  NavigationURLLoader::NavigationInterceptionCB GetNavigationInterceptionCB(
-      const scoped_refptr<ResourceResponse>& response,
-      mojo::ScopedDataPipeConsumerHandle consumer_handle,
+  // Continue a navigation that ends up to be a download after it reaches the
+  // OnResponseStarted() step. It has to be called on the UI thread.
+  void InterceptNavigation(
+      std::unique_ptr<ResourceRequest> resource_request,
+      std::vector<GURL> url_chain,
+      scoped_refptr<ResourceResponse> response,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
       net::CertStatus cert_status,
       int frame_tree_node_id);
 
-  // Checks if a download is allowed, |on_download_allowed_cb| is called if
-  // the download is allowed.
-  void CheckDownloadAllowed(
-      const ResourceRequestInfo::WebContentsGetter& web_contents_getter,
-      const GURL& url,
-      const std::string& request_method,
-      UniqueUrlDownloadHandlerPtr downloader,
-      base::OnceClosure on_download_allowed_cb);
-
  private:
   using DownloadSet = std::set<DownloadItem*>;
   using DownloadGuidMap = std::unordered_map<std::string, DownloadItemImpl*>;
@@ -222,10 +216,26 @@
       std::unique_ptr<content::DownloadUrlParameters> params,
       uint32_t id);
 
-  // Called when download permission check is complete.
-  void OnDownloadAllowedCheckComplete(UniqueUrlDownloadHandlerPtr downloader,
-                                      base::OnceClosure callback,
-                                      bool allow);
+  void InterceptNavigationOnChecksComplete(
+      ResourceRequestInfo::WebContentsGetter web_contents_getter,
+      std::unique_ptr<ResourceRequest> resource_request,
+      std::vector<GURL> url_chain,
+      scoped_refptr<ResourceResponse> response,
+      net::CertStatus cert_status,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
+      bool is_download_allowed);
+
+  // Called when a navigation turns to be a download. Create a new
+  // DownloadHandler. It will be used to continue the loading instead of the
+  // regular document loader. Must be called on the IO thread.
+  static void CreateDownloadHandlerForNavigation(
+      base::WeakPtr<DownloadManagerImpl> download_manager,
+      ResourceRequestInfo::WebContentsGetter web_contents_getter,
+      std::unique_ptr<ResourceRequest> resource_request,
+      std::vector<GURL> url_chain,
+      scoped_refptr<ResourceResponse> response,
+      net::CertStatus cert_status,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints);
 
   // Factory for creation of downloads items.
   std::unique_ptr<DownloadItemFactory> item_factory_;
diff --git a/content/browser/download/resource_downloader.cc b/content/browser/download/resource_downloader.cc
index 3ae46f6..b7cc98c9 100644
--- a/content/browser/download/resource_downloader.cc
+++ b/content/browser/download/resource_downloader.cc
@@ -8,7 +8,6 @@
 
 #include "content/browser/blob_storage/blob_url_loader_factory.h"
 #include "content/browser/download/download_utils.h"
-#include "content/common/throttling_url_loader.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "storage/browser/fileapi/file_system_context.h"
@@ -98,16 +97,21 @@
 }
 
 // static
-std::unique_ptr<ResourceDownloader> ResourceDownloader::CreateWithURLLoader(
+std::unique_ptr<ResourceDownloader>
+ResourceDownloader::InterceptNavigationResponse(
     base::WeakPtr<UrlDownloadHandler::Delegate> delegate,
     std::unique_ptr<ResourceRequest> resource_request,
     const ResourceRequestInfo::WebContentsGetter& web_contents_getter,
-    std::unique_ptr<ThrottlingURLLoader> url_loader,
-    base::Optional<network::URLLoaderCompletionStatus> status) {
+    std::vector<GURL> url_chain,
+    const scoped_refptr<ResourceResponse>& response,
+    net::CertStatus cert_status,
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints) {
   auto downloader = std::make_unique<ResourceDownloader>(
       delegate, std::move(resource_request), web_contents_getter,
       DownloadItem::kInvalidId);
-  downloader->InitializeURLLoader(std::move(url_loader), std::move(status));
+  downloader->InterceptResponse(std::move(response), std::move(url_chain),
+                                cert_status,
+                                std::move(url_loader_client_endpoints));
   return downloader;
 }
 
@@ -131,6 +135,8 @@
     bool is_parallel_request) {
   callback_ = download_url_parameters->callback();
   guid_ = download_url_parameters->guid();
+
+  // Set up the URLLoaderClient.
   url_loader_client_ = std::make_unique<DownloadResponseHandler>(
       resource_request_.get(), this,
       std::make_unique<DownloadSaveInfo>(
@@ -138,70 +144,57 @@
       is_parallel_request, download_url_parameters->is_transient(),
       download_url_parameters->fetch_error_body(),
       std::vector<GURL>(1, resource_request_->url));
+  mojom::URLLoaderClientPtr url_loader_client_ptr;
+  url_loader_client_binding_ =
+      std::make_unique<mojo::Binding<mojom::URLLoaderClient>>(
+          url_loader_client_.get(), mojo::MakeRequest(&url_loader_client_ptr));
 
+  // Set up the URLLoader
+  mojom::URLLoaderRequest url_loader_request = mojo::MakeRequest(&url_loader_);
   if (download_url_parameters->url().SchemeIs(url::kBlobScheme)) {
-    // To avoid race conditions with blob URL being immediately revoked after
-    // the download starting (which ThrottlingURLLoader doesn't handle), call
-    // directly into BlobURLLoaderFactory for blob URLs.
-    mojom::URLLoaderRequest url_loader_request;
-    mojom::URLLoaderClientPtr client;
-    blob_client_binding_ =
-        std::make_unique<mojo::Binding<mojom::URLLoaderClient>>(
-            url_loader_client_.get());
-    blob_client_binding_->Bind(mojo::MakeRequest(&client));
     BlobURLLoaderFactory::CreateLoaderAndStart(
         std::move(url_loader_request), *(resource_request_.get()),
-        std::move(client), download_url_parameters->GetBlobDataHandle());
+        std::move(url_loader_client_ptr),
+        download_url_parameters->GetBlobDataHandle());
   } else {
-    url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
-        url_loader_factory_getter->GetNetworkFactory(),
-        std::vector<std::unique_ptr<URLLoaderThrottle>>(),
+    url_loader_factory_getter->GetNetworkFactory()->CreateLoaderAndStart(
+        std::move(url_loader_request),
         0,  // routing_id
         0,  // request_id
         mojom::kURLLoadOptionSendSSLInfoWithResponse |
             mojom::kURLLoadOptionSniffMimeType,
-        *(resource_request_.get()), url_loader_client_.get(),
-        download_url_parameters->GetNetworkTrafficAnnotation(),
-        base::ThreadTaskRunnerHandle::Get());
+        *(resource_request_.get()), std::move(url_loader_client_ptr),
+        net::MutableNetworkTrafficAnnotationTag(
+            download_url_parameters->GetNetworkTrafficAnnotation()));
     url_loader_->SetPriority(net::RequestPriority::IDLE,
                              0 /* intra_priority_value */);
   }
 }
 
-void ResourceDownloader::InitializeURLLoader(
-    std::unique_ptr<ThrottlingURLLoader> url_loader,
-    base::Optional<network::URLLoaderCompletionStatus> status) {
-  url_loader_status_ = std::move(status);
-  url_loader_ = std::move(url_loader);
-  url_loader_client_ = std::make_unique<URLLoaderStatusMonitor>(
-      base::Bind(&ResourceDownloader::OnURLLoaderStatusChanged,
-                 weak_ptr_factory_.GetWeakPtr()));
-  url_loader_->set_forwarding_client(url_loader_client_.get());
-}
-
-void ResourceDownloader::OnURLLoaderStatusChanged(
-    const network::URLLoaderCompletionStatus& status) {
-  DCHECK(!url_loader_status_);
-  url_loader_status_ = status;
-}
-
-void ResourceDownloader::StartNavigationInterception(
+void ResourceDownloader::InterceptResponse(
     const scoped_refptr<ResourceResponse>& response,
-    mojo::ScopedDataPipeConsumerHandle consumer_handle,
+    std::vector<GURL> url_chain,
     net::CertStatus cert_status,
-    std::vector<GURL> url_chain) {
+    mojom::URLLoaderClientEndpointsPtr endpoints) {
+  // Set the URLLoader.
+  url_loader_.Bind(std::move(endpoints->url_loader));
+
+  // Create the new URLLoaderClient that will intercept the navigation.
   url_loader_client_ = std::make_unique<DownloadResponseHandler>(
       resource_request_.get(), this, std::make_unique<DownloadSaveInfo>(),
-      false, false, false, url_chain);
-  url_loader_->set_forwarding_client(url_loader_client_.get());
+      false, false, false, std::move(url_chain));
+
+  // Simulate on the new URLLoaderClient calls that happened on the old client.
   net::SSLInfo info;
   info.cert_status = cert_status;
   url_loader_client_->OnReceiveResponse(response->head,
                                         base::Optional<net::SSLInfo>(info),
                                         mojom::DownloadedTempFilePtr());
-  url_loader_client_->OnStartLoadingResponseBody(std::move(consumer_handle));
-  if (url_loader_status_)
-    url_loader_client_->OnComplete(url_loader_status_.value());
+
+  // Bind the new client.
+  url_loader_client_binding_ =
+      base::MakeUnique<mojo::Binding<mojom::URLLoaderClient>>(
+          url_loader_client_.get(), std::move(endpoints->url_loader_client));
 }
 
 void ResourceDownloader::OnResponseStarted(
diff --git a/content/browser/download/resource_downloader.h b/content/browser/download/resource_downloader.h
index 3c4e130..bba7316 100644
--- a/content/browser/download/resource_downloader.h
+++ b/content/browser/download/resource_downloader.h
@@ -20,8 +20,6 @@
 
 namespace content {
 
-class ThrottlingURLLoader;
-
 // Class for handing the download of a url.
 class ResourceDownloader : public UrlDownloadHandler,
                            public DownloadResponseHandler::Delegate {
@@ -37,13 +35,17 @@
       uint32_t download_id,
       bool is_parallel_request);
 
-  // Create the object with a URLLoader.
-  static std::unique_ptr<ResourceDownloader> CreateWithURLLoader(
+  // Create a ResourceDownloader from a navigation that turns to be a download.
+  // No URLLoader is created, but the URLLoaderClient implementation is
+  // transferred.
+  static std::unique_ptr<ResourceDownloader> InterceptNavigationResponse(
       base::WeakPtr<UrlDownloadHandler::Delegate> delegate,
       std::unique_ptr<ResourceRequest> resource_request,
       const ResourceRequestInfo::WebContentsGetter& web_contents_getter,
-      std::unique_ptr<ThrottlingURLLoader> url_loader,
-      base::Optional<network::URLLoaderCompletionStatus> status);
+      std::vector<GURL> url_chain,
+      const scoped_refptr<ResourceResponse>& response,
+      net::CertStatus cert_status,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints);
 
   ResourceDownloader(
       base::WeakPtr<UrlDownloadHandler::Delegate> delegate,
@@ -58,13 +60,6 @@
       mojom::DownloadStreamHandlePtr stream_handle) override;
   void OnReceiveRedirect() override;
 
-  // Helper method to start the navigation interception.
-  void StartNavigationInterception(
-      const scoped_refptr<ResourceResponse>& response,
-      mojo::ScopedDataPipeConsumerHandle consumer_handle,
-      net::CertStatus cert_status,
-      std::vector<GURL> url_chain);
-
  private:
   // Helper method to start the network request.
   void Start(scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter,
@@ -72,39 +67,27 @@
              std::unique_ptr<DownloadUrlParameters> download_url_parameters,
              bool is_parallel_request);
 
-  // Initializes |url_loader_| to take ownership of the |url_loader|.
-  void InitializeURLLoader(
-      std::unique_ptr<ThrottlingURLLoader> url_loader,
-      base::Optional<network::URLLoaderCompletionStatus> status);
-
-  // Intercepts the navigation response and takes ownership of the |url_loader|.
+  // Intercepts the navigation response.
   void InterceptResponse(
-      std::unique_ptr<ThrottlingURLLoader> url_loader,
       const scoped_refptr<ResourceResponse>& response,
-      mojo::ScopedDataPipeConsumerHandle consumer_handle,
-      const SSLStatus& ssl_status,
-      int frame_tree_node_id,
       std::vector<GURL> url_chain,
-      base::Optional<network::URLLoaderCompletionStatus> status);
-
-  // Called when URLLoader status is changed.
-  void OnURLLoaderStatusChanged(
-      const network::URLLoaderCompletionStatus& status);
+      net::CertStatus cert_status,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints);
 
   base::WeakPtr<UrlDownloadHandler::Delegate> delegate_;
 
-  // URLLoader for sending out the request.
-  std::unique_ptr<ThrottlingURLLoader> url_loader_;
-
   // The ResourceRequest for this object.
   std::unique_ptr<ResourceRequest> resource_request_;
 
-  // Object for handing the server response.
+  // Object that will handle the response.
   std::unique_ptr<mojom::URLLoaderClient> url_loader_client_;
 
-  // URLLoaderClient binding for loading a blob.
-  std::unique_ptr<mojo::Binding<mojom::URLLoaderClient>> blob_client_binding_;
-  // mojo::Binding<mojom::URLLoaderClient> blob_client_binding_;
+  // URLLoaderClient binding. It sends any requests to the |url_loader_client_|.
+  std::unique_ptr<mojo::Binding<mojom::URLLoaderClient>>
+      url_loader_client_binding_;
+
+  // URLLoader for sending out the request.
+  mojom::URLLoaderPtr url_loader_;
 
   // ID of the download, or DownloadItem::kInvalidId if this is a new
   // download.
diff --git a/content/browser/file_url_loader_factory.cc b/content/browser/file_url_loader_factory.cc
index 28b6cd8..9a52132 100644
--- a/content/browser/file_url_loader_factory.cc
+++ b/content/browser/file_url_loader_factory.cc
@@ -255,8 +255,13 @@
       completion_status = net::ERR_FAILED;
     }
 
+    // All the data has been written now. Close the data pipe. The consumer will
+    // be notified that there will be no more data to read from now.
+    data_producer_.reset();
+
     client_->OnComplete(network::URLLoaderCompletionStatus(completion_status));
     client_.reset();
+
     MaybeDeleteSelf();
   }
 
@@ -517,6 +522,10 @@
   }
 
   void OnFileWritten(MojoResult result) {
+    // All the data has been written now. Close the data pipe. The consumer will
+    // be notified that there will be no more data to read from now.
+    data_producer_.reset();
+
     if (result == MOJO_RESULT_OK)
       client_->OnComplete(network::URLLoaderCompletionStatus(net::OK));
     else
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 234e2041..a5a5bfb 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -41,6 +41,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/stream_handle.h"
 #include "content/public/common/appcache_info.h"
+#include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
@@ -739,8 +740,8 @@
 
 void NavigationRequest::OnResponseStarted(
     const scoped_refptr<ResourceResponse>& response,
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
     std::unique_ptr<StreamHandle> body,
-    mojo::ScopedDataPipeConsumerHandle consumer_handle,
     const net::SSLInfo& ssl_info,
     std::unique_ptr<NavigationData> navigation_data,
     const GlobalRequestID& request_id,
@@ -817,7 +818,7 @@
   // Store the response and the StreamHandle until checks have been processed.
   response_ = response;
   body_ = std::move(body);
-  handle_ = std::move(consumer_handle);
+  url_loader_client_endpoints_ = std::move(url_loader_client_endpoints);
   ssl_info_ = ssl_info;
   is_download_ = is_download;
 
@@ -1133,14 +1134,23 @@
     // DownloadManager, and cancel the navigation.
     if (is_download_ &&
         base::FeatureList::IsEnabled(features::kNetworkService)) {
+      // TODO(arthursonzogni): Pass the real ResourceRequest. For the moment
+      // only these 4 parameters will be used, but it may evolve quickly.
+      auto resource_request = std::make_unique<ResourceRequest>();
+      resource_request->url = common_params_.url;
+      resource_request->method = common_params_.method;
+      resource_request->request_initiator = begin_params_->initiator_origin;
+      resource_request->referrer = common_params_.referrer.url;
+
       BrowserContext* browser_context =
           frame_tree_node_->navigator()->GetController()->GetBrowserContext();
       DownloadManagerImpl* download_manager = static_cast<DownloadManagerImpl*>(
           BrowserContext::GetDownloadManager(browser_context));
-      loader_->InterceptNavigation(
-          download_manager->GetNavigationInterceptionCB(
-              response_, std::move(handle_), ssl_info_.cert_status,
-              frame_tree_node_->frame_tree_node_id()));
+      download_manager->InterceptNavigation(
+          std::move(resource_request), navigation_handle_->GetRedirectChain(),
+          response_, std::move(url_loader_client_endpoints_),
+          ssl_info_.cert_status, frame_tree_node_->frame_tree_node_id());
+
       OnRequestFailed(false, net::ERR_ABORTED, base::nullopt);
       return;
     }
@@ -1207,9 +1217,9 @@
   TransferNavigationHandleOwnership(render_frame_host);
 
   render_frame_host->CommitNavigation(
-      response_.get(), std::move(body_), std::move(handle_), common_params_,
-      request_params_, is_view_source_, std::move(subresource_loader_params_),
-      devtools_navigation_token_);
+      response_.get(), std::move(url_loader_client_endpoints_),
+      std::move(body_), common_params_, request_params_, is_view_source_,
+      std::move(subresource_loader_params_), devtools_navigation_token_);
 
   frame_tree_node_->ResetNavigationRequest(true, true);
 }
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 7b1cccab..e9cba4a2 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -21,7 +21,6 @@
 #include "content/common/navigation_subresource_loader_params.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/common/previews_state.h"
-#include "mojo/public/cpp/system/data_pipe.h"
 
 namespace content {
 
@@ -222,16 +221,17 @@
   void OnRequestRedirected(
       const net::RedirectInfo& redirect_info,
       const scoped_refptr<ResourceResponse>& response) override;
-  void OnResponseStarted(const scoped_refptr<ResourceResponse>& response,
-                         std::unique_ptr<StreamHandle> body,
-                         mojo::ScopedDataPipeConsumerHandle consumer_handle,
-                         const net::SSLInfo& ssl_info,
-                         std::unique_ptr<NavigationData> navigation_data,
-                         const GlobalRequestID& request_id,
-                         bool is_download,
-                         bool is_stream,
-                         base::Optional<SubresourceLoaderParams>
-                             subresource_loader_params) override;
+  void OnResponseStarted(
+      const scoped_refptr<ResourceResponse>& response,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
+      std::unique_ptr<StreamHandle> body,
+      const net::SSLInfo& ssl_info,
+      std::unique_ptr<NavigationData> navigation_data,
+      const GlobalRequestID& request_id,
+      bool is_download,
+      bool is_stream,
+      base::Optional<SubresourceLoaderParams> subresource_loader_params)
+      override;
   void OnRequestFailed(bool has_stale_copy_in_cache,
                        int net_error,
                        const base::Optional<net::SSLInfo>& ssl_info) override;
@@ -347,12 +347,14 @@
 
   std::unique_ptr<NavigationHandleImpl> navigation_handle_;
 
-  // Holds the ResourceResponse and the StreamHandle (or
-  // DataPipeConsumerHandle) for the navigation while the WillProcessResponse
-  // checks are performed by the NavigationHandle.
+  // Holds objects received from OnResponseStarted while the WillProcessResponse
+  // checks are performed by the NavigationHandle. Once the checks have been
+  // completed, these objects will be used to continue the navigation.
+  // The URLLoaderClientEndpointsPtr is used when the Network Service or
+  // NavigationMojoResponse is enabled. Otherwise the StreamHandle is used.
   scoped_refptr<ResourceResponse> response_;
   std::unique_ptr<StreamHandle> body_;
-  mojo::ScopedDataPipeConsumerHandle handle_;
+  mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints_;
   net::SSLInfo ssl_info_;
   bool is_download_;
 
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index c6f7420..69f9b05 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -1159,10 +1159,11 @@
             *scoped_request.get());
     render_frame_host->CommitNavigation(
         nullptr,  // response
+        mojom::URLLoaderClientEndpointsPtr(),
         nullptr,  // body
-        mojo::ScopedDataPipeConsumerHandle(), scoped_request->common_params(),
-        scoped_request->request_params(), scoped_request->is_view_source(),
-        base::nullopt, scoped_request->devtools_navigation_token());
+        scoped_request->common_params(), scoped_request->request_params(),
+        scoped_request->is_view_source(), base::nullopt,
+        scoped_request->devtools_navigation_token());
     return;
   }
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 3e91634..c0794a4 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3316,9 +3316,9 @@
       CSPDisposition::CHECK /* should_check_main_world_csp */,
       false /* started_from_context_menu */, false /* has_user_gesture */);
   if (IsBrowserSideNavigationEnabled()) {
-    CommitNavigation(nullptr, nullptr, mojo::ScopedDataPipeConsumerHandle(),
-                     common_params, RequestNavigationParams(), false,
-                     base::nullopt,
+    CommitNavigation(nullptr, mojom::URLLoaderClientEndpointsPtr(),
+                     std::unique_ptr<StreamHandle>(), common_params,
+                     RequestNavigationParams(), false, base::nullopt,
                      base::UnguessableToken::Create() /* not traced */);
   } else {
     Navigate(common_params, StartNavigationParams(), RequestNavigationParams());
@@ -3465,8 +3465,8 @@
 // PlzNavigate
 void RenderFrameHostImpl::CommitNavigation(
     ResourceResponse* response,
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
     std::unique_ptr<StreamHandle> body,
-    mojo::ScopedDataPipeConsumerHandle handle,
     const CommonNavigationParams& common_params,
     const RequestNavigationParams& request_params,
     bool is_view_source,
@@ -3475,8 +3475,9 @@
   TRACE_EVENT2("navigation", "RenderFrameHostImpl::CommitNavigation",
                "frame_tree_node", frame_tree_node_->frame_tree_node_id(), "url",
                common_params.url.possibly_invalid_spec());
+
   DCHECK(
-      (response && (body.get() || handle.is_valid())) ||
+      (response && (url_loader_client_endpoints || body)) ||
       common_params.url.SchemeIs(url::kDataScheme) ||
       !IsURLHandledByNetworkStack(common_params.url) ||
       FrameMsg_Navigate_Type::IsSameDocument(common_params.navigation_type) ||
@@ -3490,9 +3491,10 @@
   // prevent us from doing inappropriate things with javascript-url.
   // See https://crbug.com/766149.
   if (common_params.url.SchemeIs(url::kJavaScriptScheme)) {
+    DCHECK(!url_loader_client_endpoints && !body);
     GetNavigationControl()->CommitNavigation(
         ResourceResponseHead(), GURL(), common_params, request_params,
-        mojo::ScopedDataPipeConsumerHandle(),
+        mojom::URLLoaderClientEndpointsPtr(),
         /*subresource_loader_factories=*/base::nullopt,
         devtools_navigation_token);
     return;
@@ -3614,7 +3616,8 @@
          subresource_loader_factories.has_value());
 
   GetNavigationControl()->CommitNavigation(
-      head, body_url, common_params, request_params, std::move(handle),
+      head, body_url, common_params, request_params,
+      std::move(url_loader_client_endpoints),
       std::move(subresource_loader_factories), devtools_navigation_token);
 
   // If a network request was made, update the Previews state.
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 0285f51..1f6a749 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -571,8 +571,8 @@
   // process, e.g. by AppCache etc.
   void CommitNavigation(
       ResourceResponse* response,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
       std::unique_ptr<StreamHandle> body,
-      mojo::ScopedDataPipeConsumerHandle handle,
       const CommonNavigationParams& common_params,
       const RequestNavigationParams& request_params,
       bool is_view_source,
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index 10d0bde..1894610 100644
--- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -21,6 +21,7 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
@@ -521,9 +522,12 @@
 // After a renderer crash, the StreamHandle must be released.
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
                        StreamHandleReleasedOnRendererCrash) {
-  // |stream_handle_| is only used with PlzNavigate.
-  if (!IsBrowserSideNavigationEnabled())
+  // Disable this test when the |stream_handle_| is not used.
+  if (!IsBrowserSideNavigationEnabled() ||
+      base::FeatureList::IsEnabled(features::kNetworkService) ||
+      IsNavigationMojoResponseEnabled()) {
     return;
+  }
 
   GURL url_1(embedded_test_server()->GetURL("a.com", "/title1.html"));
   GURL url_2(embedded_test_server()->GetURL("a.com", "/title2.html"));
diff --git a/content/browser/loader/navigation_url_loader.h b/content/browser/loader/navigation_url_loader.h
index bb784aa..e015126 100644
--- a/content/browser/loader/navigation_url_loader.h
+++ b/content/browser/loader/navigation_url_loader.h
@@ -6,15 +6,9 @@
 #define CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_H_
 
 #include <memory>
-#include <vector>
 
-#include "base/callback.h"
 #include "base/macros.h"
-#include "base/optional.h"
 #include "content/common/content_export.h"
-#include "services/network/public/cpp/url_loader_completion_status.h"
-
-class GURL;
 
 namespace content {
 
@@ -25,9 +19,7 @@
 class ResourceContext;
 class ServiceWorkerNavigationHandle;
 class StoragePartition;
-class ThrottlingURLLoader;
 struct NavigationRequestInfo;
-struct ResourceRequest;
 
 // PlzNavigate: The navigation logic's UI thread entry point into the resource
 // loading stack. It exposes an interface to control the request prior to
@@ -64,21 +56,6 @@
   // Called in response to OnResponseStarted to process the response.
   virtual void ProceedWithResponse() = 0;
 
-  // Callback to intercept the response from the URLLoader. Only used when
-  // network service is enabled. Args: the initial resource request,
-  // the URLLoader for sending the request, url chain, optional completion
-  // status if it has already been received.
-  using NavigationInterceptionCB = base::OnceCallback<void(
-      std::unique_ptr<ResourceRequest>,
-      std::unique_ptr<ThrottlingURLLoader>,
-      std::vector<GURL>,
-      base::Optional<network::URLLoaderCompletionStatus>)>;
-
-  // This method is called to intercept the url response. Caller is responsible
-  // for handling the URLLoader later on. The callback should be called on the
-  // same thread that URLLoader is constructed.
-  virtual void InterceptNavigation(NavigationInterceptionCB callback) = 0;
-
  protected:
   NavigationURLLoader() {}
 
diff --git a/content/browser/loader/navigation_url_loader_delegate.h b/content/browser/loader/navigation_url_loader_delegate.h
index cf6cc7e..49013ca 100644
--- a/content/browser/loader/navigation_url_loader_delegate.h
+++ b/content/browser/loader/navigation_url_loader_delegate.h
@@ -11,7 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "content/common/content_export.h"
-#include "mojo/public/cpp/system/data_pipe.h"
+#include "content/public/common/url_loader.mojom.h"
 
 namespace net {
 struct RedirectInfo;
@@ -38,16 +38,17 @@
   // Called when the request receives its response. No further calls will be
   // made to the delegate. The response body is returned as a stream in
   // |body_stream|. |navigation_data| is passed to the NavigationHandle.
-  // If --enable-network-service, then |consumer_handle| will be used,
-  // otherwise |body_stream|. Only one of these will ever be non-null.
-  // |subresource_loader_params| is used in the network service only
-  // for passing necessary info to create a custom subresource loader in
-  // the renderer process if the navigated context is controlled by a request
-  // interceptor like AppCache or ServiceWorker.
+  // If the Network Service or NavigationMojoResponse is enabled, then the
+  // |url_loader_client_endpoints| will be used, otherwise |body_stream|. Only
+  // one of these will ever be non-null.
+  // |subresource_loader_params| is used in the network service only for passing
+  // necessary info to create a custom subresource loader in the renderer
+  // process if the navigated context is controlled by a request interceptor
+  // like AppCache or ServiceWorker.
   virtual void OnResponseStarted(
       const scoped_refptr<ResourceResponse>& response,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
       std::unique_ptr<StreamHandle> body_stream,
-      mojo::ScopedDataPipeConsumerHandle consumer_handle,
       const net::SSLInfo& ssl_info,
       std::unique_ptr<NavigationData> navigation_data,
       const GlobalRequestID& request_id,
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 3eb0d63..b1ab4a0 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -87,9 +87,6 @@
       base::BindOnce(&NavigationURLLoaderImplCore::ProceedWithResponse, core_));
 }
 
-void NavigationURLLoaderImpl::InterceptNavigation(
-    NavigationURLLoader::NavigationInterceptionCB callback) {}
-
 void NavigationURLLoaderImpl::NotifyRequestRedirected(
     const net::RedirectInfo& redirect_info,
     const scoped_refptr<ResourceResponse>& response) {
@@ -108,8 +105,8 @@
     bool is_stream) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  delegate_->OnResponseStarted(response, std::move(body),
-                               mojo::ScopedDataPipeConsumerHandle(), ssl_info,
+  delegate_->OnResponseStarted(response, mojom::URLLoaderClientEndpointsPtr(),
+                               std::move(body), ssl_info,
                                std::move(navigation_data), request_id,
                                is_download, is_stream, base::nullopt);
 }
diff --git a/content/browser/loader/navigation_url_loader_impl.h b/content/browser/loader/navigation_url_loader_impl.h
index 49c745e..a8d0030 100644
--- a/content/browser/loader/navigation_url_loader_impl.h
+++ b/content/browser/loader/navigation_url_loader_impl.h
@@ -44,7 +44,6 @@
   // NavigationURLLoader implementation.
   void FollowRedirect() override;
   void ProceedWithResponse() override;
-  void InterceptNavigation(NavigationInterceptionCB callback) override;
 
  private:
   friend class NavigationURLLoaderImplCore;
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc
index 5c6e9b6..4cbb900 100644
--- a/content/browser/loader/navigation_url_loader_network_service.cc
+++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -115,6 +115,40 @@
         "combination of both) limits the scope of these requests."
       )");
 
+// TODO(arthursonzogni): IsDownload can't be determined only by the response's
+// headers. The response's body might contain information to guess it.
+// See MimeSniffingResourceHandler.
+bool IsDownload(const ResourceResponse& response, const GURL& url) {
+  if (response.head.headers) {
+    std::string disposition;
+    if (response.head.headers->GetNormalizedHeader("content-disposition",
+                                                   &disposition) &&
+        !disposition.empty() &&
+        net::HttpContentDisposition(disposition, std::string())
+            .is_attachment()) {
+      return true;
+    } else if (GetContentClient()->browser()->ShouldForceDownloadResource(
+                   url, response.head.mime_type)) {
+      return true;
+    } else if (response.head.mime_type == "multipart/related") {
+      // TODO(https://crbug.com/790734): retrieve the new NavigationUIData from
+      // the request and and pass it to AllowRenderingMhtmlOverHttp().
+      return !GetContentClient()->browser()->AllowRenderingMhtmlOverHttp(
+          nullptr);
+    }
+    // TODO(qinmin): Check whether this is special-case user script that needs
+    // to be downloaded.
+  }
+
+  if (blink::IsSupportedMimeType(response.head.mime_type))
+    return false;
+
+  // TODO(qinmin): Check whether there is a plugin handler.
+
+  return (!response.head.headers ||
+          response.head.headers->response_code() / 100 == 2);
+}
+
 }  // namespace
 
 // Kept around during the lifetime of the navigation request, and is
@@ -356,16 +390,6 @@
     Restart();
   }
 
-  // Navigation is intercepted, transfer the |resource_request_|, |url_loader_|
-  // and the |status_| to the new owner. The new owner is responsible for
-  // handling all the mojom::URLLoaderClient callbacks from now on.
-  void InterceptNavigation(
-      NavigationURLLoader::NavigationInterceptionCB callback) {
-    std::move(callback).Run(std::move(resource_request_),
-                            std::move(url_loader_), std::move(url_chain_),
-                            std::move(status_));
-  }
-
   base::Optional<SubresourceLoaderParams> TakeSubresourceLoaderParams() {
     return std::move(subresource_loader_params_);
   }
@@ -382,6 +406,16 @@
     // for the response. e.g. AppCache.
     if (MaybeCreateLoaderForResponse(head))
       return;
+
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints;
+    if (url_loader_) {
+      url_loader_client_endpoints = url_loader_->Unbind();
+    } else {
+      url_loader_client_endpoints = mojom::URLLoaderClientEndpoints::New(
+          response_url_loader_.PassInterface(),
+          response_loader_binding_.Unbind());
+    }
+
     scoped_refptr<ResourceResponse> response(new ResourceResponse());
     response->head = head;
 
@@ -394,7 +428,8 @@
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
         base::BindOnce(&NavigationURLLoaderNetworkService::OnReceiveResponse,
-                       owner_, response->DeepCopy(), ssl_info,
+                       owner_, std::move(url_loader_client_endpoints),
+                       response->DeepCopy(), ssl_info,
                        base::Passed(&downloaded_file)));
   }
 
@@ -432,13 +467,10 @@
   void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override {}
   void OnTransferSizeUpdated(int32_t transfer_size_diff) override {}
 
-  void OnStartLoadingResponseBody(
-      mojo::ScopedDataPipeConsumerHandle body) override {
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::BindOnce(
-            &NavigationURLLoaderNetworkService::OnStartLoadingResponseBody,
-            owner_, base::Passed(&body)));
+  void OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle) override {
+    // Not reached. At this point, the loader and client endpoints must have
+    // been unbound and forwarded to the renderer.
+    CHECK(false);
   }
 
   void OnComplete(const network::URLLoaderCompletionStatus& status) override {
@@ -670,25 +702,32 @@
 
 void NavigationURLLoaderNetworkService::ProceedWithResponse() {}
 
-void NavigationURLLoaderNetworkService::InterceptNavigation(
-    NavigationURLLoader::NavigationInterceptionCB callback) {
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&URLLoaderRequestController::InterceptNavigation,
-                     base::Unretained(request_controller_.get()),
-                     std::move(callback)));
-}
-
 void NavigationURLLoaderNetworkService::OnReceiveResponse(
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
     scoped_refptr<ResourceResponse> response,
-    const base::Optional<net::SSLInfo>& ssl_info,
+    const base::Optional<net::SSLInfo>& maybe_ssl_info,
     mojom::DownloadedTempFilePtr downloaded_file) {
+  TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this,
+                         "&NavigationURLLoaderNetworkService", this, "success",
+                         true);
+
   // TODO(scottmg): This needs to do more of what
-  // NavigationResourceHandler::OnResponseStarted() does. Or maybe in
-  // OnStartLoadingResponseBody().
-  response_ = std::move(response);
-  if (ssl_info.has_value())
-    ssl_info_ = ssl_info.value();
+  // NavigationResourceHandler::OnResponseStarted() does.
+  net::SSLInfo ssl_info;
+  if (maybe_ssl_info.has_value())
+    ssl_info = maybe_ssl_info.value();
+
+  // TODO(arthursonzogni): In NavigationMojoResponse, this is false. The info
+  // coming from the MimeSniffingResourceHandler must be used.
+  DCHECK(response);
+  bool is_download = allow_download_ && IsDownload(*response.get(), url_);
+
+  delegate_->OnResponseStarted(
+      std::move(response), std::move(url_loader_client_endpoints), nullptr,
+      std::move(ssl_info), std::unique_ptr<NavigationData>(),
+      GlobalRequestID(-1, g_next_request_id), is_download,
+      false /* is_stream */,
+      request_controller_->TakeSubresourceLoaderParams());
 }
 
 void NavigationURLLoaderNetworkService::OnReceiveRedirect(
@@ -699,24 +738,6 @@
   delegate_->OnRequestRedirected(redirect_info, std::move(response));
 }
 
-void NavigationURLLoaderNetworkService::OnStartLoadingResponseBody(
-    mojo::ScopedDataPipeConsumerHandle body) {
-  DCHECK(response_);
-
-  TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this,
-                         "&NavigationURLLoaderNetworkService", this, "success",
-                         true);
-
-  // Temporarily, we pass both a stream (null) and the data pipe to the
-  // delegate until PlzNavigate has shipped and we can be comfortable fully
-  // switching to the data pipe.
-  delegate_->OnResponseStarted(
-      response_, nullptr, std::move(body), ssl_info_,
-      std::unique_ptr<NavigationData>(), GlobalRequestID(-1, g_next_request_id),
-      IsDownload(), false /* is_stream */,
-      request_controller_->TakeSubresourceLoaderParams());
-}
-
 void NavigationURLLoaderNetworkService::OnComplete(
     const network::URLLoaderCompletionStatus& status) {
   if (status.error_code == net::OK)
@@ -730,42 +751,6 @@
                              status.ssl_info);
 }
 
-bool NavigationURLLoaderNetworkService::IsDownload() const {
-  DCHECK(response_);
-
-  if (!allow_download_)
-    return false;
-
-  if (response_->head.headers) {
-    std::string disposition;
-    if (response_->head.headers->GetNormalizedHeader("content-disposition",
-                                                     &disposition) &&
-        !disposition.empty() &&
-        net::HttpContentDisposition(disposition, std::string())
-            .is_attachment()) {
-      return true;
-    } else if (GetContentClient()->browser()->ShouldForceDownloadResource(
-                   url_, response_->head.mime_type)) {
-      return true;
-    } else if (response_->head.mime_type == "multipart/related") {
-      // TODO(https://crbug.com/790734): retrieve the new NavigationUIData from
-      // the request and and pass it to AllowRenderingMhtmlOverHttp().
-      return !GetContentClient()->browser()->AllowRenderingMhtmlOverHttp(
-          nullptr);
-    }
-    // TODO(qinmin): Check whether this is special-case user script that needs
-    // to be downloaded.
-  }
-
-  if (blink::IsSupportedMimeType(response_->head.mime_type))
-    return false;
-
-  // TODO(qinmin): Check whether there is a plugin handler.
-
-  return (!response_->head.headers ||
-          response_->head.headers->response_code() / 100 == 2);
-}
-
 void NavigationURLLoaderNetworkService::BindNonNetworkURLLoaderFactoryRequest(
     const GURL& url,
     mojom::URLLoaderFactoryRequest factory) {
diff --git a/content/browser/loader/navigation_url_loader_network_service.h b/content/browser/loader/navigation_url_loader_network_service.h
index 893e5e3..3c39e6d 100644
--- a/content/browser/loader/navigation_url_loader_network_service.h
+++ b/content/browser/loader/navigation_url_loader_network_service.h
@@ -45,30 +45,25 @@
   // NavigationURLLoader implementation:
   void FollowRedirect() override;
   void ProceedWithResponse() override;
-  void InterceptNavigation(NavigationInterceptionCB callback) override;
 
-  void OnReceiveResponse(scoped_refptr<ResourceResponse> response,
-                         const base::Optional<net::SSLInfo>& ssl_info,
-                         mojom::DownloadedTempFilePtr downloaded_file);
+  void OnReceiveResponse(
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
+      scoped_refptr<ResourceResponse> response,
+      const base::Optional<net::SSLInfo>& ssl_info,
+      mojom::DownloadedTempFilePtr downloaded_file);
   void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
                          scoped_refptr<ResourceResponse> response);
-  void OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body);
   void OnComplete(const network::URLLoaderCompletionStatus& status);
 
  private:
   class URLLoaderRequestController;
 
-  bool IsDownload() const;
-
   void BindNonNetworkURLLoaderFactoryRequest(
       const GURL& url,
       mojom::URLLoaderFactoryRequest factory);
 
   NavigationURLLoaderDelegate* delegate_;
 
-  scoped_refptr<ResourceResponse> response_;
-  net::SSLInfo ssl_info_;
-
   // Lives on the IO thread.
   std::unique_ptr<URLLoaderRequestController> request_controller_;
 
diff --git a/content/common/frame.mojom b/content/common/frame.mojom
index 27bfead..b5589cf 100644
--- a/content/common/frame.mojom
+++ b/content/common/frame.mojom
@@ -38,13 +38,20 @@
 // Implemented by the frame provider and currently must be associated with the
 // legacy IPC channel.
 interface FrameNavigationControl {
-  // Tells the renderer that a navigation is ready to commit.  The renderer
-  // should request |body_url| to get access to the stream containing the body
-  // of the response. When the Network Service is enabled, |body_url| is not
-  // used and instead the data is passed to the renderer via |body_data|. In
-  // that case |subresource_loader_factories| may also be provided by the
-  // browser as a a means for the renderer to load subresources where
-  // applicable.
+  // Tells the renderer that a navigation is ready to commit.
+  //
+  // The renderer should request |body_url| to get access to the stream
+  // containing the body of the response. When the Network Service or
+  // NavigationMojoResponse is enabled, |body_url| is not used and instead
+  // |url_loader_client_endpoints| provides a way to continue the navigation.
+  //
+  // Note: |body_url| and |url_loader_client_endpoints| will be empty iff the
+  // navigation URL wasn't handled by the network stack (i.e. JavaScript URLs,
+  // renderer debug URLs, same document navigations, about:blank, ...)
+  //
+  // When the Network Service is enabled, |subresource_loader_factories| may
+  // also be provided by the browser as a a means for the renderer to load
+  // subresources where applicable.
   //
   // For automation driver-initiated navigations over the devtools protocol,
   // |devtools_navigation_token_| is used to tag the navigation. This navigation
@@ -56,13 +63,14 @@
   // cases, and thus shouldn't be trusted.
   // TODO(crbug.com/783506): Replace devtools navigation token with the generic
   // navigation token that can be passed from renderer to the browser.
-  CommitNavigation(URLResponseHead head,
-                   url.mojom.Url body_url,
-                   CommonNavigationParams common_params,
-                   RequestNavigationParams request_params,
-                   handle<data_pipe_consumer>? body_data,
-                   URLLoaderFactoryBundle? subresource_loader_factories,
-                   mojo.common.mojom.UnguessableToken devtools_navigation_token);
+  CommitNavigation(
+      URLResponseHead head,
+      url.mojom.Url body_url,
+      CommonNavigationParams common_params,
+      RequestNavigationParams request_params,
+      URLLoaderClientEndpoints? url_loader_client_endpoints,
+      URLLoaderFactoryBundle? subresource_loader_factories,
+      mojo.common.mojom.UnguessableToken devtools_navigation_token);
 };
 
 // Implemented by the frame (e.g. renderer processes).
@@ -225,4 +233,3 @@
       blink.mojom.WebSandboxFlags sandbox_flags,
       array<blink.mojom.ParsedFeaturePolicyDeclaration> parsed_header);
 };
-
diff --git a/content/common/throttling_url_loader.cc b/content/common/throttling_url_loader.cc
index cfa211a..d96223e 100644
--- a/content/common/throttling_url_loader.cc
+++ b/content/common/throttling_url_loader.cc
@@ -202,6 +202,11 @@
   loader_cancelled_ = true;
 }
 
+mojom::URLLoaderClientEndpointsPtr ThrottlingURLLoader::Unbind() {
+  return mojom::URLLoaderClientEndpoints::New(url_loader_.PassInterface(),
+                                              client_binding_.Unbind());
+}
+
 ThrottlingURLLoader::ThrottlingURLLoader(
     std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
     mojom::URLLoaderClient* client,
diff --git a/content/common/throttling_url_loader.h b/content/common/throttling_url_loader.h
index dd1138f7..4628efd 100644
--- a/content/common/throttling_url_loader.h
+++ b/content/common/throttling_url_loader.h
@@ -71,6 +71,10 @@
   // Disconnects the client connection and releases the URLLoader.
   void DisconnectClient();
 
+  // Disconnect the forwarding URLLoaderClient and the URLLoader. Returns the
+  // datapipe endpoints.
+  mojom::URLLoaderClientEndpointsPtr Unbind();
+
   // Sets the forwarding client to receive all subsequent notifications.
   void set_forwarding_client(mojom::URLLoaderClient* client) {
     forwarding_client_ = client;
diff --git a/content/public/common/url_loader.mojom b/content/public/common/url_loader.mojom
index 1db483e..26cf7e4f 100644
--- a/content/public/common/url_loader.mojom
+++ b/content/public/common/url_loader.mojom
@@ -112,3 +112,9 @@
   OnComplete(URLLoaderCompletionStatus status);
 };
 
+// Convenient struct that groups the two communication endpoints most
+// implementations of URLLoaderClient use to communicate with their URLLoader.
+struct URLLoaderClientEndpoints {
+  URLLoader url_loader;
+  URLLoaderClient& url_loader_client;
+};
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 80085bd..d7c3a2ae 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -2061,6 +2061,10 @@
   handle_->CallResumeForTesting();
 }
 
+NavigationHandle* TestNavigationManager::GetNavigationHandle() {
+  return handle_;
+}
+
 bool TestNavigationManager::WaitForResponse() {
   desired_state_ = NavigationState::RESPONSE;
   return WaitForDesiredState();
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 92579ad..1180be5 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -861,7 +861,7 @@
 // resumed automatically if a Wait method is called for a future event.
 // Note: This class is one time use only! After it successfully tracks a
 // navigation it will ignore all subsequent navigations. Explicitly create
-// mutliple instances of this class if you want to pause multiple navigations.
+// multiple instances of this class if you want to pause multiple navigations.
 class TestNavigationManager : public WebContentsObserver {
  public:
   // Monitors any frame in WebContents.
@@ -886,6 +886,10 @@
   // * Called after |WaitForResponse|, it causes the response to be committed.
   void ResumeNavigation();
 
+  // Returns the NavigationHandle associated with the navigation. It is non-null
+  // only in between DidStartNavigation(...) and DidFinishNavigation(...).
+  NavigationHandle* GetNavigationHandle();
+
  protected:
   // Derived classes can override if they want to filter out navigations. This
   // is called from DidStartNavigation.
diff --git a/content/renderer/loader/resource_dispatcher.cc b/content/renderer/loader/resource_dispatcher.cc
index 7d6b8a0..22a89f4 100644
--- a/content/renderer/loader/resource_dispatcher.cc
+++ b/content/renderer/loader/resource_dispatcher.cc
@@ -659,7 +659,7 @@
     blink::WebURLRequest::LoadingIPCType ipc_type,
     mojom::URLLoaderFactory* url_loader_factory,
     std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
-    mojo::ScopedDataPipeConsumerHandle consumer_handle) {
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints) {
   CheckSchemeForReferrerPolicy(*request);
 
   // Compute a unique request_id for this renderer process.
@@ -677,14 +677,14 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       loading_task_runner ? loading_task_runner : thread_task_runner_;
 
-  if (consumer_handle.is_valid()) {
+  if (url_loader_client_endpoints) {
     pending_requests_[request_id]->url_loader_client =
         std::make_unique<URLLoaderClientImpl>(request_id, this, task_runner);
 
     task_runner->PostTask(
         FROM_HERE, base::BindOnce(&ResourceDispatcher::ContinueForNavigation,
                                   weak_factory_.GetWeakPtr(), request_id,
-                                  base::Passed(std::move(consumer_handle))));
+                                  std::move(url_loader_client_endpoints)));
 
     return request_id;
   }
@@ -787,7 +787,8 @@
 
 void ResourceDispatcher::ContinueForNavigation(
     int request_id,
-    mojo::ScopedDataPipeConsumerHandle consumer_handle) {
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints) {
+  DCHECK(url_loader_client_endpoints);
   PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
   if (!request_info)
     return;
@@ -800,28 +801,13 @@
   // WebURLLoaderImpl::Context::OnReceivedResponse.
   client_ptr->OnReceiveResponse(ResourceResponseHead(), base::nullopt,
                                 mojom::DownloadedTempFilePtr());
+  // TODO(clamy): Move the replaying of redirects from WebURLLoaderImpl here.
 
   // Abort if the request is cancelled.
   if (!GetPendingRequestInfo(request_id))
     return;
 
-  // Start streaming now.
-  client_ptr->OnStartLoadingResponseBody(std::move(consumer_handle));
-
-  // Abort if the request is cancelled.
-  if (!GetPendingRequestInfo(request_id))
-    return;
-
-  // Call OnComplete now too, as it won't get called on the client.
-  // TODO(kinuko): Fill this properly.
-  network::URLLoaderCompletionStatus status;
-  status.error_code = net::OK;
-  status.exists_in_cache = false;
-  status.completion_time = base::TimeTicks::Now();
-  status.encoded_data_length = -1;
-  status.encoded_body_length = -1;
-  status.decoded_body_length = -1;
-  client_ptr->OnComplete(status);
+  client_ptr->Bind(std::move(url_loader_client_endpoints));
 }
 
 // static
diff --git a/content/renderer/loader/resource_dispatcher.h b/content/renderer/loader/resource_dispatcher.h
index 0313783..c0f96e9 100644
--- a/content/renderer/loader/resource_dispatcher.h
+++ b/content/renderer/loader/resource_dispatcher.h
@@ -122,7 +122,7 @@
       blink::WebURLRequest::LoadingIPCType ipc_type,
       mojom::URLLoaderFactory* url_loader_factory,
       std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
-      mojo::ScopedDataPipeConsumerHandle consumer_handle);
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints);
 
   // Removes a request from the |pending_requests_| list, returning true if the
   // request was found and removed.
@@ -267,7 +267,7 @@
 
   void ContinueForNavigation(
       int request_id,
-      mojo::ScopedDataPipeConsumerHandle consumer_handle);
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints);
 
   // Returns true if the message passed in is a resource related message.
   static bool IsResourceDispatcherMessage(const IPC::Message& message);
diff --git a/content/renderer/loader/resource_dispatcher_unittest.cc b/content/renderer/loader/resource_dispatcher_unittest.cc
index 104d4686..c85306e9 100644
--- a/content/renderer/loader/resource_dispatcher_unittest.cc
+++ b/content/renderer/loader/resource_dispatcher_unittest.cc
@@ -237,7 +237,7 @@
         TRAFFIC_ANNOTATION_FOR_TESTS, false, std::move(peer),
         blink::WebURLRequest::LoadingIPCType::kChromeIPC, nullptr,
         std::vector<std::unique_ptr<URLLoaderThrottle>>(),
-        mojo::ScopedDataPipeConsumerHandle());
+        mojom::URLLoaderClientEndpointsPtr());
     peer_context->request_id = request_id;
     return request_id;
   }
diff --git a/content/renderer/loader/sync_load_context.cc b/content/renderer/loader/sync_load_context.cc
index b367bda..898e358d 100644
--- a/content/renderer/loader/sync_load_context.cc
+++ b/content/renderer/loader/sync_load_context.cc
@@ -34,7 +34,7 @@
       true /* is_sync */, base::WrapUnique(context),
       blink::WebURLRequest::LoadingIPCType::kMojo,
       context->url_loader_factory_.get(), std::move(throttles),
-      mojo::ScopedDataPipeConsumerHandle());
+      mojom::URLLoaderClientEndpointsPtr());
 }
 
 SyncLoadContext::SyncLoadContext(
diff --git a/content/renderer/loader/url_loader_client_impl.cc b/content/renderer/loader/url_loader_client_impl.cc
index 885b798..a38faf48 100644
--- a/content/renderer/loader/url_loader_client_impl.cc
+++ b/content/renderer/loader/url_loader_client_impl.cc
@@ -20,6 +20,7 @@
     : request_id_(request_id),
       resource_dispatcher_(resource_dispatcher),
       task_runner_(std::move(task_runner)),
+      url_loader_client_binding_(this),
       weak_factory_(this) {}
 
 URLLoaderClientImpl::~URLLoaderClientImpl() {
@@ -103,6 +104,13 @@
   }
 }
 
+void URLLoaderClientImpl::Bind(mojom::URLLoaderClientEndpointsPtr endpoints) {
+  url_loader_.Bind(std::move(endpoints->url_loader));
+  url_loader_client_binding_.Bind(std::move(endpoints->url_loader_client));
+  url_loader_client_binding_.set_connection_error_handler(base::BindOnce(
+      &URLLoaderClientImpl::OnConnectionClosed, weak_factory_.GetWeakPtr()));
+}
+
 void URLLoaderClientImpl::OnReceiveResponse(
     const ResourceResponseHead& response_head,
     const base::Optional<net::SSLInfo>& ssl_info,
@@ -175,6 +183,7 @@
 
 void URLLoaderClientImpl::OnComplete(
     const network::URLLoaderCompletionStatus& status) {
+  has_received_complete_ = true;
   if (!body_consumer_) {
     if (NeedsStoringMessage()) {
       StoreAndDispatch(ResourceMsg_RequestComplete(request_id_, status));
@@ -216,4 +225,12 @@
   std::move(ack_callback).Run();
 }
 
+void URLLoaderClientImpl::OnConnectionClosed() {
+  // If the connection aborts before the load completes, mark it as aborted.
+  if (!has_received_complete_) {
+    OnComplete(network::URLLoaderCompletionStatus(net::ERR_ABORTED));
+    return;
+  }
+}
+
 }  // namespace content
diff --git a/content/renderer/loader/url_loader_client_impl.h b/content/renderer/loader/url_loader_client_impl.h
index e474354..7497bbe 100644
--- a/content/renderer/loader/url_loader_client_impl.h
+++ b/content/renderer/loader/url_loader_client_impl.h
@@ -13,6 +13,7 @@
 #include "content/common/content_export.h"
 #include "content/public/common/url_loader.mojom.h"
 #include "ipc/ipc_message.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 
 namespace base {
@@ -49,6 +50,14 @@
   // Disaptches the messages received after SetDefersLoading is called.
   void FlushDeferredMessages();
 
+  // Binds this instance to the given URLLoaderClient endpoints so that it can
+  // start getting the mojo calls from the given loader. This is used only for
+  // the main resource loading when NavigationMojoResponse and/or NetworkService
+  // is enabled. Otherwise (in regular subresource loading cases) |this| is not
+  // bound to a client request, but used via ThrottlingURLLoader to get client
+  // upcalls from the loader.
+  void Bind(mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints);
+
   // mojom::URLLoaderClient implementation
   void OnReceiveResponse(const ResourceResponseHead& response_head,
                          const base::Optional<net::SSLInfo>& ssl_info,
@@ -68,16 +77,23 @@
  private:
   bool NeedsStoringMessage() const;
   void StoreAndDispatch(const IPC::Message& message);
+  void OnConnectionClosed();
 
   scoped_refptr<URLResponseBodyConsumer> body_consumer_;
   mojom::DownloadedTempFilePtr downloaded_file_;
   std::vector<IPC::Message> deferred_messages_;
   const int request_id_;
   bool has_received_response_ = false;
+  bool has_received_complete_ = false;
   bool is_deferred_ = false;
   int32_t accumulated_transfer_size_diff_during_deferred_ = 0;
   ResourceDispatcher* const resource_dispatcher_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // Used in NavigationMojoResponse and NetworkService.
+  mojom::URLLoaderPtr url_loader_;
+  mojo::Binding<mojom::URLLoaderClient> url_loader_client_binding_;
+
   base::WeakPtrFactory<URLLoaderClientImpl> weak_factory_;
 };
 
diff --git a/content/renderer/loader/url_loader_client_impl_unittest.cc b/content/renderer/loader/url_loader_client_impl_unittest.cc
index 572a0cf..9bb5741 100644
--- a/content/renderer/loader/url_loader_client_impl_unittest.cc
+++ b/content/renderer/loader/url_loader_client_impl_unittest.cc
@@ -37,7 +37,7 @@
         blink::WebURLRequest::LoadingIPCType::kMojo,
         url_loader_factory_proxy_.get(),
         std::vector<std::unique_ptr<URLLoaderThrottle>>(),
-        mojo::ScopedDataPipeConsumerHandle());
+        mojom::URLLoaderClientEndpointsPtr());
     request_peer_context_.request_id = request_id_;
 
     base::RunLoop().RunUntilIdle();
diff --git a/content/renderer/loader/url_response_body_consumer_unittest.cc b/content/renderer/loader/url_response_body_consumer_unittest.cc
index 3b26571..302789c 100644
--- a/content/renderer/loader/url_response_body_consumer_unittest.cc
+++ b/content/renderer/loader/url_response_body_consumer_unittest.cc
@@ -139,7 +139,7 @@
         std::make_unique<TestRequestPeer>(context, message_loop_.task_runner()),
         blink::WebURLRequest::LoadingIPCType::kChromeIPC, nullptr,
         std::vector<std::unique_ptr<URLLoaderThrottle>>(),
-        mojo::ScopedDataPipeConsumerHandle());
+        mojom::URLLoaderClientEndpointsPtr());
   }
 
   void Run(TestRequestPeer::Context* context) {
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index 4d6f3aa8..e0978bc 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -655,16 +655,18 @@
   resource_request->previews_state =
       static_cast<PreviewsState>(request.GetPreviewsState());
 
-  // PlzNavigate: during navigation, the renderer should request a stream which
-  // contains the body of the response. The network request has already been
-  // made by the browser.
-  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  // PlzNavigate: The network request has already been made by the browser.
+  // The renderer should request a stream which contains the body of the
+  // response. If the Network Service or NavigationMojoResponse is enabled, the
+  // URLLoaderClientEndpoints is used instead to get the body.
+  mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints;
   if (stream_override_) {
     CHECK(IsBrowserSideNavigationEnabled());
     DCHECK(!sync_load_response);
     DCHECK_NE(WebURLRequest::kFrameTypeNone, request.GetFrameType());
-    if (stream_override_->consumer_handle.is_valid()) {
-      consumer_handle = std::move(stream_override_->consumer_handle);
+    if (stream_override_->url_loader_client_endpoints) {
+      url_loader_client_endpoints =
+          std::move(stream_override_->url_loader_client_endpoints);
     } else {
       resource_request->resource_body_stream_url = stream_override_->stream_url;
     }
@@ -697,7 +699,8 @@
       false /* is_sync */,
       std::make_unique<WebURLLoaderImpl::RequestPeerImpl>(this),
       request.GetLoadingIPCType(), url_loader_factory_,
-      extra_data->TakeURLLoaderThrottles(), std::move(consumer_handle));
+      extra_data->TakeURLLoaderThrottles(),
+      std::move(url_loader_client_endpoints));
 
   if (defers_loading_ != NOT_DEFERRING)
     resource_dispatcher_->SetDefersLoading(request_id_, true);
diff --git a/content/renderer/loader/web_url_loader_impl.h b/content/renderer/loader/web_url_loader_impl.h
index 2d19fe09..3c049d6 100644
--- a/content/renderer/loader/web_url_loader_impl.h
+++ b/content/renderer/loader/web_url_loader_impl.h
@@ -13,6 +13,7 @@
 #include "content/common/content_export.h"
 #include "content/common/frame.mojom.h"
 #include "content/public/common/resource_response.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "content/public/common/url_loader_factory.mojom.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/url_request/redirect_info.h"
@@ -37,7 +38,7 @@
   ~StreamOverrideParameters();
 
   GURL stream_url;
-  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints;
   ResourceResponseHead response;
   std::vector<GURL> redirects;
   std::vector<ResourceResponseInfo> redirect_responses;
diff --git a/content/renderer/loader/web_url_loader_impl_unittest.cc b/content/renderer/loader/web_url_loader_impl_unittest.cc
index 5b096cf..70d6f3e0 100644
--- a/content/renderer/loader/web_url_loader_impl_unittest.cc
+++ b/content/renderer/loader/web_url_loader_impl_unittest.cc
@@ -91,7 +91,7 @@
       blink::WebURLRequest::LoadingIPCType ipc_type,
       mojom::URLLoaderFactory* url_loader_factory,
       std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
-      mojo::ScopedDataPipeConsumerHandle consumer_handle) override {
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints) override {
     EXPECT_FALSE(peer_);
     if (sync_load_response_.encoded_body_length != -1)
       EXPECT_TRUE(is_sync);
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index b7456cf4..9e7102b7 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -3079,7 +3079,7 @@
     const GURL& body_url,
     const CommonNavigationParams& common_params,
     const RequestNavigationParams& request_params,
-    mojo::ScopedDataPipeConsumerHandle body_data,
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
     base::Optional<URLLoaderFactoryBundle> subresource_loader_factories,
     const base::UnguessableToken& devtools_navigation_token) {
   CHECK(IsBrowserSideNavigationEnabled());
@@ -3098,7 +3098,8 @@
   std::unique_ptr<StreamOverrideParameters> stream_override(
       new StreamOverrideParameters());
   stream_override->stream_url = body_url;
-  stream_override->consumer_handle = std::move(body_data);
+  stream_override->url_loader_client_endpoints =
+      std::move(url_loader_client_endpoints);
   stream_override->response = head;
   stream_override->redirects = request_params.redirects;
   stream_override->redirect_responses = request_params.redirect_response;
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 81d9b66..6b2a1a7 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -521,7 +521,7 @@
       const GURL& body_url,
       const CommonNavigationParams& common_params,
       const RequestNavigationParams& request_params,
-      mojo::ScopedDataPipeConsumerHandle body_data,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
       base::Optional<URLLoaderFactoryBundle> subresource_loaders,
       const base::UnguessableToken& devtools_navigation_token) override;
 
diff --git a/content/test/test_navigation_url_loader.cc b/content/test/test_navigation_url_loader.cc
index e3c48ea..853cd74 100644
--- a/content/test/test_navigation_url_loader.cc
+++ b/content/test/test_navigation_url_loader.cc
@@ -40,9 +40,6 @@
   response_proceeded_ = true;
 }
 
-void TestNavigationURLLoader::InterceptNavigation(
-    NavigationURLLoader::NavigationInterceptionCB callback) {}
-
 void TestNavigationURLLoader::SimulateServerRedirect(const GURL& redirect_url) {
   net::RedirectInfo redirect_info;
   redirect_info.status_code = 302;
@@ -76,10 +73,10 @@
           ->GetProcess()
           ->GetID();
   GlobalRequestID global_id(child_id, ++request_id);
-  delegate_->OnResponseStarted(response, std::move(body),
-                               mojo::ScopedDataPipeConsumerHandle(),
-                               net::SSLInfo(), std::move(navigation_data),
-                               global_id, false, false, base::nullopt);
+  delegate_->OnResponseStarted(response, mojom::URLLoaderClientEndpointsPtr(),
+                               std::move(body), net::SSLInfo(),
+                               std::move(navigation_data), global_id, false,
+                               false, base::nullopt);
 }
 
 TestNavigationURLLoader::~TestNavigationURLLoader() {}
diff --git a/content/test/test_navigation_url_loader.h b/content/test/test_navigation_url_loader.h
index 741e1054..8edcdfc 100644
--- a/content/test/test_navigation_url_loader.h
+++ b/content/test/test_navigation_url_loader.h
@@ -36,7 +36,6 @@
   // NavigationURLLoader implementation.
   void FollowRedirect() override;
   void ProceedWithResponse() override;
-  void InterceptNavigation(NavigationInterceptionCB callback) override;
 
   NavigationRequestInfo* request_info() const { return request_info_.get(); }
 
diff --git a/content/test/test_navigation_url_loader_delegate.cc b/content/test/test_navigation_url_loader_delegate.cc
index cbe46b9..19311a19 100644
--- a/content/test/test_navigation_url_loader_delegate.cc
+++ b/content/test/test_navigation_url_loader_delegate.cc
@@ -44,8 +44,8 @@
 }
 
 void TestNavigationURLLoaderDelegate::ReleaseBody() {
+  url_loader_client_endpoints_ = nullptr;
   body_.reset();
-  handle_.reset();
 }
 
 void TestNavigationURLLoaderDelegate::OnRequestRedirected(
@@ -59,8 +59,8 @@
 
 void TestNavigationURLLoaderDelegate::OnResponseStarted(
     const scoped_refptr<ResourceResponse>& response,
+    mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
     std::unique_ptr<StreamHandle> body,
-    mojo::ScopedDataPipeConsumerHandle consumer_handle,
     const net::SSLInfo& ssl_info,
     std::unique_ptr<NavigationData> navigation_data,
     const GlobalRequestID& request_id,
@@ -68,8 +68,8 @@
     bool is_stream,
     base::Optional<SubresourceLoaderParams> subresource_loader_params) {
   response_ = response;
+  url_loader_client_endpoints_ = std::move(url_loader_client_endpoints);
   body_ = std::move(body);
-  handle_ = std::move(consumer_handle);
   ssl_info_ = ssl_info;
   is_download_ = is_download;
   if (response_started_)
diff --git a/content/test/test_navigation_url_loader_delegate.h b/content/test/test_navigation_url_loader_delegate.h
index ae2c26d..e0a6941 100644
--- a/content/test/test_navigation_url_loader_delegate.h
+++ b/content/test/test_navigation_url_loader_delegate.h
@@ -12,7 +12,6 @@
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "content/browser/loader/navigation_url_loader_delegate.h"
-#include "mojo/public/cpp/system/data_pipe.h"
 #include "net/url_request/redirect_info.h"
 
 namespace base {
@@ -58,16 +57,17 @@
   void OnRequestRedirected(
       const net::RedirectInfo& redirect_info,
       const scoped_refptr<ResourceResponse>& response) override;
-  void OnResponseStarted(const scoped_refptr<ResourceResponse>& response,
-                         std::unique_ptr<StreamHandle> body,
-                         mojo::ScopedDataPipeConsumerHandle consumer_handle,
-                         const net::SSLInfo& ssl_info,
-                         std::unique_ptr<NavigationData> navigation_data,
-                         const GlobalRequestID& request_id,
-                         bool is_download,
-                         bool is_stream,
-                         base::Optional<SubresourceLoaderParams>
-                             subresource_loader_params) override;
+  void OnResponseStarted(
+      const scoped_refptr<ResourceResponse>& response,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
+      std::unique_ptr<StreamHandle> body,
+      const net::SSLInfo& ssl_info,
+      std::unique_ptr<NavigationData> navigation_data,
+      const GlobalRequestID& request_id,
+      bool is_download,
+      bool is_stream,
+      base::Optional<SubresourceLoaderParams> subresource_loader_params)
+      override;
   void OnRequestFailed(bool in_cache,
                        int net_error,
                        const base::Optional<net::SSLInfo>& ssl_info) override;
@@ -76,9 +76,9 @@
  private:
   net::RedirectInfo redirect_info_;
   scoped_refptr<ResourceResponse> redirect_response_;
+  mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints_;
   scoped_refptr<ResourceResponse> response_;
   std::unique_ptr<StreamHandle> body_;
-  mojo::ScopedDataPipeConsumerHandle handle_;
   int net_error_;
   net::SSLInfo ssl_info_;
   int on_request_handled_counter_;
diff --git a/content/test/test_render_frame.cc b/content/test/test_render_frame.cc
index fe25617f..762c7ec 100644
--- a/content/test/test_render_frame.cc
+++ b/content/test/test_render_frame.cc
@@ -138,7 +138,7 @@
   // PlzNavigate
   if (IsBrowserSideNavigationEnabled()) {
     CommitNavigation(ResourceResponseHead(), GURL(), common_params,
-                     request_params, mojo::ScopedDataPipeConsumerHandle(),
+                     request_params, mojom::URLLoaderClientEndpointsPtr(),
                      URLLoaderFactoryBundle(),
                      base::UnguessableToken::Create());
   } else {
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index d34a542..4b4101d2 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -63,12 +63,13 @@
       const GURL& body_url,
       const CommonNavigationParams& common_params,
       const RequestNavigationParams& request_params,
-      mojo::ScopedDataPipeConsumerHandle body_data,
+      mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
       base::Optional<URLLoaderFactoryBundle> subresource_loader_factories,
       const base::UnguessableToken& devtools_navigation_token) override {
     frame_host_->GetProcess()->set_did_frame_commit_navigation(true);
     frame_host_->GetInternalNavigationControl()->CommitNavigation(
-        head, body_url, common_params, request_params, std::move(body_data),
+        head, body_url, common_params, request_params,
+        std::move(url_loader_client_endpoints),
         std::move(subresource_loader_factories), devtools_navigation_token);
   }
 
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
index d49ee817..f10a27c 100644
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -132,3 +132,7 @@
 -CrossSiteDocumentBlockingTest.BlockDocuments
 -CrossSiteDocumentBlockingIsolatedOriginTest.BlockDocumentsFromIsolatedOrigin
 -CrossSiteDocumentBlockingTest.RangeRequest
+
+# https://crbug.com/790933
+-ServiceWorkerNavigationPreloadTest.RespondWithNavigationPreload
+-ServiceWorkerNavigationPreloadTest.RedirectAndRespondWithNavigationPreload
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index ebcc5853..072bb861 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -25,14 +25,10 @@
 Bug(none) external/wpt/preload/fetch-destination.https.html [ Crash Failure Timeout ]
 Bug(none) external/wpt/resource-timing/test_resource_timing.html [ Failure Timeout ]
 Bug(none) external/wpt/service-workers/service-worker/claim-shared-worker-fetch.https.html [ Failure ]
-Bug(none) external/wpt/service-workers/service-worker/client-navigate.https.html [ Failure ]
 Bug(none) external/wpt/service-workers/service-worker/clients-get-client-types.https.html [ Failure ]
 Bug(none) external/wpt/service-workers/service-worker/clients-get.https.html [ Failure ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Crash ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-redirect.https.html [ Pass Crash ]
 Bug(none) external/wpt/service-workers/service-worker/fetch-event-respond-with-response-body-with-invalid-chunk.https.html [ Failure ]
 Bug(none) external/wpt/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event.https.html [ Timeout ]
 Bug(none) external/wpt/service-workers/service-worker/fetch-request-resources.https.html [ Pass Failure Crash ]
 Bug(none) external/wpt/service-workers/service-worker/fetch-request-xhr-sync.https.html [ Timeout ]
 Bug(none) external/wpt/service-workers/service-worker/fetch-request-xhr.https.html [ Timeout ]
@@ -134,7 +130,6 @@
 Bug(none) http/tests/loading/307-after-303-after-post.html [ Failure ]
 Bug(none) http/tests/doc-write-intervention/doc-write-sync-third-party-script-reload.html [ Crash ]
 Bug(none) http/tests/loading/bad-scheme-subframe.html [ Failure ]
-Bug(none) http/tests/local/serviceworker/fetch-request-body-file.html [ Crash Timeout ]
 Bug(none) http/tests/media/video-buffered.html [ Timeout ]
 Bug(none) http/tests/misc/embed-image-load-outlives-gc-without-crashing.html [ Failure Pass ]
 Bug(none) http/tests/misc/image-input-type-outlives-gc-without-crashing.html [ Failure Pass ]
@@ -168,7 +163,6 @@
 Bug(none) http/tests/serviceworker/chromium.update-served-from-cache.html [ Failure ]
 Bug(none) http/tests/serviceworker/chromium/register-error-messages.html [ Failure ]
 Bug(none) http/tests/serviceworker/chromium/request-body-blob-crash.html [ Crash Failure ]
-Bug(none) http/tests/serviceworker/chromium/stop-worker-during-respond-with.html [ Failure ]
 Bug(none) http/tests/serviceworker/chromium/stop-worker-with-pending-fetch.html [ Timeout ]
 Bug(none) http/tests/websocket/cookie-document-to-ws.html [ Failure ]
 Bug(none) http/tests/websocket/cookie-http-to-ws.pl [ Failure ]
@@ -238,3 +232,47 @@
 crbug.com/777879 external/wpt/FileAPI/file/send-file-form-windows-1252.tentative.html [ Crash ]
 crbug.com/777879 external/wpt/FileAPI/file/send-file-form-x-user-defined.tentative.html [ Crash ]
 crbug.com/777879 external/wpt/FileAPI/file/send-file-form.html [ Crash ]
+
+# NetworkService: ServiceWorker URLLoader are destroyed before the end of a navigation.
+# See https://crbug.com/790933
+Bug(790933) external/wpt/fetch/api/abort/serviceworker-intercepted.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/claim-affect-other-registration.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/client-navigate.https.html [ Pass Failure Crash Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-canvas-tainting-cache.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-canvas-tainting.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-cors-xhr.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-csp.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-event-redirect.https.html [ Pass Crash Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-event.https.html [ Pass Failure Crash Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-frame-resource.https.html [ Pass Failure Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Pass Failure Crash Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-request-css-cross-origin-mime-check.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-request-no-freshness-headers.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-request-redirect.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/fetch-response-taint.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/getregistrations.https.html [ Pass Failure Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/navigation-preload/chunked-encoding.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/navigation-preload/redirect.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/navigation-preload/request-headers.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/navigation-redirect-body.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/referer.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/referrer-policy-header.https.html [ Pass Timeout ]
+Bug(790933) external/wpt/service-workers/service-worker/sandboxed-iframe-fetch-event.https.html [ Pass Timeout ]
+Bug(790933) http/tests/devtools/service-workers/service-workers-navigation-preload.js [ Pass Timeout ]
+Bug(790933) http/tests/fetch/referrer/origin-when-cross-origin-serviceworker-from-document.html [ Pass Timeout Crash ]
+Bug(790933) http/tests/fetch/referrer/serviceworker-from-origin-only-document.html [ Pass Timeout ]
+Bug(790933) http/tests/local/serviceworker/fetch-request-body-file.html [ Pass Crash Failure Timeout ]
+Bug(790933) http/tests/serviceworker/chromium.fetch-canvas-tainting.html [ Pass Timeout ]
+Bug(790933) http/tests/serviceworker/chromium.fetch-cors-xhr.html [ Pass Timeout ]
+Bug(790933) http/tests/serviceworker/chromium.fetch-csp.html [ Pass Timeout ]
+Bug(790933) http/tests/serviceworker/chromium.redirected-response.html [ Pass Timeout ]
+Bug(790933) http/tests/serviceworker/chromium.respond-to-same-origin-request-with-cross-origin-response.html [ Pass Timeout ]
+Bug(790933) http/tests/serviceworker/chromium.respond-to-same-origin-request-with-redirected-cross-origin-response.html [ Pass Timeout ]
+Bug(790933) http/tests/serviceworker/chromium/css-import-crash.html [ Pass Timeout ]
+Bug(790933) http/tests/serviceworker/chromium/fetch-request-with-gc.html [ Pass Timeout ]
+Bug(790933) http/tests/serviceworker/chromium/fetch-script-onerror.html [ Pass Timeout ]
+Bug(790933) http/tests/serviceworker/chromium/stop-worker-during-respond-with.html [ Pass Failure Timeout ]
+Bug(790933) http/tests/serviceworker/navigation-preload/chromium/navigation-preload-gc-before-response.html [ Pass Timeout ]
+Bug(790933) http/tests/serviceworker/navigation-preload/chromium/use-counter.html [ Pass Timeout ]