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. 4 5 // See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading 6 7 #include "content/child/resource_dispatcher.h" 8 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/files/file_path.h" 14 #include "base/memory/shared_memory.h" 15 #include "base/message_loop/message_loop.h" 16 #include "base/metrics/histogram.h" 17 #include "base/strings/string_util.h" 18 #include "content/child/request_extra_data.h" 19 #include "content/child/request_info.h" 20 #include "content/child/site_isolation_policy.h" 21 #include "content/child/sync_load_response.h" 22 #include "content/child/threaded_data_provider.h" 23 #include "content/common/inter_process_time_ticks_converter.h" 24 #include "content/common/resource_messages.h" 25 #include "content/public/child/request_peer.h" 26 #include "content/public/child/resource_dispatcher_delegate.h" 27 #include "content/public/common/resource_response.h" 28 #include "net/base/net_errors.h" 29 #include "net/base/net_util.h" 30 #include "net/base/request_priority.h" 31 #include "net/http/http_response_headers.h" 32 #include "webkit/child/resource_loader_bridge.h" 33 #include "webkit/common/resource_type.h" 34 35 using webkit_glue::ResourceLoaderBridge; 36 37 namespace content { 38 39 namespace { 40 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(); 48 } 49 50 void CrashOnMapFailure() { 51 #if defined(OS_WIN) 52 DWORD last_err = GetLastError(); 53 base::debug::Alias(&last_err); 54 #endif 55 CHECK(false); 56 } 57 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++; 65 } 66 67 } // namespace 68 69 // ResourceLoaderBridge implementation ---------------------------------------- 70 71 class IPCResourceLoaderBridge : public ResourceLoaderBridge { 72 public: 73 IPCResourceLoaderBridge(ResourceDispatcher* dispatcher, 74 const RequestInfo& request_info); 75 virtual ~IPCResourceLoaderBridge(); 76 77 // ResourceLoaderBridge 78 virtual void SetRequestBody(ResourceRequestBody* request_body) OVERRIDE; 79 virtual bool Start(RequestPeer* peer) OVERRIDE; 80 virtual void Cancel() OVERRIDE; 81 virtual void SetDefersLoading(bool value) OVERRIDE; 82 virtual void DidChangePriority(net::RequestPriority new_priority, 83 int intra_priority_value) OVERRIDE; 84 virtual bool AttachThreadedDataReceiver( 85 blink::WebThreadedDataReceiver* threaded_data_receiver) OVERRIDE; 86 virtual void SyncLoad(SyncLoadResponse* response) OVERRIDE; 87 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_; 92 93 // The request to send, created on initialization for modification and 94 // appending data. 95 ResourceHostMsg_Request request_; 96 97 // ID for the request, valid once Start()ed, -1 if not valid yet. 98 int request_id_; 99 100 // The routing id used when sending IPC messages. 101 int routing_id_; 102 103 // The security origin of the frame that initiates this request. 104 GURL frame_origin_; 105 106 bool is_synchronous_request_; 107 }; 108 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; 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 132 const RequestExtraData kEmptyData; 133 const RequestExtraData* extra_data; 134 if (request_info.extra_data) 135 extra_data = static_cast<RequestExtraData*>(request_info.extra_data); 136 else 137 extra_data = &kEmptyData; 138 request_.visiblity_state = extra_data->visibility_state(); 139 request_.render_frame_id = extra_data->render_frame_id(); 140 request_.is_main_frame = extra_data->is_main_frame(); 141 request_.parent_is_main_frame = extra_data->parent_is_main_frame(); 142 request_.parent_render_frame_id = extra_data->parent_render_frame_id(); 143 request_.allow_download = extra_data->allow_download(); 144 request_.transition_type = extra_data->transition_type(); 145 request_.should_replace_current_entry = 146 extra_data->should_replace_current_entry(); 147 request_.transferred_request_child_id = 148 extra_data->transferred_request_child_id(); 149 request_.transferred_request_request_id = 150 extra_data->transferred_request_request_id(); 151 request_.service_worker_provider_id = 152 extra_data->service_worker_provider_id(); 153 frame_origin_ = extra_data->frame_origin(); 154 } 155 156 IPCResourceLoaderBridge::~IPCResourceLoaderBridge() { 157 // we remove our hook for the resource dispatcher only when going away, since 158 // it doesn't keep track of whether we've force terminated the request 159 if (request_id_ >= 0) { 160 // this operation may fail, as the dispatcher will have preemptively 161 // removed us when the renderer sends the ReceivedAllData message. 162 dispatcher_->RemovePendingRequest(request_id_); 163 } 164 } 165 166 void IPCResourceLoaderBridge::SetRequestBody( 167 ResourceRequestBody* request_body) { 168 DCHECK(request_id_ == -1) << "request already started"; 169 request_.request_body = request_body; 170 } 171 172 // Writes a footer on the message and sends it 173 bool IPCResourceLoaderBridge::Start(RequestPeer* peer) { 174 if (request_id_ != -1) { 175 NOTREACHED() << "Starting a request twice"; 176 return false; 177 } 178 179 // generate the request ID, and append it to the message 180 request_id_ = dispatcher_->AddPendingRequest(peer, 181 request_.resource_type, 182 request_.origin_pid, 183 frame_origin_, 184 request_.url, 185 request_.download_to_file); 186 187 return dispatcher_->message_sender()->Send( 188 new ResourceHostMsg_RequestResource(routing_id_, request_id_, request_)); 189 } 190 191 void IPCResourceLoaderBridge::Cancel() { 192 if (request_id_ < 0) { 193 NOTREACHED() << "Trying to cancel an unstarted request"; 194 return; 195 } 196 197 if (!is_synchronous_request_) { 198 // This also removes the the request from the dispatcher. 199 dispatcher_->CancelPendingRequest(request_id_); 200 } 201 } 202 203 void IPCResourceLoaderBridge::SetDefersLoading(bool value) { 204 if (request_id_ < 0) { 205 NOTREACHED() << "Trying to (un)defer an unstarted request"; 206 return; 207 } 208 209 dispatcher_->SetDefersLoading(request_id_, value); 210 } 211 212 void IPCResourceLoaderBridge::DidChangePriority( 213 net::RequestPriority new_priority, 214 int intra_priority_value) { 215 if (request_id_ < 0) { 216 NOTREACHED() << "Trying to change priority of an unstarted request"; 217 return; 218 } 219 220 dispatcher_->DidChangePriority( 221 request_id_, new_priority, intra_priority_value); 222 } 223 224 bool IPCResourceLoaderBridge::AttachThreadedDataReceiver( 225 blink::WebThreadedDataReceiver* threaded_data_receiver) { 226 if (request_id_ < 0) { 227 NOTREACHED() << "Trying to attach threaded receiver on unstarted request"; 228 return false; 229 } 230 231 return dispatcher_->AttachThreadedDataReceiver(request_id_, 232 threaded_data_receiver); 233 } 234 235 void IPCResourceLoaderBridge::SyncLoad(SyncLoadResponse* response) { 236 if (request_id_ != -1) { 237 NOTREACHED() << "Starting a request twice"; 238 response->error_code = net::ERR_FAILED; 239 return; 240 } 241 242 request_id_ = MakeRequestID(); 243 is_synchronous_request_ = true; 244 245 SyncLoadResult result; 246 IPC::SyncMessage* msg = new ResourceHostMsg_SyncLoad(routing_id_, request_id_, 247 request_, &result); 248 // NOTE: This may pump events (see RenderThread::Send). 249 if (!dispatcher_->message_sender()->Send(msg)) { 250 response->error_code = net::ERR_FAILED; 251 return; 252 } 253 254 response->error_code = result.error_code; 255 response->url = result.final_url; 256 response->headers = result.headers; 257 response->mime_type = result.mime_type; 258 response->charset = result.charset; 259 response->request_time = result.request_time; 260 response->response_time = result.response_time; 261 response->encoded_data_length = result.encoded_data_length; 262 response->load_timing = result.load_timing; 263 response->devtools_info = result.devtools_info; 264 response->data.swap(result.data); 265 response->download_file_path = result.download_file_path; 266 } 267 268 // ResourceDispatcher --------------------------------------------------------- 269 270 ResourceDispatcher::ResourceDispatcher(IPC::Sender* sender) 271 : message_sender_(sender), 272 weak_factory_(this), 273 delegate_(NULL), 274 io_timestamp_(base::TimeTicks()) { 275 } 276 277 ResourceDispatcher::~ResourceDispatcher() { 278 } 279 280 // ResourceDispatcher implementation ------------------------------------------ 281 282 bool ResourceDispatcher::OnMessageReceived(const IPC::Message& message) { 283 if (!IsResourceDispatcherMessage(message)) { 284 return false; 285 } 286 287 int request_id; 288 289 PickleIterator iter(message); 290 if (!message.ReadInt(&iter, &request_id)) { 291 NOTREACHED() << "malformed resource message"; 292 return true; 293 } 294 295 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); 296 if (!request_info) { 297 // Release resources in the message if it is a data message. 298 ReleaseResourcesInDataMessage(message); 299 return true; 300 } 301 302 if (request_info->is_deferred) { 303 request_info->deferred_message_queue.push_back(new IPC::Message(message)); 304 return true; 305 } 306 // Make sure any deferred messages are dispatched before we dispatch more. 307 if (!request_info->deferred_message_queue.empty()) { 308 FlushDeferredMessages(request_id); 309 // The request could have been deferred now. If yes then the current 310 // message has to be queued up. The request_info instance should remain 311 // valid here as there are pending messages for it. 312 DCHECK(pending_requests_.find(request_id) != pending_requests_.end()); 313 if (request_info->is_deferred) { 314 request_info->deferred_message_queue.push_back(new IPC::Message(message)); 315 return true; 316 } 317 } 318 319 DispatchMessage(message); 320 return true; 321 } 322 323 ResourceDispatcher::PendingRequestInfo* 324 ResourceDispatcher::GetPendingRequestInfo(int request_id) { 325 PendingRequestList::iterator it = pending_requests_.find(request_id); 326 if (it == pending_requests_.end()) { 327 // This might happen for kill()ed requests on the webkit end. 328 return NULL; 329 } 330 return &(it->second); 331 } 332 333 void ResourceDispatcher::OnUploadProgress(int request_id, int64 position, 334 int64 size) { 335 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); 336 if (!request_info) 337 return; 338 339 request_info->peer->OnUploadProgress(position, size); 340 341 // Acknowledge receipt 342 message_sender_->Send(new ResourceHostMsg_UploadProgress_ACK(request_id)); 343 } 344 345 void ResourceDispatcher::OnReceivedResponse( 346 int request_id, const ResourceResponseHead& response_head) { 347 TRACE_EVENT0("loader", "ResourceDispatcher::OnReceivedResponse"); 348 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); 349 if (!request_info) 350 return; 351 request_info->response_start = ConsumeIOTimestamp(); 352 353 if (delegate_) { 354 RequestPeer* new_peer = 355 delegate_->OnReceivedResponse( 356 request_info->peer, response_head.mime_type, request_info->url); 357 if (new_peer) 358 request_info->peer = new_peer; 359 } 360 361 ResourceResponseInfo renderer_response_info; 362 ToResourceResponseInfo(*request_info, response_head, &renderer_response_info); 363 request_info->site_isolation_metadata = 364 SiteIsolationPolicy::OnReceivedResponse(request_info->frame_origin, 365 request_info->response_url, 366 request_info->resource_type, 367 request_info->origin_pid, 368 renderer_response_info); 369 request_info->peer->OnReceivedResponse(renderer_response_info); 370 } 371 372 void ResourceDispatcher::OnReceivedCachedMetadata( 373 int request_id, const std::vector<char>& data) { 374 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); 375 if (!request_info) 376 return; 377 378 if (data.size()) 379 request_info->peer->OnReceivedCachedMetadata(&data.front(), data.size()); 380 } 381 382 void ResourceDispatcher::OnSetDataBuffer(int request_id, 383 base::SharedMemoryHandle shm_handle, 384 int shm_size, 385 base::ProcessId renderer_pid) { 386 TRACE_EVENT0("loader", "ResourceDispatcher::OnSetDataBuffer"); 387 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); 388 if (!request_info) 389 return; 390 391 bool shm_valid = base::SharedMemory::IsHandleValid(shm_handle); 392 CHECK((shm_valid && shm_size > 0) || (!shm_valid && !shm_size)); 393 394 request_info->buffer.reset( 395 new base::SharedMemory(shm_handle, true)); // read only 396 397 bool ok = request_info->buffer->Map(shm_size); 398 if (!ok) { 399 // Added to help debug crbug/160401. 400 base::ProcessId renderer_pid_copy = renderer_pid; 401 base::debug::Alias(&renderer_pid_copy); 402 403 base::SharedMemoryHandle shm_handle_copy = shm_handle; 404 base::debug::Alias(&shm_handle_copy); 405 406 CrashOnMapFailure(); 407 return; 408 } 409 410 request_info->buffer_size = shm_size; 411 } 412 413 void ResourceDispatcher::OnReceivedData(int request_id, 414 int data_offset, 415 int data_length, 416 int encoded_data_length) { 417 TRACE_EVENT0("loader", "ResourceDispatcher::OnReceivedData"); 418 DCHECK_GT(data_length, 0); 419 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); 420 bool send_ack = true; 421 if (request_info && data_length > 0) { 422 CHECK(base::SharedMemory::IsHandleValid(request_info->buffer->handle())); 423 CHECK_GE(request_info->buffer_size, data_offset + data_length); 424 425 // Ensure that the SHM buffer remains valid for the duration of this scope. 426 // It is possible for CancelPendingRequest() to be called before we exit 427 // this scope. 428 linked_ptr<base::SharedMemory> retain_buffer(request_info->buffer); 429 430 base::TimeTicks time_start = base::TimeTicks::Now(); 431 432 const char* data_start = static_cast<char*>(request_info->buffer->memory()); 433 CHECK(data_start); 434 CHECK(data_start + data_offset); 435 const char* data_ptr = data_start + data_offset; 436 437 // Check whether this response data is compliant with our cross-site 438 // document blocking policy. We only do this for the first packet. 439 std::string alternative_data; 440 if (request_info->site_isolation_metadata.get()) { 441 request_info->blocked_response = 442 SiteIsolationPolicy::ShouldBlockResponse( 443 request_info->site_isolation_metadata, data_ptr, data_length, 444 &alternative_data); 445 request_info->site_isolation_metadata.reset(); 446 447 // When the response is blocked we may have any alternative data to 448 // send to the renderer. When |alternative_data| is zero-sized, we do not 449 // call peer's callback. 450 if (request_info->blocked_response && !alternative_data.empty()) { 451 data_ptr = alternative_data.data(); 452 data_length = alternative_data.size(); 453 encoded_data_length = alternative_data.size(); 454 } 455 } 456 457 if (!request_info->blocked_response || !alternative_data.empty()) { 458 if (request_info->threaded_data_provider) { 459 request_info->threaded_data_provider->OnReceivedDataOnForegroundThread( 460 data_ptr, data_length, encoded_data_length); 461 // A threaded data provider will take care of its own ACKing, as the 462 // data may be processed later on another thread. 463 send_ack = false; 464 } else { 465 request_info->peer->OnReceivedData( 466 data_ptr, data_length, encoded_data_length); 467 } 468 } 469 470 UMA_HISTOGRAM_TIMES("ResourceDispatcher.OnReceivedDataTime", 471 base::TimeTicks::Now() - time_start); 472 } 473 474 // Acknowledge the reception of this data. 475 if (send_ack) 476 message_sender_->Send(new ResourceHostMsg_DataReceived_ACK(request_id)); 477 } 478 479 void ResourceDispatcher::OnDownloadedData(int request_id, 480 int data_len, 481 int encoded_data_length) { 482 // Acknowledge the reception of this message. 483 message_sender_->Send(new ResourceHostMsg_DataDownloaded_ACK(request_id)); 484 485 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); 486 if (!request_info) 487 return; 488 489 request_info->peer->OnDownloadedData(data_len, encoded_data_length); 490 } 491 492 void ResourceDispatcher::OnReceivedRedirect( 493 int request_id, 494 const GURL& new_url, 495 const GURL& new_first_party_for_cookies, 496 const ResourceResponseHead& response_head) { 497 TRACE_EVENT0("loader", "ResourceDispatcher::OnReceivedRedirect"); 498 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); 499 if (!request_info) 500 return; 501 request_info->response_start = ConsumeIOTimestamp(); 502 503 ResourceResponseInfo renderer_response_info; 504 ToResourceResponseInfo(*request_info, response_head, &renderer_response_info); 505 if (request_info->peer->OnReceivedRedirect( 506 new_url, new_first_party_for_cookies, renderer_response_info)) { 507 // Double-check if the request is still around. The call above could 508 // potentially remove it. 509 request_info = GetPendingRequestInfo(request_id); 510 if (!request_info) 511 return; 512 // We update the response_url here so that we can send it to 513 // SiteIsolationPolicy later when OnReceivedResponse is called. 514 request_info->response_url = new_url; 515 request_info->pending_redirect_message.reset( 516 new ResourceHostMsg_FollowRedirect(request_id)); 517 if (!request_info->is_deferred) { 518 FollowPendingRedirect(request_id, *request_info); 519 } 520 } else { 521 CancelPendingRequest(request_id); 522 } 523 } 524 525 void ResourceDispatcher::FollowPendingRedirect( 526 int request_id, 527 PendingRequestInfo& request_info) { 528 IPC::Message* msg = request_info.pending_redirect_message.release(); 529 if (msg) 530 message_sender_->Send(msg); 531 } 532 533 void ResourceDispatcher::OnRequestComplete( 534 int request_id, 535 const ResourceMsg_RequestCompleteData& request_complete_data) { 536 TRACE_EVENT0("loader", "ResourceDispatcher::OnRequestComplete"); 537 538 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); 539 if (!request_info) 540 return; 541 request_info->completion_time = ConsumeIOTimestamp(); 542 request_info->buffer.reset(); 543 request_info->buffer_size = 0; 544 545 RequestPeer* peer = request_info->peer; 546 547 if (delegate_) { 548 RequestPeer* new_peer = 549 delegate_->OnRequestComplete( 550 request_info->peer, request_info->resource_type, 551 request_complete_data.error_code); 552 if (new_peer) 553 request_info->peer = new_peer; 554 } 555 556 base::TimeTicks renderer_completion_time = ToRendererCompletionTime( 557 *request_info, request_complete_data.completion_time); 558 // The request ID will be removed from our pending list in the destructor. 559 // Normally, dispatching this message causes the reference-counted request to 560 // die immediately. 561 peer->OnCompletedRequest(request_complete_data.error_code, 562 request_complete_data.was_ignored_by_handler, 563 request_complete_data.exists_in_cache, 564 request_complete_data.security_info, 565 renderer_completion_time, 566 request_complete_data.encoded_data_length); 567 } 568 569 int ResourceDispatcher::AddPendingRequest(RequestPeer* callback, 570 ResourceType::Type resource_type, 571 int origin_pid, 572 const GURL& frame_origin, 573 const GURL& request_url, 574 bool download_to_file) { 575 // Compute a unique request_id for this renderer process. 576 int id = MakeRequestID(); 577 pending_requests_[id] = PendingRequestInfo(callback, 578 resource_type, 579 origin_pid, 580 frame_origin, 581 request_url, 582 download_to_file); 583 return id; 584 } 585 586 bool ResourceDispatcher::RemovePendingRequest(int request_id) { 587 PendingRequestList::iterator it = pending_requests_.find(request_id); 588 if (it == pending_requests_.end()) 589 return false; 590 591 PendingRequestInfo& request_info = it->second; 592 593 bool release_downloaded_file = request_info.download_to_file; 594 595 ReleaseResourcesInMessageQueue(&request_info.deferred_message_queue); 596 pending_requests_.erase(it); 597 598 if (release_downloaded_file) { 599 message_sender_->Send( 600 new ResourceHostMsg_ReleaseDownloadedFile(request_id)); 601 } 602 603 return true; 604 } 605 606 void ResourceDispatcher::CancelPendingRequest(int request_id) { 607 PendingRequestList::iterator it = pending_requests_.find(request_id); 608 if (it == pending_requests_.end()) { 609 DVLOG(1) << "unknown request"; 610 return; 611 } 612 613 // Cancel the request, and clean it up so the bridge will receive no more 614 // messages. 615 message_sender_->Send(new ResourceHostMsg_CancelRequest(request_id)); 616 RemovePendingRequest(request_id); 617 } 618 619 void ResourceDispatcher::SetDefersLoading(int request_id, bool value) { 620 PendingRequestList::iterator it = pending_requests_.find(request_id); 621 if (it == pending_requests_.end()) { 622 DLOG(ERROR) << "unknown request"; 623 return; 624 } 625 PendingRequestInfo& request_info = it->second; 626 if (value) { 627 request_info.is_deferred = value; 628 } else if (request_info.is_deferred) { 629 request_info.is_deferred = false; 630 631 FollowPendingRedirect(request_id, request_info); 632 633 base::MessageLoop::current()->PostTask( 634 FROM_HERE, 635 base::Bind(&ResourceDispatcher::FlushDeferredMessages, 636 weak_factory_.GetWeakPtr(), 637 request_id)); 638 } 639 } 640 641 void ResourceDispatcher::DidChangePriority(int request_id, 642 net::RequestPriority new_priority, 643 int intra_priority_value) { 644 DCHECK(ContainsKey(pending_requests_, request_id)); 645 message_sender_->Send(new ResourceHostMsg_DidChangePriority( 646 request_id, new_priority, intra_priority_value)); 647 } 648 649 bool ResourceDispatcher::AttachThreadedDataReceiver( 650 int request_id, blink::WebThreadedDataReceiver* threaded_data_receiver) { 651 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); 652 DCHECK(request_info); 653 654 if (request_info->buffer != NULL) { 655 DCHECK(!request_info->threaded_data_provider); 656 request_info->threaded_data_provider = new ThreadedDataProvider( 657 request_id, threaded_data_receiver, request_info->buffer, 658 request_info->buffer_size); 659 return true; 660 } 661 662 return false; 663 } 664 665 ResourceDispatcher::PendingRequestInfo::PendingRequestInfo() 666 : peer(NULL), 667 threaded_data_provider(NULL), 668 resource_type(ResourceType::SUB_RESOURCE), 669 is_deferred(false), 670 download_to_file(false), 671 blocked_response(false), 672 buffer_size(0) { 673 } 674 675 ResourceDispatcher::PendingRequestInfo::PendingRequestInfo( 676 RequestPeer* peer, 677 ResourceType::Type resource_type, 678 int origin_pid, 679 const GURL& frame_origin, 680 const GURL& request_url, 681 bool download_to_file) 682 : peer(peer), 683 threaded_data_provider(NULL), 684 resource_type(resource_type), 685 origin_pid(origin_pid), 686 is_deferred(false), 687 url(request_url), 688 frame_origin(frame_origin), 689 response_url(request_url), 690 download_to_file(download_to_file), 691 request_start(base::TimeTicks::Now()), 692 blocked_response(false) {} 693 694 ResourceDispatcher::PendingRequestInfo::~PendingRequestInfo() { 695 if (threaded_data_provider) 696 threaded_data_provider->Stop(); 697 } 698 699 void ResourceDispatcher::DispatchMessage(const IPC::Message& message) { 700 IPC_BEGIN_MESSAGE_MAP(ResourceDispatcher, message) 701 IPC_MESSAGE_HANDLER(ResourceMsg_UploadProgress, OnUploadProgress) 702 IPC_MESSAGE_HANDLER(ResourceMsg_ReceivedResponse, OnReceivedResponse) 703 IPC_MESSAGE_HANDLER(ResourceMsg_ReceivedCachedMetadata, 704 OnReceivedCachedMetadata) 705 IPC_MESSAGE_HANDLER(ResourceMsg_ReceivedRedirect, OnReceivedRedirect) 706 IPC_MESSAGE_HANDLER(ResourceMsg_SetDataBuffer, OnSetDataBuffer) 707 IPC_MESSAGE_HANDLER(ResourceMsg_DataReceived, OnReceivedData) 708 IPC_MESSAGE_HANDLER(ResourceMsg_DataDownloaded, OnDownloadedData) 709 IPC_MESSAGE_HANDLER(ResourceMsg_RequestComplete, OnRequestComplete) 710 IPC_END_MESSAGE_MAP() 711 } 712 713 void ResourceDispatcher::FlushDeferredMessages(int request_id) { 714 PendingRequestList::iterator it = pending_requests_.find(request_id); 715 if (it == pending_requests_.end()) // The request could have become invalid. 716 return; 717 PendingRequestInfo& request_info = it->second; 718 if (request_info.is_deferred) 719 return; 720 // Because message handlers could result in request_info being destroyed, 721 // we need to work with a stack reference to the deferred queue. 722 MessageQueue q; 723 q.swap(request_info.deferred_message_queue); 724 while (!q.empty()) { 725 IPC::Message* m = q.front(); 726 q.pop_front(); 727 DispatchMessage(*m); 728 delete m; 729 // If this request is deferred in the context of the above message, then 730 // we should honor the same and stop dispatching further messages. 731 // We need to find the request again in the list as it may have completed 732 // by now and the request_info instance above may be invalid. 733 PendingRequestList::iterator index = pending_requests_.find(request_id); 734 if (index != pending_requests_.end()) { 735 PendingRequestInfo& pending_request = index->second; 736 if (pending_request.is_deferred) { 737 pending_request.deferred_message_queue.swap(q); 738 return; 739 } 740 } 741 } 742 } 743 744 ResourceLoaderBridge* ResourceDispatcher::CreateBridge( 745 const RequestInfo& request_info) { 746 return new IPCResourceLoaderBridge(this, request_info); 747 } 748 749 void ResourceDispatcher::ToResourceResponseInfo( 750 const PendingRequestInfo& request_info, 751 const ResourceResponseHead& browser_info, 752 ResourceResponseInfo* renderer_info) const { 753 *renderer_info = browser_info; 754 if (request_info.request_start.is_null() || 755 request_info.response_start.is_null() || 756 browser_info.request_start.is_null() || 757 browser_info.response_start.is_null() || 758 browser_info.load_timing.request_start.is_null()) { 759 return; 760 } 761 InterProcessTimeTicksConverter converter( 762 LocalTimeTicks::FromTimeTicks(request_info.request_start), 763 LocalTimeTicks::FromTimeTicks(request_info.response_start), 764 RemoteTimeTicks::FromTimeTicks(browser_info.request_start), 765 RemoteTimeTicks::FromTimeTicks(browser_info.response_start)); 766 767 net::LoadTimingInfo* load_timing = &renderer_info->load_timing; 768 RemoteToLocalTimeTicks(converter, &load_timing->request_start); 769 RemoteToLocalTimeTicks(converter, &load_timing->proxy_resolve_start); 770 RemoteToLocalTimeTicks(converter, &load_timing->proxy_resolve_end); 771 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.dns_start); 772 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.dns_end); 773 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.connect_start); 774 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.connect_end); 775 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.ssl_start); 776 RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.ssl_end); 777 RemoteToLocalTimeTicks(converter, &load_timing->send_start); 778 RemoteToLocalTimeTicks(converter, &load_timing->send_end); 779 RemoteToLocalTimeTicks(converter, &load_timing->receive_headers_end); 780 } 781 782 base::TimeTicks ResourceDispatcher::ToRendererCompletionTime( 783 const PendingRequestInfo& request_info, 784 const base::TimeTicks& browser_completion_time) const { 785 if (request_info.completion_time.is_null()) { 786 return browser_completion_time; 787 } 788 789 // TODO(simonjam): The optimal lower bound should be the most recent value of 790 // TimeTicks::Now() returned to WebKit. Is it worth trying to cache that? 791 // Until then, |response_start| is used as it is the most recent value 792 // returned for this request. 793 int64 result = std::max(browser_completion_time.ToInternalValue(), 794 request_info.response_start.ToInternalValue()); 795 result = std::min(result, request_info.completion_time.ToInternalValue()); 796 return base::TimeTicks::FromInternalValue(result); 797 } 798 799 base::TimeTicks ResourceDispatcher::ConsumeIOTimestamp() { 800 if (io_timestamp_ == base::TimeTicks()) 801 return base::TimeTicks::Now(); 802 base::TimeTicks result = io_timestamp_; 803 io_timestamp_ = base::TimeTicks(); 804 return result; 805 } 806 807 // static 808 bool ResourceDispatcher::IsResourceDispatcherMessage( 809 const IPC::Message& message) { 810 switch (message.type()) { 811 case ResourceMsg_UploadProgress::ID: 812 case ResourceMsg_ReceivedResponse::ID: 813 case ResourceMsg_ReceivedCachedMetadata::ID: 814 case ResourceMsg_ReceivedRedirect::ID: 815 case ResourceMsg_SetDataBuffer::ID: 816 case ResourceMsg_DataReceived::ID: 817 case ResourceMsg_DataDownloaded::ID: 818 case ResourceMsg_RequestComplete::ID: 819 return true; 820 821 default: 822 break; 823 } 824 825 return false; 826 } 827 828 // static 829 void ResourceDispatcher::ReleaseResourcesInDataMessage( 830 const IPC::Message& message) { 831 PickleIterator iter(message); 832 int request_id; 833 if (!message.ReadInt(&iter, &request_id)) { 834 NOTREACHED() << "malformed resource message"; 835 return; 836 } 837 838 // If the message contains a shared memory handle, we should close the handle 839 // or there will be a memory leak. 840 if (message.type() == ResourceMsg_SetDataBuffer::ID) { 841 base::SharedMemoryHandle shm_handle; 842 if (IPC::ParamTraits<base::SharedMemoryHandle>::Read(&message, 843 &iter, 844 &shm_handle)) { 845 if (base::SharedMemory::IsHandleValid(shm_handle)) 846 base::SharedMemory::CloseHandle(shm_handle); 847 } 848 } 849 } 850 851 // static 852 void ResourceDispatcher::ReleaseResourcesInMessageQueue(MessageQueue* queue) { 853 while (!queue->empty()) { 854 IPC::Message* message = queue->front(); 855 ReleaseResourcesInDataMessage(*message); 856 queue->pop_front(); 857 delete message; 858 } 859 } 860 861 } // namespace content 862