Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / browser / loader / resource_dispatcher_host_impl.cc
index ac3fdf6..26ecf1d 100644 (file)
 #include "content/browser/download/save_file_resource_handler.h"
 #include "content/browser/fileapi/chrome_blob_storage_context.h"
 #include "content/browser/frame_host/navigation_request_info.h"
+#include "content/browser/frame_host/navigator.h"
 #include "content/browser/loader/async_resource_handler.h"
 #include "content/browser/loader/buffered_resource_handler.h"
 #include "content/browser/loader/cross_site_resource_handler.h"
 #include "content/browser/loader/detachable_resource_handler.h"
+#include "content/browser/loader/navigation_resource_handler.h"
+#include "content/browser/loader/navigation_url_loader_impl_core.h"
 #include "content/browser/loader/power_save_block_resource_throttle.h"
 #include "content/browser/loader/redirect_to_file_resource_handler.h"
 #include "content/browser/loader/resource_message_filter.h"
@@ -53,6 +56,7 @@
 #include "content/browser/streams/stream_registry.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/appcache_interfaces.h"
+#include "content/common/navigation_params.h"
 #include "content/common/resource_messages.h"
 #include "content/common/ssl_status_serialization.h"
 #include "content/common/view_messages.h"
@@ -65,6 +69,7 @@
 #include "content/public/browser/resource_request_details.h"
 #include "content/public/browser/resource_throttle.h"
 #include "content/public/browser/stream_handle.h"
+#include "content/public/browser/stream_info.h"
 #include "content/public/browser/user_metrics.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/process_type.h"
@@ -357,10 +362,10 @@ bool IsValidatedSCT(
 }
 
 storage::BlobStorageContext* GetBlobStorageContext(
-    ResourceMessageFilter* filter) {
-  if (!filter->blob_storage_context())
+    ChromeBlobStorageContext* blob_storage_context) {
+  if (!blob_storage_context)
     return NULL;
-  return filter->blob_storage_context()->context();
+  return blob_storage_context->context();
 }
 
 void AttachRequestBodyBlobDataHandles(
@@ -383,6 +388,24 @@ void AttachRequestBodyBlobDataHandles(
   }
 }
 
+// PlzNavigate
+// This method is called in the UI thread to send the timestamp of a resource
+// request to the respective Navigator (for an UMA histogram).
+void LogResourceRequestTimeOnUI(
+    base::TimeTicks timestamp,
+    int render_process_id,
+    int render_frame_id,
+    const GURL& url) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  RenderFrameHostImpl* host =
+      RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
+  if (host != NULL) {
+    DCHECK(host->frame_tree_node()->IsMainFrame());
+    host->frame_tree_node()->navigator()->LogResourceRequestTime(
+        timestamp, url);
+  }
+}
+
 }  // namespace
 
 // static
