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