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