• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
RemoteToLocalTimeTicks(const InterProcessTimeTicksConverter & converter,base::TimeTicks * time)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 
CrashOnMapFailure()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.
MakeRequestID()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 
IPCResourceLoaderBridge(ResourceDispatcher * dispatcher,const ResourceLoaderBridge::RequestInfo & request_info)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 
~IPCResourceLoaderBridge()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 
SetRequestBody(ResourceRequestBody * request_body)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
Start(Peer * peer)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 
Cancel()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 
SetDefersLoading(bool value)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 
DidChangePriority(net::RequestPriority new_priority)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 
SyncLoad(SyncLoadResponse * response)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 
ResourceDispatcher(IPC::Sender * sender)269 ResourceDispatcher::ResourceDispatcher(IPC::Sender* sender)
270     : message_sender_(sender),
271       weak_factory_(this),
272       delegate_(NULL),
273       io_timestamp_(base::TimeTicks()) {
274 }
275 
~ResourceDispatcher()276 ResourceDispatcher::~ResourceDispatcher() {
277 }
278 
279 // ResourceDispatcher implementation ------------------------------------------
280 
OnMessageReceived(const IPC::Message & message)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*
GetPendingRequestInfo(int request_id)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 
OnUploadProgress(int request_id,int64 position,int64 size)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 
OnReceivedResponse(int request_id,const ResourceResponseHead & response_head)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 
OnReceivedCachedMetadata(int request_id,const std::vector<char> & data)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 
OnSetDataBuffer(int request_id,base::SharedMemoryHandle shm_handle,int shm_size,base::ProcessId renderer_pid)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 
OnReceivedData(int request_id,int data_offset,int data_length,int encoded_data_length)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 
OnDownloadedData(int request_id,int data_len,int encoded_data_length)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 
OnReceivedRedirect(int request_id,const GURL & new_url,const ResourceResponseHead & response_head)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 
FollowPendingRedirect(int request_id,PendingRequestInfo & request_info)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 
OnRequestComplete(int request_id,int error_code,bool was_ignored_by_handler,const std::string & security_info,const base::TimeTicks & browser_completion_time)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 
AddPendingRequest(ResourceLoaderBridge::Peer * callback,ResourceType::Type resource_type,int origin_pid,const GURL & frame_origin,const GURL & request_url)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 
RemovePendingRequest(int request_id)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 
CancelPendingRequest(int request_id)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 
SetDefersLoading(int request_id,bool value)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 
DidChangePriority(int routing_id,int request_id,net::RequestPriority new_priority)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 
PendingRequestInfo()625 ResourceDispatcher::PendingRequestInfo::PendingRequestInfo()
626     : peer(NULL),
627       resource_type(ResourceType::SUB_RESOURCE),
628       is_deferred(false),
629       buffer_size(0) {
630 }
631 
PendingRequestInfo(webkit_glue::ResourceLoaderBridge::Peer * peer,ResourceType::Type resource_type,int origin_pid,const GURL & frame_origin,const GURL & request_url)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 
~PendingRequestInfo()648 ResourceDispatcher::PendingRequestInfo::~PendingRequestInfo() {}
649 
DispatchMessage(const IPC::Message & message)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 
FlushDeferredMessages(int request_id)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 
CreateBridge(const ResourceLoaderBridge::RequestInfo & request_info)695 ResourceLoaderBridge* ResourceDispatcher::CreateBridge(
696     const ResourceLoaderBridge::RequestInfo& request_info) {
697   return new IPCResourceLoaderBridge(this, request_info);
698 }
699 
ToResourceResponseInfo(const PendingRequestInfo & request_info,const ResourceResponseHead & browser_info,ResourceResponseInfo * renderer_info) const700 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 
ToRendererCompletionTime(const PendingRequestInfo & request_info,const base::TimeTicks & browser_completion_time) const733 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 
ConsumeIOTimestamp()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
IsResourceDispatcherMessage(const IPC::Message & message)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
ReleaseResourcesInDataMessage(const IPC::Message & message)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
ReleaseResourcesInMessageQueue(MessageQueue * queue)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