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