base: Change DCHECK_IS_ON to a macro DCHECK_IS_ON().
[chromium-blink-merge.git] / content / child / resource_dispatcher.cc
blob7cbc7fa53e3249f3e79802d1b407a3cf02f3a70e
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading
7 #include "content/child/resource_dispatcher.h"
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/debug/alias.h"
13 #include "base/debug/dump_without_crashing.h"
14 #include "base/files/file_path.h"
15 #include "base/memory/shared_memory.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/metrics/histogram.h"
18 #include "base/strings/string_util.h"
19 #include "content/child/child_thread.h"
20 #include "content/child/request_extra_data.h"
21 #include "content/child/request_info.h"
22 #include "content/child/resource_loader_bridge.h"
23 #include "content/child/site_isolation_policy.h"
24 #include "content/child/sync_load_response.h"
25 #include "content/child/threaded_data_provider.h"
26 #include "content/common/inter_process_time_ticks_converter.h"
27 #include "content/common/resource_messages.h"
28 #include "content/public/child/request_peer.h"
29 #include "content/public/child/resource_dispatcher_delegate.h"
30 #include "content/public/common/resource_response.h"
31 #include "content/public/common/resource_type.h"
32 #include "net/base/net_errors.h"
33 #include "net/base/net_util.h"
34 #include "net/base/request_priority.h"
35 #include "net/http/http_response_headers.h"
37 namespace content {
39 namespace {
41 // Converts |time| from a remote to local TimeTicks, overwriting the original
42 // value.
43 void RemoteToLocalTimeTicks(
44 const InterProcessTimeTicksConverter& converter,
45 base::TimeTicks* time) {
46 RemoteTimeTicks remote_time = RemoteTimeTicks::FromTimeTicks(*time);
47 *time = converter.ToLocalTimeTicks(remote_time).ToTimeTicks();
50 void CrashOnMapFailure() {
51 #if defined(OS_WIN)
52 DWORD last_err = GetLastError();
53 base::debug::Alias(&last_err);
54 #endif
55 CHECK(false);
58 // Each resource request is assigned an ID scoped to this process.
59 int MakeRequestID() {
60 // NOTE: The resource_dispatcher_host also needs probably unique
61 // request_ids, so they count down from -2 (-1 is a special we're
62 // screwed value), while the renderer process counts up.
63 static int next_request_id = 0;
64 return next_request_id++;
67 } // namespace
69 // ResourceLoaderBridge implementation ----------------------------------------
71 class IPCResourceLoaderBridge : public ResourceLoaderBridge {
72 public:
73 IPCResourceLoaderBridge(ResourceDispatcher* dispatcher,
74 const RequestInfo& request_info);
75 ~IPCResourceLoaderBridge() override;
77 // ResourceLoaderBridge
78 void SetRequestBody(ResourceRequestBody* request_body) override;
79 bool Start(RequestPeer* peer) override;
80 void Cancel() override;
81 void SetDefersLoading(bool value) override;
82 void DidChangePriority(net::RequestPriority new_priority,
83 int intra_priority_value) override;
84 bool AttachThreadedDataReceiver(
85 blink::WebThreadedDataReceiver* threaded_data_receiver) override;
86 void SyncLoad(SyncLoadResponse* response) override;
88 private:
89 // The resource dispatcher for this loader. The bridge doesn't own it, but
90 // it's guaranteed to outlive the bridge.
91 ResourceDispatcher* dispatcher_;
93 // The request to send, created on initialization for modification and
94 // appending data.
95 ResourceHostMsg_Request request_;
97 // ID for the request, valid once Start()ed, -1 if not valid yet.
98 int request_id_;
100 // The routing id used when sending IPC messages.
101 int routing_id_;
103 // The security origin of the frame that initiates this request.
104 GURL frame_origin_;
106 bool is_synchronous_request_;
109 IPCResourceLoaderBridge::IPCResourceLoaderBridge(
110 ResourceDispatcher* dispatcher,
111 const RequestInfo& request_info)
112 : dispatcher_(dispatcher),
113 request_id_(-1),
114 routing_id_(request_info.routing_id),
115 is_synchronous_request_(false) {
116 DCHECK(dispatcher_) << "no resource dispatcher";
117 request_.method = request_info.method;
118 request_.url = request_info.url;
119 request_.first_party_for_cookies = request_info.first_party_for_cookies;
120 request_.referrer = request_info.referrer.url;
121 request_.referrer_policy = request_info.referrer.policy;
122 request_.headers = request_info.headers;
123 request_.load_flags = request_info.load_flags;
124 request_.origin_pid = request_info.requestor_pid;
125 request_.resource_type = request_info.request_type;
126 request_.priority = request_info.priority;
127 request_.request_context = request_info.request_context;
128 request_.appcache_host_id = request_info.appcache_host_id;
129 request_.download_to_file = request_info.download_to_file;
130 request_.has_user_gesture = request_info.has_user_gesture;
131 request_.skip_service_worker = request_info.skip_service_worker;
132 request_.should_reset_appcache = request_info.should_reset_appcache;
133 request_.fetch_request_mode = request_info.fetch_request_mode;
134 request_.fetch_credentials_mode = request_info.fetch_credentials_mode;
135 request_.fetch_request_context_type = request_info.fetch_request_context_type;
136 request_.fetch_frame_type = request_info.fetch_frame_type;
137 request_.enable_load_timing = request_info.enable_load_timing;
138 request_.enable_upload_progress = request_info.enable_upload_progress;
140 if ((request_info.referrer.policy == blink::WebReferrerPolicyDefault ||
141 request_info.referrer.policy ==
142 blink::WebReferrerPolicyNoReferrerWhenDowngrade) &&
143 request_info.referrer.url.SchemeIsSecure() &&
144 !request_info.url.SchemeIsSecure()) {
145 // Debug code for crbug.com/422871
146 base::debug::DumpWithoutCrashing();
147 DLOG(FATAL) << "Trying to send secure referrer for insecure request "
148 << "without an appropriate referrer policy.\n"
149 << "URL = " << request_info.url << "\n"
150 << "Referrer = " << request_info.referrer.url;
153 const RequestExtraData kEmptyData;
154 const RequestExtraData* extra_data;
155 if (request_info.extra_data)
156 extra_data = static_cast<RequestExtraData*>(request_info.extra_data);
157 else
158 extra_data = &kEmptyData;
159 request_.visiblity_state = extra_data->visibility_state();
160 request_.render_frame_id = extra_data->render_frame_id();
161 request_.is_main_frame = extra_data->is_main_frame();
162 request_.parent_is_main_frame = extra_data->parent_is_main_frame();
163 request_.parent_render_frame_id = extra_data->parent_render_frame_id();
164 request_.allow_download = extra_data->allow_download();
165 request_.transition_type = extra_data->transition_type();
166 request_.should_replace_current_entry =
167 extra_data->should_replace_current_entry();
168 request_.transferred_request_child_id =
169 extra_data->transferred_request_child_id();
170 request_.transferred_request_request_id =
171 extra_data->transferred_request_request_id();
172 request_.service_worker_provider_id =
173 extra_data->service_worker_provider_id();
174 frame_origin_ = extra_data->frame_origin();
177 IPCResourceLoaderBridge::~IPCResourceLoaderBridge() {
178 // we remove our hook for the resource dispatcher only when going away, since
179 // it doesn't keep track of whether we've force terminated the request
180 if (request_id_ >= 0) {
181 // this operation may fail, as the dispatcher will have preemptively
182 // removed us when the renderer sends the ReceivedAllData message.
183 dispatcher_->RemovePendingRequest(request_id_);
187 void IPCResourceLoaderBridge::SetRequestBody(
188 ResourceRequestBody* request_body) {
189 DCHECK(request_id_ == -1) << "request already started";
190 request_.request_body = request_body;
193 // Writes a footer on the message and sends it
194 bool IPCResourceLoaderBridge::Start(RequestPeer* peer) {
195 if (request_id_ != -1) {
196 NOTREACHED() << "Starting a request twice";
197 return false;
200 // generate the request ID, and append it to the message
201 request_id_ = dispatcher_->AddPendingRequest(peer,
202 request_.resource_type,
203 request_.origin_pid,
204 frame_origin_,
205 request_.url,
206 request_.download_to_file);
208 return dispatcher_->message_sender()->Send(
209 new ResourceHostMsg_RequestResource(routing_id_, request_id_, request_));
212 void IPCResourceLoaderBridge::Cancel() {
213 if (request_id_ < 0) {
214 NOTREACHED() << "Trying to cancel an unstarted request";
215 return;
218 if (!is_synchronous_request_) {
219 // This also removes the the request from the dispatcher.
220 dispatcher_->CancelPendingRequest(request_id_);
224 void IPCResourceLoaderBridge::SetDefersLoading(bool value) {
225 if (request_id_ < 0) {
226 NOTREACHED() << "Trying to (un)defer an unstarted request";
227 return;
230 dispatcher_->SetDefersLoading(request_id_, value);
233 void IPCResourceLoaderBridge::DidChangePriority(
234 net::RequestPriority new_priority,
235 int intra_priority_value) {
236 if (request_id_ < 0) {
237 NOTREACHED() << "Trying to change priority of an unstarted request";
238 return;
241 dispatcher_->DidChangePriority(
242 request_id_, new_priority, intra_priority_value);
245 bool IPCResourceLoaderBridge::AttachThreadedDataReceiver(
246 blink::WebThreadedDataReceiver* threaded_data_receiver) {
247 if (request_id_ < 0) {
248 NOTREACHED() << "Trying to attach threaded receiver on unstarted request";
249 return false;
252 return dispatcher_->AttachThreadedDataReceiver(request_id_,
253 threaded_data_receiver);
256 void IPCResourceLoaderBridge::SyncLoad(SyncLoadResponse* response) {
257 if (request_id_ != -1) {
258 NOTREACHED() << "Starting a request twice";
259 response->error_code = net::ERR_FAILED;
260 return;
263 request_id_ = MakeRequestID();
264 is_synchronous_request_ = true;
266 SyncLoadResult result;
267 IPC::SyncMessage* msg = new ResourceHostMsg_SyncLoad(routing_id_, request_id_,
268 request_, &result);
269 // NOTE: This may pump events (see RenderThread::Send).
270 if (!dispatcher_->message_sender()->Send(msg)) {
271 response->error_code = net::ERR_FAILED;
272 return;
275 response->error_code = result.error_code;
276 response->url = result.final_url;
277 response->headers = result.headers;
278 response->mime_type = result.mime_type;
279 response->charset = result.charset;
280 response->request_time = result.request_time;
281 response->response_time = result.response_time;
282 response->encoded_data_length = result.encoded_data_length;
283 response->load_timing = result.load_timing;
284 response->devtools_info = result.devtools_info;
285 response->data.swap(result.data);
286 response->download_file_path = result.download_file_path;
289 // ResourceDispatcher ---------------------------------------------------------
291 ResourceDispatcher::ResourceDispatcher(
292 IPC::Sender* sender,
293 scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
294 : message_sender_(sender),
295 delegate_(NULL),
296 io_timestamp_(base::TimeTicks()),
297 main_thread_task_runner_(main_thread_task_runner),
298 weak_factory_(this) {
301 ResourceDispatcher::~ResourceDispatcher() {
304 // ResourceDispatcher implementation ------------------------------------------
306 bool ResourceDispatcher::OnMessageReceived(const IPC::Message& message) {
307 if (!IsResourceDispatcherMessage(message)) {
308 return false;
311 int request_id;
313 PickleIterator iter(message);
314 if (!iter.ReadInt(&request_id)) {
315 NOTREACHED() << "malformed resource message";
316 return true;
319 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
320 if (!request_info) {
321 // Release resources in the message if it is a data message.
322 ReleaseResourcesInDataMessage(message);
323 return true;
326 if (request_info->is_deferred) {
327 request_info->deferred_message_queue.push_back(new IPC::Message(message));
328 return true;
330 // Make sure any deferred messages are dispatched before we dispatch more.
331 if (!request_info->deferred_message_queue.empty()) {
332 FlushDeferredMessages(request_id);
333 // The request could have been deferred now. If yes then the current
334 // message has to be queued up. The request_info instance should remain
335 // valid here as there are pending messages for it.
336 DCHECK(pending_requests_.find(request_id) != pending_requests_.end());
337 if (request_info->is_deferred) {
338 request_info->deferred_message_queue.push_back(new IPC::Message(message));
339 return true;
343 DispatchMessage(message);
344 return true;
347 ResourceDispatcher::PendingRequestInfo*
348 ResourceDispatcher::GetPendingRequestInfo(int request_id) {
349 PendingRequestList::iterator it = pending_requests_.find(request_id);
350 if (it == pending_requests_.end()) {
351 // This might happen for kill()ed requests on the webkit end.
352 return NULL;
354 return &(it->second);
357 void ResourceDispatcher::OnUploadProgress(int request_id, int64 position,
358 int64 size) {
359 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
360 if (!request_info)
361 return;
363 request_info->peer->OnUploadProgress(position, size);
365 // Acknowledge receipt
366 message_sender_->Send(new ResourceHostMsg_UploadProgress_ACK(request_id));
369 void ResourceDispatcher::OnReceivedResponse(
370 int request_id, const ResourceResponseHead& response_head) {
371 TRACE_EVENT0("loader", "ResourceDispatcher::OnReceivedResponse");
372 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
373 if (!request_info)
374 return;
375 request_info->response_start = ConsumeIOTimestamp();
377 if (delegate_) {
378 RequestPeer* new_peer =
379 delegate_->OnReceivedResponse(
380 request_info->peer, response_head.mime_type, request_info->url);
381 if (new_peer)
382 request_info->peer = new_peer;
385 ResourceResponseInfo renderer_response_info;
386 ToResourceResponseInfo(*request_info, response_head, &renderer_response_info);
387 request_info->site_isolation_metadata =
388 SiteIsolationPolicy::OnReceivedResponse(request_info->frame_origin,
389 request_info->response_url,
390 request_info->resource_type,
391 request_info->origin_pid,
392 renderer_response_info);
393 request_info->peer->OnReceivedResponse(renderer_response_info);
396 void ResourceDispatcher::OnReceivedCachedMetadata(
397 int request_id, const std::vector<char>& data) {
398 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
399 if (!request_info)
400 return;
402 if (data.size())
403 request_info->peer->OnReceivedCachedMetadata(&data.front(), data.size());
406 void ResourceDispatcher::OnSetDataBuffer(int request_id,
407 base::SharedMemoryHandle shm_handle,
408 int shm_size,
409 base::ProcessId renderer_pid) {
410 TRACE_EVENT0("loader", "ResourceDispatcher::OnSetDataBuffer");
411 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
412 if (!request_info)
413 return;
415 bool shm_valid = base::SharedMemory::IsHandleValid(shm_handle);
416 CHECK((shm_valid && shm_size > 0) || (!shm_valid && !shm_size));
418 request_info->buffer.reset(
419 new base::SharedMemory(shm_handle, true)); // read only
421 bool ok = request_info->buffer->Map(shm_size);
422 if (!ok) {
423 // Added to help debug crbug/160401.
424 base::ProcessId renderer_pid_copy = renderer_pid;
425 base::debug::Alias(&renderer_pid_copy);
427 base::SharedMemoryHandle shm_handle_copy = shm_handle;
428 base::debug::Alias(&shm_handle_copy);
430 CrashOnMapFailure();
431 return;
434 request_info->buffer_size = shm_size;
437 void ResourceDispatcher::OnReceivedData(int request_id,
438 int data_offset,
439 int data_length,
440 int encoded_data_length) {
441 TRACE_EVENT0("loader", "ResourceDispatcher::OnReceivedData");
442 DCHECK_GT(data_length, 0);
443 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
444 bool send_ack = true;
445 if (request_info && data_length > 0) {
446 CHECK(base::SharedMemory::IsHandleValid(request_info->buffer->handle()));
447 CHECK_GE(request_info->buffer_size, data_offset + data_length);
449 // Ensure that the SHM buffer remains valid for the duration of this scope.
450 // It is possible for CancelPendingRequest() to be called before we exit
451 // this scope.
452 linked_ptr<base::SharedMemory> retain_buffer(request_info->buffer);
454 base::TimeTicks time_start = base::TimeTicks::Now();
456 const char* data_start = static_cast<char*>(request_info->buffer->memory());
457 CHECK(data_start);
458 CHECK(data_start + data_offset);
459 const char* data_ptr = data_start + data_offset;
461 // Check whether this response data is compliant with our cross-site
462 // document blocking policy. We only do this for the first packet.
463 std::string alternative_data;
464 if (request_info->site_isolation_metadata.get()) {
465 request_info->blocked_response =
466 SiteIsolationPolicy::ShouldBlockResponse(
467 request_info->site_isolation_metadata, data_ptr, data_length,
468 &alternative_data);
469 request_info->site_isolation_metadata.reset();
471 // When the response is blocked we may have any alternative data to
472 // send to the renderer. When |alternative_data| is zero-sized, we do not
473 // call peer's callback.
474 if (request_info->blocked_response && !alternative_data.empty()) {
475 data_ptr = alternative_data.data();
476 data_length = alternative_data.size();
477 encoded_data_length = alternative_data.size();
481 if (!request_info->blocked_response || !alternative_data.empty()) {
482 if (request_info->threaded_data_provider) {
483 request_info->threaded_data_provider->OnReceivedDataOnForegroundThread(
484 data_ptr, data_length, encoded_data_length);
485 // A threaded data provider will take care of its own ACKing, as the
486 // data may be processed later on another thread.
487 send_ack = false;
488 } else {
489 request_info->peer->OnReceivedData(
490 data_ptr, data_length, encoded_data_length);
494 UMA_HISTOGRAM_TIMES("ResourceDispatcher.OnReceivedDataTime",
495 base::TimeTicks::Now() - time_start);
498 // Acknowledge the reception of this data.
499 if (send_ack)
500 message_sender_->Send(new ResourceHostMsg_DataReceived_ACK(request_id));
503 void ResourceDispatcher::OnDownloadedData(int request_id,
504 int data_len,
505 int encoded_data_length) {
506 // Acknowledge the reception of this message.
507 message_sender_->Send(new ResourceHostMsg_DataDownloaded_ACK(request_id));
509 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
510 if (!request_info)
511 return;
513 request_info->peer->OnDownloadedData(data_len, encoded_data_length);
516 void ResourceDispatcher::OnReceivedRedirect(
517 int request_id,
518 const net::RedirectInfo& redirect_info,
519 const ResourceResponseHead& response_head) {
520 TRACE_EVENT0("loader", "ResourceDispatcher::OnReceivedRedirect");
521 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
522 if (!request_info)
523 return;
524 request_info->response_start = ConsumeIOTimestamp();
526 ResourceResponseInfo renderer_response_info;
527 ToResourceResponseInfo(*request_info, response_head, &renderer_response_info);
528 if (request_info->peer->OnReceivedRedirect(redirect_info,
529 renderer_response_info)) {
530 // Double-check if the request is still around. The call above could
531 // potentially remove it.
532 request_info = GetPendingRequestInfo(request_id);
533 if (!request_info)
534 return;
535 // We update the response_url here so that we can send it to
536 // SiteIsolationPolicy later when OnReceivedResponse is called.
537 request_info->response_url = redirect_info.new_url;
538 request_info->pending_redirect_message.reset(
539 new ResourceHostMsg_FollowRedirect(request_id));
540 if (!request_info->is_deferred) {
541 FollowPendingRedirect(request_id, *request_info);
543 } else {
544 CancelPendingRequest(request_id);
548 void ResourceDispatcher::FollowPendingRedirect(
549 int request_id,
550 PendingRequestInfo& request_info) {
551 IPC::Message* msg = request_info.pending_redirect_message.release();
552 if (msg)
553 message_sender_->Send(msg);
556 void ResourceDispatcher::OnRequestComplete(
557 int request_id,
558 const ResourceMsg_RequestCompleteData& request_complete_data) {
559 TRACE_EVENT0("loader", "ResourceDispatcher::OnRequestComplete");
561 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
562 if (!request_info)
563 return;
564 request_info->completion_time = ConsumeIOTimestamp();
565 request_info->buffer.reset();
566 request_info->buffer_size = 0;
568 RequestPeer* peer = request_info->peer;
570 if (delegate_) {
571 RequestPeer* new_peer =
572 delegate_->OnRequestComplete(
573 request_info->peer, request_info->resource_type,
574 request_complete_data.error_code);
575 if (new_peer)
576 request_info->peer = new_peer;
579 base::TimeTicks renderer_completion_time = ToRendererCompletionTime(
580 *request_info, request_complete_data.completion_time);
581 // The request ID will be removed from our pending list in the destructor.
582 // Normally, dispatching this message causes the reference-counted request to
583 // die immediately.
584 peer->OnCompletedRequest(request_complete_data.error_code,
585 request_complete_data.was_ignored_by_handler,
586 request_complete_data.exists_in_cache,
587 request_complete_data.security_info,
588 renderer_completion_time,
589 request_complete_data.encoded_data_length);
592 int ResourceDispatcher::AddPendingRequest(RequestPeer* callback,
593 ResourceType resource_type,
594 int origin_pid,
595 const GURL& frame_origin,
596 const GURL& request_url,
597 bool download_to_file) {
598 // Compute a unique request_id for this renderer process.
599 int id = MakeRequestID();
600 pending_requests_[id] = PendingRequestInfo(callback,
601 resource_type,
602 origin_pid,
603 frame_origin,
604 request_url,
605 download_to_file);
606 return id;
609 bool ResourceDispatcher::RemovePendingRequest(int request_id) {
610 PendingRequestList::iterator it = pending_requests_.find(request_id);
611 if (it == pending_requests_.end())
612 return false;
614 PendingRequestInfo& request_info = it->second;
616 bool release_downloaded_file = request_info.download_to_file;
618 ReleaseResourcesInMessageQueue(&request_info.deferred_message_queue);
619 pending_requests_.erase(it);
621 if (release_downloaded_file) {
622 message_sender_->Send(
623 new ResourceHostMsg_ReleaseDownloadedFile(request_id));
626 return true;
629 void ResourceDispatcher::CancelPendingRequest(int request_id) {
630 PendingRequestList::iterator it = pending_requests_.find(request_id);
631 if (it == pending_requests_.end()) {
632 DVLOG(1) << "unknown request";
633 return;
636 // Cancel the request, and clean it up so the bridge will receive no more
637 // messages.
638 message_sender_->Send(new ResourceHostMsg_CancelRequest(request_id));
639 RemovePendingRequest(request_id);
642 void ResourceDispatcher::SetDefersLoading(int request_id, bool value) {
643 PendingRequestList::iterator it = pending_requests_.find(request_id);
644 if (it == pending_requests_.end()) {
645 DLOG(ERROR) << "unknown request";
646 return;
648 PendingRequestInfo& request_info = it->second;
649 if (value) {
650 request_info.is_deferred = value;
651 } else if (request_info.is_deferred) {
652 request_info.is_deferred = false;
654 FollowPendingRedirect(request_id, request_info);
656 main_thread_task_runner_->PostTask(
657 FROM_HERE, base::Bind(&ResourceDispatcher::FlushDeferredMessages,
658 weak_factory_.GetWeakPtr(), request_id));
662 void ResourceDispatcher::DidChangePriority(int request_id,
663 net::RequestPriority new_priority,
664 int intra_priority_value) {
665 DCHECK(ContainsKey(pending_requests_, request_id));
666 message_sender_->Send(new ResourceHostMsg_DidChangePriority(
667 request_id, new_priority, intra_priority_value));
670 bool ResourceDispatcher::AttachThreadedDataReceiver(
671 int request_id, blink::WebThreadedDataReceiver* threaded_data_receiver) {
672 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
673 DCHECK(request_info);
675 if (request_info->buffer != NULL) {
676 DCHECK(!request_info->threaded_data_provider);
677 request_info->threaded_data_provider = new ThreadedDataProvider(
678 request_id, threaded_data_receiver, request_info->buffer,
679 request_info->buffer_size, main_thread_task_runner_);
680 return true;
683 return false;
686 ResourceDispatcher::PendingRequestInfo::PendingRequestInfo()
687 : peer(NULL),
688 threaded_data_provider(NULL),
689 resource_type(RESOURCE_TYPE_SUB_RESOURCE),
690 is_deferred(false),
691 download_to_file(false),
692 blocked_response(false),
693 buffer_size(0) {
696 ResourceDispatcher::PendingRequestInfo::PendingRequestInfo(
697 RequestPeer* peer,
698 ResourceType resource_type,
699 int origin_pid,
700 const GURL& frame_origin,
701 const GURL& request_url,
702 bool download_to_file)
703 : peer(peer),
704 threaded_data_provider(NULL),
705 resource_type(resource_type),
706 origin_pid(origin_pid),
707 is_deferred(false),
708 url(request_url),
709 frame_origin(frame_origin),
710 response_url(request_url),
711 download_to_file(download_to_file),
712 request_start(base::TimeTicks::Now()),
713 blocked_response(false) {}
715 ResourceDispatcher::PendingRequestInfo::~PendingRequestInfo() {
716 if (threaded_data_provider)
717 threaded_data_provider->Stop();
720 void ResourceDispatcher::DispatchMessage(const IPC::Message& message) {
721 IPC_BEGIN_MESSAGE_MAP(ResourceDispatcher, message)
722 IPC_MESSAGE_HANDLER(ResourceMsg_UploadProgress, OnUploadProgress)
723 IPC_MESSAGE_HANDLER(ResourceMsg_ReceivedResponse, OnReceivedResponse)
724 IPC_MESSAGE_HANDLER(ResourceMsg_ReceivedCachedMetadata,
725 OnReceivedCachedMetadata)
726 IPC_MESSAGE_HANDLER(ResourceMsg_ReceivedRedirect, OnReceivedRedirect)
727 IPC_MESSAGE_HANDLER(ResourceMsg_SetDataBuffer, OnSetDataBuffer)
728 IPC_MESSAGE_HANDLER(ResourceMsg_DataReceived, OnReceivedData)
729 IPC_MESSAGE_HANDLER(ResourceMsg_DataDownloaded, OnDownloadedData)
730 IPC_MESSAGE_HANDLER(ResourceMsg_RequestComplete, OnRequestComplete)
731 IPC_END_MESSAGE_MAP()
734 void ResourceDispatcher::FlushDeferredMessages(int request_id) {
735 PendingRequestList::iterator it = pending_requests_.find(request_id);
736 if (it == pending_requests_.end()) // The request could have become invalid.
737 return;
738 PendingRequestInfo& request_info = it->second;
739 if (request_info.is_deferred)
740 return;
741 // Because message handlers could result in request_info being destroyed,
742 // we need to work with a stack reference to the deferred queue.
743 MessageQueue q;
744 q.swap(request_info.deferred_message_queue);
745 while (!q.empty()) {
746 IPC::Message* m = q.front();
747 q.pop_front();
748 DispatchMessage(*m);
749 delete m;
750 // If this request is deferred in the context of the above message, then
751 // we should honor the same and stop dispatching further messages.
752 // We need to find the request again in the list as it may have completed
753 // by now and the request_info instance above may be invalid.
754 PendingRequestList::iterator index = pending_requests_.find(request_id);
755 if (index != pending_requests_.end()) {
756 PendingRequestInfo& pending_request = index->second;
757 if (pending_request.is_deferred) {
758 pending_request.deferred_message_queue.swap(q);
759 return;
765 ResourceLoaderBridge* ResourceDispatcher::CreateBridge(
766 const RequestInfo& request_info) {
767 return new IPCResourceLoaderBridge(this, request_info);
770 void ResourceDispatcher::ToResourceResponseInfo(
771 const PendingRequestInfo& request_info,
772 const ResourceResponseHead& browser_info,
773 ResourceResponseInfo* renderer_info) const {
774 *renderer_info = browser_info;
775 if (request_info.request_start.is_null() ||
776 request_info.response_start.is_null() ||
777 browser_info.request_start.is_null() ||
778 browser_info.response_start.is_null() ||
779 browser_info.load_timing.request_start.is_null()) {
780 return;
782 InterProcessTimeTicksConverter converter(
783 LocalTimeTicks::FromTimeTicks(request_info.request_start),
784 LocalTimeTicks::FromTimeTicks(request_info.response_start),
785 RemoteTimeTicks::FromTimeTicks(browser_info.request_start),
786 RemoteTimeTicks::FromTimeTicks(browser_info.response_start));
788 net::LoadTimingInfo* load_timing = &renderer_info->load_timing;
789 RemoteToLocalTimeTicks(converter, &load_timing->request_start);
790 RemoteToLocalTimeTicks(converter, &load_timing->proxy_resolve_start);
791 RemoteToLocalTimeTicks(converter, &load_timing->proxy_resolve_end);
792 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.dns_start);
793 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.dns_end);
794 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.connect_start);
795 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.connect_end);
796 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.ssl_start);
797 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.ssl_end);
798 RemoteToLocalTimeTicks(converter, &load_timing->send_start);
799 RemoteToLocalTimeTicks(converter, &load_timing->send_end);
800 RemoteToLocalTimeTicks(converter, &load_timing->receive_headers_end);
801 RemoteToLocalTimeTicks(converter,
802 &renderer_info->service_worker_fetch_start);
803 RemoteToLocalTimeTicks(converter,
804 &renderer_info->service_worker_fetch_ready);
805 RemoteToLocalTimeTicks(converter,
806 &renderer_info->service_worker_fetch_end);
808 // Collect UMA on the inter-process skew.
809 bool is_skew_additive = false;
810 if (converter.IsSkewAdditiveForMetrics()) {
811 is_skew_additive = true;
812 base::TimeDelta skew = converter.GetSkewForMetrics();
813 if (skew >= base::TimeDelta()) {
814 UMA_HISTOGRAM_TIMES(
815 "InterProcessTimeTicks.BrowserAhead_BrowserToRenderer", skew);
816 } else {
817 UMA_HISTOGRAM_TIMES(
818 "InterProcessTimeTicks.BrowserBehind_BrowserToRenderer", -skew);
821 UMA_HISTOGRAM_BOOLEAN(
822 "InterProcessTimeTicks.IsSkewAdditive_BrowserToRenderer",
823 is_skew_additive);
826 base::TimeTicks ResourceDispatcher::ToRendererCompletionTime(
827 const PendingRequestInfo& request_info,
828 const base::TimeTicks& browser_completion_time) const {
829 if (request_info.completion_time.is_null()) {
830 return browser_completion_time;
833 // TODO(simonjam): The optimal lower bound should be the most recent value of
834 // TimeTicks::Now() returned to WebKit. Is it worth trying to cache that?
835 // Until then, |response_start| is used as it is the most recent value
836 // returned for this request.
837 int64 result = std::max(browser_completion_time.ToInternalValue(),
838 request_info.response_start.ToInternalValue());
839 result = std::min(result, request_info.completion_time.ToInternalValue());
840 return base::TimeTicks::FromInternalValue(result);
843 base::TimeTicks ResourceDispatcher::ConsumeIOTimestamp() {
844 if (io_timestamp_ == base::TimeTicks())
845 return base::TimeTicks::Now();
846 base::TimeTicks result = io_timestamp_;
847 io_timestamp_ = base::TimeTicks();
848 return result;
851 // static
852 bool ResourceDispatcher::IsResourceDispatcherMessage(
853 const IPC::Message& message) {
854 switch (message.type()) {
855 case ResourceMsg_UploadProgress::ID:
856 case ResourceMsg_ReceivedResponse::ID:
857 case ResourceMsg_ReceivedCachedMetadata::ID:
858 case ResourceMsg_ReceivedRedirect::ID:
859 case ResourceMsg_SetDataBuffer::ID:
860 case ResourceMsg_DataReceived::ID:
861 case ResourceMsg_DataDownloaded::ID:
862 case ResourceMsg_RequestComplete::ID:
863 return true;
865 default:
866 break;
869 return false;
872 // static
873 void ResourceDispatcher::ReleaseResourcesInDataMessage(
874 const IPC::Message& message) {
875 PickleIterator iter(message);
876 int request_id;
877 if (!iter.ReadInt(&request_id)) {
878 NOTREACHED() << "malformed resource message";
879 return;
882 // If the message contains a shared memory handle, we should close the handle
883 // or there will be a memory leak.
884 if (message.type() == ResourceMsg_SetDataBuffer::ID) {
885 base::SharedMemoryHandle shm_handle;
886 if (IPC::ParamTraits<base::SharedMemoryHandle>::Read(&message,
887 &iter,
888 &shm_handle)) {
889 if (base::SharedMemory::IsHandleValid(shm_handle))
890 base::SharedMemory::CloseHandle(shm_handle);
895 // static
896 void ResourceDispatcher::ReleaseResourcesInMessageQueue(MessageQueue* queue) {
897 while (!queue->empty()) {
898 IPC::Message* message = queue->front();
899 ReleaseResourcesInDataMessage(*message);
900 queue->pop_front();
901 delete message;
905 } // namespace content