OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 // See http://dev.chromium.org/developers/design-documents/multi-process-resourc
e-loading | |
6 | |
7 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" | |
8 | |
9 #include <set> | |
10 #include <vector> | |
11 | |
12 #include "base/logging.h" | |
13 #include "base/command_line.h" | |
14 #include "base/message_loop.h" | |
15 #include "base/metrics/histogram.h" | |
16 #include "base/scoped_ptr.h" | |
17 #include "base/shared_memory.h" | |
18 #include "base/stl_util-inl.h" | |
19 #include "base/time.h" | |
20 #include "chrome/browser/cert_store.h" | |
21 #include "chrome/browser/child_process_security_policy.h" | |
22 #include "chrome/browser/chrome_blob_storage_context.h" | |
23 #include "chrome/browser/cross_site_request_manager.h" | |
24 #include "chrome/browser/download/download_file_manager.h" | |
25 #include "chrome/browser/download/download_manager.h" | |
26 #include "chrome/browser/download/download_request_limiter.h" | |
27 #include "chrome/browser/download/download_util.h" | |
28 #include "chrome/browser/download/save_file_manager.h" | |
29 #include "chrome/browser/extensions/user_script_listener.h" | |
30 #include "chrome/browser/external_protocol_handler.h" | |
31 #include "chrome/browser/in_process_webkit/webkit_thread.h" | |
32 #include "chrome/browser/net/chrome_url_request_context.h" | |
33 #include "chrome/browser/net/url_request_tracking.h" | |
34 #include "chrome/browser/plugin_service.h" | |
35 #include "chrome/browser/prerender/prerender_manager.h" | |
36 #include "chrome/browser/prerender/prerender_resource_handler.h" | |
37 #include "chrome/browser/profiles/profile.h" | |
38 #include "chrome/browser/renderer_host/async_resource_handler.h" | |
39 #include "chrome/browser/renderer_host/buffered_resource_handler.h" | |
40 #include "chrome/browser/renderer_host/cross_site_resource_handler.h" | |
41 #include "chrome/browser/renderer_host/download_resource_handler.h" | |
42 #include "chrome/browser/renderer_host/global_request_id.h" | |
43 #include "chrome/browser/renderer_host/redirect_to_file_resource_handler.h" | |
44 #include "chrome/browser/renderer_host/render_view_host.h" | |
45 #include "chrome/browser/renderer_host/render_view_host_delegate.h" | |
46 #include "chrome/browser/renderer_host/render_view_host_notification_task.h" | |
47 #include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" | |
48 #include "chrome/browser/renderer_host/resource_message_filter.h" | |
49 #include "chrome/browser/renderer_host/resource_queue.h" | |
50 #include "chrome/browser/renderer_host/resource_request_details.h" | |
51 #include "chrome/browser/renderer_host/safe_browsing_resource_handler.h" | |
52 #include "chrome/browser/renderer_host/save_file_resource_handler.h" | |
53 #include "chrome/browser/renderer_host/sync_resource_handler.h" | |
54 #include "chrome/browser/safe_browsing/safe_browsing_service.h" | |
55 #include "chrome/browser/ssl/ssl_client_auth_handler.h" | |
56 #include "chrome/browser/ssl/ssl_manager.h" | |
57 #include "chrome/browser/ui/login/login_prompt.h" | |
58 #include "chrome/browser/worker_host/worker_service.h" | |
59 #include "chrome/common/chrome_switches.h" | |
60 #include "chrome/common/notification_service.h" | |
61 #include "chrome/common/render_messages.h" | |
62 #include "chrome/common/render_messages_params.h" | |
63 #include "chrome/common/url_constants.h" | |
64 #include "net/base/auth.h" | |
65 #include "net/base/cert_status_flags.h" | |
66 #include "net/base/cookie_monster.h" | |
67 #include "net/base/load_flags.h" | |
68 #include "net/base/mime_util.h" | |
69 #include "net/base/net_errors.h" | |
70 #include "net/base/request_priority.h" | |
71 #include "net/base/ssl_cert_request_info.h" | |
72 #include "net/base/upload_data.h" | |
73 #include "net/http/http_response_headers.h" | |
74 #include "net/url_request/url_request.h" | |
75 #include "net/url_request/url_request_context.h" | |
76 #include "webkit/appcache/appcache_interceptor.h" | |
77 #include "webkit/appcache/appcache_interfaces.h" | |
78 #include "webkit/blob/blob_storage_controller.h" | |
79 #include "webkit/blob/deletable_file_reference.h" | |
80 | |
81 // TODO(oshima): Enable this for other platforms. | |
82 #if defined(OS_CHROMEOS) | |
83 #include "chrome/browser/renderer_host/offline_resource_handler.h" | |
84 #endif | |
85 | |
86 using base::Time; | |
87 using base::TimeDelta; | |
88 using base::TimeTicks; | |
89 using webkit_blob::DeletableFileReference; | |
90 | |
91 // ---------------------------------------------------------------------------- | |
92 | |
93 // A ShutdownTask proxies a shutdown task from the UI thread to the IO thread. | |
94 // It should be constructed on the UI thread and run in the IO thread. | |
95 class ResourceDispatcherHost::ShutdownTask : public Task { | |
96 public: | |
97 explicit ShutdownTask(ResourceDispatcherHost* resource_dispatcher_host) | |
98 : rdh_(resource_dispatcher_host) { | |
99 } | |
100 | |
101 void Run() { | |
102 rdh_->OnShutdown(); | |
103 } | |
104 | |
105 private: | |
106 ResourceDispatcherHost* rdh_; | |
107 }; | |
108 | |
109 namespace { | |
110 | |
111 // The interval for calls to ResourceDispatcherHost::UpdateLoadStates | |
112 const int kUpdateLoadStatesIntervalMsec = 100; | |
113 | |
114 // Maximum number of pending data messages sent to the renderer at any | |
115 // given time for a given request. | |
116 const int kMaxPendingDataMessages = 20; | |
117 | |
118 // Maximum byte "cost" of all the outstanding requests for a renderer. | |
119 // See delcaration of |max_outstanding_requests_cost_per_process_| for details. | |
120 // This bound is 25MB, which allows for around 6000 outstanding requests. | |
121 const int kMaxOutstandingRequestsCostPerProcess = 26214400; | |
122 | |
123 // Consults the RendererSecurity policy to determine whether the | |
124 // ResourceDispatcherHost should service this request. A request might be | |
125 // disallowed if the renderer is not authorized to retrieve the request URL or | |
126 // if the renderer is attempting to upload an unauthorized file. | |
127 bool ShouldServiceRequest(ChildProcessInfo::ProcessType process_type, | |
128 int child_id, | |
129 const ViewHostMsg_Resource_Request& request_data) { | |
130 if (process_type == ChildProcessInfo::PLUGIN_PROCESS) | |
131 return true; | |
132 | |
133 if (request_data.resource_type == ResourceType::PREFETCH) { | |
134 prerender::PrerenderManager::RecordPrefetchTagObserved(); | |
135 if (!ResourceDispatcherHost::is_prefetch_enabled()) | |
136 return false; | |
137 } | |
138 | |
139 ChildProcessSecurityPolicy* policy = | |
140 ChildProcessSecurityPolicy::GetInstance(); | |
141 | |
142 // Check if the renderer is permitted to request the requested URL. | |
143 if (!policy->CanRequestURL(child_id, request_data.url)) { | |
144 VLOG(1) << "Denied unauthorized request for " | |
145 << request_data.url.possibly_invalid_spec(); | |
146 return false; | |
147 } | |
148 | |
149 // Check if the renderer is permitted to upload the requested files. | |
150 if (request_data.upload_data) { | |
151 const std::vector<net::UploadData::Element>* uploads = | |
152 request_data.upload_data->elements(); | |
153 std::vector<net::UploadData::Element>::const_iterator iter; | |
154 for (iter = uploads->begin(); iter != uploads->end(); ++iter) { | |
155 if (iter->type() == net::UploadData::TYPE_FILE && | |
156 !policy->CanReadFile(child_id, iter->file_path())) { | |
157 NOTREACHED() << "Denied unauthorized upload of " | |
158 << iter->file_path().value(); | |
159 return false; | |
160 } | |
161 } | |
162 } | |
163 | |
164 return true; | |
165 } | |
166 | |
167 void PopulateResourceResponse(net::URLRequest* request, | |
168 bool replace_extension_localization_templates, | |
169 ResourceResponse* response) { | |
170 response->response_head.status = request->status(); | |
171 response->response_head.request_time = request->request_time(); | |
172 response->response_head.response_time = request->response_time(); | |
173 response->response_head.headers = request->response_headers(); | |
174 request->GetCharset(&response->response_head.charset); | |
175 response->response_head.replace_extension_localization_templates = | |
176 replace_extension_localization_templates; | |
177 response->response_head.content_length = request->GetExpectedContentSize(); | |
178 request->GetMimeType(&response->response_head.mime_type); | |
179 response->response_head.was_fetched_via_spdy = | |
180 request->was_fetched_via_spdy(); | |
181 response->response_head.was_npn_negotiated = request->was_npn_negotiated(); | |
182 response->response_head.was_alternate_protocol_available = | |
183 request->was_alternate_protocol_available(); | |
184 response->response_head.was_fetched_via_proxy = | |
185 request->was_fetched_via_proxy(); | |
186 appcache::AppCacheInterceptor::GetExtraResponseInfo( | |
187 request, | |
188 &response->response_head.appcache_id, | |
189 &response->response_head.appcache_manifest_url); | |
190 } | |
191 | |
192 // Returns a list of all the possible error codes from the network module. | |
193 // Note that the error codes are all positive (since histograms expect positive | |
194 // sample values). | |
195 std::vector<int> GetAllNetErrorCodes() { | |
196 std::vector<int> all_error_codes; | |
197 #define NET_ERROR(label, value) all_error_codes.push_back(-(value)); | |
198 #include "net/base/net_error_list.h" | |
199 #undef NET_ERROR | |
200 return all_error_codes; | |
201 } | |
202 | |
203 #if defined(OS_WIN) | |
204 #pragma warning(disable: 4748) | |
205 #pragma optimize("", off) | |
206 #endif | |
207 | |
208 #if defined(OS_WIN) | |
209 #pragma optimize("", on) | |
210 #pragma warning(default: 4748) | |
211 #endif | |
212 | |
213 } // namespace | |
214 | |
215 ResourceDispatcherHost::ResourceDispatcherHost() | |
216 : ALLOW_THIS_IN_INITIALIZER_LIST( | |
217 download_file_manager_(new DownloadFileManager(this))), | |
218 download_request_limiter_(new DownloadRequestLimiter()), | |
219 ALLOW_THIS_IN_INITIALIZER_LIST( | |
220 save_file_manager_(new SaveFileManager(this))), | |
221 user_script_listener_(new UserScriptListener(&resource_queue_)), | |
222 safe_browsing_(SafeBrowsingService::CreateSafeBrowsingService()), | |
223 webkit_thread_(new WebKitThread), | |
224 request_id_(-1), | |
225 ALLOW_THIS_IN_INITIALIZER_LIST(method_runner_(this)), | |
226 is_shutdown_(false), | |
227 max_outstanding_requests_cost_per_process_( | |
228 kMaxOutstandingRequestsCostPerProcess), | |
229 filter_(NULL) { | |
230 ResourceQueue::DelegateSet resource_queue_delegates; | |
231 resource_queue_delegates.insert(user_script_listener_.get()); | |
232 resource_queue_.Initialize(resource_queue_delegates); | |
233 } | |
234 | |
235 ResourceDispatcherHost::~ResourceDispatcherHost() { | |
236 AsyncResourceHandler::GlobalCleanup(); | |
237 STLDeleteValues(&pending_requests_); | |
238 | |
239 user_script_listener_->ShutdownMainThread(); | |
240 } | |
241 | |
242 void ResourceDispatcherHost::Initialize() { | |
243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
244 webkit_thread_->Initialize(); | |
245 safe_browsing_->Initialize(); | |
246 BrowserThread::PostTask( | |
247 BrowserThread::IO, FROM_HERE, | |
248 NewRunnableFunction(&appcache::AppCacheInterceptor::EnsureRegistered)); | |
249 } | |
250 | |
251 void ResourceDispatcherHost::Shutdown() { | |
252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
253 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, new ShutdownTask(this)); | |
254 } | |
255 | |
256 void ResourceDispatcherHost::SetRequestInfo( | |
257 net::URLRequest* request, | |
258 ResourceDispatcherHostRequestInfo* info) { | |
259 request->SetUserData(NULL, info); | |
260 } | |
261 | |
262 void ResourceDispatcherHost::OnShutdown() { | |
263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
264 is_shutdown_ = true; | |
265 resource_queue_.Shutdown(); | |
266 STLDeleteValues(&pending_requests_); | |
267 // Make sure we shutdown the timer now, otherwise by the time our destructor | |
268 // runs if the timer is still running the Task is deleted twice (once by | |
269 // the MessageLoop and the second time by RepeatingTimer). | |
270 update_load_states_timer_.Stop(); | |
271 | |
272 // Clear blocked requests if any left. | |
273 // Note that we have to do this in 2 passes as we cannot call | |
274 // CancelBlockedRequestsForRoute while iterating over | |
275 // blocked_requests_map_, as it modifies it. | |
276 std::set<ProcessRouteIDs> ids; | |
277 for (BlockedRequestMap::const_iterator iter = blocked_requests_map_.begin(); | |
278 iter != blocked_requests_map_.end(); ++iter) { | |
279 std::pair<std::set<ProcessRouteIDs>::iterator, bool> result = | |
280 ids.insert(iter->first); | |
281 // We should not have duplicates. | |
282 DCHECK(result.second); | |
283 } | |
284 for (std::set<ProcessRouteIDs>::const_iterator iter = ids.begin(); | |
285 iter != ids.end(); ++iter) { | |
286 CancelBlockedRequestsForRoute(iter->first, iter->second); | |
287 } | |
288 } | |
289 | |
290 bool ResourceDispatcherHost::HandleExternalProtocol(int request_id, | |
291 int child_id, | |
292 int route_id, | |
293 const GURL& url, | |
294 ResourceType::Type type, | |
295 ResourceHandler* handler) { | |
296 if (!ResourceType::IsFrame(type) || net::URLRequest::IsHandledURL(url)) | |
297 return false; | |
298 | |
299 BrowserThread::PostTask( | |
300 BrowserThread::UI, FROM_HERE, | |
301 NewRunnableFunction( | |
302 &ExternalProtocolHandler::LaunchUrl, url, child_id, route_id)); | |
303 | |
304 handler->OnResponseCompleted( | |
305 request_id, | |
306 net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_ABORTED), | |
307 std::string()); // No security info necessary. | |
308 return true; | |
309 } | |
310 | |
311 bool ResourceDispatcherHost::OnMessageReceived(const IPC::Message& message, | |
312 ResourceMessageFilter* filter, | |
313 bool* message_was_ok) { | |
314 filter_ = filter; | |
315 bool handled = true; | |
316 IPC_BEGIN_MESSAGE_MAP_EX(ResourceDispatcherHost, message, *message_was_ok) | |
317 IPC_MESSAGE_HANDLER(ViewHostMsg_RequestResource, OnRequestResource) | |
318 IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_SyncLoad, OnSyncLoad) | |
319 IPC_MESSAGE_HANDLER(ViewHostMsg_ReleaseDownloadedFile, | |
320 OnReleaseDownloadedFile) | |
321 IPC_MESSAGE_HANDLER(ViewHostMsg_DataReceived_ACK, OnDataReceivedACK) | |
322 IPC_MESSAGE_HANDLER(ViewHostMsg_DataDownloaded_ACK, OnDataDownloadedACK) | |
323 IPC_MESSAGE_HANDLER(ViewHostMsg_UploadProgress_ACK, OnUploadProgressACK) | |
324 IPC_MESSAGE_HANDLER(ViewHostMsg_CancelRequest, OnCancelRequest) | |
325 IPC_MESSAGE_HANDLER(ViewHostMsg_FollowRedirect, OnFollowRedirect) | |
326 IPC_MESSAGE_HANDLER(ViewHostMsg_ClosePage_ACK, OnClosePageACK) | |
327 IPC_MESSAGE_UNHANDLED(handled = false) | |
328 IPC_END_MESSAGE_MAP_EX() | |
329 | |
330 filter_ = NULL; | |
331 return handled; | |
332 } | |
333 | |
334 void ResourceDispatcherHost::OnRequestResource( | |
335 const IPC::Message& message, | |
336 int request_id, | |
337 const ViewHostMsg_Resource_Request& request_data) { | |
338 BeginRequest(request_id, request_data, NULL, message.routing_id()); | |
339 } | |
340 | |
341 // Begins a resource request with the given params on behalf of the specified | |
342 // child process. Responses will be dispatched through the given receiver. The | |
343 // process ID is used to lookup TabContents from routing_id's in the case of a | |
344 // request from a renderer. request_context is the cookie/cache context to be | |
345 // used for this request. | |
346 // | |
347 // If sync_result is non-null, then a SyncLoad reply will be generated, else | |
348 // a normal asynchronous set of response messages will be generated. | |
349 void ResourceDispatcherHost::OnSyncLoad( | |
350 int request_id, | |
351 const ViewHostMsg_Resource_Request& request_data, | |
352 IPC::Message* sync_result) { | |
353 BeginRequest(request_id, request_data, sync_result, | |
354 sync_result->routing_id()); | |
355 } | |
356 | |
357 void ResourceDispatcherHost::BeginRequest( | |
358 int request_id, | |
359 const ViewHostMsg_Resource_Request& request_data, | |
360 IPC::Message* sync_result, // only valid for sync | |
361 int route_id) { | |
362 ChildProcessInfo::ProcessType process_type = filter_->process_type(); | |
363 int child_id = filter_->child_id(); | |
364 | |
365 ChromeURLRequestContext* context = filter_->GetURLRequestContext( | |
366 request_data); | |
367 | |
368 // Might need to resolve the blob references in the upload data. | |
369 if (request_data.upload_data && context) { | |
370 context->blob_storage_context()->controller()-> | |
371 ResolveBlobReferencesInUploadData(request_data.upload_data.get()); | |
372 } | |
373 | |
374 if (is_shutdown_ || | |
375 !ShouldServiceRequest(process_type, child_id, request_data)) { | |
376 net::URLRequestStatus status(net::URLRequestStatus::FAILED, | |
377 net::ERR_ABORTED); | |
378 if (sync_result) { | |
379 SyncLoadResult result; | |
380 result.status = status; | |
381 ViewHostMsg_SyncLoad::WriteReplyParams(sync_result, result); | |
382 filter_->Send(sync_result); | |
383 } else { | |
384 // Tell the renderer that this request was disallowed. | |
385 filter_->Send(new ViewMsg_Resource_RequestComplete( | |
386 route_id, | |
387 request_id, | |
388 status, | |
389 std::string(), // No security info needed, connection was not | |
390 base::Time())); // established. | |
391 } | |
392 return; | |
393 } | |
394 | |
395 // Ensure the Chrome plugins are loaded, as they may intercept network | |
396 // requests. Does nothing if they are already loaded. | |
397 // TODO(mpcomplete): This takes 200 ms! Investigate parallelizing this by | |
398 // starting the load earlier in a BG thread. | |
399 PluginService::GetInstance()->LoadChromePlugins(this); | |
400 | |
401 // Construct the event handler. | |
402 scoped_refptr<ResourceHandler> handler; | |
403 if (sync_result) { | |
404 handler = new SyncResourceHandler( | |
405 filter_, request_data.url, sync_result, this); | |
406 } else { | |
407 handler = new AsyncResourceHandler( | |
408 filter_, route_id, request_data.url, this); | |
409 } | |
410 | |
411 // The RedirectToFileResourceHandler depends on being next in the chain. | |
412 if (request_data.download_to_file) | |
413 handler = new RedirectToFileResourceHandler(handler, child_id, this); | |
414 | |
415 if (HandleExternalProtocol(request_id, child_id, route_id, | |
416 request_data.url, request_data.resource_type, | |
417 handler)) { | |
418 return; | |
419 } | |
420 | |
421 // Construct the request. | |
422 net::URLRequest* request = new net::URLRequest(request_data.url, this); | |
423 request->set_method(request_data.method); | |
424 request->set_first_party_for_cookies(request_data.first_party_for_cookies); | |
425 request->set_referrer(CommandLine::ForCurrentProcess()->HasSwitch( | |
426 switches::kNoReferrers) ? std::string() : request_data.referrer.spec()); | |
427 net::HttpRequestHeaders headers; | |
428 headers.AddHeadersFromString(request_data.headers); | |
429 request->SetExtraRequestHeaders(headers); | |
430 | |
431 int load_flags = request_data.load_flags; | |
432 // Although EV status is irrelevant to sub-frames and sub-resources, we have | |
433 // to perform EV certificate verification on all resources because an HTTP | |
434 // keep-alive connection created to load a sub-frame or a sub-resource could | |
435 // be reused to load a main frame. | |
436 load_flags |= net::LOAD_VERIFY_EV_CERT; | |
437 if (request_data.resource_type == ResourceType::MAIN_FRAME) { | |
438 load_flags |= net::LOAD_MAIN_FRAME; | |
439 } else if (request_data.resource_type == ResourceType::SUB_FRAME) { | |
440 load_flags |= net::LOAD_SUB_FRAME; | |
441 } else if (request_data.resource_type == ResourceType::PREFETCH) { | |
442 load_flags |= net::LOAD_PREFETCH; | |
443 } | |
444 // Raw headers are sensitive, as they inclide Cookie/Set-Cookie, so only | |
445 // allow requesting them if requestor has ReadRawCookies permission. | |
446 if ((load_flags & net::LOAD_REPORT_RAW_HEADERS) | |
447 && !ChildProcessSecurityPolicy::GetInstance()-> | |
448 CanReadRawCookies(child_id)) { | |
449 VLOG(1) << "Denied unathorized request for raw headers"; | |
450 load_flags &= ~net::LOAD_REPORT_RAW_HEADERS; | |
451 } | |
452 | |
453 request->set_load_flags(load_flags); | |
454 request->set_context(context); | |
455 request->set_priority(DetermineRequestPriority(request_data.resource_type)); | |
456 | |
457 // Set upload data. | |
458 uint64 upload_size = 0; | |
459 if (request_data.upload_data) { | |
460 request->set_upload(request_data.upload_data); | |
461 upload_size = request_data.upload_data->GetContentLength(); | |
462 } | |
463 | |
464 // Install a PrerenderResourceHandler if the requested URL could | |
465 // be prerendered. This should be in front of the [a]syncResourceHandler, | |
466 // but after the BufferedResourceHandler since it depends on the MIME | |
467 // sniffing capabilities in the BufferedResourceHandler. | |
468 prerender::PrerenderResourceHandler* pre_handler = | |
469 prerender::PrerenderResourceHandler::MaybeCreate(*request, | |
470 context, | |
471 handler); | |
472 if (pre_handler) | |
473 handler = pre_handler; | |
474 | |
475 // Install a CrossSiteResourceHandler if this request is coming from a | |
476 // RenderViewHost with a pending cross-site request. We only check this for | |
477 // MAIN_FRAME requests. Unblock requests only come from a blocked page, do | |
478 // not count as cross-site, otherwise it gets blocked indefinitely. | |
479 if (request_data.resource_type == ResourceType::MAIN_FRAME && | |
480 process_type == ChildProcessInfo::RENDER_PROCESS && | |
481 CrossSiteRequestManager::GetInstance()-> | |
482 HasPendingCrossSiteRequest(child_id, route_id)) { | |
483 // Wrap the event handler to be sure the current page's onunload handler | |
484 // has a chance to run before we render the new page. | |
485 handler = new CrossSiteResourceHandler(handler, | |
486 child_id, | |
487 route_id, | |
488 this); | |
489 } | |
490 | |
491 // Insert a buffered event handler before the actual one. | |
492 handler = new BufferedResourceHandler(handler, this, request); | |
493 | |
494 // Insert safe browsing at the front of the chain, so it gets to decide | |
495 // on policies first. | |
496 if (safe_browsing_->enabled()) { | |
497 handler = CreateSafeBrowsingResourceHandler(handler, child_id, route_id, | |
498 request_data.resource_type); | |
499 } | |
500 | |
501 #if defined(OS_CHROMEOS) | |
502 // We check offline first, then check safe browsing so that we still can block | |
503 // unsafe site after we remove offline page. | |
504 handler = | |
505 new OfflineResourceHandler(handler, child_id, route_id, this, request); | |
506 #endif | |
507 | |
508 // Make extra info and read footer (contains request ID). | |
509 ResourceDispatcherHostRequestInfo* extra_info = | |
510 new ResourceDispatcherHostRequestInfo( | |
511 handler, | |
512 process_type, | |
513 child_id, | |
514 route_id, | |
515 request_id, | |
516 request_data.resource_type, | |
517 upload_size, | |
518 false, // is download | |
519 ResourceType::IsFrame(request_data.resource_type), // allow_download | |
520 request_data.has_user_gesture, | |
521 request_data.host_renderer_id, | |
522 request_data.host_render_view_id); | |
523 ApplyExtensionLocalizationFilter(request_data.url, request_data.resource_type, | |
524 extra_info); | |
525 SetRequestInfo(request, extra_info); // Request takes ownership. | |
526 chrome_browser_net::SetOriginPIDForRequest( | |
527 request_data.origin_pid, request); | |
528 | |
529 if (request->url().SchemeIs(chrome::kBlobScheme) && context) { | |
530 // Hang on to a reference to ensure the blob is not released prior | |
531 // to the job being started. | |
532 webkit_blob::BlobStorageController* controller = | |
533 context->blob_storage_context()->controller(); | |
534 extra_info->set_requested_blob_data( | |
535 controller->GetBlobDataFromUrl(request->url())); | |
536 } | |
537 | |
538 // Have the appcache associate its extra info with the request. | |
539 appcache::AppCacheInterceptor::SetExtraRequestInfo( | |
540 request, context ? context->appcache_service() : NULL, child_id, | |
541 request_data.appcache_host_id, request_data.resource_type); | |
542 | |
543 BeginRequestInternal(request); | |
544 } | |
545 | |
546 void ResourceDispatcherHost::OnReleaseDownloadedFile(int request_id) { | |
547 DCHECK(pending_requests_.end() == | |
548 pending_requests_.find( | |
549 GlobalRequestID(filter_->child_id(), request_id))); | |
550 UnregisterDownloadedTempFile(filter_->child_id(), request_id); | |
551 } | |
552 | |
553 void ResourceDispatcherHost::OnDataReceivedACK(int request_id) { | |
554 DataReceivedACK(filter_->child_id(), request_id); | |
555 } | |
556 | |
557 void ResourceDispatcherHost::DataReceivedACK(int child_id, | |
558 int request_id) { | |
559 PendingRequestList::iterator i = pending_requests_.find( | |
560 GlobalRequestID(child_id, request_id)); | |
561 if (i == pending_requests_.end()) | |
562 return; | |
563 | |
564 ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second); | |
565 | |
566 // Decrement the number of pending data messages. | |
567 info->DecrementPendingDataCount(); | |
568 | |
569 // If the pending data count was higher than the max, resume the request. | |
570 if (info->pending_data_count() == kMaxPendingDataMessages) { | |
571 // Decrement the pending data count one more time because we also | |
572 // incremented it before pausing the request. | |
573 info->DecrementPendingDataCount(); | |
574 | |
575 // Resume the request. | |
576 PauseRequest(child_id, request_id, false); | |
577 } | |
578 } | |
579 | |
580 void ResourceDispatcherHost::OnDataDownloadedACK(int request_id) { | |
581 // TODO(michaeln): maybe throttle DataDownloaded messages | |
582 } | |
583 | |
584 void ResourceDispatcherHost::RegisterDownloadedTempFile( | |
585 int child_id, int request_id, DeletableFileReference* reference) { | |
586 registered_temp_files_[child_id][request_id] = reference; | |
587 ChildProcessSecurityPolicy::GetInstance()->GrantReadFile( | |
588 child_id, reference->path()); | |
589 } | |
590 | |
591 void ResourceDispatcherHost::UnregisterDownloadedTempFile( | |
592 int child_id, int request_id) { | |
593 DeletableFilesMap& map = registered_temp_files_[child_id]; | |
594 DeletableFilesMap::iterator found = map.find(request_id); | |
595 if (found == map.end()) | |
596 return; | |
597 | |
598 ChildProcessSecurityPolicy::GetInstance()->RevokeAllPermissionsForFile( | |
599 child_id, found->second->path()); | |
600 map.erase(found); | |
601 } | |
602 | |
603 bool ResourceDispatcherHost::Send(IPC::Message* message) { | |
604 delete message; | |
605 return false; | |
606 } | |
607 | |
608 void ResourceDispatcherHost::OnUploadProgressACK(int request_id) { | |
609 int child_id = filter_->child_id(); | |
610 PendingRequestList::iterator i = pending_requests_.find( | |
611 GlobalRequestID(child_id, request_id)); | |
612 if (i == pending_requests_.end()) | |
613 return; | |
614 | |
615 ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second); | |
616 info->set_waiting_for_upload_progress_ack(false); | |
617 } | |
618 | |
619 void ResourceDispatcherHost::OnCancelRequest(int request_id) { | |
620 CancelRequest(filter_->child_id(), request_id, true); | |
621 } | |
622 | |
623 void ResourceDispatcherHost::OnFollowRedirect( | |
624 int request_id, | |
625 bool has_new_first_party_for_cookies, | |
626 const GURL& new_first_party_for_cookies) { | |
627 FollowDeferredRedirect(filter_->child_id(), request_id, | |
628 has_new_first_party_for_cookies, | |
629 new_first_party_for_cookies); | |
630 } | |
631 | |
632 ResourceHandler* ResourceDispatcherHost::CreateSafeBrowsingResourceHandler( | |
633 ResourceHandler* handler, int child_id, int route_id, | |
634 ResourceType::Type resource_type) { | |
635 return new SafeBrowsingResourceHandler( | |
636 handler, child_id, route_id, resource_type, safe_browsing_, this); | |
637 } | |
638 | |
639 ResourceDispatcherHostRequestInfo* | |
640 ResourceDispatcherHost::CreateRequestInfoForBrowserRequest( | |
641 ResourceHandler* handler, int child_id, int route_id, bool download) { | |
642 return new ResourceDispatcherHostRequestInfo(handler, | |
643 ChildProcessInfo::RENDER_PROCESS, | |
644 child_id, | |
645 route_id, | |
646 request_id_, | |
647 ResourceType::SUB_RESOURCE, | |
648 0, // upload_size | |
649 download, // is_download | |
650 download, // allow_download | |
651 false, // has_user_gesture | |
652 -1, // host renderer id | |
653 -1); // host render view id | |
654 } | |
655 | |
656 void ResourceDispatcherHost::OnClosePageACK( | |
657 const ViewMsg_ClosePage_Params& params) { | |
658 if (params.for_cross_site_transition) { | |
659 // Closes for cross-site transitions are handled such that the cross-site | |
660 // transition continues. | |
661 GlobalRequestID global_id(params.new_render_process_host_id, | |
662 params.new_request_id); | |
663 PendingRequestList::iterator i = pending_requests_.find(global_id); | |
664 if (i != pending_requests_.end()) { | |
665 // The response we were meant to resume could have already been canceled. | |
666 ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second); | |
667 if (info->cross_site_handler()) | |
668 info->cross_site_handler()->ResumeResponse(); | |
669 } | |
670 } else { | |
671 // This is a tab close, so just forward the message to close it. | |
672 DCHECK(params.new_render_process_host_id == -1); | |
673 DCHECK(params.new_request_id == -1); | |
674 CallRenderViewHost(params.closing_process_id, | |
675 params.closing_route_id, | |
676 &RenderViewHost::ClosePageIgnoringUnloadEvents); | |
677 } | |
678 } | |
679 | |
680 // We are explicitly forcing the download of 'url'. | |
681 void ResourceDispatcherHost::BeginDownload( | |
682 const GURL& url, | |
683 const GURL& referrer, | |
684 const DownloadSaveInfo& save_info, | |
685 bool prompt_for_save_location, | |
686 int child_id, | |
687 int route_id, | |
688 net::URLRequestContext* request_context) { | |
689 if (is_shutdown_) | |
690 return; | |
691 | |
692 // Check if the renderer is permitted to request the requested URL. | |
693 if (!ChildProcessSecurityPolicy::GetInstance()-> | |
694 CanRequestURL(child_id, url)) { | |
695 VLOG(1) << "Denied unauthorized download request for " | |
696 << url.possibly_invalid_spec(); | |
697 return; | |
698 } | |
699 | |
700 BrowserThread::PostTask( | |
701 BrowserThread::UI, FROM_HERE, | |
702 NewRunnableFunction(&download_util::NotifyDownloadInitiated, | |
703 child_id, route_id)); | |
704 | |
705 // Ensure the Chrome plugins are loaded, as they may intercept network | |
706 // requests. Does nothing if they are already loaded. | |
707 PluginService::GetInstance()->LoadChromePlugins(this); | |
708 net::URLRequest* request = new net::URLRequest(url, this); | |
709 | |
710 request_id_--; | |
711 | |
712 scoped_refptr<ResourceHandler> handler( | |
713 new DownloadResourceHandler(this, | |
714 child_id, | |
715 route_id, | |
716 request_id_, | |
717 url, | |
718 download_file_manager_.get(), | |
719 request, | |
720 prompt_for_save_location, | |
721 save_info)); | |
722 | |
723 if (safe_browsing_->enabled()) { | |
724 handler = CreateSafeBrowsingResourceHandler(handler, child_id, route_id, | |
725 ResourceType::MAIN_FRAME); | |
726 } | |
727 | |
728 if (!net::URLRequest::IsHandledURL(url)) { | |
729 VLOG(1) << "Download request for unsupported protocol: " | |
730 << url.possibly_invalid_spec(); | |
731 return; | |
732 } | |
733 | |
734 request->set_method("GET"); | |
735 request->set_referrer(CommandLine::ForCurrentProcess()->HasSwitch( | |
736 switches::kNoReferrers) ? std::string() : referrer.spec()); | |
737 request->set_context(request_context); | |
738 request->set_load_flags(request->load_flags() | | |
739 net::LOAD_IS_DOWNLOAD); | |
740 | |
741 ResourceDispatcherHostRequestInfo* extra_info = | |
742 CreateRequestInfoForBrowserRequest(handler, child_id, route_id, true); | |
743 SetRequestInfo(request, extra_info); // Request takes ownership. | |
744 | |
745 BeginRequestInternal(request); | |
746 } | |
747 | |
748 // This function is only used for saving feature. | |
749 void ResourceDispatcherHost::BeginSaveFile( | |
750 const GURL& url, | |
751 const GURL& referrer, | |
752 int child_id, | |
753 int route_id, | |
754 net::URLRequestContext* request_context) { | |
755 if (is_shutdown_) | |
756 return; | |
757 | |
758 // Ensure the Chrome plugins are loaded, as they may intercept network | |
759 // requests. Does nothing if they are already loaded. | |
760 PluginService::GetInstance()->LoadChromePlugins(this); | |
761 | |
762 scoped_refptr<ResourceHandler> handler( | |
763 new SaveFileResourceHandler(child_id, | |
764 route_id, | |
765 url, | |
766 save_file_manager_.get())); | |
767 request_id_--; | |
768 | |
769 bool known_proto = net::URLRequest::IsHandledURL(url); | |
770 if (!known_proto) { | |
771 // Since any URLs which have non-standard scheme have been filtered | |
772 // by save manager(see GURL::SchemeIsStandard). This situation | |
773 // should not happen. | |
774 NOTREACHED(); | |
775 return; | |
776 } | |
777 | |
778 net::URLRequest* request = new net::URLRequest(url, this); | |
779 request->set_method("GET"); | |
780 request->set_referrer(CommandLine::ForCurrentProcess()->HasSwitch( | |
781 switches::kNoReferrers) ? std::string() : referrer.spec()); | |
782 // So far, for saving page, we need fetch content from cache, in the | |
783 // future, maybe we can use a configuration to configure this behavior. | |
784 request->set_load_flags(net::LOAD_PREFERRING_CACHE); | |
785 request->set_context(request_context); | |
786 | |
787 // Since we're just saving some resources we need, disallow downloading. | |
788 ResourceDispatcherHostRequestInfo* extra_info = | |
789 CreateRequestInfoForBrowserRequest(handler, child_id, route_id, false); | |
790 SetRequestInfo(request, extra_info); // Request takes ownership. | |
791 | |
792 BeginRequestInternal(request); | |
793 } | |
794 | |
795 void ResourceDispatcherHost::FollowDeferredRedirect( | |
796 int child_id, | |
797 int request_id, | |
798 bool has_new_first_party_for_cookies, | |
799 const GURL& new_first_party_for_cookies) { | |
800 PendingRequestList::iterator i = pending_requests_.find( | |
801 GlobalRequestID(child_id, request_id)); | |
802 if (i == pending_requests_.end() || !i->second->status().is_success()) { | |
803 DLOG(WARNING) << "FollowDeferredRedirect for invalid request"; | |
804 return; | |
805 } | |
806 | |
807 if (has_new_first_party_for_cookies) | |
808 i->second->set_first_party_for_cookies(new_first_party_for_cookies); | |
809 i->second->FollowDeferredRedirect(); | |
810 } | |
811 | |
812 void ResourceDispatcherHost::StartDeferredRequest(int process_unique_id, | |
813 int request_id) { | |
814 GlobalRequestID global_id(process_unique_id, request_id); | |
815 PendingRequestList::iterator i = pending_requests_.find(global_id); | |
816 if (i == pending_requests_.end()) { | |
817 // The request may have been destroyed | |
818 LOG(WARNING) << "Trying to resume a non-existent request (" | |
819 << process_unique_id << ", " << request_id << ")"; | |
820 return; | |
821 } | |
822 | |
823 // TODO(eroman): are there other considerations for paused or blocked | |
824 // requests? | |
825 | |
826 net::URLRequest* request = i->second; | |
827 InsertIntoResourceQueue(request, *InfoForRequest(request)); | |
828 } | |
829 | |
830 bool ResourceDispatcherHost::WillSendData(int child_id, | |
831 int request_id) { | |
832 PendingRequestList::iterator i = pending_requests_.find( | |
833 GlobalRequestID(child_id, request_id)); | |
834 if (i == pending_requests_.end()) { | |
835 NOTREACHED() << "WillSendData for invalid request"; | |
836 return false; | |
837 } | |
838 | |
839 ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second); | |
840 | |
841 info->IncrementPendingDataCount(); | |
842 if (info->pending_data_count() > kMaxPendingDataMessages) { | |
843 // We reached the max number of data messages that can be sent to | |
844 // the renderer for a given request. Pause the request and wait for | |
845 // the renderer to start processing them before resuming it. | |
846 PauseRequest(child_id, request_id, true); | |
847 return false; | |
848 } | |
849 | |
850 return true; | |
851 } | |
852 | |
853 void ResourceDispatcherHost::PauseRequest(int child_id, | |
854 int request_id, | |
855 bool pause) { | |
856 GlobalRequestID global_id(child_id, request_id); | |
857 PendingRequestList::iterator i = pending_requests_.find(global_id); | |
858 if (i == pending_requests_.end()) { | |
859 DLOG(WARNING) << "Pausing a request that wasn't found"; | |
860 return; | |
861 } | |
862 | |
863 ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second); | |
864 int pause_count = info->pause_count() + (pause ? 1 : -1); | |
865 if (pause_count < 0) { | |
866 NOTREACHED(); // Unbalanced call to pause. | |
867 return; | |
868 } | |
869 info->set_pause_count(pause_count); | |
870 | |
871 VLOG(1) << "To pause (" << pause << "): " << i->second->url().spec(); | |
872 | |
873 // If we're resuming, kick the request to start reading again. Run the read | |
874 // asynchronously to avoid recursion problems. | |
875 if (info->pause_count() == 0) { | |
876 MessageLoop::current()->PostTask(FROM_HERE, | |
877 method_runner_.NewRunnableMethod( | |
878 &ResourceDispatcherHost::ResumeRequest, global_id)); | |
879 } | |
880 } | |
881 | |
882 int ResourceDispatcherHost::GetOutstandingRequestsMemoryCost( | |
883 int child_id) const { | |
884 OutstandingRequestsMemoryCostMap::const_iterator entry = | |
885 outstanding_requests_memory_cost_map_.find(child_id); | |
886 return (entry == outstanding_requests_memory_cost_map_.end()) ? | |
887 0 : entry->second; | |
888 } | |
889 | |
890 // The object died, so cancel and detach all requests associated with it except | |
891 // for downloads, which belong to the browser process even if initiated via a | |
892 // renderer. | |
893 void ResourceDispatcherHost::CancelRequestsForProcess(int child_id) { | |
894 CancelRequestsForRoute(child_id, -1 /* cancel all */); | |
895 registered_temp_files_.erase(child_id); | |
896 } | |
897 | |
898 void ResourceDispatcherHost::CancelRequestsForRoute(int child_id, | |
899 int route_id) { | |
900 // Since pending_requests_ is a map, we first build up a list of all of the | |
901 // matching requests to be cancelled, and then we cancel them. Since there | |
902 // may be more than one request to cancel, we cannot simply hold onto the map | |
903 // iterators found in the first loop. | |
904 | |
905 // Find the global ID of all matching elements. | |
906 std::vector<GlobalRequestID> matching_requests; | |
907 for (PendingRequestList::const_iterator i = pending_requests_.begin(); | |
908 i != pending_requests_.end(); ++i) { | |
909 if (i->first.child_id == child_id) { | |
910 ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second); | |
911 if (!info->is_download() && | |
912 (route_id == -1 || route_id == info->route_id())) { | |
913 matching_requests.push_back( | |
914 GlobalRequestID(child_id, i->first.request_id)); | |
915 } | |
916 } | |
917 } | |
918 | |
919 // Remove matches. | |
920 for (size_t i = 0; i < matching_requests.size(); ++i) { | |
921 PendingRequestList::iterator iter = | |
922 pending_requests_.find(matching_requests[i]); | |
923 // Although every matching request was in pending_requests_ when we built | |
924 // matching_requests, it is normal for a matching request to be not found | |
925 // in pending_requests_ after we have removed some matching requests from | |
926 // pending_requests_. For example, deleting a net::URLRequest that has | |
927 // exclusive (write) access to an HTTP cache entry may unblock another | |
928 // net::URLRequest that needs exclusive access to the same cache entry, and | |
929 // that net::URLRequest may complete and remove itself from | |
930 // pending_requests_. So we need to check that iter is not equal to | |
931 // pending_requests_.end(). | |
932 if (iter != pending_requests_.end()) | |
933 RemovePendingRequest(iter); | |
934 } | |
935 | |
936 // Now deal with blocked requests if any. | |
937 if (route_id != -1) { | |
938 if (blocked_requests_map_.find(std::pair<int, int>(child_id, route_id)) != | |
939 blocked_requests_map_.end()) { | |
940 CancelBlockedRequestsForRoute(child_id, route_id); | |
941 } | |
942 } else { | |
943 // We have to do all render views for the process |child_id|. | |
944 // Note that we have to do this in 2 passes as we cannot call | |
945 // CancelBlockedRequestsForRoute while iterating over | |
946 // blocked_requests_map_, as it modifies it. | |
947 std::set<int> route_ids; | |
948 for (BlockedRequestMap::const_iterator iter = blocked_requests_map_.begin(); | |
949 iter != blocked_requests_map_.end(); ++iter) { | |
950 if (iter->first.first == child_id) | |
951 route_ids.insert(iter->first.second); | |
952 } | |
953 for (std::set<int>::const_iterator iter = route_ids.begin(); | |
954 iter != route_ids.end(); ++iter) { | |
955 CancelBlockedRequestsForRoute(child_id, *iter); | |
956 } | |
957 } | |
958 } | |
959 | |
960 // Cancels the request and removes it from the list. | |
961 void ResourceDispatcherHost::RemovePendingRequest(int child_id, | |
962 int request_id) { | |
963 PendingRequestList::iterator i = pending_requests_.find( | |
964 GlobalRequestID(child_id, request_id)); | |
965 if (i == pending_requests_.end()) { | |
966 NOTREACHED() << "Trying to remove a request that's not here"; | |
967 return; | |
968 } | |
969 RemovePendingRequest(i); | |
970 } | |
971 | |
972 void ResourceDispatcherHost::RemovePendingRequest( | |
973 const PendingRequestList::iterator& iter) { | |
974 ResourceDispatcherHostRequestInfo* info = InfoForRequest(iter->second); | |
975 | |
976 // Remove the memory credit that we added when pushing the request onto | |
977 // the pending list. | |
978 IncrementOutstandingRequestsMemoryCost(-1 * info->memory_cost(), | |
979 info->child_id()); | |
980 | |
981 // Notify interested parties that the request object is going away. | |
982 if (info->login_handler()) | |
983 info->login_handler()->OnRequestCancelled(); | |
984 if (info->ssl_client_auth_handler()) | |
985 info->ssl_client_auth_handler()->OnRequestCancelled(); | |
986 resource_queue_.RemoveRequest(iter->first); | |
987 | |
988 delete iter->second; | |
989 pending_requests_.erase(iter); | |
990 | |
991 // If we have no more pending requests, then stop the load state monitor | |
992 if (pending_requests_.empty()) | |
993 update_load_states_timer_.Stop(); | |
994 } | |
995 | |
996 // net::URLRequest::Delegate --------------------------------------------------- | |
997 | |
998 void ResourceDispatcherHost::OnReceivedRedirect(net::URLRequest* request, | |
999 const GURL& new_url, | |
1000 bool* defer_redirect) { | |
1001 VLOG(1) << "OnReceivedRedirect: " << request->url().spec(); | |
1002 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1003 | |
1004 DCHECK(request->status().is_success()); | |
1005 | |
1006 if (info->process_type() != ChildProcessInfo::PLUGIN_PROCESS && | |
1007 !ChildProcessSecurityPolicy::GetInstance()-> | |
1008 CanRequestURL(info->child_id(), new_url)) { | |
1009 VLOG(1) << "Denied unauthorized request for " | |
1010 << new_url.possibly_invalid_spec(); | |
1011 | |
1012 // Tell the renderer that this request was disallowed. | |
1013 CancelRequestInternal(request, false); | |
1014 return; | |
1015 } | |
1016 | |
1017 NotifyReceivedRedirect(request, info->child_id(), new_url); | |
1018 | |
1019 if (HandleExternalProtocol(info->request_id(), info->child_id(), | |
1020 info->route_id(), new_url, | |
1021 info->resource_type(), info->resource_handler())) { | |
1022 // The request is complete so we can remove it. | |
1023 RemovePendingRequest(info->child_id(), info->request_id()); | |
1024 return; | |
1025 } | |
1026 | |
1027 scoped_refptr<ResourceResponse> response(new ResourceResponse); | |
1028 PopulateResourceResponse(request, | |
1029 info->replace_extension_localization_templates(), response); | |
1030 if (!info->resource_handler()->OnRequestRedirected(info->request_id(), | |
1031 new_url, | |
1032 response, defer_redirect)) | |
1033 CancelRequestInternal(request, false); | |
1034 } | |
1035 | |
1036 void ResourceDispatcherHost::OnAuthRequired( | |
1037 net::URLRequest* request, | |
1038 net::AuthChallengeInfo* auth_info) { | |
1039 if (request->load_flags() & net::LOAD_PREFETCH) { | |
1040 request->CancelAuth(); | |
1041 return; | |
1042 } | |
1043 // Create a login dialog on the UI thread to get authentication data, | |
1044 // or pull from cache and continue on the IO thread. | |
1045 // TODO(mpcomplete): We should block the parent tab while waiting for | |
1046 // authentication. | |
1047 // That would also solve the problem of the net::URLRequest being cancelled | |
1048 // before we receive authentication. | |
1049 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1050 DCHECK(!info->login_handler()) << | |
1051 "OnAuthRequired called with login_handler pending"; | |
1052 info->set_login_handler(CreateLoginPrompt(auth_info, request)); | |
1053 } | |
1054 | |
1055 void ResourceDispatcherHost::OnCertificateRequested( | |
1056 net::URLRequest* request, | |
1057 net::SSLCertRequestInfo* cert_request_info) { | |
1058 DCHECK(request); | |
1059 | |
1060 if (cert_request_info->client_certs.empty()) { | |
1061 // No need to query the user if there are no certs to choose from. | |
1062 request->ContinueWithCertificate(NULL); | |
1063 return; | |
1064 } | |
1065 | |
1066 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1067 DCHECK(!info->ssl_client_auth_handler()) << | |
1068 "OnCertificateRequested called with ssl_client_auth_handler pending"; | |
1069 info->set_ssl_client_auth_handler( | |
1070 new SSLClientAuthHandler(request, cert_request_info)); | |
1071 info->ssl_client_auth_handler()->SelectCertificate(); | |
1072 } | |
1073 | |
1074 void ResourceDispatcherHost::OnSSLCertificateError( | |
1075 net::URLRequest* request, | |
1076 int cert_error, | |
1077 net::X509Certificate* cert) { | |
1078 DCHECK(request); | |
1079 SSLManager::OnSSLCertificateError(this, request, cert_error, cert); | |
1080 } | |
1081 | |
1082 void ResourceDispatcherHost::OnGetCookies( | |
1083 net::URLRequest* request, | |
1084 bool blocked_by_policy) { | |
1085 VLOG(1) << "OnGetCookies: " << request->url().spec(); | |
1086 | |
1087 int render_process_id, render_view_id; | |
1088 if (!RenderViewForRequest(request, &render_process_id, &render_view_id)) | |
1089 return; | |
1090 | |
1091 net::URLRequestContext* context = request->context(); | |
1092 | |
1093 net::CookieMonster* cookie_monster = | |
1094 context->cookie_store()->GetCookieMonster(); | |
1095 net::CookieList cookie_list = | |
1096 cookie_monster->GetAllCookiesForURL(request->url()); | |
1097 CallRenderViewHostContentSettingsDelegate( | |
1098 render_process_id, render_view_id, | |
1099 &RenderViewHostDelegate::ContentSettings::OnCookiesRead, | |
1100 request->url(), cookie_list, blocked_by_policy); | |
1101 } | |
1102 | |
1103 void ResourceDispatcherHost::OnSetCookie(net::URLRequest* request, | |
1104 const std::string& cookie_line, | |
1105 const net::CookieOptions& options, | |
1106 bool blocked_by_policy) { | |
1107 VLOG(1) << "OnSetCookie: " << request->url().spec(); | |
1108 | |
1109 int render_process_id, render_view_id; | |
1110 if (!RenderViewForRequest(request, &render_process_id, &render_view_id)) | |
1111 return; | |
1112 | |
1113 CallRenderViewHostContentSettingsDelegate( | |
1114 render_process_id, render_view_id, | |
1115 &RenderViewHostDelegate::ContentSettings::OnCookieChanged, | |
1116 request->url(), cookie_line, options, blocked_by_policy); | |
1117 } | |
1118 | |
1119 void ResourceDispatcherHost::OnResponseStarted(net::URLRequest* request) { | |
1120 VLOG(1) << "OnResponseStarted: " << request->url().spec(); | |
1121 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1122 if (PauseRequestIfNeeded(info)) { | |
1123 VLOG(1) << "OnResponseStarted pausing: " << request->url().spec(); | |
1124 return; | |
1125 } | |
1126 | |
1127 if (request->status().is_success()) { | |
1128 // We want to send a final upload progress message prior to sending | |
1129 // the response complete message even if we're waiting for an ack to | |
1130 // to a previous upload progress message. | |
1131 info->set_waiting_for_upload_progress_ack(false); | |
1132 MaybeUpdateUploadProgress(info, request); | |
1133 | |
1134 if (!CompleteResponseStarted(request)) { | |
1135 CancelRequestInternal(request, false); | |
1136 } else { | |
1137 // Check if the handler paused the request in their OnResponseStarted. | |
1138 if (PauseRequestIfNeeded(info)) { | |
1139 VLOG(1) << "OnResponseStarted pausing2: " << request->url().spec(); | |
1140 return; | |
1141 } | |
1142 | |
1143 StartReading(request); | |
1144 } | |
1145 } else { | |
1146 OnResponseCompleted(request); | |
1147 } | |
1148 } | |
1149 | |
1150 bool ResourceDispatcherHost::CompleteResponseStarted(net::URLRequest* request) { | |
1151 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1152 | |
1153 scoped_refptr<ResourceResponse> response(new ResourceResponse); | |
1154 PopulateResourceResponse(request, | |
1155 info->replace_extension_localization_templates(), response); | |
1156 | |
1157 if (request->ssl_info().cert) { | |
1158 int cert_id = | |
1159 CertStore::GetInstance()->StoreCert(request->ssl_info().cert, | |
1160 info->child_id()); | |
1161 response->response_head.security_info = | |
1162 SSLManager::SerializeSecurityInfo( | |
1163 cert_id, request->ssl_info().cert_status, | |
1164 request->ssl_info().security_bits, | |
1165 request->ssl_info().connection_status); | |
1166 } else { | |
1167 // We should not have any SSL state. | |
1168 DCHECK(!request->ssl_info().cert_status && | |
1169 request->ssl_info().security_bits == -1 && | |
1170 !request->ssl_info().connection_status); | |
1171 } | |
1172 | |
1173 NotifyResponseStarted(request, info->child_id()); | |
1174 info->set_called_on_response_started(true); | |
1175 return info->resource_handler()->OnResponseStarted(info->request_id(), | |
1176 response.get()); | |
1177 } | |
1178 | |
1179 void ResourceDispatcherHost::CancelRequest(int child_id, | |
1180 int request_id, | |
1181 bool from_renderer) { | |
1182 PendingRequestList::iterator i = pending_requests_.find( | |
1183 GlobalRequestID(child_id, request_id)); | |
1184 if (i == pending_requests_.end()) { | |
1185 // We probably want to remove this warning eventually, but I wanted to be | |
1186 // able to notice when this happens during initial development since it | |
1187 // should be rare and may indicate a bug. | |
1188 DLOG(WARNING) << "Canceling a request that wasn't found"; | |
1189 return; | |
1190 } | |
1191 CancelRequestInternal(i->second, from_renderer); | |
1192 } | |
1193 | |
1194 void ResourceDispatcherHost::CancelRequestInternal(net::URLRequest* request, | |
1195 bool from_renderer) { | |
1196 VLOG(1) << "CancelRequest: " << request->url().spec(); | |
1197 | |
1198 // WebKit will send us a cancel for downloads since it no longer handles them. | |
1199 // In this case, ignore the cancel since we handle downloads in the browser. | |
1200 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1201 if (!from_renderer || !info->is_download()) { | |
1202 if (info->login_handler()) { | |
1203 info->login_handler()->OnRequestCancelled(); | |
1204 info->set_login_handler(NULL); | |
1205 } | |
1206 if (info->ssl_client_auth_handler()) { | |
1207 info->ssl_client_auth_handler()->OnRequestCancelled(); | |
1208 info->set_ssl_client_auth_handler(NULL); | |
1209 } | |
1210 request->Cancel(); | |
1211 // Our callers assume |request| is valid after we return. | |
1212 DCHECK(IsValidRequest(request)); | |
1213 } | |
1214 | |
1215 // Do not remove from the pending requests, as the request will still | |
1216 // call AllDataReceived, and may even have more data before it does | |
1217 // that. | |
1218 } | |
1219 | |
1220 int ResourceDispatcherHost::IncrementOutstandingRequestsMemoryCost( | |
1221 int cost, | |
1222 int child_id) { | |
1223 // Retrieve the previous value (defaulting to 0 if not found). | |
1224 OutstandingRequestsMemoryCostMap::iterator prev_entry = | |
1225 outstanding_requests_memory_cost_map_.find(child_id); | |
1226 int new_cost = 0; | |
1227 if (prev_entry != outstanding_requests_memory_cost_map_.end()) | |
1228 new_cost = prev_entry->second; | |
1229 | |
1230 // Insert/update the total; delete entries when their value reaches 0. | |
1231 new_cost += cost; | |
1232 CHECK(new_cost >= 0); | |
1233 if (new_cost == 0) | |
1234 outstanding_requests_memory_cost_map_.erase(prev_entry); | |
1235 else | |
1236 outstanding_requests_memory_cost_map_[child_id] = new_cost; | |
1237 | |
1238 return new_cost; | |
1239 } | |
1240 | |
1241 // static | |
1242 int ResourceDispatcherHost::CalculateApproximateMemoryCost( | |
1243 net::URLRequest* request) { | |
1244 // The following fields should be a minor size contribution (experimentally | |
1245 // on the order of 100). However since they are variable length, it could | |
1246 // in theory be a sizeable contribution. | |
1247 int strings_cost = request->extra_request_headers().ToString().size() + | |
1248 request->original_url().spec().size() + | |
1249 request->referrer().size() + | |
1250 request->method().size(); | |
1251 | |
1252 int upload_cost = 0; | |
1253 | |
1254 // TODO(eroman): don't enable the upload throttling until we have data | |
1255 // showing what a reasonable limit is (limiting to 25MB of uploads may | |
1256 // be too restrictive). | |
1257 #if 0 | |
1258 // Sum all the (non-file) upload data attached to the request, if any. | |
1259 if (request->has_upload()) { | |
1260 const std::vector<net::UploadData::Element>& uploads = | |
1261 request->get_upload()->elements(); | |
1262 std::vector<net::UploadData::Element>::const_iterator iter; | |
1263 for (iter = uploads.begin(); iter != uploads.end(); ++iter) { | |
1264 if (iter->type() == net::UploadData::TYPE_BYTES) { | |
1265 int64 element_size = iter->GetContentLength(); | |
1266 // This cast should not result in truncation. | |
1267 upload_cost += static_cast<int>(element_size); | |
1268 } | |
1269 } | |
1270 } | |
1271 #endif | |
1272 | |
1273 // Note that this expression will typically be dominated by: | |
1274 // |kAvgBytesPerOutstandingRequest|. | |
1275 return kAvgBytesPerOutstandingRequest + strings_cost + upload_cost; | |
1276 } | |
1277 | |
1278 void ResourceDispatcherHost::BeginRequestInternal(net::URLRequest* request) { | |
1279 DCHECK(!request->is_pending()); | |
1280 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1281 | |
1282 // Add the memory estimate that starting this request will consume. | |
1283 info->set_memory_cost(CalculateApproximateMemoryCost(request)); | |
1284 int memory_cost = IncrementOutstandingRequestsMemoryCost(info->memory_cost(), | |
1285 info->child_id()); | |
1286 | |
1287 // If enqueing/starting this request will exceed our per-process memory | |
1288 // bound, abort it right away. | |
1289 if (memory_cost > max_outstanding_requests_cost_per_process_) { | |
1290 // We call "SimulateError()" as a way of setting the net::URLRequest's | |
1291 // status -- it has no effect beyond this, since the request hasn't started. | |
1292 request->SimulateError(net::ERR_INSUFFICIENT_RESOURCES); | |
1293 | |
1294 // TODO(eroman): this is kinda funky -- we insert the unstarted request into | |
1295 // |pending_requests_| simply to please OnResponseCompleted(). | |
1296 GlobalRequestID global_id(info->child_id(), info->request_id()); | |
1297 pending_requests_[global_id] = request; | |
1298 OnResponseCompleted(request); | |
1299 return; | |
1300 } | |
1301 | |
1302 std::pair<int, int> pair_id(info->child_id(), info->route_id()); | |
1303 BlockedRequestMap::const_iterator iter = blocked_requests_map_.find(pair_id); | |
1304 if (iter != blocked_requests_map_.end()) { | |
1305 // The request should be blocked. | |
1306 iter->second->push_back(request); | |
1307 return; | |
1308 } | |
1309 | |
1310 GlobalRequestID global_id(info->child_id(), info->request_id()); | |
1311 pending_requests_[global_id] = request; | |
1312 | |
1313 // Give the resource handlers an opportunity to delay the net::URLRequest from | |
1314 // being started. | |
1315 // | |
1316 // There are three cases: | |
1317 // | |
1318 // (1) if OnWillStart() returns false, the request is cancelled (regardless | |
1319 // of whether |defer_start| was set). | |
1320 // (2) If |defer_start| was set to true, then the request is not added | |
1321 // into the resource queue, and will only be started in response to | |
1322 // calling StartDeferredRequest(). | |
1323 // (3) If |defer_start| is not set, then the request is inserted into | |
1324 // the resource_queue_ (which may pause it further, or start it). | |
1325 bool defer_start = false; | |
1326 if (!info->resource_handler()->OnWillStart( | |
1327 info->request_id(), request->url(), | |
1328 &defer_start)) { | |
1329 CancelRequestInternal(request, false); | |
1330 return; | |
1331 } | |
1332 | |
1333 if (!defer_start) { | |
1334 InsertIntoResourceQueue(request, *info); | |
1335 } | |
1336 } | |
1337 | |
1338 void ResourceDispatcherHost::InsertIntoResourceQueue( | |
1339 net::URLRequest* request, | |
1340 const ResourceDispatcherHostRequestInfo& request_info) { | |
1341 resource_queue_.AddRequest(request, request_info); | |
1342 | |
1343 // Make sure we have the load state monitor running | |
1344 if (!update_load_states_timer_.IsRunning()) { | |
1345 update_load_states_timer_.Start( | |
1346 TimeDelta::FromMilliseconds(kUpdateLoadStatesIntervalMsec), | |
1347 this, &ResourceDispatcherHost::UpdateLoadStates); | |
1348 } | |
1349 } | |
1350 | |
1351 bool ResourceDispatcherHost::PauseRequestIfNeeded( | |
1352 ResourceDispatcherHostRequestInfo* info) { | |
1353 if (info->pause_count() > 0) | |
1354 info->set_is_paused(true); | |
1355 return info->is_paused(); | |
1356 } | |
1357 | |
1358 void ResourceDispatcherHost::ResumeRequest(const GlobalRequestID& request_id) { | |
1359 PendingRequestList::iterator i = pending_requests_.find(request_id); | |
1360 if (i == pending_requests_.end()) // The request may have been destroyed | |
1361 return; | |
1362 | |
1363 net::URLRequest* request = i->second; | |
1364 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1365 if (!info->is_paused()) | |
1366 return; | |
1367 | |
1368 VLOG(1) << "Resuming: " << i->second->url().spec(); | |
1369 | |
1370 info->set_is_paused(false); | |
1371 | |
1372 if (info->called_on_response_started()) { | |
1373 if (info->has_started_reading()) { | |
1374 OnReadCompleted(i->second, info->paused_read_bytes()); | |
1375 } else { | |
1376 StartReading(request); | |
1377 } | |
1378 } else { | |
1379 OnResponseStarted(i->second); | |
1380 } | |
1381 } | |
1382 | |
1383 void ResourceDispatcherHost::StartReading(net::URLRequest* request) { | |
1384 // Start reading. | |
1385 int bytes_read = 0; | |
1386 if (Read(request, &bytes_read)) { | |
1387 OnReadCompleted(request, bytes_read); | |
1388 } else if (!request->status().is_io_pending()) { | |
1389 DCHECK(!InfoForRequest(request)->is_paused()); | |
1390 // If the error is not an IO pending, then we're done reading. | |
1391 OnResponseCompleted(request); | |
1392 } | |
1393 } | |
1394 | |
1395 bool ResourceDispatcherHost::Read(net::URLRequest* request, int* bytes_read) { | |
1396 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1397 DCHECK(!info->is_paused()); | |
1398 | |
1399 net::IOBuffer* buf; | |
1400 int buf_size; | |
1401 if (!info->resource_handler()->OnWillRead(info->request_id(), | |
1402 &buf, &buf_size, -1)) { | |
1403 return false; | |
1404 } | |
1405 | |
1406 DCHECK(buf); | |
1407 DCHECK(buf_size > 0); | |
1408 | |
1409 info->set_has_started_reading(true); | |
1410 return request->Read(buf, buf_size, bytes_read); | |
1411 } | |
1412 | |
1413 void ResourceDispatcherHost::OnReadCompleted(net::URLRequest* request, | |
1414 int bytes_read) { | |
1415 DCHECK(request); | |
1416 VLOG(1) << "OnReadCompleted: " << request->url().spec(); | |
1417 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1418 | |
1419 // OnReadCompleted can be called without Read (e.g., for chrome:// URLs). | |
1420 // Make sure we know that a read has begun. | |
1421 info->set_has_started_reading(true); | |
1422 | |
1423 if (PauseRequestIfNeeded(info)) { | |
1424 info->set_paused_read_bytes(bytes_read); | |
1425 VLOG(1) << "OnReadCompleted pausing: " << request->url().spec(); | |
1426 return; | |
1427 } | |
1428 | |
1429 if (request->status().is_success() && CompleteRead(request, &bytes_read)) { | |
1430 // The request can be paused if we realize that the renderer is not | |
1431 // servicing messages fast enough. | |
1432 if (info->pause_count() == 0 && | |
1433 Read(request, &bytes_read) && | |
1434 request->status().is_success()) { | |
1435 if (bytes_read == 0) { | |
1436 CompleteRead(request, &bytes_read); | |
1437 } else { | |
1438 // Force the next CompleteRead / Read pair to run as a separate task. | |
1439 // This avoids a fast, large network request from monopolizing the IO | |
1440 // thread and starving other IO operations from running. | |
1441 info->set_paused_read_bytes(bytes_read); | |
1442 info->set_is_paused(true); | |
1443 GlobalRequestID id(info->child_id(), info->request_id()); | |
1444 MessageLoop::current()->PostTask( | |
1445 FROM_HERE, | |
1446 method_runner_.NewRunnableMethod( | |
1447 &ResourceDispatcherHost::ResumeRequest, id)); | |
1448 return; | |
1449 } | |
1450 } | |
1451 } | |
1452 | |
1453 if (PauseRequestIfNeeded(info)) { | |
1454 info->set_paused_read_bytes(bytes_read); | |
1455 VLOG(1) << "OnReadCompleted (CompleteRead) pausing: " | |
1456 << request->url().spec(); | |
1457 return; | |
1458 } | |
1459 | |
1460 // If the status is not IO pending then we've either finished (success) or we | |
1461 // had an error. Either way, we're done! | |
1462 if (!request->status().is_io_pending()) | |
1463 OnResponseCompleted(request); | |
1464 } | |
1465 | |
1466 bool ResourceDispatcherHost::CompleteRead(net::URLRequest* request, | |
1467 int* bytes_read) { | |
1468 if (!request || !request->status().is_success()) { | |
1469 NOTREACHED(); | |
1470 return false; | |
1471 } | |
1472 | |
1473 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1474 if (!info->resource_handler()->OnReadCompleted(info->request_id(), | |
1475 bytes_read)) { | |
1476 CancelRequestInternal(request, false); | |
1477 return false; | |
1478 } | |
1479 | |
1480 return *bytes_read != 0; | |
1481 } | |
1482 | |
1483 void ResourceDispatcherHost::OnResponseCompleted(net::URLRequest* request) { | |
1484 VLOG(1) << "OnResponseCompleted: " << request->url().spec(); | |
1485 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1486 | |
1487 // If the load for a main frame has failed, track it in a histogram, | |
1488 // since it will probably cause the user to see an error page. | |
1489 if (!request->status().is_success() && | |
1490 info->resource_type() == ResourceType::MAIN_FRAME && | |
1491 request->status().os_error() != net::ERR_ABORTED) { | |
1492 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.ErrorCodesForMainFrame", | |
1493 -request->status().os_error(), | |
1494 GetAllNetErrorCodes()); | |
1495 } | |
1496 | |
1497 std::string security_info; | |
1498 const net::SSLInfo& ssl_info = request->ssl_info(); | |
1499 if (ssl_info.cert != NULL) { | |
1500 int cert_id = CertStore::GetInstance()->StoreCert(ssl_info.cert, | |
1501 info->child_id()); | |
1502 security_info = SSLManager::SerializeSecurityInfo( | |
1503 cert_id, ssl_info.cert_status, ssl_info.security_bits, | |
1504 ssl_info.connection_status); | |
1505 } | |
1506 | |
1507 if (info->resource_handler()->OnResponseCompleted(info->request_id(), | |
1508 request->status(), | |
1509 security_info)) { | |
1510 NotifyResponseCompleted(request, info->child_id()); | |
1511 | |
1512 // The request is complete so we can remove it. | |
1513 RemovePendingRequest(info->child_id(), info->request_id()); | |
1514 } | |
1515 // If the handler's OnResponseCompleted returns false, we are deferring the | |
1516 // call until later. We will notify the world and clean up when we resume. | |
1517 } | |
1518 | |
1519 // static | |
1520 ResourceDispatcherHostRequestInfo* ResourceDispatcherHost::InfoForRequest( | |
1521 net::URLRequest* request) { | |
1522 // Avoid writing this function twice by casting the cosnt version. | |
1523 const net::URLRequest* const_request = request; | |
1524 return const_cast<ResourceDispatcherHostRequestInfo*>( | |
1525 InfoForRequest(const_request)); | |
1526 } | |
1527 | |
1528 // static | |
1529 const ResourceDispatcherHostRequestInfo* ResourceDispatcherHost::InfoForRequest( | |
1530 const net::URLRequest* request) { | |
1531 const ResourceDispatcherHostRequestInfo* info = | |
1532 static_cast<const ResourceDispatcherHostRequestInfo*>( | |
1533 request->GetUserData(NULL)); | |
1534 DLOG_IF(WARNING, !info) << "Request doesn't seem to have our data"; | |
1535 return info; | |
1536 } | |
1537 | |
1538 // static | |
1539 bool ResourceDispatcherHost::RenderViewForRequest( | |
1540 const net::URLRequest* request, | |
1541 int* render_process_host_id, | |
1542 int* render_view_host_id) { | |
1543 const ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1544 if (!info) { | |
1545 *render_process_host_id = -1; | |
1546 *render_view_host_id = -1; | |
1547 return false; | |
1548 } | |
1549 | |
1550 // If the request is from the worker process, find a tab that owns the worker. | |
1551 if (info->process_type() == ChildProcessInfo::WORKER_PROCESS) { | |
1552 // Need to display some related UI for this network request - pick an | |
1553 // arbitrary parent to do so. | |
1554 if (!WorkerService::GetInstance()->GetRendererForWorker( | |
1555 info->child_id(), render_process_host_id, render_view_host_id)) { | |
1556 *render_process_host_id = -1; | |
1557 *render_view_host_id = -1; | |
1558 return false; | |
1559 } | |
1560 } else { | |
1561 *render_process_host_id = info->child_id(); | |
1562 *render_view_host_id = info->route_id(); | |
1563 } | |
1564 return true; | |
1565 } | |
1566 | |
1567 void ResourceDispatcherHost::AddObserver(Observer* obs) { | |
1568 observer_list_.AddObserver(obs); | |
1569 } | |
1570 | |
1571 void ResourceDispatcherHost::RemoveObserver(Observer* obs) { | |
1572 observer_list_.RemoveObserver(obs); | |
1573 } | |
1574 | |
1575 net::URLRequest* ResourceDispatcherHost::GetURLRequest( | |
1576 const GlobalRequestID& request_id) const { | |
1577 // This should be running in the IO loop. | |
1578 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1579 | |
1580 PendingRequestList::const_iterator i = pending_requests_.find(request_id); | |
1581 if (i == pending_requests_.end()) | |
1582 return NULL; | |
1583 | |
1584 return i->second; | |
1585 } | |
1586 | |
1587 static int GetCertID(net::URLRequest* request, int child_id) { | |
1588 if (request->ssl_info().cert) { | |
1589 return CertStore::GetInstance()->StoreCert(request->ssl_info().cert, | |
1590 child_id); | |
1591 } | |
1592 return 0; | |
1593 } | |
1594 | |
1595 void ResourceDispatcherHost::NotifyResponseStarted(net::URLRequest* request, | |
1596 int child_id) { | |
1597 // Notify the observers on the IO thread. | |
1598 FOR_EACH_OBSERVER(Observer, observer_list_, OnRequestStarted(this, request)); | |
1599 | |
1600 int render_process_id, render_view_id; | |
1601 if (!RenderViewForRequest(request, &render_process_id, &render_view_id)) | |
1602 return; | |
1603 | |
1604 // Notify the observers on the UI thread. | |
1605 ResourceRequestDetails* detail = new ResourceRequestDetails( | |
1606 request, GetCertID(request, child_id)); | |
1607 BrowserThread::PostTask( | |
1608 BrowserThread::UI, FROM_HERE, | |
1609 NewRunnableFunction( | |
1610 &ResourceDispatcherHost::NotifyOnUI<ResourceRequestDetails>, | |
1611 NotificationType::RESOURCE_RESPONSE_STARTED, | |
1612 render_process_id, render_view_id, detail)); | |
1613 } | |
1614 | |
1615 void ResourceDispatcherHost::NotifyResponseCompleted(net::URLRequest* request, | |
1616 int child_id) { | |
1617 // Notify the observers on the IO thread. | |
1618 FOR_EACH_OBSERVER(Observer, observer_list_, | |
1619 OnResponseCompleted(this, request)); | |
1620 } | |
1621 | |
1622 void ResourceDispatcherHost::NotifyReceivedRedirect(net::URLRequest* request, | |
1623 int child_id, | |
1624 const GURL& new_url) { | |
1625 // Notify the observers on the IO thread. | |
1626 FOR_EACH_OBSERVER(Observer, observer_list_, | |
1627 OnReceivedRedirect(this, request, new_url)); | |
1628 | |
1629 int render_process_id, render_view_id; | |
1630 if (!RenderViewForRequest(request, &render_process_id, &render_view_id)) | |
1631 return; | |
1632 | |
1633 // Notify the observers on the UI thread. | |
1634 ResourceRedirectDetails* detail = new ResourceRedirectDetails( | |
1635 request, GetCertID(request, child_id), new_url); | |
1636 BrowserThread::PostTask( | |
1637 BrowserThread::UI, FROM_HERE, | |
1638 NewRunnableFunction( | |
1639 &ResourceDispatcherHost::NotifyOnUI<ResourceRedirectDetails>, | |
1640 NotificationType::RESOURCE_RECEIVED_REDIRECT, | |
1641 render_process_id, render_view_id, detail)); | |
1642 } | |
1643 | |
1644 template <class T> | |
1645 void ResourceDispatcherHost::NotifyOnUI(NotificationType type, | |
1646 int render_process_id, | |
1647 int render_view_id, | |
1648 T* detail) { | |
1649 RenderViewHost* rvh = | |
1650 RenderViewHost::FromID(render_process_id, render_view_id); | |
1651 if (rvh) { | |
1652 RenderViewHostDelegate* rvhd = rvh->delegate(); | |
1653 NotificationService::current()->Notify( | |
1654 type, Source<RenderViewHostDelegate>(rvhd), Details<T>(detail)); | |
1655 } | |
1656 delete detail; | |
1657 } | |
1658 | |
1659 namespace { | |
1660 | |
1661 // This function attempts to return the "more interesting" load state of |a| | |
1662 // and |b|. We don't have temporal information about these load states | |
1663 // (meaning we don't know when we transitioned into these states), so we just | |
1664 // rank them according to how "interesting" the states are. | |
1665 // | |
1666 // We take advantage of the fact that the load states are an enumeration listed | |
1667 // in the order in which they occur during the lifetime of a request, so we can | |
1668 // regard states with larger numeric values as being further along toward | |
1669 // completion. We regard those states as more interesting to report since they | |
1670 // represent progress. | |
1671 // | |
1672 // For example, by this measure "tranferring data" is a more interesting state | |
1673 // than "resolving host" because when we are transferring data we are actually | |
1674 // doing something that corresponds to changes that the user might observe, | |
1675 // whereas waiting for a host name to resolve implies being stuck. | |
1676 // | |
1677 net::LoadState MoreInterestingLoadState(net::LoadState a, net::LoadState b) { | |
1678 return (a < b) ? b : a; | |
1679 } | |
1680 | |
1681 // Carries information about a load state change. | |
1682 struct LoadInfo { | |
1683 GURL url; | |
1684 net::LoadState load_state; | |
1685 uint64 upload_position; | |
1686 uint64 upload_size; | |
1687 }; | |
1688 | |
1689 // Map from ProcessID+ViewID pair to LoadState | |
1690 typedef std::map<std::pair<int, int>, LoadInfo> LoadInfoMap; | |
1691 | |
1692 // Used to marshall calls to LoadStateChanged from the IO to UI threads. We do | |
1693 // them all as a single task to avoid spamming the UI thread. | |
1694 class LoadInfoUpdateTask : public Task { | |
1695 public: | |
1696 virtual void Run() { | |
1697 LoadInfoMap::const_iterator i; | |
1698 for (i = info_map.begin(); i != info_map.end(); ++i) { | |
1699 RenderViewHost* view = | |
1700 RenderViewHost::FromID(i->first.first, i->first.second); | |
1701 if (view) // The view could be gone at this point. | |
1702 view->LoadStateChanged(i->second.url, i->second.load_state, | |
1703 i->second.upload_position, | |
1704 i->second.upload_size); | |
1705 } | |
1706 } | |
1707 LoadInfoMap info_map; | |
1708 }; | |
1709 | |
1710 } // namespace | |
1711 | |
1712 void ResourceDispatcherHost::UpdateLoadStates() { | |
1713 // Populate this map with load state changes, and then send them on to the UI | |
1714 // thread where they can be passed along to the respective RVHs. | |
1715 LoadInfoMap info_map; | |
1716 | |
1717 PendingRequestList::const_iterator i; | |
1718 | |
1719 // Determine the largest upload size of all requests | |
1720 // in each View (good chance it's zero). | |
1721 std::map<std::pair<int, int>, uint64> largest_upload_size; | |
1722 for (i = pending_requests_.begin(); i != pending_requests_.end(); ++i) { | |
1723 net::URLRequest* request = i->second; | |
1724 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1725 uint64 upload_size = info->upload_size(); | |
1726 if (request->GetLoadState() != net::LOAD_STATE_SENDING_REQUEST) | |
1727 upload_size = 0; | |
1728 std::pair<int, int> key(info->child_id(), info->route_id()); | |
1729 if (upload_size && largest_upload_size[key] < upload_size) | |
1730 largest_upload_size[key] = upload_size; | |
1731 } | |
1732 | |
1733 for (i = pending_requests_.begin(); i != pending_requests_.end(); ++i) { | |
1734 net::URLRequest* request = i->second; | |
1735 net::LoadState load_state = request->GetLoadState(); | |
1736 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1737 | |
1738 // We also poll for upload progress on this timer and send upload | |
1739 // progress ipc messages to the plugin process. | |
1740 bool update_upload_progress = MaybeUpdateUploadProgress(info, request); | |
1741 | |
1742 if (info->last_load_state() != load_state || update_upload_progress) { | |
1743 std::pair<int, int> key(info->child_id(), info->route_id()); | |
1744 | |
1745 // If a request is uploading data, ignore all other requests so that the | |
1746 // upload progress takes priority for being shown in the status bar. | |
1747 if (largest_upload_size.find(key) != largest_upload_size.end() && | |
1748 info->upload_size() < largest_upload_size[key]) | |
1749 continue; | |
1750 | |
1751 info->set_last_load_state(load_state); | |
1752 | |
1753 net::LoadState to_insert; | |
1754 LoadInfoMap::iterator existing = info_map.find(key); | |
1755 if (existing == info_map.end()) { | |
1756 to_insert = load_state; | |
1757 } else { | |
1758 to_insert = | |
1759 MoreInterestingLoadState(existing->second.load_state, load_state); | |
1760 if (to_insert == existing->second.load_state) | |
1761 continue; | |
1762 } | |
1763 LoadInfo& load_info = info_map[key]; | |
1764 load_info.url = request->url(); | |
1765 load_info.load_state = to_insert; | |
1766 load_info.upload_size = info->upload_size(); | |
1767 load_info.upload_position = request->GetUploadProgress(); | |
1768 } | |
1769 } | |
1770 | |
1771 if (info_map.empty()) | |
1772 return; | |
1773 | |
1774 LoadInfoUpdateTask* task = new LoadInfoUpdateTask; | |
1775 task->info_map.swap(info_map); | |
1776 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task); | |
1777 } | |
1778 | |
1779 // Calls the ResourceHandler to send upload progress messages to the renderer. | |
1780 // Returns true iff an upload progress message should be sent to the UI thread. | |
1781 bool ResourceDispatcherHost::MaybeUpdateUploadProgress( | |
1782 ResourceDispatcherHostRequestInfo *info, | |
1783 net::URLRequest *request) { | |
1784 | |
1785 if (!info->upload_size() || info->waiting_for_upload_progress_ack()) | |
1786 return false; | |
1787 | |
1788 uint64 size = info->upload_size(); | |
1789 uint64 position = request->GetUploadProgress(); | |
1790 if (position == info->last_upload_position()) | |
1791 return false; // no progress made since last time | |
1792 | |
1793 const uint64 kHalfPercentIncrements = 200; | |
1794 const TimeDelta kOneSecond = TimeDelta::FromMilliseconds(1000); | |
1795 | |
1796 uint64 amt_since_last = position - info->last_upload_position(); | |
1797 TimeDelta time_since_last = TimeTicks::Now() - info->last_upload_ticks(); | |
1798 | |
1799 bool is_finished = (size == position); | |
1800 bool enough_new_progress = (amt_since_last > (size / kHalfPercentIncrements)); | |
1801 bool too_much_time_passed = time_since_last > kOneSecond; | |
1802 | |
1803 if (is_finished || enough_new_progress || too_much_time_passed) { | |
1804 if (request->load_flags() & net::LOAD_ENABLE_UPLOAD_PROGRESS) { | |
1805 info->resource_handler()->OnUploadProgress(info->request_id(), | |
1806 position, size); | |
1807 info->set_waiting_for_upload_progress_ack(true); | |
1808 } | |
1809 info->set_last_upload_ticks(TimeTicks::Now()); | |
1810 info->set_last_upload_position(position); | |
1811 return true; | |
1812 } | |
1813 return false; | |
1814 } | |
1815 | |
1816 void ResourceDispatcherHost::BlockRequestsForRoute(int child_id, int route_id) { | |
1817 std::pair<int, int> key(child_id, route_id); | |
1818 DCHECK(blocked_requests_map_.find(key) == blocked_requests_map_.end()) << | |
1819 "BlockRequestsForRoute called multiple time for the same RVH"; | |
1820 blocked_requests_map_[key] = new BlockedRequestsList(); | |
1821 } | |
1822 | |
1823 void ResourceDispatcherHost::ResumeBlockedRequestsForRoute(int child_id, | |
1824 int route_id) { | |
1825 ProcessBlockedRequestsForRoute(child_id, route_id, false); | |
1826 } | |
1827 | |
1828 void ResourceDispatcherHost::CancelBlockedRequestsForRoute(int child_id, | |
1829 int route_id) { | |
1830 ProcessBlockedRequestsForRoute(child_id, route_id, true); | |
1831 } | |
1832 | |
1833 void ResourceDispatcherHost::ProcessBlockedRequestsForRoute( | |
1834 int child_id, | |
1835 int route_id, | |
1836 bool cancel_requests) { | |
1837 BlockedRequestMap::iterator iter = blocked_requests_map_.find( | |
1838 std::pair<int, int>(child_id, route_id)); | |
1839 if (iter == blocked_requests_map_.end()) { | |
1840 // It's possible to reach here if the renderer crashed while an interstitial | |
1841 // page was showing. | |
1842 return; | |
1843 } | |
1844 | |
1845 BlockedRequestsList* requests = iter->second; | |
1846 | |
1847 // Removing the vector from the map unblocks any subsequent requests. | |
1848 blocked_requests_map_.erase(iter); | |
1849 | |
1850 for (BlockedRequestsList::iterator req_iter = requests->begin(); | |
1851 req_iter != requests->end(); ++req_iter) { | |
1852 // Remove the memory credit that we added when pushing the request onto | |
1853 // the blocked list. | |
1854 net::URLRequest* request = *req_iter; | |
1855 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1856 IncrementOutstandingRequestsMemoryCost(-1 * info->memory_cost(), | |
1857 info->child_id()); | |
1858 if (cancel_requests) | |
1859 delete request; | |
1860 else | |
1861 BeginRequestInternal(request); | |
1862 } | |
1863 | |
1864 delete requests; | |
1865 } | |
1866 | |
1867 bool ResourceDispatcherHost::IsValidRequest(net::URLRequest* request) { | |
1868 if (!request) | |
1869 return false; | |
1870 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); | |
1871 return pending_requests_.find( | |
1872 GlobalRequestID(info->child_id(), info->request_id())) != | |
1873 pending_requests_.end(); | |
1874 } | |
1875 | |
1876 // static | |
1877 void ResourceDispatcherHost::ApplyExtensionLocalizationFilter( | |
1878 const GURL& url, | |
1879 const ResourceType::Type& resource_type, | |
1880 ResourceDispatcherHostRequestInfo* request_info) { | |
1881 // Apply filter to chrome extension CSS files. | |
1882 if (url.SchemeIs(chrome::kExtensionScheme) && | |
1883 resource_type == ResourceType::STYLESHEET) | |
1884 request_info->set_replace_extension_localization_templates(); | |
1885 } | |
1886 | |
1887 // static | |
1888 net::RequestPriority ResourceDispatcherHost::DetermineRequestPriority( | |
1889 ResourceType::Type type) { | |
1890 // Determine request priority based on how critical this resource typically | |
1891 // is to user-perceived page load performance. Important considerations are: | |
1892 // * Can this resource block the download of other resources. | |
1893 // * Can this resource block the rendering of the page. | |
1894 // * How useful is the page to the user if this resource is not loaded yet. | |
1895 switch (type) { | |
1896 // Main frames are the highest priority because they can block nearly every | |
1897 // type of other resource and there is no useful display without them. | |
1898 // Sub frames are a close second, however it is a common pattern to wrap | |
1899 // ads in an iframe or even in multiple nested iframes. It is worth | |
1900 // investigating if there is a better priority for them. | |
1901 case ResourceType::MAIN_FRAME: | |
1902 case ResourceType::SUB_FRAME: | |
1903 return net::HIGHEST; | |
1904 | |
1905 // Stylesheets and scripts can block rendering and loading of other | |
1906 // resources. Fonts can block text from rendering. | |
1907 case ResourceType::STYLESHEET: | |
1908 case ResourceType::SCRIPT: | |
1909 case ResourceType::FONT_RESOURCE: | |
1910 return net::MEDIUM; | |
1911 | |
1912 // Sub resources, objects and media are lower priority than potentially | |
1913 // blocking stylesheets, scripts and fonts, but are higher priority than | |
1914 // images because if they exist they are probably more central to the page | |
1915 // focus than images on the page. | |
1916 case ResourceType::SUB_RESOURCE: | |
1917 case ResourceType::OBJECT: | |
1918 case ResourceType::MEDIA: | |
1919 case ResourceType::WORKER: | |
1920 case ResourceType::SHARED_WORKER: | |
1921 return net::LOW; | |
1922 | |
1923 // Images are the "lowest" priority because they typically do not block | |
1924 // downloads or rendering and most pages have some useful content without | |
1925 // them. | |
1926 case ResourceType::IMAGE: | |
1927 return net::LOWEST; | |
1928 | |
1929 // Prefetches are at a lower priority than even LOWEST, since they | |
1930 // are not even required for rendering of the current page. | |
1931 case ResourceType::PREFETCH: | |
1932 return net::IDLE; | |
1933 | |
1934 default: | |
1935 // When new resource types are added, their priority must be considered. | |
1936 NOTREACHED(); | |
1937 return net::LOW; | |
1938 } | |
1939 } | |
1940 | |
1941 // static | |
1942 bool ResourceDispatcherHost::is_prefetch_enabled() { | |
1943 return is_prefetch_enabled_; | |
1944 } | |
1945 | |
1946 // static | |
1947 void ResourceDispatcherHost::set_is_prefetch_enabled(bool value) { | |
1948 is_prefetch_enabled_ = value; | |
1949 } | |
1950 | |
1951 // static | |
1952 bool ResourceDispatcherHost::is_prefetch_enabled_ = false; | |
OLD | NEW |