Print Preview: Refactoring print/cancel button and print summary.
[chromium-blink-merge.git] / content / common / resource_dispatcher.cc
blob0ea306ecd7aedf66f4fe90189dc0017bfb735875
1 // Copyright (c) 2011 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.
5 // See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading
7 #include "content/common/resource_dispatcher.h"
9 #include "base/basictypes.h"
10 #include "base/compiler_specific.h"
11 #include "base/file_path.h"
12 #include "base/message_loop.h"
13 #include "base/shared_memory.h"
14 #include "base/string_util.h"
15 #include "content/common/resource_dispatcher_delegate.h"
16 #include "content/common/resource_messages.h"
17 #include "content/common/resource_response.h"
18 #include "net/base/net_errors.h"
19 #include "net/base/net_util.h"
20 #include "net/base/upload_data.h"
21 #include "net/http/http_response_headers.h"
22 #include "webkit/glue/resource_type.h"
24 // Each resource request is assigned an ID scoped to this process.
25 static int MakeRequestID() {
26 // NOTE: The resource_dispatcher_host also needs probably unique
27 // request_ids, so they count down from -2 (-1 is a special we're
28 // screwed value), while the renderer process counts up.
29 static int next_request_id = 0;
30 return next_request_id++;
33 // ResourceLoaderBridge implementation ----------------------------------------
35 namespace webkit_glue {
37 class IPCResourceLoaderBridge : public ResourceLoaderBridge {
38 public:
39 IPCResourceLoaderBridge(ResourceDispatcher* dispatcher,
40 const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info);
41 virtual ~IPCResourceLoaderBridge();
43 // ResourceLoaderBridge
44 virtual void AppendDataToUpload(const char* data, int data_len);
45 virtual void AppendFileRangeToUpload(
46 const FilePath& path,
47 uint64 offset,
48 uint64 length,
49 const base::Time& expected_modification_time);
50 virtual void AppendBlobToUpload(const GURL& blob_url);
51 virtual void SetUploadIdentifier(int64 identifier);
52 virtual bool Start(Peer* peer);
53 virtual void Cancel();
54 virtual void SetDefersLoading(bool value);
55 virtual void SyncLoad(SyncLoadResponse* response);
57 private:
58 ResourceLoaderBridge::Peer* peer_;
60 // The resource dispatcher for this loader. The bridge doesn't own it, but
61 // it's guaranteed to outlive the bridge.
62 ResourceDispatcher* dispatcher_;
64 // The request to send, created on initialization for modification and
65 // appending data.
66 ResourceHostMsg_Request request_;
68 // ID for the request, valid once Start()ed, -1 if not valid yet.
69 int request_id_;
71 // The routing id used when sending IPC messages.
72 int routing_id_;
75 IPCResourceLoaderBridge::IPCResourceLoaderBridge(
76 ResourceDispatcher* dispatcher,
77 const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info)
78 : peer_(NULL),
79 dispatcher_(dispatcher),
80 request_id_(-1),
81 routing_id_(request_info.routing_id) {
82 DCHECK(dispatcher_) << "no resource dispatcher";
83 request_.method = request_info.method;
84 request_.url = request_info.url;
85 request_.first_party_for_cookies = request_info.first_party_for_cookies;
86 request_.referrer = request_info.referrer;
87 request_.headers = request_info.headers;
88 request_.load_flags = request_info.load_flags;
89 request_.origin_pid = request_info.requestor_pid;
90 request_.resource_type = request_info.request_type;
91 request_.request_context = request_info.request_context;
92 request_.appcache_host_id = request_info.appcache_host_id;
93 request_.download_to_file = request_info.download_to_file;
94 request_.has_user_gesture = request_info.has_user_gesture;
97 IPCResourceLoaderBridge::~IPCResourceLoaderBridge() {
98 // we remove our hook for the resource dispatcher only when going away, since
99 // it doesn't keep track of whether we've force terminated the request
100 if (request_id_ >= 0) {
101 // this operation may fail, as the dispatcher will have preemptively
102 // removed us when the renderer sends the ReceivedAllData message.
103 dispatcher_->RemovePendingRequest(request_id_);
105 if (request_.download_to_file) {
106 dispatcher_->message_sender()->Send(
107 new ResourceHostMsg_ReleaseDownloadedFile(request_id_));
112 void IPCResourceLoaderBridge::AppendDataToUpload(const char* data,
113 int data_len) {
114 DCHECK(request_id_ == -1) << "request already started";
116 // don't bother appending empty data segments
117 if (data_len == 0)
118 return;
120 if (!request_.upload_data)
121 request_.upload_data = new net::UploadData();
122 request_.upload_data->AppendBytes(data, data_len);
125 void IPCResourceLoaderBridge::AppendFileRangeToUpload(
126 const FilePath& path, uint64 offset, uint64 length,
127 const base::Time& expected_modification_time) {
128 DCHECK(request_id_ == -1) << "request already started";
130 if (!request_.upload_data)
131 request_.upload_data = new net::UploadData();
132 request_.upload_data->AppendFileRange(path, offset, length,
133 expected_modification_time);
136 void IPCResourceLoaderBridge::AppendBlobToUpload(const GURL& blob_url) {
137 DCHECK(request_id_ == -1) << "request already started";
139 if (!request_.upload_data)
140 request_.upload_data = new net::UploadData();
141 request_.upload_data->AppendBlob(blob_url);
144 void IPCResourceLoaderBridge::SetUploadIdentifier(int64 identifier) {
145 DCHECK(request_id_ == -1) << "request already started";
147 if (!request_.upload_data)
148 request_.upload_data = new net::UploadData();
149 request_.upload_data->set_identifier(identifier);
152 // Writes a footer on the message and sends it
153 bool IPCResourceLoaderBridge::Start(Peer* peer) {
154 if (request_id_ != -1) {
155 NOTREACHED() << "Starting a request twice";
156 return false;
159 peer_ = peer;
161 // generate the request ID, and append it to the message
162 request_id_ = dispatcher_->AddPendingRequest(
163 peer_, request_.resource_type, request_.url);
165 return dispatcher_->message_sender()->Send(
166 new ResourceHostMsg_RequestResource(routing_id_, request_id_, request_));
169 void IPCResourceLoaderBridge::Cancel() {
170 if (request_id_ < 0) {
171 NOTREACHED() << "Trying to cancel an unstarted request";
172 return;
175 dispatcher_->CancelPendingRequest(routing_id_, request_id_);
177 // We can't remove the request ID from the resource dispatcher because more
178 // data might be pending. Sending the cancel message may cause more data
179 // to be flushed, and will then cause a complete message to be sent.
182 void IPCResourceLoaderBridge::SetDefersLoading(bool value) {
183 if (request_id_ < 0) {
184 NOTREACHED() << "Trying to (un)defer an unstarted request";
185 return;
188 dispatcher_->SetDefersLoading(request_id_, value);
191 void IPCResourceLoaderBridge::SyncLoad(SyncLoadResponse* response) {
192 if (request_id_ != -1) {
193 NOTREACHED() << "Starting a request twice";
194 response->status.set_status(net::URLRequestStatus::FAILED);
195 return;
198 request_id_ = MakeRequestID();
200 SyncLoadResult result;
201 IPC::SyncMessage* msg = new ResourceHostMsg_SyncLoad(routing_id_, request_id_,
202 request_, &result);
203 // NOTE: This may pump events (see RenderThread::Send).
204 if (!dispatcher_->message_sender()->Send(msg)) {
205 response->status.set_status(net::URLRequestStatus::FAILED);
206 return;
209 response->status = result.status;
210 response->url = result.final_url;
211 response->headers = result.headers;
212 response->mime_type = result.mime_type;
213 response->charset = result.charset;
214 response->request_time = result.request_time;
215 response->response_time = result.response_time;
216 response->encoded_data_length = result.encoded_data_length;
217 response->connection_id = result.connection_id;
218 response->connection_reused = result.connection_reused;
219 response->load_timing = result.load_timing;
220 response->devtools_info = result.devtools_info;
221 response->data.swap(result.data);
222 response->download_file_path = result.download_file_path;
225 } // namespace webkit_glue
227 // ResourceDispatcher ---------------------------------------------------------
229 ResourceDispatcher::ResourceDispatcher(IPC::Message::Sender* sender)
230 : message_sender_(sender),
231 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
232 delegate_(NULL) {
235 ResourceDispatcher::~ResourceDispatcher() {
238 // ResourceDispatcher implementation ------------------------------------------
240 bool ResourceDispatcher::OnMessageReceived(const IPC::Message& message) {
241 if (!IsResourceDispatcherMessage(message)) {
242 return false;
245 int request_id;
247 void* iter = NULL;
248 if (!message.ReadInt(&iter, &request_id)) {
249 NOTREACHED() << "malformed resource message";
250 return true;
253 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
254 if (!request_info) {
255 // Release resources in the message if it is a data message.
256 ReleaseResourcesInDataMessage(message);
257 return true;
260 if (request_info->is_deferred) {
261 request_info->deferred_message_queue.push_back(new IPC::Message(message));
262 return true;
264 // Make sure any deferred messages are dispatched before we dispatch more.
265 if (!request_info->deferred_message_queue.empty()) {
266 FlushDeferredMessages(request_id);
267 // The request could have been deferred now. If yes then the current
268 // message has to be queued up. The request_info instance should remain
269 // valid here as there are pending messages for it.
270 DCHECK(pending_requests_.find(request_id) != pending_requests_.end());
271 if (request_info->is_deferred) {
272 request_info->deferred_message_queue.push_back(new IPC::Message(message));
273 return true;
277 DispatchMessage(message);
278 return true;
281 ResourceDispatcher::PendingRequestInfo*
282 ResourceDispatcher::GetPendingRequestInfo(int request_id) {
283 PendingRequestList::iterator it = pending_requests_.find(request_id);
284 if (it == pending_requests_.end()) {
285 // This might happen for kill()ed requests on the webkit end, so perhaps it
286 // shouldn't be a warning...
287 DLOG(WARNING) << "Received message for a nonexistent or finished request";
288 return NULL;
290 return &(it->second);
293 void ResourceDispatcher::OnUploadProgress(
294 const IPC::Message& message, int request_id, int64 position, int64 size) {
295 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
296 if (!request_info)
297 return;
299 request_info->peer->OnUploadProgress(position, size);
301 // Acknowledge receipt
302 message_sender()->Send(
303 new ResourceHostMsg_UploadProgress_ACK(message.routing_id(), request_id));
306 void ResourceDispatcher::OnReceivedResponse(
307 int request_id, const ResourceResponseHead& response_head) {
308 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
309 if (!request_info)
310 return;
312 if (delegate_) {
313 webkit_glue::ResourceLoaderBridge::Peer* new_peer =
314 delegate_->OnReceivedResponse(
315 request_info->peer, response_head.mime_type, request_info->url);
316 if (new_peer)
317 request_info->peer = new_peer;
320 request_info->peer->OnReceivedResponse(response_head);
323 void ResourceDispatcher::OnReceivedCachedMetadata(
324 int request_id, const std::vector<char>& data) {
325 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
326 if (!request_info)
327 return;
329 if (data.size())
330 request_info->peer->OnReceivedCachedMetadata(&data.front(), data.size());
333 void ResourceDispatcher::OnReceivedData(const IPC::Message& message,
334 int request_id,
335 base::SharedMemoryHandle shm_handle,
336 int data_len,
337 int encoded_data_length) {
338 // Acknowledge the reception of this data.
339 message_sender()->Send(
340 new ResourceHostMsg_DataReceived_ACK(message.routing_id(), request_id));
342 const bool shm_valid = base::SharedMemory::IsHandleValid(shm_handle);
343 DCHECK((shm_valid && data_len > 0) || (!shm_valid && !data_len));
344 base::SharedMemory shared_mem(shm_handle, true); // read only
346 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
347 if (!request_info)
348 return;
350 if (data_len > 0 && shared_mem.Map(data_len)) {
351 const char* data = static_cast<char*>(shared_mem.memory());
352 request_info->peer->OnReceivedData(data, data_len, encoded_data_length);
356 void ResourceDispatcher::OnDownloadedData(const IPC::Message& message,
357 int request_id,
358 int data_len) {
359 // Acknowledge the reception of this message.
360 message_sender()->Send(
361 new ResourceHostMsg_DataDownloaded_ACK(message.routing_id(), request_id));
363 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
364 if (!request_info)
365 return;
367 request_info->peer->OnDownloadedData(data_len);
370 void ResourceDispatcher::OnReceivedRedirect(
371 const IPC::Message& message,
372 int request_id,
373 const GURL& new_url,
374 const webkit_glue::ResourceResponseInfo& info) {
375 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
376 if (!request_info)
377 return;
379 int32 routing_id = message.routing_id();
380 bool has_new_first_party_for_cookies = false;
381 GURL new_first_party_for_cookies;
382 if (request_info->peer->OnReceivedRedirect(new_url, info,
383 &has_new_first_party_for_cookies,
384 &new_first_party_for_cookies)) {
385 // Double-check if the request is still around. The call above could
386 // potentially remove it.
387 request_info = GetPendingRequestInfo(request_id);
388 if (!request_info)
389 return;
390 request_info->pending_redirect_message.reset(
391 new ResourceHostMsg_FollowRedirect(routing_id, request_id,
392 has_new_first_party_for_cookies,
393 new_first_party_for_cookies));
394 if (!request_info->is_deferred) {
395 FollowPendingRedirect(request_id, *request_info);
397 } else {
398 CancelPendingRequest(routing_id, request_id);
402 void ResourceDispatcher::FollowPendingRedirect(
403 int request_id,
404 PendingRequestInfo& request_info) {
405 IPC::Message* msg = request_info.pending_redirect_message.release();
406 if (msg)
407 message_sender()->Send(msg);
410 void ResourceDispatcher::OnRequestComplete(int request_id,
411 const net::URLRequestStatus& status,
412 const std::string& security_info,
413 const base::Time& completion_time) {
414 PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
415 if (!request_info)
416 return;
418 webkit_glue::ResourceLoaderBridge::Peer* peer = request_info->peer;
420 if (delegate_) {
421 webkit_glue::ResourceLoaderBridge::Peer* new_peer =
422 delegate_->OnRequestComplete(
423 request_info->peer, request_info->resource_type, status);
424 if (new_peer)
425 request_info->peer = new_peer;
428 // The request ID will be removed from our pending list in the destructor.
429 // Normally, dispatching this message causes the reference-counted request to
430 // die immediately.
431 peer->OnCompletedRequest(status, security_info, completion_time);
434 int ResourceDispatcher::AddPendingRequest(
435 webkit_glue::ResourceLoaderBridge::Peer* callback,
436 ResourceType::Type resource_type,
437 const GURL& request_url) {
438 // Compute a unique request_id for this renderer process.
439 int id = MakeRequestID();
440 pending_requests_[id] =
441 PendingRequestInfo(callback, resource_type, request_url);
442 return id;
445 bool ResourceDispatcher::RemovePendingRequest(int request_id) {
446 PendingRequestList::iterator it = pending_requests_.find(request_id);
447 if (it == pending_requests_.end())
448 return false;
450 PendingRequestInfo& request_info = it->second;
451 ReleaseResourcesInMessageQueue(&request_info.deferred_message_queue);
452 pending_requests_.erase(it);
454 return true;
457 void ResourceDispatcher::CancelPendingRequest(int routing_id,
458 int request_id) {
459 PendingRequestList::iterator it = pending_requests_.find(request_id);
460 if (it == pending_requests_.end()) {
461 DLOG(WARNING) << "unknown request";
462 return;
465 PendingRequestInfo& request_info = it->second;
466 ReleaseResourcesInMessageQueue(&request_info.deferred_message_queue);
467 pending_requests_.erase(it);
469 message_sender()->Send(
470 new ResourceHostMsg_CancelRequest(routing_id, request_id));
473 void ResourceDispatcher::SetDefersLoading(int request_id, bool value) {
474 PendingRequestList::iterator it = pending_requests_.find(request_id);
475 if (it == pending_requests_.end()) {
476 DLOG(ERROR) << "unknown request";
477 return;
479 PendingRequestInfo& request_info = it->second;
480 if (value) {
481 request_info.is_deferred = value;
482 } else if (request_info.is_deferred) {
483 request_info.is_deferred = false;
485 FollowPendingRedirect(request_id, request_info);
487 MessageLoop::current()->PostTask(FROM_HERE,
488 method_factory_.NewRunnableMethod(
489 &ResourceDispatcher::FlushDeferredMessages, request_id));
493 void ResourceDispatcher::DispatchMessage(const IPC::Message& message) {
494 IPC_BEGIN_MESSAGE_MAP(ResourceDispatcher, message)
495 IPC_MESSAGE_HANDLER(ResourceMsg_UploadProgress, OnUploadProgress)
496 IPC_MESSAGE_HANDLER(ResourceMsg_ReceivedResponse, OnReceivedResponse)
497 IPC_MESSAGE_HANDLER(ResourceMsg_ReceivedCachedMetadata,
498 OnReceivedCachedMetadata)
499 IPC_MESSAGE_HANDLER(ResourceMsg_ReceivedRedirect, OnReceivedRedirect)
500 IPC_MESSAGE_HANDLER(ResourceMsg_DataReceived, OnReceivedData)
501 IPC_MESSAGE_HANDLER(ResourceMsg_DataDownloaded, OnDownloadedData)
502 IPC_MESSAGE_HANDLER(ResourceMsg_RequestComplete, OnRequestComplete)
503 IPC_END_MESSAGE_MAP()
506 void ResourceDispatcher::FlushDeferredMessages(int request_id) {
507 PendingRequestList::iterator it = pending_requests_.find(request_id);
508 if (it == pending_requests_.end()) // The request could have become invalid.
509 return;
510 PendingRequestInfo& request_info = it->second;
511 if (request_info.is_deferred)
512 return;
513 // Because message handlers could result in request_info being destroyed,
514 // we need to work with a stack reference to the deferred queue.
515 MessageQueue q;
516 q.swap(request_info.deferred_message_queue);
517 while (!q.empty()) {
518 IPC::Message* m = q.front();
519 q.pop_front();
520 DispatchMessage(*m);
521 delete m;
522 // If this request is deferred in the context of the above message, then
523 // we should honor the same and stop dispatching further messages.
524 // We need to find the request again in the list as it may have completed
525 // by now and the request_info instance above may be invalid.
526 PendingRequestList::iterator index = pending_requests_.find(request_id);
527 if (index != pending_requests_.end()) {
528 PendingRequestInfo& pending_request = index->second;
529 if (pending_request.is_deferred) {
530 pending_request.deferred_message_queue.swap(q);
531 return;
537 webkit_glue::ResourceLoaderBridge* ResourceDispatcher::CreateBridge(
538 const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info) {
539 return new webkit_glue::IPCResourceLoaderBridge(this, request_info);
542 bool ResourceDispatcher::IsResourceDispatcherMessage(
543 const IPC::Message& message) {
544 switch (message.type()) {
545 case ResourceMsg_UploadProgress::ID:
546 case ResourceMsg_ReceivedResponse::ID:
547 case ResourceMsg_ReceivedCachedMetadata::ID:
548 case ResourceMsg_ReceivedRedirect::ID:
549 case ResourceMsg_DataReceived::ID:
550 case ResourceMsg_DataDownloaded::ID:
551 case ResourceMsg_RequestComplete::ID:
552 return true;
554 default:
555 break;
558 return false;
561 // static
562 void ResourceDispatcher::ReleaseResourcesInDataMessage(
563 const IPC::Message& message) {
564 void* iter = NULL;
565 int request_id;
566 if (!message.ReadInt(&iter, &request_id)) {
567 NOTREACHED() << "malformed resource message";
568 return;
571 // If the message contains a shared memory handle, we should close the
572 // handle or there will be a memory leak.
573 if (message.type() == ResourceMsg_DataReceived::ID) {
574 base::SharedMemoryHandle shm_handle;
575 if (IPC::ParamTraits<base::SharedMemoryHandle>::Read(&message,
576 &iter,
577 &shm_handle)) {
578 base::SharedMemory::CloseHandle(shm_handle);
583 // static
584 void ResourceDispatcher::ReleaseResourcesInMessageQueue(MessageQueue* queue) {
585 while (!queue->empty()) {
586 IPC::Message* message = queue->front();
587 ReleaseResourcesInDataMessage(*message);
588 queue->pop_front();
589 delete message;