#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"
#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"
#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"
}
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(
}
}
+// 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
// 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();
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
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*
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);
}
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
// 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()) {
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,
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,
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.
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));
if (delegate_) {
delegate_->RequestBeginning(request,
resource_context,
- filter_->appcache_service(),
- request_data.resource_type,
+ appcache_service,
+ resource_type,
&throttles);
}
download, // allow_download
false, // has_user_gesture
false, // enable_load_timing
+ false, // enable_upload_progress
blink::WebReferrerPolicyDefault,
blink::WebPageVisibilityStateVisible,
context,
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(
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,
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;
}
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