- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / loader / async_resource_handler.cc
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 #include "content/browser/loader/async_resource_handler.h"
6
7 #include <algorithm>
8 #include <vector>
9
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/global_request_id.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
34 using base::TimeTicks;
35
36 namespace content {
37 namespace {
38
39 static int kBufferSize = 1024 * 512;
40 static int kMinAllocationSize = 1024 * 4;
41 static int kMaxAllocationSize = 1024 * 32;
42
43 void GetNumericArg(const std::string& name, int* result) {
44   const std::string& value =
45       CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name);
46   if (!value.empty())
47     base::StringToInt(value, result);
48 }
49
50 void InitializeResourceBufferConstants() {
51   static bool did_init = false;
52   if (did_init)
53     return;
54   did_init = true;
55
56   GetNumericArg("resource-buffer-size", &kBufferSize);
57   GetNumericArg("resource-buffer-min-allocation-size", &kMinAllocationSize);
58   GetNumericArg("resource-buffer-max-allocation-size", &kMaxAllocationSize);
59 }
60
61 int CalcUsedPercentage(int bytes_read, int buffer_size) {
62   double ratio = static_cast<double>(bytes_read) / buffer_size;
63   return static_cast<int>(ratio * 100.0 + 0.5);  // Round to nearest integer.
64 }
65
66 }  // namespace
67
68 class DependentIOBuffer : public net::WrappedIOBuffer {
69  public:
70   DependentIOBuffer(ResourceBuffer* backing, char* memory)
71       : net::WrappedIOBuffer(memory),
72         backing_(backing) {
73   }
74  private:
75   virtual ~DependentIOBuffer() {}
76   scoped_refptr<ResourceBuffer> backing_;
77 };
78
79 AsyncResourceHandler::AsyncResourceHandler(
80     net::URLRequest* request,
81     ResourceDispatcherHostImpl* rdh)
82     : ResourceHandler(request),
83       ResourceMessageDelegate(request),
84       rdh_(rdh),
85       pending_data_count_(0),
86       allocation_size_(0),
87       did_defer_(false),
88       has_checked_for_sufficient_resources_(false),
89       sent_received_response_msg_(false),
90       sent_first_data_msg_(false) {
91   InitializeResourceBufferConstants();
92 }
93
94 AsyncResourceHandler::~AsyncResourceHandler() {
95   if (has_checked_for_sufficient_resources_)
96     rdh_->FinishedWithResourcesForRequest(request());
97 }
98
99 bool AsyncResourceHandler::OnMessageReceived(const IPC::Message& message,
100                                              bool* message_was_ok) {
101   bool handled = true;
102   IPC_BEGIN_MESSAGE_MAP_EX(AsyncResourceHandler, message, *message_was_ok)
103     IPC_MESSAGE_HANDLER(ResourceHostMsg_FollowRedirect, OnFollowRedirect)
104     IPC_MESSAGE_HANDLER(ResourceHostMsg_DataReceived_ACK, OnDataReceivedACK)
105     IPC_MESSAGE_UNHANDLED(handled = false)
106   IPC_END_MESSAGE_MAP_EX()
107   return handled;
108 }
109
110 void AsyncResourceHandler::OnFollowRedirect(
111     int request_id,
112     bool has_new_first_party_for_cookies,
113     const GURL& new_first_party_for_cookies) {
114   if (!request()->status().is_success()) {
115     DVLOG(1) << "OnFollowRedirect for invalid request";
116     return;
117   }
118
119   if (has_new_first_party_for_cookies)
120     request()->set_first_party_for_cookies(new_first_party_for_cookies);
121
122   ResumeIfDeferred();
123 }
124
125 void AsyncResourceHandler::OnDataReceivedACK(int request_id) {
126   if (pending_data_count_) {
127     --pending_data_count_;
128
129     buffer_->RecycleLeastRecentlyAllocated();
130     if (buffer_->CanAllocate())
131       ResumeIfDeferred();
132   }
133 }
134
135 bool AsyncResourceHandler::OnUploadProgress(int request_id,
136                                             uint64 position,
137                                             uint64 size) {
138   ResourceMessageFilter* filter = GetFilter();
139   if (!filter)
140     return false;
141   return filter->Send(
142       new ResourceMsg_UploadProgress(request_id, position, size));
143 }
144
145 bool AsyncResourceHandler::OnRequestRedirected(int request_id,
146                                                const GURL& new_url,
147                                                ResourceResponse* response,
148                                                bool* defer) {
149   const ResourceRequestInfoImpl* info = GetRequestInfo();
150   if (!info->filter())
151     return false;
152
153   *defer = did_defer_ = true;
154
155   if (rdh_->delegate()) {
156     rdh_->delegate()->OnRequestRedirected(
157         new_url, request(), info->GetContext(), response);
158   }
159
160   DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
161   response->head.request_start = request()->creation_time();
162   response->head.response_start = TimeTicks::Now();
163   return info->filter()->Send(new ResourceMsg_ReceivedRedirect(
164       request_id, new_url, response->head));
165 }
166
167 bool AsyncResourceHandler::OnResponseStarted(int request_id,
168                                              ResourceResponse* response,
169                                              bool* defer) {
170   // For changes to the main frame, inform the renderer of the new URL's
171   // per-host settings before the request actually commits.  This way the
172   // renderer will be able to set these precisely at the time the
173   // request commits, avoiding the possibility of e.g. zooming the old content
174   // or of having to layout the new content twice.
175
176   const ResourceRequestInfoImpl* info = GetRequestInfo();
177   if (!info->filter())
178     return false;
179
180   if (rdh_->delegate()) {
181     rdh_->delegate()->OnResponseStarted(
182         request(), info->GetContext(), response, info->filter());
183   }
184
185   DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
186
187   HostZoomMap* host_zoom_map =
188       GetHostZoomMapForResourceContext(info->GetContext());
189
190   if (info->GetResourceType() == ResourceType::MAIN_FRAME && host_zoom_map) {
191     const GURL& request_url = request()->url();
192     info->filter()->Send(new ViewMsg_SetZoomLevelForLoadingURL(
193         info->GetRouteID(),
194         request_url, host_zoom_map->GetZoomLevelForHostAndScheme(
195             request_url.scheme(),
196             net::GetHostOrSpecFromURL(request_url))));
197   }
198
199   response->head.request_start = request()->creation_time();
200   response->head.response_start = TimeTicks::Now();
201   info->filter()->Send(new ResourceMsg_ReceivedResponse(request_id,
202                                                         response->head));
203   sent_received_response_msg_ = true;
204
205   if (request()->response_info().metadata.get()) {
206     std::vector<char> copy(request()->response_info().metadata->data(),
207                            request()->response_info().metadata->data() +
208                                request()->response_info().metadata->size());
209     info->filter()->Send(new ResourceMsg_ReceivedCachedMetadata(request_id,
210                                                                 copy));
211   }
212
213   return true;
214 }
215
216 bool AsyncResourceHandler::OnWillStart(int request_id,
217                                        const GURL& url,
218                                        bool* defer) {
219   return true;
220 }
221
222 bool AsyncResourceHandler::OnWillRead(int request_id,
223                                       scoped_refptr<net::IOBuffer>* buf,
224                                       int* buf_size,
225                                       int min_size) {
226   DCHECK_EQ(-1, min_size);
227
228   if (!EnsureResourceBufferIsInitialized())
229     return false;
230
231   DCHECK(buffer_->CanAllocate());
232   char* memory = buffer_->Allocate(&allocation_size_);
233   CHECK(memory);
234
235   *buf = new DependentIOBuffer(buffer_.get(), memory);
236   *buf_size = allocation_size_;
237
238   UMA_HISTOGRAM_CUSTOM_COUNTS(
239       "Net.AsyncResourceHandler_SharedIOBuffer_Alloc",
240       *buf_size, 0, kMaxAllocationSize, 100);
241   return true;
242 }
243
244 bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
245                                            bool* defer) {
246   DCHECK_GE(bytes_read, 0);
247
248   if (!bytes_read)
249     return true;
250
251   ResourceMessageFilter* filter = GetFilter();
252   if (!filter)
253     return false;
254
255   buffer_->ShrinkLastAllocation(bytes_read);
256
257   UMA_HISTOGRAM_CUSTOM_COUNTS(
258       "Net.AsyncResourceHandler_SharedIOBuffer_Used",
259       bytes_read, 0, kMaxAllocationSize, 100);
260   UMA_HISTOGRAM_PERCENTAGE(
261       "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage",
262       CalcUsedPercentage(bytes_read, allocation_size_));
263
264   if (!sent_first_data_msg_) {
265     base::SharedMemoryHandle handle;
266     int size;
267     if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size))
268       return false;
269     filter->Send(new ResourceMsg_SetDataBuffer(
270         request_id, handle, size, filter->peer_pid()));
271     sent_first_data_msg_ = true;
272   }
273
274   int data_offset = buffer_->GetLastAllocationOffset();
275   int encoded_data_length =
276       DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
277
278   filter->Send(new ResourceMsg_DataReceived(
279       request_id, data_offset, bytes_read, encoded_data_length));
280   ++pending_data_count_;
281   UMA_HISTOGRAM_CUSTOM_COUNTS(
282       "Net.AsyncResourceHandler_PendingDataCount",
283       pending_data_count_, 0, 100, 100);
284
285   if (!buffer_->CanAllocate()) {
286     UMA_HISTOGRAM_CUSTOM_COUNTS(
287         "Net.AsyncResourceHandler_PendingDataCount_WhenFull",
288         pending_data_count_, 0, 100, 100);
289     *defer = did_defer_ = true;
290   }
291
292   return true;
293 }
294
295 void AsyncResourceHandler::OnDataDownloaded(
296     int request_id, int bytes_downloaded) {
297   int encoded_data_length =
298       DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
299
300   ResourceMessageFilter* filter = GetFilter();
301   if (filter) {
302     filter->Send(new ResourceMsg_DataDownloaded(
303         request_id, bytes_downloaded, encoded_data_length));
304   }
305 }
306
307 bool AsyncResourceHandler::OnResponseCompleted(
308     int request_id,
309     const net::URLRequestStatus& status,
310     const std::string& security_info) {
311   const ResourceRequestInfoImpl* info = GetRequestInfo();
312   if (!info->filter())
313     return false;
314
315   // If we crash here, figure out what URL the renderer was requesting.
316   // http://crbug.com/107692
317   char url_buf[128];
318   base::strlcpy(url_buf, request()->url().spec().c_str(), arraysize(url_buf));
319   base::debug::Alias(url_buf);
320
321   // TODO(gavinp): Remove this CHECK when we figure out the cause of
322   // http://crbug.com/124680 . This check mirrors closely check in
323   // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore
324   // ResourceHandleInternal which asserts on its state and crashes. By crashing
325   // when the message is sent, we should get better crash reports.
326   CHECK(status.status() != net::URLRequestStatus::SUCCESS ||
327         sent_received_response_msg_);
328
329   TimeTicks completion_time = TimeTicks::Now();
330
331   int error_code = status.error();
332   bool was_ignored_by_handler = info->WasIgnoredByHandler();
333
334   DCHECK(status.status() != net::URLRequestStatus::IO_PENDING);
335   // If this check fails, then we're in an inconsistent state because all
336   // requests ignored by the handler should be canceled (which should result in
337   // the ERR_ABORTED error code).
338   DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED);
339
340   // TODO(mkosiba): Fix up cases where we create a URLRequestStatus
341   // with a status() != SUCCESS and an error_code() == net::OK.
342   if (status.status() == net::URLRequestStatus::CANCELED &&
343       error_code == net::OK) {
344     error_code = net::ERR_ABORTED;
345   } else if (status.status() == net::URLRequestStatus::FAILED &&
346              error_code == net::OK) {
347     error_code = net::ERR_FAILED;
348   }
349
350   info->filter()->Send(
351       new ResourceMsg_RequestComplete(request_id,
352                                       error_code,
353                                       was_ignored_by_handler,
354                                       security_info,
355                                       completion_time));
356   return true;
357 }
358
359 bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
360   if (buffer_.get() && buffer_->IsInitialized())
361     return true;
362
363   if (!has_checked_for_sufficient_resources_) {
364     has_checked_for_sufficient_resources_ = true;
365     if (!rdh_->HasSufficientResourcesForRequest(request())) {
366       controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
367       return false;
368     }
369   }
370
371   buffer_ = new ResourceBuffer();
372   return buffer_->Initialize(kBufferSize,
373                              kMinAllocationSize,
374                              kMaxAllocationSize);
375 }
376
377 void AsyncResourceHandler::ResumeIfDeferred() {
378   if (did_defer_) {
379     did_defer_ = false;
380     controller()->Resume();
381   }
382 }
383
384 }  // namespace content