@@ -475,7 +498,7 @@ void ResourceDispatcherHostImpl::CancelRequestsForContext(
   // the requests to cancel first, and then we start cancelling. We assert at
   // the end that there are no more to cancel since the context is about to go
   // away.
-  typedef std::vector<linked_ptr<ResourceLoader> > LoaderList;
+  typedef std::vector<linked_ptr<ResourceLoader>> LoaderList;
   LoaderList loaders_to_cancel;
 
   for (LoaderMap::iterator i = pending_loaders_.begin();
@@ -521,15 +544,16 @@ void ResourceDispatcherHostImpl::CancelRequestsForContext(
   for (LoaderList::iterator i = loaders_to_cancel.begin();
        i != loaders_to_cancel.end(); ++i) {
     // There is no strict requirement that this be the case, but currently
-    // downloads, streams, detachable requests, and transferred requests are the
-    // only requests that aren't cancelled when the associated processes go
-    // away. It may be OK for this invariant to change in the future, but if
-    // this assertion fires without the invariant changing, then it's indicative
-    // of a leak.
+    // downloads, streams, detachable requests, transferred requests, and
+    // browser-owned requests are the only requests that aren't cancelled when
+    // the associated processes go away. It may be OK for this invariant to
+    // change in the future, but if this assertion fires without the invariant
+    // changing, then it's indicative of a leak.
     DCHECK((*i)->GetRequestInfo()->IsDownload() ||
            (*i)->GetRequestInfo()->is_stream() ||
            ((*i)->GetRequestInfo()->detachable_handler() &&
             (*i)->GetRequestInfo()->detachable_handler()->is_detached()) ||
+           (*i)->GetRequestInfo()->GetProcessType() == PROCESS_TYPE_BROWSER ||
            (*i)->is_transferring());
   }
 #endif
@@ -717,23 +741,19 @@ ResourceDispatcherHostImpl::MaybeInterceptAsStream(net::URLRequest* request,
                                 origin));
 
   info->set_is_stream(true);
-  delegate_->OnStreamCreated(
-      request,
-      handler->stream()->CreateHandle(
-          request->url(),
-          mime_type,
-          response->head.headers));
-  return handler.PassAs<ResourceHandler>();
-}
-
-void ResourceDispatcherHostImpl::ClearSSLClientAuthHandlerForRequest(
-    net::URLRequest* request) {
-  ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request);
-  if (info) {
-    ResourceLoader* loader = GetLoader(info->GetGlobalRequestID());
-    if (loader)
-      loader->ClearSSLClientAuthHandler();
-  }
+  scoped_ptr<StreamInfo> stream_info(new StreamInfo);
+  stream_info->handle = handler->stream()->CreateHandle();
+  stream_info->original_url = request->url();
+  stream_info->mime_type = mime_type;
+  // Make a copy of the response headers so it is safe to pass across threads;
+  // the old handler (AsyncResourceHandler) may modify it in parallel via the
+  // ResourceDispatcherHostDelegate.
+  if (response->head.headers.get()) {
+    stream_info->response_headers =
+        new net::HttpResponseHeaders(response->head.headers->raw_headers());
+  }
+  delegate_->OnStreamCreated(request, stream_info.Pass());
+  return handler.Pass();
 }
 
 ResourceDispatcherHostLoginDelegate*
@@ -947,6 +967,19 @@ void ResourceDispatcherHostImpl::OnRequestResource(
     int routing_id,
     int request_id,
     const ResourceHostMsg_Request& request_data) {
+  // When logging time-to-network only care about main frame and non-transfer
+  // navigations.
+  if (request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME &&
+      request_data.transferred_request_request_id == -1) {
+    BrowserThread::PostTask(
+        BrowserThread::UI,
+        FROM_HERE,
+        base::Bind(&LogResourceRequestTimeOnUI,
+                   TimeTicks::Now(),
+                   filter_->child_id(),
+                   request_data.render_frame_id,
+                   request_data.url));
+  }
   BeginRequest(request_id, request_data, NULL, routing_id);
 }
 
