Reland debugging for ipc related crash.
[chromium-blink-merge.git] / content / browser / loader / async_resource_handler.cc
blob96cb4f401ff18b0eb6ae5e2169e049cba342f5b6
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.
5 #include "content/browser/loader/async_resource_handler.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/command_line.h"
11 #include "base/containers/hash_tables.h"
12 #include "base/debug/alias.h"
13 #include "base/logging.h"
14 #include "base/memory/shared_memory.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/time/time.h"
18 #include "content/browser/devtools/devtools_netlog_observer.h"
19 #include "content/browser/host_zoom_map_impl.h"
20 #include "content/browser/loader/resource_buffer.h"
21 #include "content/browser/loader/resource_dispatcher_host_impl.h"
22 #include "content/browser/loader/resource_message_filter.h"
23 #include "content/browser/loader/resource_request_info_impl.h"
24 #include "content/browser/resource_context_impl.h"
25 #include "content/common/resource_messages.h"
26 #include "content/common/view_messages.h"
27 #include "content/public/browser/resource_dispatcher_host_delegate.h"
28 #include "content/public/common/resource_response.h"
29 #include "net/base/io_buffer.h"
30 #include "net/base/load_flags.h"
31 #include "net/base/net_util.h"
32 #include "net/log/net_log.h"
33 #include "net/url_request/redirect_info.h"
35 using base::TimeDelta;
36 using base::TimeTicks;
38 namespace content {
39 namespace {
41 static int kBufferSize = 1024 * 512;
42 static int kMinAllocationSize = 1024 * 4;
43 static int kMaxAllocationSize = 1024 * 32;
44 // The interval for calls to ReportUploadProgress.
45 static int kUploadProgressIntervalMsec = 10;
47 void GetNumericArg(const std::string& name, int* result) {
48 const std::string& value =
49 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name);
50 if (!value.empty())
51 base::StringToInt(value, result);
54 void InitializeResourceBufferConstants() {
55 static bool did_init = false;
56 if (did_init)
57 return;
58 did_init = true;
60 GetNumericArg("resource-buffer-size", &kBufferSize);
61 GetNumericArg("resource-buffer-min-allocation-size", &kMinAllocationSize);
62 GetNumericArg("resource-buffer-max-allocation-size", &kMaxAllocationSize);
65 } // namespace
67 class DependentIOBuffer : public net::WrappedIOBuffer {
68 public:
69 DependentIOBuffer(ResourceBuffer* backing, char* memory)
70 : net::WrappedIOBuffer(memory),
71 backing_(backing) {
73 private:
74 ~DependentIOBuffer() override {}
75 scoped_refptr<ResourceBuffer> backing_;
78 AsyncResourceHandler::AsyncResourceHandler(
79 net::URLRequest* request,
80 ResourceDispatcherHostImpl* rdh)
81 : ResourceHandler(request),
82 ResourceMessageDelegate(request),
83 rdh_(rdh),
84 pending_data_count_(0),
85 allocation_size_(0),
86 did_defer_(false),
87 has_checked_for_sufficient_resources_(false),
88 sent_received_response_msg_(false),
89 sent_first_data_msg_(false),
90 last_upload_position_(0),
91 waiting_for_upload_progress_ack_(false),
92 reported_transfer_size_(0) {
93 InitializeResourceBufferConstants();
96 AsyncResourceHandler::~AsyncResourceHandler() {
97 if (has_checked_for_sufficient_resources_)
98 rdh_->FinishedWithResourcesForRequest(request());
101 bool AsyncResourceHandler::OnMessageReceived(const IPC::Message& message) {
102 bool handled = true;
103 IPC_BEGIN_MESSAGE_MAP(AsyncResourceHandler, message)
104 IPC_MESSAGE_HANDLER(ResourceHostMsg_FollowRedirect, OnFollowRedirect)
105 IPC_MESSAGE_HANDLER(ResourceHostMsg_DataReceived_ACK, OnDataReceivedACK)
106 IPC_MESSAGE_HANDLER(ResourceHostMsg_UploadProgress_ACK, OnUploadProgressACK)
107 IPC_MESSAGE_UNHANDLED(handled = false)
108 IPC_END_MESSAGE_MAP()
109 return handled;
112 void AsyncResourceHandler::OnFollowRedirect(int request_id) {
113 if (!request()->status().is_success()) {
114 DVLOG(1) << "OnFollowRedirect for invalid request";
115 return;
118 ResumeIfDeferred();
121 void AsyncResourceHandler::OnDataReceivedACK(int request_id) {
122 if (pending_data_count_) {
123 --pending_data_count_;
125 buffer_->RecycleLeastRecentlyAllocated();
126 if (buffer_->CanAllocate())
127 ResumeIfDeferred();
131 void AsyncResourceHandler::OnUploadProgressACK(int request_id) {
132 waiting_for_upload_progress_ack_ = false;
135 void AsyncResourceHandler::ReportUploadProgress() {
136 DCHECK(GetRequestInfo()->is_upload_progress_enabled());
137 if (waiting_for_upload_progress_ack_)
138 return; // Send one progress event at a time.
140 net::UploadProgress progress = request()->GetUploadProgress();
141 if (!progress.size())
142 return; // Nothing to upload.
144 if (progress.position() == last_upload_position_)
145 return; // No progress made since last time.
147 const uint64 kHalfPercentIncrements = 200;
148 const TimeDelta kOneSecond = TimeDelta::FromMilliseconds(1000);
150 uint64 amt_since_last = progress.position() - last_upload_position_;
151 TimeDelta time_since_last = TimeTicks::Now() - last_upload_ticks_;
153 bool is_finished = (progress.size() == progress.position());
154 bool enough_new_progress =
155 (amt_since_last > (progress.size() / kHalfPercentIncrements));
156 bool too_much_time_passed = time_since_last > kOneSecond;
158 if (is_finished || enough_new_progress || too_much_time_passed) {
159 ResourceMessageFilter* filter = GetFilter();
160 if (filter) {
161 filter->Send(
162 new ResourceMsg_UploadProgress(GetRequestID(),
163 progress.position(),
164 progress.size()));
166 waiting_for_upload_progress_ack_ = true;
167 last_upload_ticks_ = TimeTicks::Now();
168 last_upload_position_ = progress.position();
172 bool AsyncResourceHandler::OnRequestRedirected(
173 const net::RedirectInfo& redirect_info,
174 ResourceResponse* response,
175 bool* defer) {
176 const ResourceRequestInfoImpl* info = GetRequestInfo();
177 if (!info->filter())
178 return false;
180 *defer = did_defer_ = true;
181 OnDefer();
183 if (rdh_->delegate()) {
184 rdh_->delegate()->OnRequestRedirected(
185 redirect_info.new_url, request(), info->GetContext(), response);
188 DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
189 response->head.encoded_data_length = request()->GetTotalReceivedBytes();
190 reported_transfer_size_ = 0;
191 response->head.request_start = request()->creation_time();
192 response->head.response_start = TimeTicks::Now();
193 // TODO(davidben): Is it necessary to pass the new first party URL for
194 // cookies? The only case where it can change is top-level navigation requests
195 // and hopefully those will eventually all be owned by the browser. It's
196 // possible this is still needed while renderer-owned ones exist.
197 return info->filter()->Send(new ResourceMsg_ReceivedRedirect(
198 GetRequestID(), redirect_info, response->head));
201 bool AsyncResourceHandler::OnResponseStarted(ResourceResponse* response,
202 bool* defer) {
203 // For changes to the main frame, inform the renderer of the new URL's
204 // per-host settings before the request actually commits. This way the
205 // renderer will be able to set these precisely at the time the
206 // request commits, avoiding the possibility of e.g. zooming the old content
207 // or of having to layout the new content twice.
209 progress_timer_.Stop();
210 const ResourceRequestInfoImpl* info = GetRequestInfo();
211 if (!info->filter())
212 return false;
214 // We want to send a final upload progress message prior to sending the
215 // response complete message even if we're waiting for an ack to to a
216 // previous upload progress message.
217 if (info->is_upload_progress_enabled()) {
218 waiting_for_upload_progress_ack_ = false;
219 ReportUploadProgress();
222 if (rdh_->delegate()) {
223 rdh_->delegate()->OnResponseStarted(
224 request(), info->GetContext(), response, info->filter());
227 DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
229 const HostZoomMapImpl* host_zoom_map =
230 static_cast<const HostZoomMapImpl*>(info->filter()->GetHostZoomMap());
232 if (info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME && host_zoom_map) {
233 const GURL& request_url = request()->url();
234 int render_process_id = info->GetChildID();
235 int render_view_id = info->GetRouteID();
237 double zoom_level = host_zoom_map->GetZoomLevelForView(
238 request_url, render_process_id, render_view_id);
240 info->filter()->Send(new ViewMsg_SetZoomLevelForLoadingURL(
241 render_view_id, request_url, zoom_level));
244 // If the parent handler downloaded the resource to a file, grant the child
245 // read permissions on it.
246 if (!response->head.download_file_path.empty()) {
247 rdh_->RegisterDownloadedTempFile(
248 info->GetChildID(), info->GetRequestID(),
249 response->head.download_file_path);
252 response->head.request_start = request()->creation_time();
253 response->head.response_start = TimeTicks::Now();
254 info->filter()->Send(new ResourceMsg_ReceivedResponse(GetRequestID(),
255 response->head));
256 sent_received_response_msg_ = true;
258 if (request()->response_info().metadata.get()) {
259 std::vector<char> copy(request()->response_info().metadata->data(),
260 request()->response_info().metadata->data() +
261 request()->response_info().metadata->size());
262 info->filter()->Send(new ResourceMsg_ReceivedCachedMetadata(GetRequestID(),
263 copy));
266 return true;
269 bool AsyncResourceHandler::OnWillStart(const GURL& url, bool* defer) {
270 if (GetRequestInfo()->is_upload_progress_enabled() &&
271 request()->has_upload()) {
272 ReportUploadProgress();
273 progress_timer_.Start(
274 FROM_HERE,
275 base::TimeDelta::FromMilliseconds(kUploadProgressIntervalMsec),
276 this,
277 &AsyncResourceHandler::ReportUploadProgress);
279 return true;
282 bool AsyncResourceHandler::OnBeforeNetworkStart(const GURL& url, bool* defer) {
283 return true;
286 bool AsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
287 int* buf_size,
288 int min_size) {
289 DCHECK_EQ(-1, min_size);
291 if (!EnsureResourceBufferIsInitialized())
292 return false;
294 DCHECK(buffer_->CanAllocate());
295 char* memory = buffer_->Allocate(&allocation_size_);
296 CHECK(memory);
298 *buf = new DependentIOBuffer(buffer_.get(), memory);
299 *buf_size = allocation_size_;
301 return true;
304 bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
305 DCHECK_GE(bytes_read, 0);
307 if (!bytes_read)
308 return true;
310 ResourceMessageFilter* filter = GetFilter();
311 if (!filter)
312 return false;
314 buffer_->ShrinkLastAllocation(bytes_read);
316 if (!sent_first_data_msg_) {
317 base::SharedMemoryHandle handle;
318 int size;
319 if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size))
320 return false;
321 filter->Send(new ResourceMsg_SetDataBuffer(
322 GetRequestID(), handle, size, filter->peer_pid()));
323 sent_first_data_msg_ = true;
326 int data_offset = buffer_->GetLastAllocationOffset();
328 int64_t current_transfer_size = request()->GetTotalReceivedBytes();
329 int encoded_data_length = current_transfer_size - reported_transfer_size_;
330 reported_transfer_size_ = current_transfer_size;
332 // TODO(erikchen): Remove me after finished debugging for
333 // http://crbug.com/527588.
334 // The data offset can't exceed the size of the buffer.
335 CHECK_LE(data_offset, kBufferSize);
337 filter->Send(new ResourceMsg_DataReceived(
338 GetRequestID(), data_offset, bytes_read, encoded_data_length));
339 ++pending_data_count_;
341 if (!buffer_->CanAllocate()) {
342 *defer = did_defer_ = true;
343 OnDefer();
346 return true;
349 void AsyncResourceHandler::OnDataDownloaded(int bytes_downloaded) {
350 int64_t current_transfer_size = request()->GetTotalReceivedBytes();
351 int encoded_data_length = current_transfer_size - reported_transfer_size_;
352 reported_transfer_size_ = current_transfer_size;
354 ResourceMessageFilter* filter = GetFilter();
355 if (filter) {
356 filter->Send(new ResourceMsg_DataDownloaded(
357 GetRequestID(), bytes_downloaded, encoded_data_length));
361 void AsyncResourceHandler::OnResponseCompleted(
362 const net::URLRequestStatus& status,
363 const std::string& security_info,
364 bool* defer) {
365 const ResourceRequestInfoImpl* info = GetRequestInfo();
366 if (!info->filter())
367 return;
369 // If we crash here, figure out what URL the renderer was requesting.
370 // http://crbug.com/107692
371 char url_buf[128];
372 base::strlcpy(url_buf, request()->url().spec().c_str(), arraysize(url_buf));
373 base::debug::Alias(url_buf);
375 // TODO(gavinp): Remove this CHECK when we figure out the cause of
376 // http://crbug.com/124680 . This check mirrors closely check in
377 // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore
378 // ResourceHandleInternal which asserts on its state and crashes. By crashing
379 // when the message is sent, we should get better crash reports.
380 CHECK(status.status() != net::URLRequestStatus::SUCCESS ||
381 sent_received_response_msg_);
383 int error_code = status.error();
384 bool was_ignored_by_handler = info->WasIgnoredByHandler();
386 DCHECK(status.status() != net::URLRequestStatus::IO_PENDING);
387 // If this check fails, then we're in an inconsistent state because all
388 // requests ignored by the handler should be canceled (which should result in
389 // the ERR_ABORTED error code).
390 DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED);
392 // TODO(mkosiba): Fix up cases where we create a URLRequestStatus
393 // with a status() != SUCCESS and an error_code() == net::OK.
394 if (status.status() == net::URLRequestStatus::CANCELED &&
395 error_code == net::OK) {
396 error_code = net::ERR_ABORTED;
397 } else if (status.status() == net::URLRequestStatus::FAILED &&
398 error_code == net::OK) {
399 error_code = net::ERR_FAILED;
402 ResourceMsg_RequestCompleteData request_complete_data;
403 request_complete_data.error_code = error_code;
404 request_complete_data.was_ignored_by_handler = was_ignored_by_handler;
405 request_complete_data.exists_in_cache = request()->response_info().was_cached;
406 request_complete_data.security_info = security_info;
407 request_complete_data.completion_time = TimeTicks::Now();
408 request_complete_data.encoded_data_length =
409 request()->GetTotalReceivedBytes();
410 info->filter()->Send(
411 new ResourceMsg_RequestComplete(GetRequestID(), request_complete_data));
414 bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
415 if (buffer_.get() && buffer_->IsInitialized())
416 return true;
418 if (!has_checked_for_sufficient_resources_) {
419 has_checked_for_sufficient_resources_ = true;
420 if (!rdh_->HasSufficientResourcesForRequest(request())) {
421 controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
422 return false;
426 buffer_ = new ResourceBuffer();
427 return buffer_->Initialize(kBufferSize,
428 kMinAllocationSize,
429 kMaxAllocationSize);
432 void AsyncResourceHandler::ResumeIfDeferred() {
433 if (did_defer_) {
434 did_defer_ = false;
435 request()->LogUnblocked();
436 controller()->Resume();
440 void AsyncResourceHandler::OnDefer() {
441 request()->LogBlockedBy("AsyncResourceHandler");
444 } // namespace content