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