When enabling new profile management programmatically, make sure to set the
[chromium-blink-merge.git] / content / browser / loader / async_resource_handler.cc
blob69e831bd29849f323538c65c841323dbed1be4f4
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 "content/browser/devtools/devtools_netlog_observer.h"
18 #include "content/browser/host_zoom_map_impl.h"
19 #include "content/browser/loader/resource_buffer.h"
20 #include "content/browser/loader/resource_dispatcher_host_impl.h"
21 #include "content/browser/loader/resource_message_filter.h"
22 #include "content/browser/loader/resource_request_info_impl.h"
23 #include "content/browser/resource_context_impl.h"
24 #include "content/common/resource_messages.h"
25 #include "content/common/view_messages.h"
26 #include "content/public/browser/resource_dispatcher_host_delegate.h"
27 #include "content/public/common/resource_response.h"
28 #include "net/base/io_buffer.h"
29 #include "net/base/load_flags.h"
30 #include "net/base/net_log.h"
31 #include "net/base/net_util.h"
33 using base::TimeTicks;
35 namespace content {
36 namespace {
38 static int kBufferSize = 1024 * 512;
39 static int kMinAllocationSize = 1024 * 4;
40 static int kMaxAllocationSize = 1024 * 32;
42 void GetNumericArg(const std::string& name, int* result) {
43 const std::string& value =
44 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name);
45 if (!value.empty())
46 base::StringToInt(value, result);
49 void InitializeResourceBufferConstants() {
50 static bool did_init = false;
51 if (did_init)
52 return;
53 did_init = true;
55 GetNumericArg("resource-buffer-size", &kBufferSize);
56 GetNumericArg("resource-buffer-min-allocation-size", &kMinAllocationSize);
57 GetNumericArg("resource-buffer-max-allocation-size", &kMaxAllocationSize);
60 int CalcUsedPercentage(int bytes_read, int buffer_size) {
61 double ratio = static_cast<double>(bytes_read) / buffer_size;
62 return static_cast<int>(ratio * 100.0 + 0.5); // Round to nearest integer.
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 virtual ~DependentIOBuffer() {}
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 reported_transfer_size_(0) {
91 InitializeResourceBufferConstants();
94 AsyncResourceHandler::~AsyncResourceHandler() {
95 if (has_checked_for_sufficient_resources_)
96 rdh_->FinishedWithResourcesForRequest(request());
99 bool AsyncResourceHandler::OnMessageReceived(const IPC::Message& message) {
100 bool handled = true;
101 IPC_BEGIN_MESSAGE_MAP(AsyncResourceHandler, message)
102 IPC_MESSAGE_HANDLER(ResourceHostMsg_FollowRedirect, OnFollowRedirect)
103 IPC_MESSAGE_HANDLER(ResourceHostMsg_DataReceived_ACK, OnDataReceivedACK)
104 IPC_MESSAGE_UNHANDLED(handled = false)
105 IPC_END_MESSAGE_MAP()
106 return handled;
109 void AsyncResourceHandler::OnFollowRedirect(int request_id) {
110 if (!request()->status().is_success()) {
111 DVLOG(1) << "OnFollowRedirect for invalid request";
112 return;
115 ResumeIfDeferred();
118 void AsyncResourceHandler::OnDataReceivedACK(int request_id) {
119 if (pending_data_count_) {
120 --pending_data_count_;
122 buffer_->RecycleLeastRecentlyAllocated();
123 if (buffer_->CanAllocate())
124 ResumeIfDeferred();
128 bool AsyncResourceHandler::OnUploadProgress(uint64 position,
129 uint64 size) {
130 ResourceMessageFilter* filter = GetFilter();
131 if (!filter)
132 return false;
133 return filter->Send(
134 new ResourceMsg_UploadProgress(GetRequestID(), position, size));
137 bool AsyncResourceHandler::OnRequestRedirected(const GURL& new_url,
138 ResourceResponse* response,
139 bool* defer) {
140 const ResourceRequestInfoImpl* info = GetRequestInfo();
141 if (!info->filter())
142 return false;
144 *defer = did_defer_ = true;
145 OnDefer();
147 if (rdh_->delegate()) {
148 rdh_->delegate()->OnRequestRedirected(
149 new_url, request(), info->GetContext(), response);
152 DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
153 response->head.encoded_data_length = request()->GetTotalReceivedBytes();
154 reported_transfer_size_ = 0;
155 response->head.request_start = request()->creation_time();
156 response->head.response_start = TimeTicks::Now();
157 // TODO(davidben): Is it necessary to pass the new first party URL for
158 // cookies? The only case where it can change is top-level navigation requests
159 // and hopefully those will eventually all be owned by the browser. It's
160 // possible this is still needed while renderer-owned ones exist.
161 return info->filter()->Send(new ResourceMsg_ReceivedRedirect(
162 GetRequestID(), new_url, request()->first_party_for_cookies(),
163 response->head));
166 bool AsyncResourceHandler::OnResponseStarted(ResourceResponse* response,
167 bool* defer) {
168 // For changes to the main frame, inform the renderer of the new URL's
169 // per-host settings before the request actually commits. This way the
170 // renderer will be able to set these precisely at the time the
171 // request commits, avoiding the possibility of e.g. zooming the old content
172 // or of having to layout the new content twice.
174 const ResourceRequestInfoImpl* info = GetRequestInfo();
175 if (!info->filter())
176 return false;
178 if (rdh_->delegate()) {
179 rdh_->delegate()->OnResponseStarted(
180 request(), info->GetContext(), response, info->filter());
183 DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
185 HostZoomMap* host_zoom_map =
186 GetHostZoomMapForResourceContext(info->GetContext());
188 if (info->GetResourceType() == ResourceType::MAIN_FRAME && host_zoom_map) {
189 const GURL& request_url = request()->url();
190 info->filter()->Send(new ViewMsg_SetZoomLevelForLoadingURL(
191 info->GetRouteID(),
192 request_url, host_zoom_map->GetZoomLevelForHostAndScheme(
193 request_url.scheme(),
194 net::GetHostOrSpecFromURL(request_url))));
197 // If the parent handler downloaded the resource to a file, grant the child
198 // read permissions on it.
199 if (!response->head.download_file_path.empty()) {
200 rdh_->RegisterDownloadedTempFile(
201 info->GetChildID(), info->GetRequestID(),
202 response->head.download_file_path);
205 response->head.request_start = request()->creation_time();
206 response->head.response_start = TimeTicks::Now();
207 info->filter()->Send(new ResourceMsg_ReceivedResponse(GetRequestID(),
208 response->head));
209 sent_received_response_msg_ = true;
211 if (request()->response_info().metadata.get()) {
212 std::vector<char> copy(request()->response_info().metadata->data(),
213 request()->response_info().metadata->data() +
214 request()->response_info().metadata->size());
215 info->filter()->Send(new ResourceMsg_ReceivedCachedMetadata(GetRequestID(),
216 copy));
219 return true;
222 bool AsyncResourceHandler::OnWillStart(const GURL& url, bool* defer) {
223 return true;
226 bool AsyncResourceHandler::OnBeforeNetworkStart(const GURL& url, bool* defer) {
227 return true;
230 bool AsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
231 int* buf_size,
232 int min_size) {
233 DCHECK_EQ(-1, min_size);
235 if (!EnsureResourceBufferIsInitialized())
236 return false;
238 DCHECK(buffer_->CanAllocate());
239 char* memory = buffer_->Allocate(&allocation_size_);
240 CHECK(memory);
242 *buf = new DependentIOBuffer(buffer_.get(), memory);
243 *buf_size = allocation_size_;
245 UMA_HISTOGRAM_CUSTOM_COUNTS(
246 "Net.AsyncResourceHandler_SharedIOBuffer_Alloc",
247 *buf_size, 0, kMaxAllocationSize, 100);
248 return true;
251 bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
252 DCHECK_GE(bytes_read, 0);
254 if (!bytes_read)
255 return true;
257 ResourceMessageFilter* filter = GetFilter();
258 if (!filter)
259 return false;
261 buffer_->ShrinkLastAllocation(bytes_read);
263 UMA_HISTOGRAM_CUSTOM_COUNTS(
264 "Net.AsyncResourceHandler_SharedIOBuffer_Used",
265 bytes_read, 0, kMaxAllocationSize, 100);
266 UMA_HISTOGRAM_PERCENTAGE(
267 "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage",
268 CalcUsedPercentage(bytes_read, allocation_size_));
270 if (!sent_first_data_msg_) {
271 base::SharedMemoryHandle handle;
272 int size;
273 if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size))
274 return false;
275 filter->Send(new ResourceMsg_SetDataBuffer(
276 GetRequestID(), handle, size, filter->peer_pid()));
277 sent_first_data_msg_ = true;
280 int data_offset = buffer_->GetLastAllocationOffset();
282 int64_t current_transfer_size = request()->GetTotalReceivedBytes();
283 int encoded_data_length = current_transfer_size - reported_transfer_size_;
284 reported_transfer_size_ = current_transfer_size;
286 filter->Send(new ResourceMsg_DataReceived(
287 GetRequestID(), data_offset, bytes_read, encoded_data_length));
288 ++pending_data_count_;
289 UMA_HISTOGRAM_CUSTOM_COUNTS(
290 "Net.AsyncResourceHandler_PendingDataCount",
291 pending_data_count_, 0, 100, 100);
293 if (!buffer_->CanAllocate()) {
294 UMA_HISTOGRAM_CUSTOM_COUNTS(
295 "Net.AsyncResourceHandler_PendingDataCount_WhenFull",
296 pending_data_count_, 0, 100, 100);
297 *defer = did_defer_ = true;
298 OnDefer();
301 return true;
304 void AsyncResourceHandler::OnDataDownloaded(int bytes_downloaded) {
305 int64_t current_transfer_size = request()->GetTotalReceivedBytes();
306 int encoded_data_length = current_transfer_size - reported_transfer_size_;
307 reported_transfer_size_ = current_transfer_size;
309 ResourceMessageFilter* filter = GetFilter();
310 if (filter) {
311 filter->Send(new ResourceMsg_DataDownloaded(
312 GetRequestID(), bytes_downloaded, encoded_data_length));
316 void AsyncResourceHandler::OnResponseCompleted(
317 const net::URLRequestStatus& status,
318 const std::string& security_info,
319 bool* defer) {
320 const ResourceRequestInfoImpl* info = GetRequestInfo();
321 if (!info->filter())
322 return;
324 // If we crash here, figure out what URL the renderer was requesting.
325 // http://crbug.com/107692
326 char url_buf[128];
327 base::strlcpy(url_buf, request()->url().spec().c_str(), arraysize(url_buf));
328 base::debug::Alias(url_buf);
330 // TODO(gavinp): Remove this CHECK when we figure out the cause of
331 // http://crbug.com/124680 . This check mirrors closely check in
332 // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore
333 // ResourceHandleInternal which asserts on its state and crashes. By crashing
334 // when the message is sent, we should get better crash reports.
335 CHECK(status.status() != net::URLRequestStatus::SUCCESS ||
336 sent_received_response_msg_);
338 int error_code = status.error();
339 bool was_ignored_by_handler = info->WasIgnoredByHandler();
341 DCHECK(status.status() != net::URLRequestStatus::IO_PENDING);
342 // If this check fails, then we're in an inconsistent state because all
343 // requests ignored by the handler should be canceled (which should result in
344 // the ERR_ABORTED error code).
345 DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED);
347 // TODO(mkosiba): Fix up cases where we create a URLRequestStatus
348 // with a status() != SUCCESS and an error_code() == net::OK.
349 if (status.status() == net::URLRequestStatus::CANCELED &&
350 error_code == net::OK) {
351 error_code = net::ERR_ABORTED;
352 } else if (status.status() == net::URLRequestStatus::FAILED &&
353 error_code == net::OK) {
354 error_code = net::ERR_FAILED;
357 ResourceMsg_RequestCompleteData request_complete_data;
358 request_complete_data.error_code = error_code;
359 request_complete_data.was_ignored_by_handler = was_ignored_by_handler;
360 request_complete_data.exists_in_cache = request()->response_info().was_cached;
361 request_complete_data.security_info = security_info;
362 request_complete_data.completion_time = TimeTicks::Now();
363 request_complete_data.encoded_data_length =
364 request()->GetTotalReceivedBytes();
365 info->filter()->Send(
366 new ResourceMsg_RequestComplete(GetRequestID(), request_complete_data));
369 bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
370 if (buffer_.get() && buffer_->IsInitialized())
371 return true;
373 if (!has_checked_for_sufficient_resources_) {
374 has_checked_for_sufficient_resources_ = true;
375 if (!rdh_->HasSufficientResourcesForRequest(request())) {
376 controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
377 return false;
381 buffer_ = new ResourceBuffer();
382 return buffer_->Initialize(kBufferSize,
383 kMinAllocationSize,
384 kMaxAllocationSize);
387 void AsyncResourceHandler::ResumeIfDeferred() {
388 if (did_defer_) {
389 did_defer_ = false;
390 request()->LogUnblocked();
391 controller()->Resume();
395 void AsyncResourceHandler::OnDefer() {
396 request()->LogBlockedBy("AsyncResourceHandler");
399 } // namespace content