#include "base/strings/string_util.h"
#include "content/child/request_extra_data.h"
#include "content/child/request_info.h"
+#include "content/child/resource_loader_bridge.h"
#include "content/child/site_isolation_policy.h"
+#include "content/child/sync_load_response.h"
+#include "content/child/threaded_data_provider.h"
#include "content/common/inter_process_time_ticks_converter.h"
#include "content/common/resource_messages.h"
-#include "content/common/service_worker/service_worker_types.h"
+#include "content/public/child/request_peer.h"
#include "content/public/child/resource_dispatcher_delegate.h"
#include "content/public/common/resource_response.h"
+#include "content/public/common/resource_type.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/base/request_priority.h"
#include "net/http/http_response_headers.h"
-#include "webkit/common/resource_type.h"
-
-using webkit_glue::ResourceLoaderBridge;
-using webkit_glue::ResourceResponseInfo;
namespace content {
*time = converter.ToLocalTimeTicks(remote_time).ToTimeTicks();
}
-
-} // namespace
-
-static void CrashOnMapFailure() {
+void CrashOnMapFailure() {
#if defined(OS_WIN)
DWORD last_err = GetLastError();
base::debug::Alias(&last_err);
}
// Each resource request is assigned an ID scoped to this process.
-static int MakeRequestID() {
+int MakeRequestID() {
// NOTE: The resource_dispatcher_host also needs probably unique
// request_ids, so they count down from -2 (-1 is a special we're
// screwed value), while the renderer process counts up.
return next_request_id++;
}
+} // namespace
+
// ResourceLoaderBridge implementation ----------------------------------------
class IPCResourceLoaderBridge : public ResourceLoaderBridge {
public:
IPCResourceLoaderBridge(ResourceDispatcher* dispatcher,
const RequestInfo& request_info);
- virtual ~IPCResourceLoaderBridge();
+ ~IPCResourceLoaderBridge() override;
// ResourceLoaderBridge
- virtual void SetRequestBody(ResourceRequestBody* request_body) OVERRIDE;
- virtual bool Start(Peer* peer) OVERRIDE;
- virtual void Cancel() OVERRIDE;
- virtual void SetDefersLoading(bool value) OVERRIDE;
- virtual void DidChangePriority(net::RequestPriority new_priority) OVERRIDE;
- virtual void SyncLoad(SyncLoadResponse* response) OVERRIDE;
+ void SetRequestBody(ResourceRequestBody* request_body) override;
+ bool Start(RequestPeer* peer) override;
+ void Cancel() override;
+ void SetDefersLoading(bool value) override;
+ void DidChangePriority(net::RequestPriority new_priority,
+ int intra_priority_value) override;
+ bool AttachThreadedDataReceiver(
+ blink::WebThreadedDataReceiver* threaded_data_receiver) override;
+ void SyncLoad(SyncLoadResponse* response) override;
private:
- ResourceLoaderBridge::Peer* peer_;
-
// The resource dispatcher for this loader. The bridge doesn't own it, but
// it's guaranteed to outlive the bridge.
ResourceDispatcher* dispatcher_;
IPCResourceLoaderBridge::IPCResourceLoaderBridge(
ResourceDispatcher* dispatcher,
const RequestInfo& request_info)
- : peer_(NULL),
- dispatcher_(dispatcher),
+ : dispatcher_(dispatcher),
request_id_(-1),
routing_id_(request_info.routing_id),
is_synchronous_request_(false) {
request_.appcache_host_id = request_info.appcache_host_id;
request_.download_to_file = request_info.download_to_file;
request_.has_user_gesture = request_info.has_user_gesture;
- if (request_info.extra_data) {
- RequestExtraData* extra_data =
- static_cast<RequestExtraData*>(request_info.extra_data);
- request_.visiblity_state = extra_data->visibility_state();
- request_.render_frame_id = extra_data->render_frame_id();
- request_.is_main_frame = extra_data->is_main_frame();
- request_.parent_is_main_frame = extra_data->parent_is_main_frame();
- request_.parent_render_frame_id = extra_data->parent_render_frame_id();
- request_.allow_download = extra_data->allow_download();
- request_.transition_type = extra_data->transition_type();
- request_.should_replace_current_entry =
- extra_data->should_replace_current_entry();
- request_.transferred_request_child_id =
- extra_data->transferred_request_child_id();
- request_.transferred_request_request_id =
- extra_data->transferred_request_request_id();
- request_.service_worker_provider_id =
- extra_data->service_worker_provider_id();
- frame_origin_ = extra_data->frame_origin();
- } else {
- request_.visiblity_state = blink::WebPageVisibilityStateVisible;
- request_.render_frame_id = MSG_ROUTING_NONE;
- request_.is_main_frame = false;
- request_.parent_is_main_frame = false;
- request_.parent_render_frame_id = -1;
- request_.allow_download = true;
- request_.transition_type = PAGE_TRANSITION_LINK;
- request_.should_replace_current_entry = false;
- request_.transferred_request_child_id = -1;
- request_.transferred_request_request_id = -1;
- request_.service_worker_provider_id = kInvalidServiceWorkerProviderId;
- }
+ request_.skip_service_worker = request_info.skip_service_worker;
+ request_.fetch_request_mode = request_info.fetch_request_mode;
+ request_.fetch_credentials_mode = request_info.fetch_credentials_mode;
+ request_.fetch_request_context_type = request_info.fetch_request_context_type;
+ request_.fetch_frame_type = request_info.fetch_frame_type;
+ request_.enable_load_timing = request_info.enable_load_timing;
+ request_.enable_upload_progress = request_info.enable_upload_progress;
+
+ const RequestExtraData kEmptyData;
+ const RequestExtraData* extra_data;
+ if (request_info.extra_data)
+ extra_data = static_cast<RequestExtraData*>(request_info.extra_data);
+ else
+ extra_data = &kEmptyData;
+ request_.visiblity_state = extra_data->visibility_state();
+ request_.render_frame_id = extra_data->render_frame_id();
+ request_.is_main_frame = extra_data->is_main_frame();
+ request_.parent_is_main_frame = extra_data->parent_is_main_frame();
+ request_.parent_render_frame_id = extra_data->parent_render_frame_id();
+ request_.allow_download = extra_data->allow_download();
+ request_.transition_type = extra_data->transition_type();
+ request_.should_replace_current_entry =
+ extra_data->should_replace_current_entry();
+ request_.transferred_request_child_id =
+ extra_data->transferred_request_child_id();
+ request_.transferred_request_request_id =
+ extra_data->transferred_request_request_id();
+ request_.service_worker_provider_id =
+ extra_data->service_worker_provider_id();
+ frame_origin_ = extra_data->frame_origin();
}
IPCResourceLoaderBridge::~IPCResourceLoaderBridge() {
// this operation may fail, as the dispatcher will have preemptively
// removed us when the renderer sends the ReceivedAllData message.
dispatcher_->RemovePendingRequest(request_id_);
-
- if (request_.download_to_file) {
- dispatcher_->message_sender()->Send(
- new ResourceHostMsg_ReleaseDownloadedFile(request_id_));
- }
}
}
}
// Writes a footer on the message and sends it
-bool IPCResourceLoaderBridge::Start(Peer* peer) {
+bool IPCResourceLoaderBridge::Start(RequestPeer* peer) {
if (request_id_ != -1) {
NOTREACHED() << "Starting a request twice";
return false;
}
- peer_ = peer;
-
// generate the request ID, and append it to the message
- request_id_ = dispatcher_->AddPendingRequest(peer_,
+ request_id_ = dispatcher_->AddPendingRequest(peer,
request_.resource_type,
request_.origin_pid,
frame_origin_,
- request_.url);
+ request_.url,
+ request_.download_to_file);
return dispatcher_->message_sender()->Send(
new ResourceHostMsg_RequestResource(routing_id_, request_id_, request_));
return;
}
- if (!is_synchronous_request_)
+ if (!is_synchronous_request_) {
+ // This also removes the the request from the dispatcher.
dispatcher_->CancelPendingRequest(request_id_);
-
- // We can't remove the request ID from the resource dispatcher because more
- // data might be pending. Sending the cancel message may cause more data
- // to be flushed, and will then cause a complete message to be sent.
+ }
}
void IPCResourceLoaderBridge::SetDefersLoading(bool value) {
}
void IPCResourceLoaderBridge::DidChangePriority(
- net::RequestPriority new_priority) {
+ net::RequestPriority new_priority,
+ int intra_priority_value) {
if (request_id_ < 0) {
NOTREACHED() << "Trying to change priority of an unstarted request";
return;
}
- dispatcher_->DidChangePriority(routing_id_, request_id_, new_priority);
+ dispatcher_->DidChangePriority(
+ request_id_, new_priority, intra_priority_value);
+}
+
+bool IPCResourceLoaderBridge::AttachThreadedDataReceiver(
+ blink::WebThreadedDataReceiver* threaded_data_receiver) {
+ if (request_id_ < 0) {
+ NOTREACHED() << "Trying to attach threaded receiver on unstarted request";
+ return false;
+ }
+
+ return dispatcher_->AttachThreadedDataReceiver(request_id_,
+ threaded_data_receiver);
}
void IPCResourceLoaderBridge::SyncLoad(SyncLoadResponse* response) {
ResourceDispatcher::ResourceDispatcher(IPC::Sender* sender)
: message_sender_(sender),
- weak_factory_(this),
delegate_(NULL),
- io_timestamp_(base::TimeTicks()) {
+ io_timestamp_(base::TimeTicks()),
+ weak_factory_(this) {
}
ResourceDispatcher::~ResourceDispatcher() {
request_info->peer->OnUploadProgress(position, size);
// Acknowledge receipt
- message_sender()->Send(new ResourceHostMsg_UploadProgress_ACK(request_id));
+ message_sender_->Send(new ResourceHostMsg_UploadProgress_ACK(request_id));
}
void ResourceDispatcher::OnReceivedResponse(
request_info->response_start = ConsumeIOTimestamp();
if (delegate_) {
- ResourceLoaderBridge::Peer* new_peer =
+ RequestPeer* new_peer =
delegate_->OnReceivedResponse(
request_info->peer, response_head.mime_type, request_info->url);
if (new_peer)
request_info->peer = new_peer;
}
+ // Updates the response_url if the response was fetched by a ServiceWorker,
+ // and it was not generated inside the ServiceWorker.
+ if (response_head.was_fetched_via_service_worker &&
+ !response_head.original_url_via_service_worker.is_empty()) {
+ request_info->response_url = response_head.original_url_via_service_worker;
+ }
+
ResourceResponseInfo renderer_response_info;
ToResourceResponseInfo(*request_info, response_head, &renderer_response_info);
request_info->site_isolation_metadata =
TRACE_EVENT0("loader", "ResourceDispatcher::OnReceivedData");
DCHECK_GT(data_length, 0);
PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
+ bool send_ack = true;
if (request_info && data_length > 0) {
CHECK(base::SharedMemory::IsHandleValid(request_info->buffer->handle()));
CHECK_GE(request_info->buffer_size, data_offset + data_length);
base::TimeTicks time_start = base::TimeTicks::Now();
- const char* data_ptr = static_cast<char*>(request_info->buffer->memory());
- CHECK(data_ptr);
- CHECK(data_ptr + data_offset);
+ const char* data_start = static_cast<char*>(request_info->buffer->memory());
+ CHECK(data_start);
+ CHECK(data_start + data_offset);
+ const char* data_ptr = data_start + data_offset;
// Check whether this response data is compliant with our cross-site
// document blocking policy. We only do this for the first packet.
if (request_info->site_isolation_metadata.get()) {
request_info->blocked_response =
SiteIsolationPolicy::ShouldBlockResponse(
- request_info->site_isolation_metadata, data_ptr + data_offset,
- data_length, &alternative_data);
+ request_info->site_isolation_metadata, data_ptr, data_length,
+ &alternative_data);
request_info->site_isolation_metadata.reset();
- }
- // When the response is not blocked.
- if (!request_info->blocked_response) {
- request_info->peer->OnReceivedData(
- data_ptr + data_offset, data_length, encoded_data_length);
- } else if (alternative_data.size() > 0) {
- // When the response is blocked, and when we have any alternative data to
+ // When the response is blocked we may have any alternative data to
// send to the renderer. When |alternative_data| is zero-sized, we do not
// call peer's callback.
- request_info->peer->OnReceivedData(alternative_data.data(),
- alternative_data.size(),
- alternative_data.size());
+ if (request_info->blocked_response && !alternative_data.empty()) {
+ data_ptr = alternative_data.data();
+ data_length = alternative_data.size();
+ encoded_data_length = alternative_data.size();
+ }
+ }
+
+ if (!request_info->blocked_response || !alternative_data.empty()) {
+ if (request_info->threaded_data_provider) {
+ request_info->threaded_data_provider->OnReceivedDataOnForegroundThread(
+ data_ptr, data_length, encoded_data_length);
+ // A threaded data provider will take care of its own ACKing, as the
+ // data may be processed later on another thread.
+ send_ack = false;
+ } else {
+ request_info->peer->OnReceivedData(
+ data_ptr, data_length, encoded_data_length);
+ }
}
UMA_HISTOGRAM_TIMES("ResourceDispatcher.OnReceivedDataTime",
}
// Acknowledge the reception of this data.
- message_sender()->Send(new ResourceHostMsg_DataReceived_ACK(request_id));
+ if (send_ack)
+ message_sender_->Send(new ResourceHostMsg_DataReceived_ACK(request_id));
}
void ResourceDispatcher::OnDownloadedData(int request_id,
int data_len,
int encoded_data_length) {
// Acknowledge the reception of this message.
- message_sender()->Send(
- new ResourceHostMsg_DataDownloaded_ACK(request_id));
+ message_sender_->Send(new ResourceHostMsg_DataDownloaded_ACK(request_id));
PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
if (!request_info)
void ResourceDispatcher::OnReceivedRedirect(
int request_id,
- const GURL& new_url,
+ const net::RedirectInfo& redirect_info,
const ResourceResponseHead& response_head) {
TRACE_EVENT0("loader", "ResourceDispatcher::OnReceivedRedirect");
PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
return;
request_info->response_start = ConsumeIOTimestamp();
- bool has_new_first_party_for_cookies = false;
- GURL new_first_party_for_cookies;
ResourceResponseInfo renderer_response_info;
ToResourceResponseInfo(*request_info, response_head, &renderer_response_info);
- if (request_info->peer->OnReceivedRedirect(new_url, renderer_response_info,
- &has_new_first_party_for_cookies,
- &new_first_party_for_cookies)) {
+ if (request_info->peer->OnReceivedRedirect(redirect_info,
+ renderer_response_info)) {
// Double-check if the request is still around. The call above could
// potentially remove it.
request_info = GetPendingRequestInfo(request_id);
return;
// We update the response_url here so that we can send it to
// SiteIsolationPolicy later when OnReceivedResponse is called.
- request_info->response_url = new_url;
+ request_info->response_url = redirect_info.new_url;
request_info->pending_redirect_message.reset(
- new ResourceHostMsg_FollowRedirect(request_id,
- has_new_first_party_for_cookies,
- new_first_party_for_cookies));
+ new ResourceHostMsg_FollowRedirect(request_id));
if (!request_info->is_deferred) {
FollowPendingRedirect(request_id, *request_info);
}
PendingRequestInfo& request_info) {
IPC::Message* msg = request_info.pending_redirect_message.release();
if (msg)
- message_sender()->Send(msg);
+ message_sender_->Send(msg);
}
void ResourceDispatcher::OnRequestComplete(
request_info->buffer.reset();
request_info->buffer_size = 0;
- ResourceLoaderBridge::Peer* peer = request_info->peer;
+ RequestPeer* peer = request_info->peer;
if (delegate_) {
- ResourceLoaderBridge::Peer* new_peer =
+ RequestPeer* new_peer =
delegate_->OnRequestComplete(
request_info->peer, request_info->resource_type,
request_complete_data.error_code);
request_complete_data.encoded_data_length);
}
-int ResourceDispatcher::AddPendingRequest(
- ResourceLoaderBridge::Peer* callback,
- ResourceType::Type resource_type,
- int origin_pid,
- const GURL& frame_origin,
- const GURL& request_url) {
+int ResourceDispatcher::AddPendingRequest(RequestPeer* callback,
+ ResourceType resource_type,
+ int origin_pid,
+ const GURL& frame_origin,
+ const GURL& request_url,
+ bool download_to_file) {
// Compute a unique request_id for this renderer process.
int id = MakeRequestID();
- pending_requests_[id] = PendingRequestInfo(
- callback, resource_type, origin_pid, frame_origin, request_url);
+ pending_requests_[id] = PendingRequestInfo(callback,
+ resource_type,
+ origin_pid,
+ frame_origin,
+ request_url,
+ download_to_file);
return id;
}
return false;
PendingRequestInfo& request_info = it->second;
+
+ bool release_downloaded_file = request_info.download_to_file;
+
ReleaseResourcesInMessageQueue(&request_info.deferred_message_queue);
pending_requests_.erase(it);
+ if (release_downloaded_file) {
+ message_sender_->Send(
+ new ResourceHostMsg_ReleaseDownloadedFile(request_id));
+ }
+
return true;
}
return;
}
- // |request_id| will be removed from |pending_requests_| when
- // OnRequestComplete returns with ERR_ABORTED.
- message_sender()->Send(new ResourceHostMsg_CancelRequest(request_id));
+ // Cancel the request, and clean it up so the bridge will receive no more
+ // messages.
+ message_sender_->Send(new ResourceHostMsg_CancelRequest(request_id));
+ RemovePendingRequest(request_id);
}
void ResourceDispatcher::SetDefersLoading(int request_id, bool value) {
}
}
-void ResourceDispatcher::DidChangePriority(
- int routing_id, int request_id, net::RequestPriority new_priority) {
+void ResourceDispatcher::DidChangePriority(int request_id,
+ net::RequestPriority new_priority,
+ int intra_priority_value) {
DCHECK(ContainsKey(pending_requests_, request_id));
- message_sender()->Send(new ResourceHostMsg_DidChangePriority(
- request_id, new_priority));
+ message_sender_->Send(new ResourceHostMsg_DidChangePriority(
+ request_id, new_priority, intra_priority_value));
+}
+
+bool ResourceDispatcher::AttachThreadedDataReceiver(
+ int request_id, blink::WebThreadedDataReceiver* threaded_data_receiver) {
+ PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
+ DCHECK(request_info);
+
+ if (request_info->buffer != NULL) {
+ DCHECK(!request_info->threaded_data_provider);
+ request_info->threaded_data_provider = new ThreadedDataProvider(
+ request_id, threaded_data_receiver, request_info->buffer,
+ request_info->buffer_size);
+ return true;
+ }
+
+ return false;
}
ResourceDispatcher::PendingRequestInfo::PendingRequestInfo()
: peer(NULL),
- resource_type(ResourceType::SUB_RESOURCE),
+ threaded_data_provider(NULL),
+ resource_type(RESOURCE_TYPE_SUB_RESOURCE),
is_deferred(false),
+ download_to_file(false),
blocked_response(false),
buffer_size(0) {
}
ResourceDispatcher::PendingRequestInfo::PendingRequestInfo(
- webkit_glue::ResourceLoaderBridge::Peer* peer,
- ResourceType::Type resource_type,
+ RequestPeer* peer,
+ ResourceType resource_type,
int origin_pid,
const GURL& frame_origin,
- const GURL& request_url)
+ const GURL& request_url,
+ bool download_to_file)
: peer(peer),
+ threaded_data_provider(NULL),
resource_type(resource_type),
origin_pid(origin_pid),
is_deferred(false),
url(request_url),
frame_origin(frame_origin),
response_url(request_url),
+ download_to_file(download_to_file),
request_start(base::TimeTicks::Now()),
- blocked_response(false) {
-}
+ blocked_response(false) {}
-ResourceDispatcher::PendingRequestInfo::~PendingRequestInfo() {}
+ResourceDispatcher::PendingRequestInfo::~PendingRequestInfo() {
+ if (threaded_data_provider)
+ threaded_data_provider->Stop();
+}
void ResourceDispatcher::DispatchMessage(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(ResourceDispatcher, message)
RemoteToLocalTimeTicks(converter, &load_timing->send_start);
RemoteToLocalTimeTicks(converter, &load_timing->send_end);
RemoteToLocalTimeTicks(converter, &load_timing->receive_headers_end);
+ RemoteToLocalTimeTicks(converter,
+ &renderer_info->service_worker_fetch_start);
+ RemoteToLocalTimeTicks(converter,
+ &renderer_info->service_worker_fetch_ready);
+ RemoteToLocalTimeTicks(converter,
+ &renderer_info->service_worker_fetch_end);
+
+ // Collect UMA on the inter-process skew.
+ bool is_skew_additive = false;
+ if (converter.IsSkewAdditiveForMetrics()) {
+ is_skew_additive = true;
+ base::TimeDelta skew = converter.GetSkewForMetrics();
+ if (skew >= base::TimeDelta()) {
+ UMA_HISTOGRAM_TIMES(
+ "InterProcessTimeTicks.BrowserAhead_BrowserToRenderer", skew);
+ } else {
+ UMA_HISTOGRAM_TIMES(
+ "InterProcessTimeTicks.BrowserBehind_BrowserToRenderer", -skew);
+ }
+ }
+ UMA_HISTOGRAM_BOOLEAN(
+ "InterProcessTimeTicks.IsSkewAdditive_BrowserToRenderer",
+ is_skew_additive);
}
base::TimeTicks ResourceDispatcher::ToRendererCompletionTime(