Roll src/third_party/WebKit 3a0ba1e:fcd7003 (svn 191141:191149)
[chromium-blink-merge.git] / content / browser / loader / async_resource_handler.cc
blob7c1ad3386958c28c75d3a5e05375b2b79d4e4e22
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.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_log.h"
32 #include "net/base/net_util.h"
33 #include "net/url_request/redirect_info.h"
35 using base::TimeTicks;
37 namespace content {
38 namespace {
40 static int kBufferSize = 1024 * 512;
41 static int kMinAllocationSize = 1024 * 4;
42 static int kMaxAllocationSize = 1024 * 32;
44 void GetNumericArg(const std::string& name, int* result) {
45 const std::string& value =
46 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name);
47 if (!value.empty())
48 base::StringToInt(value, result);
51 void InitializeResourceBufferConstants() {
52 static bool did_init = false;
53 if (did_init)
54 return;
55 did_init = true;
57 GetNumericArg("resource-buffer-size", &kBufferSize);
58 GetNumericArg("resource-buffer-min-allocation-size", &kMinAllocationSize);
59 GetNumericArg("resource-buffer-max-allocation-size", &kMaxAllocationSize);
62 int CalcUsedPercentage(int bytes_read, int buffer_size) {
63 double ratio = static_cast<double>(bytes_read) / buffer_size;
64 return static_cast<int>(ratio * 100.0 + 0.5); // Round to nearest integer.
67 } // namespace
69 class DependentIOBuffer : public net::WrappedIOBuffer {
70 public:
71 DependentIOBuffer(ResourceBuffer* backing, char* memory)
72 : net::WrappedIOBuffer(memory),
73 backing_(backing) {
75 private:
76 ~DependentIOBuffer() override {}
77 scoped_refptr<ResourceBuffer> backing_;
80 AsyncResourceHandler::AsyncResourceHandler(
81 net::URLRequest* request,
82 ResourceDispatcherHostImpl* rdh)
83 : ResourceHandler(request),
84 ResourceMessageDelegate(request),
85 rdh_(rdh),
86 pending_data_count_(0),
87 allocation_size_(0),
88 did_defer_(false),
89 has_checked_for_sufficient_resources_(false),
90 sent_received_response_msg_(false),
91 sent_first_data_msg_(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_UNHANDLED(handled = false)
107 IPC_END_MESSAGE_MAP()
108 return handled;
111 void AsyncResourceHandler::OnFollowRedirect(int request_id) {
112 if (!request()->status().is_success()) {
113 DVLOG(1) << "OnFollowRedirect for invalid request";
114 return;
117 if (!redirect_start_time_.is_null()) {
118 UMA_HISTOGRAM_TIMES("Net.AsyncResourceHandler_RedirectHopTime",
119 TimeTicks::Now() - redirect_start_time_);
120 // Reset start time.
121 redirect_start_time_ = TimeTicks();
124 ResumeIfDeferred();
127 void AsyncResourceHandler::OnDataReceivedACK(int request_id) {
128 if (pending_data_count_) {
129 --pending_data_count_;
131 buffer_->RecycleLeastRecentlyAllocated();
132 if (buffer_->CanAllocate())
133 ResumeIfDeferred();
137 bool AsyncResourceHandler::OnUploadProgress(uint64 position,
138 uint64 size) {
139 ResourceMessageFilter* filter = GetFilter();
140 if (!filter)
141 return false;
142 return filter->Send(
143 new ResourceMsg_UploadProgress(GetRequestID(), position, size));
146 bool AsyncResourceHandler::OnRequestRedirected(
147 const net::RedirectInfo& redirect_info,
148 ResourceResponse* response,
149 bool* defer) {
150 const ResourceRequestInfoImpl* info = GetRequestInfo();
151 if (!info->filter())
152 return false;
154 redirect_start_time_ = TimeTicks::Now();
156 *defer = did_defer_ = true;
157 OnDefer();
159 if (rdh_->delegate()) {
160 rdh_->delegate()->OnRequestRedirected(
161 redirect_info.new_url, request(), info->GetContext(), response);
164 DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
165 response->head.encoded_data_length = request()->GetTotalReceivedBytes();
166 reported_transfer_size_ = 0;
167 response->head.request_start = request()->creation_time();
168 response->head.response_start = TimeTicks::Now();
169 // TODO(davidben): Is it necessary to pass the new first party URL for
170 // cookies? The only case where it can change is top-level navigation requests
171 // and hopefully those will eventually all be owned by the browser. It's
172 // possible this is still needed while renderer-owned ones exist.
173 return info->filter()->Send(new ResourceMsg_ReceivedRedirect(
174 GetRequestID(), redirect_info, response->head));
177 bool AsyncResourceHandler::OnResponseStarted(ResourceResponse* response,
178 bool* defer) {
179 // For changes to the main frame, inform the renderer of the new URL's
180 // per-host settings before the request actually commits. This way the
181 // renderer will be able to set these precisely at the time the
182 // request commits, avoiding the possibility of e.g. zooming the old content
183 // or of having to layout the new content twice.
185 const ResourceRequestInfoImpl* info = GetRequestInfo();
186 if (!info->filter())
187 return false;
189 if (rdh_->delegate()) {
190 rdh_->delegate()->OnResponseStarted(
191 request(), info->GetContext(), response, info->filter());
194 DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
196 const HostZoomMapImpl* host_zoom_map =
197 static_cast<const HostZoomMapImpl*>(info->filter()->GetHostZoomMap());
199 if (info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME && host_zoom_map) {
200 const GURL& request_url = request()->url();
201 int render_process_id = info->GetChildID();
202 int render_view_id = info->GetRouteID();
204 double zoom_level = host_zoom_map->GetZoomLevelForView(
205 request_url, render_process_id, render_view_id);
207 info->filter()->Send(new ViewMsg_SetZoomLevelForLoadingURL(
208 render_view_id, request_url, zoom_level));
211 // If the parent handler downloaded the resource to a file, grant the child
212 // read permissions on it.
213 if (!response->head.download_file_path.empty()) {
214 rdh_->RegisterDownloadedTempFile(
215 info->GetChildID(), info->GetRequestID(),
216 response->head.download_file_path);
219 response->head.request_start = request()->creation_time();
220 response->head.response_start = TimeTicks::Now();
221 info->filter()->Send(new ResourceMsg_ReceivedResponse(GetRequestID(),
222 response->head));
223 sent_received_response_msg_ = true;
225 if (request()->response_info().metadata.get()) {
226 std::vector<char> copy(request()->response_info().metadata->data(),
227 request()->response_info().metadata->data() +
228 request()->response_info().metadata->size());
229 info->filter()->Send(new ResourceMsg_ReceivedCachedMetadata(GetRequestID(),
230 copy));
233 return true;
236 bool AsyncResourceHandler::OnWillStart(const GURL& url, bool* defer) {
237 return true;
240 bool AsyncResourceHandler::OnBeforeNetworkStart(const GURL& url, bool* defer) {
241 return true;
244 bool AsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
245 int* buf_size,
246 int min_size) {
247 DCHECK_EQ(-1, min_size);
249 if (!EnsureResourceBufferIsInitialized())
250 return false;
252 DCHECK(buffer_->CanAllocate());
253 char* memory = buffer_->Allocate(&allocation_size_);
254 CHECK(memory);
256 *buf = new DependentIOBuffer(buffer_.get(), memory);
257 *buf_size = allocation_size_;
259 UMA_HISTOGRAM_CUSTOM_COUNTS(
260 "Net.AsyncResourceHandler_SharedIOBuffer_Alloc",
261 *buf_size, 0, kMaxAllocationSize, 100);
262 return true;
265 bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
266 DCHECK_GE(bytes_read, 0);
268 if (!bytes_read)
269 return true;
271 ResourceMessageFilter* filter = GetFilter();
272 if (!filter)
273 return false;
275 buffer_->ShrinkLastAllocation(bytes_read);
277 UMA_HISTOGRAM_CUSTOM_COUNTS(
278 "Net.AsyncResourceHandler_SharedIOBuffer_Used",
279 bytes_read, 0, kMaxAllocationSize, 100);
280 UMA_HISTOGRAM_PERCENTAGE(
281 "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage",
282 CalcUsedPercentage(bytes_read, allocation_size_));
284 if (!sent_first_data_msg_) {
285 base::SharedMemoryHandle handle;
286 int size;
287 if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size))
288 return false;
289 filter->Send(new ResourceMsg_SetDataBuffer(
290 GetRequestID(), handle, size, filter->peer_pid()));
291 sent_first_data_msg_ = true;
294 int data_offset = buffer_->GetLastAllocationOffset();
296 int64_t current_transfer_size = request()->GetTotalReceivedBytes();
297 int encoded_data_length = current_transfer_size - reported_transfer_size_;
298 reported_transfer_size_ = current_transfer_size;
300 filter->Send(new ResourceMsg_DataReceived(
301 GetRequestID(), data_offset, bytes_read, encoded_data_length));
302 ++pending_data_count_;
303 UMA_HISTOGRAM_CUSTOM_COUNTS(
304 "Net.AsyncResourceHandler_PendingDataCount",
305 pending_data_count_, 0, 100, 100);
307 if (!buffer_->CanAllocate()) {
308 UMA_HISTOGRAM_CUSTOM_COUNTS(
309 "Net.AsyncResourceHandler_PendingDataCount_WhenFull",
310 pending_data_count_, 0, 100, 100);
311 *defer = did_defer_ = true;
312 OnDefer();
315 return true;
318 void AsyncResourceHandler::OnDataDownloaded(int bytes_downloaded) {
319 int64_t current_transfer_size = request()->GetTotalReceivedBytes();
320 int encoded_data_length = current_transfer_size - reported_transfer_size_;
321 reported_transfer_size_ = current_transfer_size;
323 ResourceMessageFilter* filter = GetFilter();
324 if (filter) {
325 filter->Send(new ResourceMsg_DataDownloaded(
326 GetRequestID(), bytes_downloaded, encoded_data_length));
330 void AsyncResourceHandler::OnResponseCompleted(
331 const net::URLRequestStatus& status,
332 const std::string& security_info,
333 bool* defer) {
334 const ResourceRequestInfoImpl* info = GetRequestInfo();
335 if (!info->filter())
336 return;
338 // If we crash here, figure out what URL the renderer was requesting.
339 // http://crbug.com/107692
340 char url_buf[128];
341 base::strlcpy(url_buf, request()->url().spec().c_str(), arraysize(url_buf));
342 base::debug::Alias(url_buf);
344 // TODO(gavinp): Remove this CHECK when we figure out the cause of
345 // http://crbug.com/124680 . This check mirrors closely check in
346 // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore
347 // ResourceHandleInternal which asserts on its state and crashes. By crashing
348 // when the message is sent, we should get better crash reports.
349 CHECK(status.status() != net::URLRequestStatus::SUCCESS ||
350 sent_received_response_msg_);
352 int error_code = status.error();
353 bool was_ignored_by_handler = info->WasIgnoredByHandler();
355 DCHECK(status.status() != net::URLRequestStatus::IO_PENDING);
356 // If this check fails, then we're in an inconsistent state because all
357 // requests ignored by the handler should be canceled (which should result in
358 // the ERR_ABORTED error code).
359 DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED);
361 // TODO(mkosiba): Fix up cases where we create a URLRequestStatus
362 // with a status() != SUCCESS and an error_code() == net::OK.
363 if (status.status() == net::URLRequestStatus::CANCELED &&
364 error_code == net::OK) {
365 error_code = net::ERR_ABORTED;
366 } else if (status.status() == net::URLRequestStatus::FAILED &&
367 error_code == net::OK) {
368 error_code = net::ERR_FAILED;
371 ResourceMsg_RequestCompleteData request_complete_data;
372 request_complete_data.error_code = error_code;
373 request_complete_data.was_ignored_by_handler = was_ignored_by_handler;
374 request_complete_data.exists_in_cache = request()->response_info().was_cached;
375 request_complete_data.security_info = security_info;
376 request_complete_data.completion_time = TimeTicks::Now();
377 request_complete_data.encoded_data_length =
378 request()->GetTotalReceivedBytes();
379 info->filter()->Send(
380 new ResourceMsg_RequestComplete(GetRequestID(), request_complete_data));
383 bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
384 if (buffer_.get() && buffer_->IsInitialized())
385 return true;
387 if (!has_checked_for_sufficient_resources_) {
388 has_checked_for_sufficient_resources_ = true;
389 if (!rdh_->HasSufficientResourcesForRequest(request())) {
390 controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
391 return false;
395 buffer_ = new ResourceBuffer();
396 return buffer_->Initialize(kBufferSize,
397 kMinAllocationSize,
398 kMaxAllocationSize);
401 void AsyncResourceHandler::ResumeIfDeferred() {
402 if (did_defer_) {
403 did_defer_ = false;
404 request()->LogUnblocked();
405 controller()->Resume();
409 void AsyncResourceHandler::OnDefer() {
410 request()->LogBlockedBy("AsyncResourceHandler");
413 } // namespace content