@@ -981,10 +1014,13 @@ void ResourceDispatcherHostImpl::UpdateRequestForTransfer(
   GlobalRequestID new_request_id(child_id, request_id);
 
   // Clear out data that depends on |info| before updating it.
+  // We always need to move the memory stats to the new process.  In contrast,
+  // stats.num_requests is only tracked for some requests (those that require
+  // file descriptors for their shared memory buffer).
   IncrementOutstandingRequestsMemory(-1, *info);
-  OustandingRequestsStats empty_stats = { 0, 0 };
-  OustandingRequestsStats old_stats = GetOutstandingRequestsStats(*info);
-  UpdateOutstandingRequestsStats(*info, empty_stats);
+  bool should_update_count = info->counted_as_in_flight_request();
+  if (should_update_count)
+    IncrementOutstandingRequestsCount(-1, info);
   pending_loaders_.erase(old_request_id);
 
   // ResourceHandlers should always get state related to the request from the
@@ -997,8 +1033,9 @@ void ResourceDispatcherHostImpl::UpdateRequestForTransfer(
   // Update maps that used the old IDs, if necessary.  Some transfers in tests
   // do not actually use a different ID, so not all maps need to be updated.
   pending_loaders_[new_request_id] = loader;
-  UpdateOutstandingRequestsStats(*info, old_stats);
   IncrementOutstandingRequestsMemory(1, *info);
+  if (should_update_count)
+    IncrementOutstandingRequestsCount(1, info);
   if (old_routing_id != new_routing_id) {
     if (blocked_loaders_map_.find(old_routing_id) !=
             blocked_loaders_map_.end()) {
@@ -1140,15 +1177,20 @@ void ResourceDispatcherHostImpl::BeginRequest(
   new_request->SetLoadFlags(load_flags);
 
   storage::BlobStorageContext* blob_context =
-      GetBlobStorageContext(filter_);
+      GetBlobStorageContext(filter_->blob_storage_context());
   // Resolve elements from request_body and prepare upload data.
   if (request_data.request_body.get()) {
-    // Attaches the BlobDataHandles to request_body not to free the blobs and
-    // any attached shareable files until upload completion. These data will be
-    // used in UploadDataStream and ServiceWorkerURLRequestJob.
-    AttachRequestBodyBlobDataHandles(
-        request_data.request_body.get(),
-        blob_context);
+    // |blob_context| could be null when the request is from the plugins because
+    // ResourceMessageFilters created in PluginProcessHost don't have the blob
+    // context.
+    if (blob_context) {
+      // Attaches the BlobDataHandles to request_body not to free the blobs and
+      // any attached shareable files until upload completion. These data will
+      // be used in UploadDataStream and ServiceWorkerURLRequestJob.
+      AttachRequestBodyBlobDataHandles(
+          request_data.request_body.get(),
+          blob_context);
+    }
     new_request->set_upload(UploadDataStreamBuilder::Build(
         request_data.request_body.get(),
         blob_context,
@@ -1180,6 +1222,7 @@ void ResourceDispatcherHostImpl::BeginRequest(
           allow_download,
           request_data.has_user_gesture,
           request_data.enable_load_timing,
+          request_data.enable_upload_progress,
           request_data.referrer_policy,
           request_data.visiblity_state,
           resource_context,
@@ -1197,15 +1240,22 @@ void ResourceDispatcherHostImpl::BeginRequest(
             new_request->url()));
   }
 
-  // Initialize the service worker handler for the request.
+  // Initialize the service worker handler for the request. We don't use
+  // ServiceWorker for synchronous loads to avoid renderer deadlocks. We
+  // don't use ServiceWorker for favicons to avoid cache tainting.
+  bool is_favicon_load = request_data.resource_type == RESOURCE_TYPE_FAVICON;
   ServiceWorkerRequestHandler::InitializeHandler(
       new_request.get(),
       filter_->service_worker_context(),
       blob_context,
       child_id,
       request_data.service_worker_provider_id,
-      request_data.skip_service_worker,
+      request_data.skip_service_worker || is_sync_load || is_favicon_load,
+      request_data.fetch_request_mode,
+      request_data.fetch_credentials_mode,
       request_data.resource_type,
+      request_data.fetch_request_context_type,
+      request_data.fetch_frame_type,
       request_data.request_body);
 
   // Have the appcache associate its extra info with the request.
@@ -1260,21 +1310,41 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler(
         handler.Pass()));
   }
 
-  // Install a CrossSiteResourceHandler for all main frame requests.  This will
-  // let us check whether a transfer is required and pause for the unload
-  // handler either if so or if a cross-process navigation is already under way.
-  bool is_swappable_navigation =
-      request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME;
-  // If we are using --site-per-process, install it for subframes as well.
-  if (!is_swappable_navigation &&
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kSitePerProcess)) {
-    is_swappable_navigation =
-        request_data.resource_type == RESOURCE_TYPE_SUB_FRAME;
+  // PlzNavigate: If using --enable-browser-side-navigation, the
+  // CrossSiteResourceHandler is not needed. This codepath is not used for the
+  // actual navigation request, but only the subsequent blob URL load. This does
+  // not require request transfers.
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableBrowserSideNavigation)) {
+    // Install a CrossSiteResourceHandler for all main frame requests. This will
+    // check whether a transfer is required and, if so, pause for the UI thread
+    // to drive the transfer.
+    bool is_swappable_navigation =
+        request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME;
+    // If we are using --site-per-process, install it for subframes as well.
+    if (!is_swappable_navigation &&
+        base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kSitePerProcess)) {
+      is_swappable_navigation =
+          request_data.resource_type == RESOURCE_TYPE_SUB_FRAME;
+    }
+    if (is_swappable_navigation && process_type == PROCESS_TYPE_RENDERER)
+      handler.reset(new CrossSiteResourceHandler(handler.Pass(), request));
   }
-  if (is_swappable_navigation && process_type == PROCESS_TYPE_RENDERER)
-    handler.reset(new CrossSiteResourceHandler(handler.Pass(), request));
 
+  return AddStandardHandlers(request, request_data.resource_type,
+                             resource_context, filter_->appcache_service(),
+                             child_id, route_id, handler.Pass());
+}
+
+scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::AddStandardHandlers(
+    net::URLRequest* request,
+    ResourceType resource_type,
+    ResourceContext* resource_context,
+    AppCacheService* appcache_service,
+    int child_id,
+    int route_id,
+    scoped_ptr<ResourceHandler> handler) {
   // Insert a buffered event handler before the actual one.
   handler.reset(
       new BufferedResourceHandler(handler.Pass(), this, request));
@@ -1283,8 +1353,8 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler(
   if (delegate_) {
     delegate_->RequestBeginning(request,
                                 resource_context,
-                                filter_->appcache_service(),
-                                request_data.resource_type,
+                                appcache_service,
+                                resource_type,
                                 &throttles);
   }
 
@@ -1402,6 +1472,7 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo(
       download,  // allow_download
       false,     // has_user_gesture
       false,     // enable_load_timing
+      false,     // enable_upload_progress
       blink::WebReferrerPolicyDefault,
       blink::WebPageVisibilityStateVisible,
       context,
@@ -1409,11 +1480,11 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo(
       true);     // is_async
 }
 
-void ResourceDispatcherHostImpl::OnRenderViewHostCreated(
-    int child_id,
-    int route_id,
-    bool is_visible) {
-  scheduler_->OnClientCreated(child_id, route_id, is_visible);
+void ResourceDispatcherHostImpl::OnRenderViewHostCreated(int child_id,
+                                                         int route_id,
+                                                         bool is_visible,
+                                                         bool is_audible) {
+  scheduler_->OnClientCreated(child_id, route_id, is_visible, is_audible);
 }
 
 void ResourceDispatcherHostImpl::OnRenderViewHostDeleted(
@@ -1441,6 +1512,13 @@ void ResourceDispatcherHostImpl::OnRenderViewHostWasShown(
   scheduler_->OnVisibilityChanged(child_id, route_id, true);
 }
 
+void ResourceDispatcherHostImpl::OnAudioRenderHostStreamStateChanged(
+    int child_id,
+    int route_id,
+    bool is_playing) {
+  scheduler_->OnAudibilityChanged(child_id, route_id, is_playing);
+}
+
 // This function is only used for saving feature.
 void ResourceDispatcherHostImpl::BeginSaveFile(
     const GURL& url,
@@ -1687,23 +1765,28 @@ ResourceDispatcherHostImpl::IncrementOutstandingRequestsMemory(
 ResourceDispatcherHostImpl::OustandingRequestsStats
 ResourceDispatcherHostImpl::IncrementOutstandingRequestsCount(
     int count,
-    const ResourceRequestInfoImpl& info) {
+    ResourceRequestInfoImpl* info) {
   DCHECK_EQ(1, abs(count));
   num_in_flight_requests_ += count;
 
-  OustandingRequestsStats stats = GetOutstandingRequestsStats(info);
+  // Keep track of whether this request is counting toward the number of
+  // in-flight requests for this process, in case we need to transfer it to
+  // another process. This should be a toggle.
+  DCHECK_NE(info->counted_as_in_flight_request(), count > 0);
+  info->set_counted_as_in_flight_request(count > 0);
+
+  OustandingRequestsStats stats = GetOutstandingRequestsStats(*info);
   stats.num_requests += count;
   DCHECK_GE(stats.num_requests, 0);
-  UpdateOutstandingRequestsStats(info, stats);
+  UpdateOutstandingRequestsStats(*info, stats);
 
   return stats;
 }
 
 bool ResourceDispatcherHostImpl::HasSufficientResourcesForRequest(
-    const net::URLRequest* request_) {
-  const ResourceRequestInfoImpl* info =
-      ResourceRequestInfoImpl::ForRequest(request_);
-  OustandingRequestsStats stats = IncrementOutstandingRequestsCount(1, *info);
+    net::URLRequest* request) {
+  ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request);
+  OustandingRequestsStats stats = IncrementOutstandingRequestsCount(1, info);
 
   if (stats.num_requests > max_num_in_flight_requests_per_process_)
     return false;
@@ -1714,24 +1797,175 @@ bool ResourceDispatcherHostImpl::HasSufficientResourcesForRequest(
 }
 
 void ResourceDispatcherHostImpl::FinishedWithResourcesForRequest(
-    const net::URLRequest* request_) {
-  const ResourceRequestInfoImpl* info =
-      ResourceRequestInfoImpl::ForRequest(request_);
-  IncrementOutstandingRequestsCount(-1, *info);
+    net::URLRequest* request) {
+  ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request);
+  IncrementOutstandingRequestsCount(-1, info);
 }
 
-void ResourceDispatcherHostImpl::StartNavigationRequest(
+void ResourceDispatcherHostImpl::BeginNavigationRequest(
+    ResourceContext* resource_context,
+    int64 frame_tree_node_id,
+    const CommonNavigationParams& params,
     const NavigationRequestInfo& info,
     scoped_refptr<ResourceRequestBody> request_body,
-    int64 navigation_request_id,
-    int64 frame_node_id) {
-  NOTIMPLEMENTED();
-}
+    NavigationURLLoaderImplCore* loader) {
+  // PlzNavigate: BeginNavigationRequest currently should only be used for the
+  // browser-side navigations project.
+  CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableBrowserSideNavigation));
+
+  ResourceType resource_type = info.is_main_frame ?
+      RESOURCE_TYPE_MAIN_FRAME : RESOURCE_TYPE_SUB_FRAME;
+
+  if (is_shutdown_ ||
+      // TODO(davidben): Check ShouldServiceRequest here. This is important; it
+      // needs to be checked relative to the child that /requested/ the
+      // navigation. It's where file upload checks, etc., come in.
+      (delegate_ && !delegate_->ShouldBeginRequest(
+          info.navigation_params.method,
+          params.url,
+          resource_type,
+          resource_context))) {
+    loader->NotifyRequestFailed(net::ERR_ABORTED);
+    return;
+  }
+
+  // Save the URL on the stack to help catch URLRequests which outlive their
+  // URLRequestContexts. See https://crbug.com/90971
+  char url_buf[128];
+  base::strlcpy(url_buf, params.url.spec().c_str(), arraysize(url_buf));
+  base::debug::Alias(url_buf);
+  CHECK(ContainsKey(active_resource_contexts_, resource_context));
+
+  const net::URLRequestContext* request_context =
+      resource_context->GetRequestContext();
+
+  int load_flags = info.navigation_params.load_flags;
+  load_flags |= net::LOAD_VERIFY_EV_CERT;
+  if (info.is_main_frame) {
+    load_flags |= net::LOAD_MAIN_FRAME;
+  } else {
+    load_flags |= net::LOAD_SUB_FRAME;
+  }
+  // Add a flag to selectively bypass the data reduction proxy if the resource
+  // type is not an image.
+  load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
+
+  // TODO(davidben): BuildLoadFlagsForRequest includes logic for
+  // CanSendCookiesForOrigin and CanReadRawCookies. Is this needed here?
+
+  // Sync loads should have maximum priority and should be the only
+  // requests that have the ignore limits flag set.
+  DCHECK(!(load_flags & net::LOAD_IGNORE_LIMITS));
+
+  // TODO(davidben): OverrideCookieStoreForRenderProcess handling for
+  // prerender. There may not be a renderer process yet, so we need to use the
+  // ResourceContext or something.
+  scoped_ptr<net::URLRequest> new_request;
+  new_request = request_context->CreateRequest(params.url, net::HIGHEST,
+                                               nullptr, nullptr);
+
+  new_request->set_method(info.navigation_params.method);
+  new_request->set_first_party_for_cookies(
+      info.first_party_for_cookies);
+  if (info.is_main_frame) {
+    new_request->set_first_party_url_policy(
+        net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT);
+  }
+
+  SetReferrerForRequest(new_request.get(), params.referrer);
+
+  net::HttpRequestHeaders headers;
+  headers.AddHeadersFromString(info.navigation_params.headers);
+  new_request->SetExtraRequestHeaders(headers);
+
+  new_request->SetLoadFlags(load_flags);
+
+  // Resolve elements from request_body and prepare upload data.
+  if (info.navigation_params.request_body.get()) {
+    storage::BlobStorageContext* blob_context = GetBlobStorageContext(
+        GetChromeBlobStorageContextForResourceContext(resource_context));
+    AttachRequestBodyBlobDataHandles(
+        info.navigation_params.request_body.get(),
+        blob_context);
+    // TODO(davidben): The FileSystemContext is null here. In the case where
+    // another renderer requested this navigation, this should be the same
+    // FileSystemContext passed into ShouldServiceRequest.
+    new_request->set_upload(UploadDataStreamBuilder::Build(
+        info.navigation_params.request_body.get(),
+        blob_context,
+        nullptr,  // file_system_context
+        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)
+            .get()));
+  }
+
+  request_id_--;
+
+  // Make extra info and read footer (contains request ID).
+  //
+  // TODO(davidben): Associate the request with the FrameTreeNode and/or tab so
+  // that IO thread -> UI thread hops will work.
+  ResourceRequestInfoImpl* extra_info =
+      new ResourceRequestInfoImpl(
+          PROCESS_TYPE_BROWSER,
+          -1,  // child_id
+          -1,  // route_id
+          -1,  // request_data.origin_pid,
+          request_id_,
+          -1,  // request_data.render_frame_id,
+          info.is_main_frame,
+          info.parent_is_main_frame,
+          -1,  // request_data.parent_render_frame_id,
+          resource_type,
+          params.transition,
+          // should_replace_current_entry. This was only maintained at layer for
+          // request transfers and isn't needed for browser-side navigations.
+          false,
+          false,  // is download
+          false,  // is stream
+          params.allow_download,
+          info.navigation_params.has_user_gesture,
+          true,   // enable_load_timing
+          false,  // enable_upload_progress
+          params.referrer.policy,
+          // TODO(davidben): This is only used for prerenders. Replace
+          // is_showing with something for that. Or maybe it just comes from the
+          // same mechanism as the cookie one.
+          blink::WebPageVisibilityStateVisible,
+          resource_context,
+          base::WeakPtr<ResourceMessageFilter>(),  // filter
+          true);
+  // Request takes ownership.
+  extra_info->AssociateWithRequest(new_request.get());
+
+  if (new_request->url().SchemeIs(url::kBlobScheme)) {
+    // Hang on to a reference to ensure the blob is not released prior
+    // to the job being started.
+    ChromeBlobStorageContext* blob_context =
+        GetChromeBlobStorageContextForResourceContext(resource_context);
+    storage::BlobProtocolHandler::SetRequestedBlobDataHandle(
+        new_request.get(),
+        blob_context->context()->GetBlobDataFromPublicURL(new_request->url()));
+  }
+
+  // TODO(davidben): Attach ServiceWorkerRequestHandler.
+
+  // TODO(davidben): Attach AppCacheInterceptor.
+
+  scoped_ptr<ResourceHandler> handler(new NavigationResourceHandler(
+      new_request.get(), loader));
+
+  // TODO(davidben): Pass in the appropriate appcache_service. Also fix the
+  // dependency on child_id/route_id. Those are used by the ResourceScheduler;
+  // currently it's a no-op.
+  handler = AddStandardHandlers(new_request.get(), resource_type,
+                                resource_context,
+                                nullptr,  // appcache_service
+                                -1,  // child_id
+                                -1,  // route_id
+                                handler.Pass());
 
-void ResourceDispatcherHostImpl::CancelNavigationRequest(
-    int64 navigation_request_id,
-    int64 frame_node_id) {
-  NOTIMPLEMENTED();
+  BeginRequestInternal(new_request.Pass(), handler.Pass());
 }
 
 // static