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