Home | History | Annotate | Download | only in child
      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