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 // See http://dev.chromium.org/developers/design-documents/multi-process-resourc
e-loading | |
6 | |
7 #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" | |
8 | |
9 #include <set> | |
10 #include <vector> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/bind_helpers.h" | |
14 #include "base/command_line.h" | |
15 #include "base/compiler_specific.h" | |
16 #include "base/debug/alias.h" | |
17 #include "base/logging.h" | |
18 #include "base/memory/scoped_ptr.h" | |
19 #include "base/message_loop.h" | |
20 #include "base/metrics/histogram.h" | |
21 #include "base/shared_memory.h" | |
22 #include "base/stl_util.h" | |
23 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | |
24 #include "content/browser/appcache/chrome_appcache_service.h" | |
25 #include "content/browser/cert_store_impl.h" | |
26 #include "content/browser/child_process_security_policy_impl.h" | |
27 #include "content/browser/cross_site_request_manager.h" | |
28 #include "content/browser/download/download_resource_handler.h" | |
29 #include "content/browser/download/save_file_manager.h" | |
30 #include "content/browser/download/save_file_resource_handler.h" | |
31 #include "content/browser/fileapi/chrome_blob_storage_context.h" | |
32 #include "content/browser/plugin_service_impl.h" | |
33 #include "content/browser/renderer_host/async_resource_handler.h" | |
34 #include "content/browser/renderer_host/buffered_resource_handler.h" | |
35 #include "content/browser/renderer_host/cross_site_resource_handler.h" | |
36 #include "content/browser/renderer_host/redirect_to_file_resource_handler.h" | |
37 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
38 #include "content/browser/renderer_host/render_view_host_impl.h" | |
39 #include "content/browser/renderer_host/resource_message_filter.h" | |
40 #include "content/browser/renderer_host/resource_request_info_impl.h" | |
41 #include "content/browser/renderer_host/sync_resource_handler.h" | |
42 #include "content/browser/renderer_host/throttling_resource_handler.h" | |
43 #include "content/browser/renderer_host/transfer_navigation_resource_throttle.h" | |
44 #include "content/browser/resource_context_impl.h" | |
45 #include "content/browser/worker_host/worker_service_impl.h" | |
46 #include "content/common/resource_messages.h" | |
47 #include "content/common/ssl_status_serialization.h" | |
48 #include "content/common/view_messages.h" | |
49 #include "content/public/browser/browser_thread.h" | |
50 #include "content/public/browser/content_browser_client.h" | |
51 #include "content/public/browser/download_manager.h" | |
52 #include "content/public/browser/global_request_id.h" | |
53 #include "content/public/browser/notification_service.h" | |
54 #include "content/public/browser/resource_dispatcher_host_delegate.h" | |
55 #include "content/public/browser/resource_request_details.h" | |
56 #include "content/public/browser/resource_throttle.h" | |
57 #include "content/public/browser/user_metrics.h" | |
58 #include "content/public/common/content_switches.h" | |
59 #include "content/public/common/process_type.h" | |
60 #include "content/public/common/url_constants.h" | |
61 #include "net/base/auth.h" | |
62 #include "net/base/cert_status_flags.h" | |
63 #include "net/base/load_flags.h" | |
64 #include "net/base/mime_util.h" | |
65 #include "net/base/net_errors.h" | |
66 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | |
67 #include "net/base/request_priority.h" | |
68 #include "net/base/ssl_cert_request_info.h" | |
69 #include "net/base/upload_data.h" | |
70 #include "net/cookies/cookie_monster.h" | |
71 #include "net/http/http_cache.h" | |
72 #include "net/http/http_response_headers.h" | |
73 #include "net/http/http_response_info.h" | |
74 #include "net/http/http_transaction_factory.h" | |
75 #include "net/url_request/url_request.h" | |
76 #include "net/url_request/url_request_context.h" | |
77 #include "net/url_request/url_request_job_factory.h" | |
78 #include "webkit/appcache/appcache_interceptor.h" | |
79 #include "webkit/appcache/appcache_interfaces.h" | |
80 #include "webkit/blob/blob_storage_controller.h" | |
81 #include "webkit/blob/shareable_file_reference.h" | |
82 #include "webkit/glue/resource_request_body.h" | |
83 #include "webkit/glue/webkit_glue.h" | |
84 | |
85 using base::Time; | |
86 using base::TimeDelta; | |
87 using base::TimeTicks; | |
88 using webkit_blob::ShareableFileReference; | |
89 using webkit_glue::ResourceRequestBody; | |
90 | |
91 // ---------------------------------------------------------------------------- | |
92 | |
93 namespace content { | |
94 | |
95 namespace { | |
96 | |
97 static ResourceDispatcherHostImpl* g_resource_dispatcher_host; | |
98 | |
99 // The interval for calls to ResourceDispatcherHostImpl::UpdateLoadStates | |
100 const int kUpdateLoadStatesIntervalMsec = 100; | |
101 | |
102 // Maximum byte "cost" of all the outstanding requests for a renderer. | |
103 // See delcaration of |max_outstanding_requests_cost_per_process_| for details. | |
104 // This bound is 25MB, which allows for around 6000 outstanding requests. | |
105 const int kMaxOutstandingRequestsCostPerProcess = 26214400; | |
106 | |
107 // The number of milliseconds after noting a user gesture that we will | |
108 // tag newly-created URLRequest objects with the | |
109 // net::LOAD_MAYBE_USER_GESTURE load flag. This is a fairly arbitrary | |
110 // guess at how long to expect direct impact from a user gesture, but | |
111 // this should be OK as the load flag is a best-effort thing only, | |
112 // rather than being intended as fully accurate. | |
113 const int kUserGestureWindowMs = 3500; | |
114 | |
115 // All possible error codes from the network module. Note that the error codes | |
116 // are all positive (since histograms expect positive sample values). | |
117 const int kAllNetErrorCodes[] = { | |
118 #define NET_ERROR(label, value) -(value), | |
119 #include "net/base/net_error_list.h" | |
120 #undef NET_ERROR | |
121 }; | |
122 | |
123 // Aborts a request before an URLRequest has actually been created. | |
124 void AbortRequestBeforeItStarts(ResourceMessageFilter* filter, | |
125 IPC::Message* sync_result, | |
126 int route_id, | |
127 int request_id) { | |
128 if (sync_result) { | |
129 SyncLoadResult result; | |
130 result.error_code = net::ERR_ABORTED; | |
131 ResourceHostMsg_SyncLoad::WriteReplyParams(sync_result, result); | |
132 filter->Send(sync_result); | |
133 } else { | |
134 // Tell the renderer that this request was disallowed. | |
135 filter->Send(new ResourceMsg_RequestComplete( | |
136 route_id, | |
137 request_id, | |
138 net::ERR_ABORTED, | |
139 false, | |
140 std::string(), // No security info needed, connection not established. | |
141 base::TimeTicks())); | |
142 } | |
143 } | |
144 | |
145 GURL MaybeStripReferrer(const GURL& possible_referrer) { | |
146 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoReferrers)) | |
147 return GURL(); | |
148 return possible_referrer; | |
149 } | |
150 | |
151 // Consults the RendererSecurity policy to determine whether the | |
152 // ResourceDispatcherHostImpl should service this request. A request might be | |
153 // disallowed if the renderer is not authorized to retrieve the request URL or | |
154 // if the renderer is attempting to upload an unauthorized file. | |
155 bool ShouldServiceRequest(ProcessType process_type, | |
156 int child_id, | |
157 const ResourceHostMsg_Request& request_data) { | |
158 if (process_type == PROCESS_TYPE_PLUGIN) | |
159 return true; | |
160 | |
161 ChildProcessSecurityPolicyImpl* policy = | |
162 ChildProcessSecurityPolicyImpl::GetInstance(); | |
163 | |
164 // Check if the renderer is permitted to request the requested URL. | |
165 if (!policy->CanRequestURL(child_id, request_data.url)) { | |
166 VLOG(1) << "Denied unauthorized request for " | |
167 << request_data.url.possibly_invalid_spec(); | |
168 return false; | |
169 } | |
170 | |
171 // Check if the renderer is permitted to upload the requested files. | |
172 if (request_data.request_body) { | |
173 const std::vector<ResourceRequestBody::Element>* uploads = | |
174 request_data.request_body->elements(); | |
175 std::vector<ResourceRequestBody::Element>::const_iterator iter; | |
176 for (iter = uploads->begin(); iter != uploads->end(); ++iter) { | |
177 if (iter->type() == ResourceRequestBody::Element::TYPE_FILE && | |
178 !policy->CanReadFile(child_id, iter->path())) { | |
179 NOTREACHED() << "Denied unauthorized upload of " | |
180 << iter->path().value(); | |
181 return false; | |
182 } | |
183 } | |
184 } | |
185 | |
186 return true; | |
187 } | |
188 | |
189 void RemoveDownloadFileFromChildSecurityPolicy(int child_id, | |
190 const FilePath& path) { | |
191 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeAllPermissionsForFile( | |
192 child_id, path); | |
193 } | |
194 | |
195 #if defined(OS_WIN) | |
196 #pragma warning(disable: 4748) | |
197 #pragma optimize("", off) | |
198 #endif | |
199 | |
200 #if defined(OS_WIN) | |
201 #pragma optimize("", on) | |
202 #pragma warning(default: 4748) | |
203 #endif | |
204 | |
205 net::RequestPriority DetermineRequestPriority(ResourceType::Type type) { | |
206 // Determine request priority based on how critical this resource typically | |
207 // is to user-perceived page load performance. Important considerations are: | |
208 // * Can this resource block the download of other resources. | |
209 // * Can this resource block the rendering of the page. | |
210 // * How useful is the page to the user if this resource is not loaded yet. | |
211 | |
212 switch (type) { | |
213 // Main frames are the highest priority because they can block nearly every | |
214 // type of other resource and there is no useful display without them. | |
215 // Sub frames are a close second, however it is a common pattern to wrap | |
216 // ads in an iframe or even in multiple nested iframes. It is worth | |
217 // investigating if there is a better priority for them. | |
218 case ResourceType::MAIN_FRAME: | |
219 case ResourceType::SUB_FRAME: | |
220 return net::HIGHEST; | |
221 | |
222 // Stylesheets and scripts can block rendering and loading of other | |
223 // resources. Fonts can block text from rendering. | |
224 case ResourceType::STYLESHEET: | |
225 case ResourceType::SCRIPT: | |
226 case ResourceType::FONT_RESOURCE: | |
227 return net::MEDIUM; | |
228 | |
229 // Sub resources, objects and media are lower priority than potentially | |
230 // blocking stylesheets, scripts and fonts, but are higher priority than | |
231 // images because if they exist they are probably more central to the page | |
232 // focus than images on the page. | |
233 case ResourceType::SUB_RESOURCE: | |
234 case ResourceType::OBJECT: | |
235 case ResourceType::MEDIA: | |
236 case ResourceType::WORKER: | |
237 case ResourceType::SHARED_WORKER: | |
238 case ResourceType::XHR: | |
239 return net::LOW; | |
240 | |
241 // Images are the "lowest" priority because they typically do not block | |
242 // downloads or rendering and most pages have some useful content without | |
243 // them. | |
244 case ResourceType::IMAGE: | |
245 // Favicons aren't required for rendering the current page, but | |
246 // are user visible. | |
247 case ResourceType::FAVICON: | |
248 return net::LOWEST; | |
249 | |
250 // Prefetches are at a lower priority than even LOWEST, since they are not | |
251 // even required for rendering of the current page. | |
252 case ResourceType::PREFETCH: | |
253 return net::IDLE; | |
254 | |
255 default: | |
256 // When new resource types are added, their priority must be considered. | |
257 NOTREACHED(); | |
258 return net::LOW; | |
259 } | |
260 } | |
261 | |
262 void OnSwapOutACKHelper(int render_process_id, | |
263 int render_view_id, | |
264 bool timed_out) { | |
265 RenderViewHostImpl* rvh = RenderViewHostImpl::FromID(render_process_id, | |
266 render_view_id); | |
267 if (rvh) | |
268 rvh->OnSwapOutACK(timed_out); | |
269 } | |
270 | |
271 net::Error CallbackAndReturn( | |
272 const DownloadResourceHandler::OnStartedCallback& started_cb, | |
273 net::Error net_error) { | |
274 if (started_cb.is_null()) | |
275 return net_error; | |
276 BrowserThread::PostTask( | |
277 BrowserThread::UI, FROM_HERE, | |
278 base::Bind(started_cb, static_cast<DownloadItem*>(NULL), net_error)); | |
279 | |
280 return net_error; | |
281 } | |
282 | |
283 int BuildLoadFlagsForRequest(const ResourceHostMsg_Request& request_data, | |
284 int child_id, bool is_sync_load) { | |
285 int load_flags = request_data.load_flags; | |
286 | |
287 // Although EV status is irrelevant to sub-frames and sub-resources, we have | |
288 // to perform EV certificate verification on all resources because an HTTP | |
289 // keep-alive connection created to load a sub-frame or a sub-resource could | |
290 // be reused to load a main frame. | |
291 load_flags |= net::LOAD_VERIFY_EV_CERT; | |
292 if (request_data.resource_type == ResourceType::MAIN_FRAME) { | |
293 load_flags |= net::LOAD_MAIN_FRAME; | |
294 } else if (request_data.resource_type == ResourceType::SUB_FRAME) { | |
295 load_flags |= net::LOAD_SUB_FRAME; | |
296 } else if (request_data.resource_type == ResourceType::PREFETCH) { | |
297 load_flags |= (net::LOAD_PREFETCH | net::LOAD_DO_NOT_PROMPT_FOR_LOGIN); | |
298 } else if (request_data.resource_type == ResourceType::FAVICON) { | |
299 load_flags |= net::LOAD_DO_NOT_PROMPT_FOR_LOGIN; | |
300 } | |
301 | |
302 if (is_sync_load) | |
303 load_flags |= net::LOAD_IGNORE_LIMITS; | |
304 | |
305 ChildProcessSecurityPolicyImpl* policy = | |
306 ChildProcessSecurityPolicyImpl::GetInstance(); | |
307 if (!policy->CanSendCookiesForOrigin(child_id, request_data.url)) { | |
308 load_flags |= (net::LOAD_DO_NOT_SEND_COOKIES | | |
309 net::LOAD_DO_NOT_SEND_AUTH_DATA | | |
310 net::LOAD_DO_NOT_SAVE_COOKIES); | |
311 } | |
312 | |
313 // Raw headers are sensitive, as they include Cookie/Set-Cookie, so only | |
314 // allow requesting them if requester has ReadRawCookies permission. | |
315 if ((load_flags & net::LOAD_REPORT_RAW_HEADERS) | |
316 && !policy->CanReadRawCookies(child_id)) { | |
317 VLOG(1) << "Denied unauthorized request for raw headers"; | |
318 load_flags &= ~net::LOAD_REPORT_RAW_HEADERS; | |
319 } | |
320 | |
321 return load_flags; | |
322 } | |
323 | |
324 int GetCertID(net::URLRequest* request, int child_id) { | |
325 if (request->ssl_info().cert) { | |
326 return CertStore::GetInstance()->StoreCert(request->ssl_info().cert, | |
327 child_id); | |
328 } | |
329 return 0; | |
330 } | |
331 | |
332 template <class T> | |
333 void NotifyOnUI(int type, int render_process_id, int render_view_id, | |
334 scoped_ptr<T> detail) { | |
335 RenderViewHostImpl* host = | |
336 RenderViewHostImpl::FromID(render_process_id, render_view_id); | |
337 if (host) { | |
338 RenderViewHostDelegate* delegate = host->GetDelegate(); | |
339 NotificationService::current()->Notify( | |
340 type, Source<WebContents>(delegate->GetAsWebContents()), | |
341 Details<T>(detail.get())); | |
342 } | |
343 } | |
344 | |
345 } // namespace | |
346 | |
347 // static | |
348 ResourceDispatcherHost* ResourceDispatcherHost::Get() { | |
349 return g_resource_dispatcher_host; | |
350 } | |
351 | |
352 ResourceDispatcherHostImpl::ResourceDispatcherHostImpl() | |
353 : save_file_manager_(new SaveFileManager()), | |
354 request_id_(-1), | |
355 is_shutdown_(false), | |
356 max_outstanding_requests_cost_per_process_( | |
357 kMaxOutstandingRequestsCostPerProcess), | |
358 filter_(NULL), | |
359 delegate_(NULL), | |
360 allow_cross_origin_auth_prompt_(false) { | |
361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
362 DCHECK(!g_resource_dispatcher_host); | |
363 g_resource_dispatcher_host = this; | |
364 | |
365 GetContentClient()->browser()->ResourceDispatcherHostCreated(); | |
366 | |
367 ANNOTATE_BENIGN_RACE( | |
368 &last_user_gesture_time_, | |
369 "We don't care about the precise value, see http://crbug.com/92889"); | |
370 | |
371 BrowserThread::PostTask( | |
372 BrowserThread::IO, FROM_HERE, | |
373 base::Bind(&appcache::AppCacheInterceptor::EnsureRegistered)); | |
374 | |
375 update_load_states_timer_.reset( | |
376 new base::RepeatingTimer<ResourceDispatcherHostImpl>()); | |
377 } | |
378 | |
379 ResourceDispatcherHostImpl::~ResourceDispatcherHostImpl() { | |
380 DCHECK(g_resource_dispatcher_host); | |
381 g_resource_dispatcher_host = NULL; | |
382 } | |
383 | |
384 // static | |
385 ResourceDispatcherHostImpl* ResourceDispatcherHostImpl::Get() { | |
386 return g_resource_dispatcher_host; | |
387 } | |
388 | |
389 void ResourceDispatcherHostImpl::SetDelegate( | |
390 ResourceDispatcherHostDelegate* delegate) { | |
391 delegate_ = delegate; | |
392 } | |
393 | |
394 void ResourceDispatcherHostImpl::SetAllowCrossOriginAuthPrompt(bool value) { | |
395 allow_cross_origin_auth_prompt_ = value; | |
396 } | |
397 | |
398 void ResourceDispatcherHostImpl::AddResourceContext(ResourceContext* context) { | |
399 active_resource_contexts_.insert(context); | |
400 } | |
401 | |
402 void ResourceDispatcherHostImpl::RemoveResourceContext( | |
403 ResourceContext* context) { | |
404 CHECK(ContainsKey(active_resource_contexts_, context)); | |
405 active_resource_contexts_.erase(context); | |
406 } | |
407 | |
408 void ResourceDispatcherHostImpl::CancelRequestsForContext( | |
409 ResourceContext* context) { | |
410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
411 DCHECK(context); | |
412 | |
413 CHECK(ContainsKey(active_resource_contexts_, context)); | |
414 | |
415 // Note that request cancellation has side effects. Therefore, we gather all | |
416 // the requests to cancel first, and then we start cancelling. We assert at | |
417 // the end that there are no more to cancel since the context is about to go | |
418 // away. | |
419 typedef std::vector<linked_ptr<ResourceLoader> > LoaderList; | |
420 LoaderList loaders_to_cancel; | |
421 | |
422 for (LoaderMap::iterator i = pending_loaders_.begin(); | |
423 i != pending_loaders_.end();) { | |
424 if (i->second->GetRequestInfo()->GetContext() == context) { | |
425 loaders_to_cancel.push_back(i->second); | |
426 pending_loaders_.erase(i++); | |
427 } else { | |
428 ++i; | |
429 } | |
430 } | |
431 | |
432 for (BlockedLoadersMap::iterator i = blocked_loaders_map_.begin(); | |
433 i != blocked_loaders_map_.end();) { | |
434 BlockedLoadersList* loaders = i->second; | |
435 if (loaders->empty()) { | |
436 // This can happen if BlockRequestsForRoute() has been called for a route, | |
437 // but we haven't blocked any matching requests yet. | |
438 ++i; | |
439 continue; | |
440 } | |
441 ResourceRequestInfoImpl* info = loaders->front()->GetRequestInfo(); | |
442 if (info->GetContext() == context) { | |
443 blocked_loaders_map_.erase(i++); | |
444 for (BlockedLoadersList::const_iterator it = loaders->begin(); | |
445 it != loaders->end(); ++it) { | |
446 linked_ptr<ResourceLoader> loader = *it; | |
447 info = loader->GetRequestInfo(); | |
448 // We make the assumption that all requests on the list have the same | |
449 // ResourceContext. | |
450 DCHECK_EQ(context, info->GetContext()); | |
451 IncrementOutstandingRequestsMemoryCost(-1 * info->memory_cost(), | |
452 info->GetChildID()); | |
453 loaders_to_cancel.push_back(loader); | |
454 } | |
455 delete loaders; | |
456 } else { | |
457 ++i; | |
458 } | |
459 } | |
460 | |
461 #ifndef NDEBUG | |
462 for (LoaderList::iterator i = loaders_to_cancel.begin(); | |
463 i != loaders_to_cancel.end(); ++i) { | |
464 // There is no strict requirement that this be the case, but currently | |
465 // downloads and transferred requests are the only requests that aren't | |
466 // cancelled when the associated processes go away. It may be OK for this | |
467 // invariant to change in the future, but if this assertion fires without | |
468 // the invariant changing, then it's indicative of a leak. | |
469 DCHECK((*i)->GetRequestInfo()->is_download() || (*i)->is_transferring()); | |
470 } | |
471 #endif | |
472 | |
473 loaders_to_cancel.clear(); | |
474 | |
475 // Validate that no more requests for this context were added. | |
476 for (LoaderMap::const_iterator i = pending_loaders_.begin(); | |
477 i != pending_loaders_.end(); ++i) { | |
478 // http://crbug.com/90971 | |
479 CHECK_NE(i->second->GetRequestInfo()->GetContext(), context); | |
480 } | |
481 | |
482 for (BlockedLoadersMap::const_iterator i = blocked_loaders_map_.begin(); | |
483 i != blocked_loaders_map_.end(); ++i) { | |
484 BlockedLoadersList* loaders = i->second; | |
485 if (!loaders->empty()) { | |
486 ResourceRequestInfoImpl* info = loaders->front()->GetRequestInfo(); | |
487 // http://crbug.com/90971 | |
488 CHECK_NE(info->GetContext(), context); | |
489 } | |
490 } | |
491 } | |
492 | |
493 net::Error ResourceDispatcherHostImpl::BeginDownload( | |
494 scoped_ptr<net::URLRequest> request, | |
495 bool is_content_initiated, | |
496 ResourceContext* context, | |
497 int child_id, | |
498 int route_id, | |
499 bool prefer_cache, | |
500 scoped_ptr<DownloadSaveInfo> save_info, | |
501 const DownloadStartedCallback& started_callback) { | |
502 if (is_shutdown_) | |
503 return CallbackAndReturn(started_callback, net::ERR_INSUFFICIENT_RESOURCES); | |
504 | |
505 const GURL& url = request->original_url(); | |
506 | |
507 // http://crbug.com/90971 | |
508 char url_buf[128]; | |
509 base::strlcpy(url_buf, url.spec().c_str(), arraysize(url_buf)); | |
510 base::debug::Alias(url_buf); | |
511 CHECK(ContainsKey(active_resource_contexts_, context)); | |
512 | |
513 request->set_referrer(MaybeStripReferrer(GURL(request->referrer())).spec()); | |
514 int extra_load_flags = net::LOAD_IS_DOWNLOAD; | |
515 if (prefer_cache) { | |
516 // If there is upload data attached, only retrieve from cache because there | |
517 // is no current mechanism to prompt the user for their consent for a | |
518 // re-post. For GETs, try to retrieve data from the cache and skip | |
519 // validating the entry if present. | |
520 if (request->get_upload() != NULL) | |
521 extra_load_flags |= net::LOAD_ONLY_FROM_CACHE; | |
522 else | |
523 extra_load_flags |= net::LOAD_PREFERRING_CACHE; | |
524 } else { | |
525 extra_load_flags |= net::LOAD_DISABLE_CACHE; | |
526 } | |
527 request->set_load_flags(request->load_flags() | extra_load_flags); | |
528 // Check if the renderer is permitted to request the requested URL. | |
529 if (!ChildProcessSecurityPolicyImpl::GetInstance()-> | |
530 CanRequestURL(child_id, url)) { | |
531 VLOG(1) << "Denied unauthorized download request for " | |
532 << url.possibly_invalid_spec(); | |
533 return CallbackAndReturn(started_callback, net::ERR_ACCESS_DENIED); | |
534 } | |
535 | |
536 request_id_--; | |
537 | |
538 const net::URLRequestContext* request_context = context->GetRequestContext(); | |
539 if (!request_context->job_factory()->IsHandledURL(url)) { | |
540 VLOG(1) << "Download request for unsupported protocol: " | |
541 << url.possibly_invalid_spec(); | |
542 return CallbackAndReturn(started_callback, net::ERR_ACCESS_DENIED); | |
543 } | |
544 | |
545 ResourceRequestInfoImpl* extra_info = | |
546 CreateRequestInfo(child_id, route_id, true, context); | |
547 extra_info->AssociateWithRequest(request.get()); // Request takes ownership. | |
548 | |
549 // From this point forward, the |DownloadResourceHandler| is responsible for | |
550 // |started_callback|. | |
551 scoped_ptr<ResourceHandler> handler( | |
552 CreateResourceHandlerForDownload(request.get(), is_content_initiated, | |
553 save_info.Pass(), started_callback)); | |
554 | |
555 BeginRequestInternal(request.Pass(), handler.Pass()); | |
556 | |
557 return net::OK; | |
558 } | |
559 | |
560 void ResourceDispatcherHostImpl::ClearLoginDelegateForRequest( | |
561 net::URLRequest* request) { | |
562 ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request); | |
563 if (info) { | |
564 ResourceLoader* loader = GetLoader(info->GetGlobalRequestID()); | |
565 if (loader) | |
566 loader->ClearLoginDelegate(); | |
567 } | |
568 } | |
569 | |
570 void ResourceDispatcherHostImpl::Shutdown() { | |
571 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
572 BrowserThread::PostTask(BrowserThread::IO, | |
573 FROM_HERE, | |
574 base::Bind(&ResourceDispatcherHostImpl::OnShutdown, | |
575 base::Unretained(this))); | |
576 } | |
577 | |
578 scoped_ptr<ResourceHandler> | |
579 ResourceDispatcherHostImpl::CreateResourceHandlerForDownload( | |
580 net::URLRequest* request, | |
581 bool is_content_initiated, | |
582 scoped_ptr<DownloadSaveInfo> save_info, | |
583 const DownloadResourceHandler::OnStartedCallback& started_cb) { | |
584 scoped_ptr<ResourceHandler> handler( | |
585 new DownloadResourceHandler(request, started_cb, save_info.Pass())); | |
586 if (delegate_) { | |
587 const ResourceRequestInfo* request_info( | |
588 ResourceRequestInfo::ForRequest(request)); | |
589 | |
590 ScopedVector<ResourceThrottle> throttles; | |
591 delegate_->DownloadStarting( | |
592 request, request_info->GetContext(), request_info->GetChildID(), | |
593 request_info->GetRouteID(), request_info->GetRequestID(), | |
594 is_content_initiated, &throttles); | |
595 if (!throttles.empty()) { | |
596 handler.reset( | |
597 new ThrottlingResourceHandler( | |
598 handler.Pass(), request_info->GetChildID(), | |
599 request_info->GetRequestID(), throttles.Pass())); | |
600 } | |
601 } | |
602 return handler.Pass(); | |
603 } | |
604 | |
605 void ResourceDispatcherHostImpl::ClearSSLClientAuthHandlerForRequest( | |
606 net::URLRequest* request) { | |
607 ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request); | |
608 if (info) { | |
609 ResourceLoader* loader = GetLoader(info->GetGlobalRequestID()); | |
610 if (loader) | |
611 loader->ClearSSLClientAuthHandler(); | |
612 } | |
613 } | |
614 | |
615 ResourceDispatcherHostLoginDelegate* | |
616 ResourceDispatcherHostImpl::CreateLoginDelegate( | |
617 ResourceLoader* loader, | |
618 net::AuthChallengeInfo* auth_info) { | |
619 if (!delegate_) | |
620 return NULL; | |
621 | |
622 return delegate_->CreateLoginDelegate(auth_info, loader->request()); | |
623 } | |
624 | |
625 bool ResourceDispatcherHostImpl::AcceptAuthRequest( | |
626 ResourceLoader* loader, | |
627 net::AuthChallengeInfo* auth_info) { | |
628 if (delegate_ && !delegate_->AcceptAuthRequest(loader->request(), auth_info)) | |
629 return false; | |
630 | |
631 // Prevent third-party content from prompting for login, unless it is | |
632 // a proxy that is trying to authenticate. This is often the foundation | |
633 // of a scam to extract credentials for another domain from the user. | |
634 if (!auth_info->is_proxy) { | |
635 HttpAuthResourceType resource_type = | |
636 HttpAuthResourceTypeOf(loader->request()); | |
637 UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthResource", | |
638 resource_type, | |
639 HTTP_AUTH_RESOURCE_LAST); | |
640 | |
641 if (resource_type == HTTP_AUTH_RESOURCE_BLOCKED_CROSS) | |
642 return false; | |
643 } | |
644 | |
645 return true; | |
646 } | |
647 | |
648 bool ResourceDispatcherHostImpl::AcceptSSLClientCertificateRequest( | |
649 ResourceLoader* loader, | |
650 net::SSLCertRequestInfo* cert_info) { | |
651 if (delegate_ && !delegate_->AcceptSSLClientCertificateRequest( | |
652 loader->request(), cert_info)) { | |
653 return false; | |
654 } | |
655 | |
656 return true; | |
657 } | |
658 | |
659 bool ResourceDispatcherHostImpl::HandleExternalProtocol(ResourceLoader* loader, | |
660 const GURL& url) { | |
661 if (!delegate_) | |
662 return false; | |
663 | |
664 ResourceRequestInfoImpl* info = loader->GetRequestInfo(); | |
665 | |
666 if (!ResourceType::IsFrame(info->GetResourceType())) | |
667 return false; | |
668 | |
669 const net::URLRequestJobFactory* job_factory = | |
670 info->GetContext()->GetRequestContext()->job_factory(); | |
671 if (job_factory->IsHandledURL(url)) | |
672 return false; | |
673 | |
674 return delegate_->HandleExternalProtocol(url, info->GetChildID(), | |
675 info->GetRouteID()); | |
676 } | |
677 | |
678 void ResourceDispatcherHostImpl::DidStartRequest(ResourceLoader* loader) { | |
679 // Make sure we have the load state monitor running | |
680 if (!update_load_states_timer_->IsRunning()) { | |
681 update_load_states_timer_->Start(FROM_HERE, | |
682 TimeDelta::FromMilliseconds(kUpdateLoadStatesIntervalMsec), | |
683 this, &ResourceDispatcherHostImpl::UpdateLoadStates); | |
684 } | |
685 } | |
686 | |
687 void ResourceDispatcherHostImpl::DidReceiveRedirect(ResourceLoader* loader, | |
688 const GURL& new_url) { | |
689 ResourceRequestInfoImpl* info = loader->GetRequestInfo(); | |
690 | |
691 int render_process_id, render_view_id; | |
692 if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id)) | |
693 return; | |
694 | |
695 // Notify the observers on the UI thread. | |
696 scoped_ptr<ResourceRedirectDetails> detail(new ResourceRedirectDetails( | |
697 loader->request(), | |
698 GetCertID(loader->request(), info->GetChildID()), | |
699 new_url)); | |
700 BrowserThread::PostTask( | |
701 BrowserThread::UI, FROM_HERE, | |
702 base::Bind( | |
703 &NotifyOnUI<ResourceRedirectDetails>, | |
704 static_cast<int>(NOTIFICATION_RESOURCE_RECEIVED_REDIRECT), | |
705 render_process_id, render_view_id, base::Passed(&detail))); | |
706 } | |
707 | |
708 void ResourceDispatcherHostImpl::DidReceiveResponse(ResourceLoader* loader) { | |
709 ResourceRequestInfoImpl* info = loader->GetRequestInfo(); | |
710 | |
711 int render_process_id, render_view_id; | |
712 if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id)) | |
713 return; | |
714 | |
715 // Notify the observers on the UI thread. | |
716 scoped_ptr<ResourceRequestDetails> detail(new ResourceRequestDetails( | |
717 loader->request(), | |
718 GetCertID(loader->request(), info->GetChildID()))); | |
719 BrowserThread::PostTask( | |
720 BrowserThread::UI, FROM_HERE, | |
721 base::Bind( | |
722 &NotifyOnUI<ResourceRequestDetails>, | |
723 static_cast<int>(NOTIFICATION_RESOURCE_RESPONSE_STARTED), | |
724 render_process_id, render_view_id, base::Passed(&detail))); | |
725 } | |
726 | |
727 void ResourceDispatcherHostImpl::DidFinishLoading(ResourceLoader* loader) { | |
728 ResourceRequestInfo* info = loader->GetRequestInfo(); | |
729 | |
730 // Record final result of all resource loads. | |
731 if (info->GetResourceType() == ResourceType::MAIN_FRAME) { | |
732 // This enumeration has "3" appended to its name to distinguish it from | |
733 // older versions. | |
734 UMA_HISTOGRAM_CUSTOM_ENUMERATION( | |
735 "Net.ErrorCodesForMainFrame3", | |
736 -loader->request()->status().error(), | |
737 base::CustomHistogram::ArrayToCustomRanges( | |
738 kAllNetErrorCodes, arraysize(kAllNetErrorCodes))); | |
739 | |
740 if (loader->request()->url().SchemeIsSecure() && | |
741 loader->request()->url().host() == "www.google.com") { | |
742 UMA_HISTOGRAM_CUSTOM_ENUMERATION( | |
743 "Net.ErrorCodesForHTTPSGoogleMainFrame2", | |
744 -loader->request()->status().error(), | |
745 base::CustomHistogram::ArrayToCustomRanges( | |
746 kAllNetErrorCodes, arraysize(kAllNetErrorCodes))); | |
747 } | |
748 } else { | |
749 // This enumeration has "2" appended to distinguish it from older versions. | |
750 UMA_HISTOGRAM_CUSTOM_ENUMERATION( | |
751 "Net.ErrorCodesForSubresources2", | |
752 -loader->request()->status().error(), | |
753 base::CustomHistogram::ArrayToCustomRanges( | |
754 kAllNetErrorCodes, arraysize(kAllNetErrorCodes))); | |
755 } | |
756 | |
757 // Destroy the ResourceLoader. | |
758 RemovePendingRequest(info->GetChildID(), info->GetRequestID()); | |
759 } | |
760 | |
761 // static | |
762 bool ResourceDispatcherHostImpl::RenderViewForRequest( | |
763 const net::URLRequest* request, | |
764 int* render_process_id, | |
765 int* render_view_id) { | |
766 const ResourceRequestInfoImpl* info = | |
767 ResourceRequestInfoImpl::ForRequest(request); | |
768 if (!info) { | |
769 *render_process_id = -1; | |
770 *render_view_id = -1; | |
771 return false; | |
772 } | |
773 | |
774 return info->GetAssociatedRenderView(render_process_id, render_view_id); | |
775 } | |
776 | |
777 void ResourceDispatcherHostImpl::OnShutdown() { | |
778 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
779 | |
780 is_shutdown_ = true; | |
781 pending_loaders_.clear(); | |
782 | |
783 // Make sure we shutdown the timer now, otherwise by the time our destructor | |
784 // runs if the timer is still running the Task is deleted twice (once by | |
785 // the MessageLoop and the second time by RepeatingTimer). | |
786 update_load_states_timer_.reset(); | |
787 | |
788 // Clear blocked requests if any left. | |
789 // Note that we have to do this in 2 passes as we cannot call | |
790 // CancelBlockedRequestsForRoute while iterating over | |
791 // blocked_loaders_map_, as it modifies it. | |
792 std::set<ProcessRouteIDs> ids; | |
793 for (BlockedLoadersMap::const_iterator iter = blocked_loaders_map_.begin(); | |
794 iter != blocked_loaders_map_.end(); ++iter) { | |
795 std::pair<std::set<ProcessRouteIDs>::iterator, bool> result = | |
796 ids.insert(iter->first); | |
797 // We should not have duplicates. | |
798 DCHECK(result.second); | |
799 } | |
800 for (std::set<ProcessRouteIDs>::const_iterator iter = ids.begin(); | |
801 iter != ids.end(); ++iter) { | |
802 CancelBlockedRequestsForRoute(iter->first, iter->second); | |
803 } | |
804 } | |
805 | |
806 bool ResourceDispatcherHostImpl::OnMessageReceived( | |
807 const IPC::Message& message, | |
808 ResourceMessageFilter* filter, | |
809 bool* message_was_ok) { | |
810 filter_ = filter; | |
811 bool handled = true; | |
812 IPC_BEGIN_MESSAGE_MAP_EX(ResourceDispatcherHostImpl, message, *message_was_ok) | |
813 IPC_MESSAGE_HANDLER(ResourceHostMsg_RequestResource, OnRequestResource) | |
814 IPC_MESSAGE_HANDLER_DELAY_REPLY(ResourceHostMsg_SyncLoad, OnSyncLoad) | |
815 IPC_MESSAGE_HANDLER(ResourceHostMsg_ReleaseDownloadedFile, | |
816 OnReleaseDownloadedFile) | |
817 IPC_MESSAGE_HANDLER(ResourceHostMsg_DataReceived_ACK, OnDataReceivedACK) | |
818 IPC_MESSAGE_HANDLER(ResourceHostMsg_DataDownloaded_ACK, OnDataDownloadedACK) | |
819 IPC_MESSAGE_HANDLER(ResourceHostMsg_UploadProgress_ACK, OnUploadProgressACK) | |
820 IPC_MESSAGE_HANDLER(ResourceHostMsg_CancelRequest, OnCancelRequest) | |
821 IPC_MESSAGE_HANDLER(ResourceHostMsg_FollowRedirect, OnFollowRedirect) | |
822 IPC_MESSAGE_HANDLER(ViewHostMsg_SwapOut_ACK, OnSwapOutACK) | |
823 IPC_MESSAGE_HANDLER(ViewHostMsg_DidLoadResourceFromMemoryCache, | |
824 OnDidLoadResourceFromMemoryCache) | |
825 IPC_MESSAGE_UNHANDLED(handled = false) | |
826 IPC_END_MESSAGE_MAP_EX() | |
827 | |
828 if (message.type() == ViewHostMsg_DidLoadResourceFromMemoryCache::ID) { | |
829 // We just needed to peek at this message. We still want it to reach its | |
830 // normal destination. | |
831 handled = false; | |
832 } | |
833 | |
834 filter_ = NULL; | |
835 return handled; | |
836 } | |
837 | |
838 void ResourceDispatcherHostImpl::OnRequestResource( | |
839 const IPC::Message& message, | |
840 int request_id, | |
841 const ResourceHostMsg_Request& request_data) { | |
842 BeginRequest(request_id, request_data, NULL, message.routing_id()); | |
843 } | |
844 | |
845 // Begins a resource request with the given params on behalf of the specified | |
846 // child process. Responses will be dispatched through the given receiver. The | |
847 // process ID is used to lookup WebContentsImpl from routing_id's in the case of | |
848 // a request from a renderer. request_context is the cookie/cache context to be | |
849 // used for this request. | |
850 // | |
851 // If sync_result is non-null, then a SyncLoad reply will be generated, else | |
852 // a normal asynchronous set of response messages will be generated. | |
853 void ResourceDispatcherHostImpl::OnSyncLoad( | |
854 int request_id, | |
855 const ResourceHostMsg_Request& request_data, | |
856 IPC::Message* sync_result) { | |
857 BeginRequest(request_id, request_data, sync_result, | |
858 sync_result->routing_id()); | |
859 } | |
860 | |
861 void ResourceDispatcherHostImpl::BeginRequest( | |
862 int request_id, | |
863 const ResourceHostMsg_Request& request_data, | |
864 IPC::Message* sync_result, // only valid for sync | |
865 int route_id) { | |
866 ProcessType process_type = filter_->process_type(); | |
867 int child_id = filter_->child_id(); | |
868 | |
869 // If we crash here, figure out what URL the renderer was requesting. | |
870 // http://crbug.com/91398 | |
871 char url_buf[128]; | |
872 base::strlcpy(url_buf, request_data.url.spec().c_str(), arraysize(url_buf)); | |
873 base::debug::Alias(url_buf); | |
874 | |
875 // If the request that's coming in is being transferred from another process, | |
876 // we want to reuse and resume the old loader rather than start a new one. | |
877 linked_ptr<ResourceLoader> deferred_loader; | |
878 { | |
879 LoaderMap::iterator it = pending_loaders_.find( | |
880 GlobalRequestID(request_data.transferred_request_child_id, | |
881 request_data.transferred_request_request_id)); | |
882 if (it != pending_loaders_.end()) { | |
883 if (it->second->is_transferring()) { | |
884 deferred_loader = it->second; | |
885 pending_loaders_.erase(it); | |
886 } else { | |
887 RecordAction(UserMetricsAction("BadMessageTerminate_RDH")); | |
888 filter_->BadMessageReceived(); | |
889 return; | |
890 } | |
891 } | |
892 } | |
893 | |
894 ResourceContext* resource_context = filter_->resource_context(); | |
895 // http://crbug.com/90971 | |
896 CHECK(ContainsKey(active_resource_contexts_, resource_context)); | |
897 | |
898 if (is_shutdown_ || | |
899 !ShouldServiceRequest(process_type, child_id, request_data)) { | |
900 AbortRequestBeforeItStarts(filter_, sync_result, route_id, request_id); | |
901 return; | |
902 } | |
903 | |
904 const Referrer referrer(MaybeStripReferrer(request_data.referrer), | |
905 request_data.referrer_policy); | |
906 | |
907 // Allow the observer to block/handle the request. | |
908 if (delegate_ && !delegate_->ShouldBeginRequest(child_id, | |
909 route_id, | |
910 request_data.method, | |
911 request_data.url, | |
912 request_data.resource_type, | |
913 resource_context, | |
914 referrer)) { | |
915 AbortRequestBeforeItStarts(filter_, sync_result, route_id, request_id); | |
916 return; | |
917 } | |
918 | |
919 int load_flags = | |
920 BuildLoadFlagsForRequest(request_data, child_id, sync_result != NULL); | |
921 | |
922 // Construct the request. | |
923 scoped_ptr<net::URLRequest> new_request; | |
924 net::URLRequest* request; | |
925 if (deferred_loader.get()) { | |
926 request = deferred_loader->request(); | |
927 | |
928 // Give the ResourceLoader (or any of the ResourceHandlers held by it) a | |
929 // chance to reset some state before we complete the transfer. | |
930 deferred_loader->WillCompleteTransfer(); | |
931 } else { | |
932 net::URLRequestContext* context = | |
933 filter_->GetURLRequestContext(request_data.resource_type); | |
934 new_request.reset(context->CreateRequest(request_data.url, NULL)); | |
935 request = new_request.get(); | |
936 | |
937 request->set_method(request_data.method); | |
938 request->set_first_party_for_cookies(request_data.first_party_for_cookies); | |
939 request->set_referrer(referrer.url.spec()); | |
940 webkit_glue::ConfigureURLRequestForReferrerPolicy(request, | |
941 referrer.policy); | |
942 net::HttpRequestHeaders headers; | |
943 headers.AddHeadersFromString(request_data.headers); | |
944 request->SetExtraRequestHeaders(headers); | |
945 } | |
946 | |
947 // TODO(darin): Do we really need all of these URLRequest setters in the | |
948 // transferred navigation case? | |
949 | |
950 request->set_load_flags(load_flags); | |
951 | |
952 request->set_priority(DetermineRequestPriority(request_data.resource_type)); | |
953 | |
954 // Resolve elements from request_body and prepare upload data. | |
955 if (request_data.request_body) { | |
956 request->set_upload( | |
957 request_data.request_body->ResolveElementsAndCreateUploadData( | |
958 filter_->blob_storage_context()->controller())); | |
959 } | |
960 | |
961 bool allow_download = request_data.allow_download && | |
962 ResourceType::IsFrame(request_data.resource_type); | |
963 | |
964 // Make extra info and read footer (contains request ID). | |
965 ResourceRequestInfoImpl* extra_info = | |
966 new ResourceRequestInfoImpl( | |
967 process_type, | |
968 child_id, | |
969 route_id, | |
970 request_data.origin_pid, | |
971 request_id, | |
972 request_data.is_main_frame, | |
973 request_data.frame_id, | |
974 request_data.parent_is_main_frame, | |
975 request_data.parent_frame_id, | |
976 request_data.resource_type, | |
977 request_data.transition_type, | |
978 false, // is download | |
979 allow_download, | |
980 request_data.has_user_gesture, | |
981 request_data.referrer_policy, | |
982 resource_context); | |
983 extra_info->AssociateWithRequest(request); // Request takes ownership. | |
984 | |
985 if (request->url().SchemeIs(chrome::kBlobScheme)) { | |
986 // Hang on to a reference to ensure the blob is not released prior | |
987 // to the job being started. | |
988 extra_info->set_requested_blob_data( | |
989 filter_->blob_storage_context()->controller()-> | |
990 GetBlobDataFromUrl(request->url())); | |
991 } | |
992 | |
993 // Have the appcache associate its extra info with the request. | |
994 appcache::AppCacheInterceptor::SetExtraRequestInfo( | |
995 request, filter_->appcache_service(), child_id, | |
996 request_data.appcache_host_id, request_data.resource_type); | |
997 | |
998 // Construct the IPC resource handler. | |
999 scoped_ptr<ResourceHandler> handler; | |
1000 if (sync_result) { | |
1001 handler.reset(new SyncResourceHandler( | |
1002 filter_, request, sync_result, this)); | |
1003 } else { | |
1004 handler.reset(new AsyncResourceHandler( | |
1005 filter_, route_id, request, this)); | |
1006 } | |
1007 | |
1008 // The RedirectToFileResourceHandler depends on being next in the chain. | |
1009 if (request_data.download_to_file) { | |
1010 handler.reset( | |
1011 new RedirectToFileResourceHandler(handler.Pass(), child_id, this)); | |
1012 } | |
1013 | |
1014 // Install a CrossSiteResourceHandler if this request is coming from a | |
1015 // RenderViewHost with a pending cross-site request. We only check this for | |
1016 // MAIN_FRAME requests. Unblock requests only come from a blocked page, do | |
1017 // not count as cross-site, otherwise it gets blocked indefinitely. | |
1018 if (request_data.resource_type == ResourceType::MAIN_FRAME && | |
1019 process_type == PROCESS_TYPE_RENDERER && | |
1020 CrossSiteRequestManager::GetInstance()-> | |
1021 HasPendingCrossSiteRequest(child_id, route_id)) { | |
1022 // Wrap the event handler to be sure the current page's onunload handler | |
1023 // has a chance to run before we render the new page. | |
1024 handler.reset(new CrossSiteResourceHandler(handler.Pass(), child_id, | |
1025 route_id, request)); | |
1026 } | |
1027 | |
1028 // Insert a buffered event handler before the actual one. | |
1029 handler.reset( | |
1030 new BufferedResourceHandler(handler.Pass(), this, request)); | |
1031 | |
1032 ScopedVector<ResourceThrottle> throttles; | |
1033 if (delegate_) { | |
1034 bool is_continuation_of_transferred_request = | |
1035 (deferred_loader.get() != NULL); | |
1036 | |
1037 delegate_->RequestBeginning(request, | |
1038 resource_context, | |
1039 filter_->appcache_service(), | |
1040 request_data.resource_type, | |
1041 child_id, | |
1042 route_id, | |
1043 is_continuation_of_transferred_request, | |
1044 &throttles); | |
1045 } | |
1046 | |
1047 if (request_data.resource_type == ResourceType::MAIN_FRAME) { | |
1048 throttles.insert( | |
1049 throttles.begin(), | |
1050 new TransferNavigationResourceThrottle(request)); | |
1051 } | |
1052 | |
1053 if (!throttles.empty()) { | |
1054 handler.reset( | |
1055 new ThrottlingResourceHandler(handler.Pass(), child_id, request_id, | |
1056 throttles.Pass())); | |
1057 } | |
1058 | |
1059 if (deferred_loader.get()) { | |
1060 pending_loaders_[extra_info->GetGlobalRequestID()] = deferred_loader; | |
1061 deferred_loader->CompleteTransfer(handler.Pass()); | |
1062 } else { | |
1063 BeginRequestInternal(new_request.Pass(), handler.Pass()); | |
1064 } | |
1065 } | |
1066 | |
1067 void ResourceDispatcherHostImpl::OnReleaseDownloadedFile(int request_id) { | |
1068 UnregisterDownloadedTempFile(filter_->child_id(), request_id); | |
1069 } | |
1070 | |
1071 void ResourceDispatcherHostImpl::OnDataReceivedACK(int request_id) { | |
1072 ResourceLoader* loader = GetLoader(filter_->child_id(), request_id); | |
1073 if (!loader) | |
1074 return; | |
1075 | |
1076 ResourceRequestInfoImpl* info = loader->GetRequestInfo(); | |
1077 if (info->async_handler()) | |
1078 info->async_handler()->OnDataReceivedACK(); | |
1079 } | |
1080 | |
1081 void ResourceDispatcherHostImpl::OnDataDownloadedACK(int request_id) { | |
1082 // TODO(michaeln): maybe throttle DataDownloaded messages | |
1083 } | |
1084 | |
1085 void ResourceDispatcherHostImpl::RegisterDownloadedTempFile( | |
1086 int child_id, int request_id, ShareableFileReference* reference) { | |
1087 registered_temp_files_[child_id][request_id] = reference; | |
1088 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile( | |
1089 child_id, reference->path()); | |
1090 | |
1091 // When the temp file is deleted, revoke permissions that the renderer has | |
1092 // to that file. This covers an edge case where the file is deleted and then | |
1093 // the same name is re-used for some other purpose, we don't want the old | |
1094 // renderer to still have access to it. | |
1095 // | |
1096 // We do this when the file is deleted because the renderer can take a blob | |
1097 // reference to the temp file that outlives the url loaded that it was | |
1098 // loaded with to keep the file (and permissions) alive. | |
1099 reference->AddFinalReleaseCallback( | |
1100 base::Bind(&RemoveDownloadFileFromChildSecurityPolicy, | |
1101 child_id)); | |
1102 } | |
1103 | |
1104 void ResourceDispatcherHostImpl::UnregisterDownloadedTempFile( | |
1105 int child_id, int request_id) { | |
1106 DeletableFilesMap& map = registered_temp_files_[child_id]; | |
1107 DeletableFilesMap::iterator found = map.find(request_id); | |
1108 if (found == map.end()) | |
1109 return; | |
1110 | |
1111 map.erase(found); | |
1112 | |
1113 // Note that we don't remove the security bits here. This will be done | |
1114 // when all file refs are deleted (see RegisterDownloadedTempFile). | |
1115 } | |
1116 | |
1117 bool ResourceDispatcherHostImpl::Send(IPC::Message* message) { | |
1118 delete message; | |
1119 return false; | |
1120 } | |
1121 | |
1122 void ResourceDispatcherHostImpl::OnUploadProgressACK(int request_id) { | |
1123 ResourceLoader* loader = GetLoader(filter_->child_id(), request_id); | |
1124 if (loader) | |
1125 loader->OnUploadProgressACK(); | |
1126 } | |
1127 | |
1128 void ResourceDispatcherHostImpl::OnCancelRequest(int request_id) { | |
1129 CancelRequest(filter_->child_id(), request_id, true); | |
1130 } | |
1131 | |
1132 void ResourceDispatcherHostImpl::OnFollowRedirect( | |
1133 int request_id, | |
1134 bool has_new_first_party_for_cookies, | |
1135 const GURL& new_first_party_for_cookies) { | |
1136 ResourceLoader* loader = GetLoader(filter_->child_id(), request_id); | |
1137 if (!loader) { | |
1138 DVLOG(1) << "OnFollowRedirect for invalid request"; | |
1139 return; | |
1140 } | |
1141 | |
1142 ResourceRequestInfoImpl* info = loader->GetRequestInfo(); | |
1143 if (info->async_handler()) { | |
1144 info->async_handler()->OnFollowRedirect( | |
1145 has_new_first_party_for_cookies, | |
1146 new_first_party_for_cookies); | |
1147 } | |
1148 } | |
1149 | |
1150 ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo( | |
1151 int child_id, | |
1152 int route_id, | |
1153 bool download, | |
1154 ResourceContext* context) { | |
1155 return new ResourceRequestInfoImpl( | |
1156 PROCESS_TYPE_RENDERER, | |
1157 child_id, | |
1158 route_id, | |
1159 0, | |
1160 request_id_, | |
1161 false, // is_main_frame | |
1162 -1, // frame_id | |
1163 false, // parent_is_main_frame | |
1164 -1, // parent_frame_id | |
1165 ResourceType::SUB_RESOURCE, | |
1166 PAGE_TRANSITION_LINK, | |
1167 download, // is_download | |
1168 download, // allow_download | |
1169 false, // has_user_gesture | |
1170 WebKit::WebReferrerPolicyDefault, | |
1171 context); | |
1172 } | |
1173 | |
1174 | |
1175 void ResourceDispatcherHostImpl::OnSwapOutACK( | |
1176 const ViewMsg_SwapOut_Params& params) { | |
1177 HandleSwapOutACK(params, false); | |
1178 } | |
1179 | |
1180 void ResourceDispatcherHostImpl::OnSimulateSwapOutACK( | |
1181 const ViewMsg_SwapOut_Params& params) { | |
1182 // Call the real implementation with true, which means that we timed out. | |
1183 HandleSwapOutACK(params, true); | |
1184 } | |
1185 | |
1186 void ResourceDispatcherHostImpl::HandleSwapOutACK( | |
1187 const ViewMsg_SwapOut_Params& params, bool timed_out) { | |
1188 // Closes for cross-site transitions are handled such that the cross-site | |
1189 // transition continues. | |
1190 ResourceLoader* loader = GetLoader(params.new_render_process_host_id, | |
1191 params.new_request_id); | |
1192 if (loader) { | |
1193 // The response we were meant to resume could have already been canceled. | |
1194 ResourceRequestInfoImpl* info = loader->GetRequestInfo(); | |
1195 if (info->cross_site_handler()) | |
1196 info->cross_site_handler()->ResumeResponse(); | |
1197 } | |
1198 | |
1199 // Update the RenderViewHost's internal state after the ACK. | |
1200 BrowserThread::PostTask( | |
1201 BrowserThread::UI, | |
1202 FROM_HERE, | |
1203 base::Bind(&OnSwapOutACKHelper, | |
1204 params.closing_process_id, | |
1205 params.closing_route_id, | |
1206 timed_out)); | |
1207 } | |
1208 | |
1209 void ResourceDispatcherHostImpl::OnDidLoadResourceFromMemoryCache( | |
1210 const GURL& url, | |
1211 const std::string& security_info, | |
1212 const std::string& http_method, | |
1213 const std::string& mime_type, | |
1214 ResourceType::Type resource_type) { | |
1215 if (!url.is_valid() || !(url.SchemeIs("http") || url.SchemeIs("https"))) | |
1216 return; | |
1217 | |
1218 filter_->GetURLRequestContext(resource_type)->http_transaction_factory()-> | |
1219 GetCache()->OnExternalCacheHit(url, http_method); | |
1220 } | |
1221 | |
1222 // This function is only used for saving feature. | |
1223 void ResourceDispatcherHostImpl::BeginSaveFile( | |
1224 const GURL& url, | |
1225 const Referrer& referrer, | |
1226 int child_id, | |
1227 int route_id, | |
1228 ResourceContext* context) { | |
1229 if (is_shutdown_) | |
1230 return; | |
1231 | |
1232 // http://crbug.com/90971 | |
1233 char url_buf[128]; | |
1234 base::strlcpy(url_buf, url.spec().c_str(), arraysize(url_buf)); | |
1235 base::debug::Alias(url_buf); | |
1236 CHECK(ContainsKey(active_resource_contexts_, context)); | |
1237 | |
1238 scoped_ptr<ResourceHandler> handler( | |
1239 new SaveFileResourceHandler(child_id, | |
1240 route_id, | |
1241 url, | |
1242 save_file_manager_.get())); | |
1243 request_id_--; | |
1244 | |
1245 const net::URLRequestContext* request_context = context->GetRequestContext(); | |
1246 bool known_proto = | |
1247 request_context->job_factory()->IsHandledURL(url); | |
1248 if (!known_proto) { | |
1249 // Since any URLs which have non-standard scheme have been filtered | |
1250 // by save manager(see GURL::SchemeIsStandard). This situation | |
1251 // should not happen. | |
1252 NOTREACHED(); | |
1253 return; | |
1254 } | |
1255 | |
1256 scoped_ptr<net::URLRequest> request( | |
1257 request_context->CreateRequest(url, NULL)); | |
1258 request->set_method("GET"); | |
1259 request->set_referrer(MaybeStripReferrer(referrer.url).spec()); | |
1260 webkit_glue::ConfigureURLRequestForReferrerPolicy(request.get(), | |
1261 referrer.policy); | |
1262 // So far, for saving page, we need fetch content from cache, in the | |
1263 // future, maybe we can use a configuration to configure this behavior. | |
1264 request->set_load_flags(net::LOAD_PREFERRING_CACHE); | |
1265 | |
1266 // Since we're just saving some resources we need, disallow downloading. | |
1267 ResourceRequestInfoImpl* extra_info = | |
1268 CreateRequestInfo(child_id, route_id, false, context); | |
1269 extra_info->AssociateWithRequest(request.get()); // Request takes ownership. | |
1270 | |
1271 BeginRequestInternal(request.Pass(), handler.Pass()); | |
1272 } | |
1273 | |
1274 void ResourceDispatcherHostImpl::MarkAsTransferredNavigation( | |
1275 const GlobalRequestID& id) { | |
1276 GetLoader(id)->MarkAsTransferring(); | |
1277 } | |
1278 | |
1279 int ResourceDispatcherHostImpl::GetOutstandingRequestsMemoryCost( | |
1280 int child_id) const { | |
1281 OutstandingRequestsMemoryCostMap::const_iterator entry = | |
1282 outstanding_requests_memory_cost_map_.find(child_id); | |
1283 return (entry == outstanding_requests_memory_cost_map_.end()) ? | |
1284 0 : entry->second; | |
1285 } | |
1286 | |
1287 // The object died, so cancel and detach all requests associated with it except | |
1288 // for downloads, which belong to the browser process even if initiated via a | |
1289 // renderer. | |
1290 void ResourceDispatcherHostImpl::CancelRequestsForProcess(int child_id) { | |
1291 CancelRequestsForRoute(child_id, -1 /* cancel all */); | |
1292 registered_temp_files_.erase(child_id); | |
1293 } | |
1294 | |
1295 void ResourceDispatcherHostImpl::CancelRequestsForRoute(int child_id, | |
1296 int route_id) { | |
1297 // Since pending_requests_ is a map, we first build up a list of all of the | |
1298 // matching requests to be cancelled, and then we cancel them. Since there | |
1299 // may be more than one request to cancel, we cannot simply hold onto the map | |
1300 // iterators found in the first loop. | |
1301 | |
1302 // Find the global ID of all matching elements. | |
1303 std::vector<GlobalRequestID> matching_requests; | |
1304 for (LoaderMap::const_iterator i = pending_loaders_.begin(); | |
1305 i != pending_loaders_.end(); ++i) { | |
1306 if (i->first.child_id != child_id) | |
1307 continue; | |
1308 | |
1309 ResourceRequestInfoImpl* info = i->second->GetRequestInfo(); | |
1310 | |
1311 GlobalRequestID id(child_id, i->first.request_id); | |
1312 DCHECK(id == i->first); | |
1313 | |
1314 // Don't cancel navigations that are transferring to another process, | |
1315 // since they belong to another process now. | |
1316 if (!info->is_download() && !IsTransferredNavigation(id) && | |
1317 (route_id == -1 || route_id == info->GetRouteID())) { | |
1318 matching_requests.push_back(id); | |
1319 } | |
1320 } | |
1321 | |
1322 // Remove matches. | |
1323 for (size_t i = 0; i < matching_requests.size(); ++i) { | |
1324 LoaderMap::iterator iter = pending_loaders_.find(matching_requests[i]); | |
1325 // Although every matching request was in pending_requests_ when we built | |
1326 // matching_requests, it is normal for a matching request to be not found | |
1327 // in pending_requests_ after we have removed some matching requests from | |
1328 // pending_requests_. For example, deleting a net::URLRequest that has | |
1329 // exclusive (write) access to an HTTP cache entry may unblock another | |
1330 // net::URLRequest that needs exclusive access to the same cache entry, and | |
1331 // that net::URLRequest may complete and remove itself from | |
1332 // pending_requests_. So we need to check that iter is not equal to | |
1333 // pending_requests_.end(). | |
1334 if (iter != pending_loaders_.end()) | |
1335 RemovePendingLoader(iter); | |
1336 } | |
1337 | |
1338 // Now deal with blocked requests if any. | |
1339 if (route_id != -1) { | |
1340 if (blocked_loaders_map_.find(ProcessRouteIDs(child_id, route_id)) != | |
1341 blocked_loaders_map_.end()) { | |
1342 CancelBlockedRequestsForRoute(child_id, route_id); | |
1343 } | |
1344 } else { | |
1345 // We have to do all render views for the process |child_id|. | |
1346 // Note that we have to do this in 2 passes as we cannot call | |
1347 // CancelBlockedRequestsForRoute while iterating over | |
1348 // blocked_loaders_map_, as it modifies it. | |
1349 std::set<int> route_ids; | |
1350 for (BlockedLoadersMap::const_iterator iter = blocked_loaders_map_.begin(); | |
1351 iter != blocked_loaders_map_.end(); ++iter) { | |
1352 if (iter->first.first == child_id) | |
1353 route_ids.insert(iter->first.second); | |
1354 } | |
1355 for (std::set<int>::const_iterator iter = route_ids.begin(); | |
1356 iter != route_ids.end(); ++iter) { | |
1357 CancelBlockedRequestsForRoute(child_id, *iter); | |
1358 } | |
1359 } | |
1360 } | |
1361 | |
1362 // Cancels the request and removes it from the list. | |
1363 void ResourceDispatcherHostImpl::RemovePendingRequest(int child_id, | |
1364 int request_id) { | |
1365 LoaderMap::iterator i = pending_loaders_.find( | |
1366 GlobalRequestID(child_id, request_id)); | |
1367 if (i == pending_loaders_.end()) { | |
1368 NOTREACHED() << "Trying to remove a request that's not here"; | |
1369 return; | |
1370 } | |
1371 RemovePendingLoader(i); | |
1372 } | |
1373 | |
1374 void ResourceDispatcherHostImpl::RemovePendingLoader( | |
1375 const LoaderMap::iterator& iter) { | |
1376 ResourceRequestInfoImpl* info = iter->second->GetRequestInfo(); | |
1377 | |
1378 // Remove the memory credit that we added when pushing the request onto | |
1379 // the pending list. | |
1380 IncrementOutstandingRequestsMemoryCost(-1 * info->memory_cost(), | |
1381 info->GetChildID()); | |
1382 | |
1383 pending_loaders_.erase(iter); | |
1384 | |
1385 // If we have no more pending requests, then stop the load state monitor | |
1386 if (pending_loaders_.empty() && update_load_states_timer_.get()) | |
1387 update_load_states_timer_->Stop(); | |
1388 } | |
1389 | |
1390 void ResourceDispatcherHostImpl::CancelRequest(int child_id, | |
1391 int request_id, | |
1392 bool from_renderer) { | |
1393 if (from_renderer) { | |
1394 // When the old renderer dies, it sends a message to us to cancel its | |
1395 // requests. | |
1396 if (IsTransferredNavigation(GlobalRequestID(child_id, request_id))) | |
1397 return; | |
1398 } | |
1399 | |
1400 ResourceLoader* loader = GetLoader(child_id, request_id); | |
1401 if (!loader) { | |
1402 // We probably want to remove this warning eventually, but I wanted to be | |
1403 // able to notice when this happens during initial development since it | |
1404 // should be rare and may indicate a bug. | |
1405 DVLOG(1) << "Canceling a request that wasn't found"; | |
1406 return; | |
1407 } | |
1408 | |
1409 loader->CancelRequest(from_renderer); | |
1410 } | |
1411 | |
1412 int ResourceDispatcherHostImpl::IncrementOutstandingRequestsMemoryCost( | |
1413 int cost, | |
1414 int child_id) { | |
1415 // Retrieve the previous value (defaulting to 0 if not found). | |
1416 OutstandingRequestsMemoryCostMap::iterator prev_entry = | |
1417 outstanding_requests_memory_cost_map_.find(child_id); | |
1418 int new_cost = 0; | |
1419 if (prev_entry != outstanding_requests_memory_cost_map_.end()) | |
1420 new_cost = prev_entry->second; | |
1421 | |
1422 // Insert/update the total; delete entries when their value reaches 0. | |
1423 new_cost += cost; | |
1424 CHECK(new_cost >= 0); | |
1425 if (new_cost == 0) | |
1426 outstanding_requests_memory_cost_map_.erase(child_id); | |
1427 else | |
1428 outstanding_requests_memory_cost_map_[child_id] = new_cost; | |
1429 | |
1430 return new_cost; | |
1431 } | |
1432 | |
1433 // static | |
1434 int ResourceDispatcherHostImpl::CalculateApproximateMemoryCost( | |
1435 net::URLRequest* request) { | |
1436 // The following fields should be a minor size contribution (experimentally | |
1437 // on the order of 100). However since they are variable length, it could | |
1438 // in theory be a sizeable contribution. | |
1439 int strings_cost = request->extra_request_headers().ToString().size() + | |
1440 request->original_url().spec().size() + | |
1441 request->referrer().size() + | |
1442 request->method().size(); | |
1443 | |
1444 // Note that this expression will typically be dominated by: | |
1445 // |kAvgBytesPerOutstandingRequest|. | |
1446 return kAvgBytesPerOutstandingRequest + strings_cost; | |
1447 } | |
1448 | |
1449 void ResourceDispatcherHostImpl::BeginRequestInternal( | |
1450 scoped_ptr<net::URLRequest> request, | |
1451 scoped_ptr<ResourceHandler> handler) { | |
1452 DCHECK(!request->is_pending()); | |
1453 ResourceRequestInfoImpl* info = | |
1454 ResourceRequestInfoImpl::ForRequest(request.get()); | |
1455 | |
1456 if ((TimeTicks::Now() - last_user_gesture_time_) < | |
1457 TimeDelta::FromMilliseconds(kUserGestureWindowMs)) { | |
1458 request->set_load_flags( | |
1459 request->load_flags() | net::LOAD_MAYBE_USER_GESTURE); | |
1460 } | |
1461 | |
1462 // Add the memory estimate that starting this request will consume. | |
1463 info->set_memory_cost(CalculateApproximateMemoryCost(request.get())); | |
1464 int memory_cost = IncrementOutstandingRequestsMemoryCost(info->memory_cost(), | |
1465 info->GetChildID()); | |
1466 | |
1467 // If enqueing/starting this request will exceed our per-process memory | |
1468 // bound, abort it right away. | |
1469 if (memory_cost > max_outstanding_requests_cost_per_process_) { | |
1470 // We call "CancelWithError()" as a way of setting the net::URLRequest's | |
1471 // status -- it has no effect beyond this, since the request hasn't started. | |
1472 request->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); | |
1473 | |
1474 if (!handler->OnResponseCompleted(info->GetRequestID(), request->status(), | |
1475 std::string())) { | |
1476 // TODO(darin): The handler is not ready for us to kill the request. Oops! | |
1477 NOTREACHED(); | |
1478 } | |
1479 | |
1480 IncrementOutstandingRequestsMemoryCost(-1 * info->memory_cost(), | |
1481 info->GetChildID()); | |
1482 | |
1483 // A ResourceHandler must not outlive its associated URLRequest. | |
1484 handler.reset(); | |
1485 return; | |
1486 } | |
1487 | |
1488 linked_ptr<ResourceLoader> loader( | |
1489 new ResourceLoader(request.Pass(), handler.Pass(), this)); | |
1490 | |
1491 ProcessRouteIDs pair_id(info->GetChildID(), info->GetRouteID()); | |
1492 BlockedLoadersMap::const_iterator iter = blocked_loaders_map_.find(pair_id); | |
1493 if (iter != blocked_loaders_map_.end()) { | |
1494 // The request should be blocked. | |
1495 iter->second->push_back(loader); | |
1496 return; | |
1497 } | |
1498 | |
1499 StartLoading(info, loader); | |
1500 } | |
1501 | |
1502 void ResourceDispatcherHostImpl::StartLoading( | |
1503 ResourceRequestInfoImpl* info, | |
1504 const linked_ptr<ResourceLoader>& loader) { | |
1505 pending_loaders_[info->GetGlobalRequestID()] = loader; | |
1506 | |
1507 loader->StartRequest(); | |
1508 } | |
1509 | |
1510 void ResourceDispatcherHostImpl::OnUserGesture(WebContentsImpl* contents) { | |
1511 last_user_gesture_time_ = TimeTicks::Now(); | |
1512 } | |
1513 | |
1514 net::URLRequest* ResourceDispatcherHostImpl::GetURLRequest( | |
1515 const GlobalRequestID& id) { | |
1516 ResourceLoader* loader = GetLoader(id); | |
1517 if (!loader) | |
1518 return NULL; | |
1519 | |
1520 return loader->request(); | |
1521 } | |
1522 | |
1523 namespace { | |
1524 | |
1525 // This function attempts to return the "more interesting" load state of |a| | |
1526 // and |b|. We don't have temporal information about these load states | |
1527 // (meaning we don't know when we transitioned into these states), so we just | |
1528 // rank them according to how "interesting" the states are. | |
1529 // | |
1530 // We take advantage of the fact that the load states are an enumeration listed | |
1531 // in the order in which they occur during the lifetime of a request, so we can | |
1532 // regard states with larger numeric values as being further along toward | |
1533 // completion. We regard those states as more interesting to report since they | |
1534 // represent progress. | |
1535 // | |
1536 // For example, by this measure "tranferring data" is a more interesting state | |
1537 // than "resolving host" because when we are transferring data we are actually | |
1538 // doing something that corresponds to changes that the user might observe, | |
1539 // whereas waiting for a host name to resolve implies being stuck. | |
1540 // | |
1541 const net::LoadStateWithParam& MoreInterestingLoadState( | |
1542 const net::LoadStateWithParam& a, const net::LoadStateWithParam& b) { | |
1543 return (a.state < b.state) ? b : a; | |
1544 } | |
1545 | |
1546 // Carries information about a load state change. | |
1547 struct LoadInfo { | |
1548 GURL url; | |
1549 net::LoadStateWithParam load_state; | |
1550 uint64 upload_position; | |
1551 uint64 upload_size; | |
1552 }; | |
1553 | |
1554 // Map from ProcessID+ViewID pair to LoadState | |
1555 typedef std::map<std::pair<int, int>, LoadInfo> LoadInfoMap; | |
1556 | |
1557 // Used to marshal calls to LoadStateChanged from the IO to UI threads. We do | |
1558 // them all as a single callback to avoid spamming the UI thread. | |
1559 void LoadInfoUpdateCallback(const LoadInfoMap& info_map) { | |
1560 LoadInfoMap::const_iterator i; | |
1561 for (i = info_map.begin(); i != info_map.end(); ++i) { | |
1562 RenderViewHostImpl* view = | |
1563 RenderViewHostImpl::FromID(i->first.first, i->first.second); | |
1564 if (view) // The view could be gone at this point. | |
1565 view->LoadStateChanged(i->second.url, i->second.load_state, | |
1566 i->second.upload_position, | |
1567 i->second.upload_size); | |
1568 } | |
1569 } | |
1570 | |
1571 } // namespace | |
1572 | |
1573 void ResourceDispatcherHostImpl::UpdateLoadStates() { | |
1574 // Populate this map with load state changes, and then send them on to the UI | |
1575 // thread where they can be passed along to the respective RVHs. | |
1576 LoadInfoMap info_map; | |
1577 | |
1578 LoaderMap::const_iterator i; | |
1579 | |
1580 // Determine the largest upload size of all requests | |
1581 // in each View (good chance it's zero). | |
1582 std::map<std::pair<int, int>, uint64> largest_upload_size; | |
1583 for (i = pending_loaders_.begin(); i != pending_loaders_.end(); ++i) { | |
1584 net::URLRequest* request = i->second->request(); | |
1585 ResourceRequestInfoImpl* info = i->second->GetRequestInfo(); | |
1586 uint64 upload_size = request->GetUploadProgress().size(); | |
1587 if (request->GetLoadState().state != net::LOAD_STATE_SENDING_REQUEST) | |
1588 upload_size = 0; | |
1589 std::pair<int, int> key(info->GetChildID(), info->GetRouteID()); | |
1590 if (upload_size && largest_upload_size[key] < upload_size) | |
1591 largest_upload_size[key] = upload_size; | |
1592 } | |
1593 | |
1594 for (i = pending_loaders_.begin(); i != pending_loaders_.end(); ++i) { | |
1595 net::URLRequest* request = i->second->request(); | |
1596 ResourceRequestInfoImpl* info = i->second->GetRequestInfo(); | |
1597 net::LoadStateWithParam load_state = request->GetLoadState(); | |
1598 net::UploadProgress progress = request->GetUploadProgress(); | |
1599 | |
1600 // We also poll for upload progress on this timer and send upload | |
1601 // progress ipc messages to the plugin process. | |
1602 i->second->ReportUploadProgress(); | |
1603 | |
1604 std::pair<int, int> key(info->GetChildID(), info->GetRouteID()); | |
1605 | |
1606 // If a request is uploading data, ignore all other requests so that the | |
1607 // upload progress takes priority for being shown in the status bar. | |
1608 if (largest_upload_size.find(key) != largest_upload_size.end() && | |
1609 progress.size() < largest_upload_size[key]) | |
1610 continue; | |
1611 | |
1612 net::LoadStateWithParam to_insert = load_state; | |
1613 LoadInfoMap::iterator existing = info_map.find(key); | |
1614 if (existing != info_map.end()) { | |
1615 to_insert = | |
1616 MoreInterestingLoadState(existing->second.load_state, load_state); | |
1617 if (to_insert.state == existing->second.load_state.state) | |
1618 continue; | |
1619 } | |
1620 LoadInfo& load_info = info_map[key]; | |
1621 load_info.url = request->url(); | |
1622 load_info.load_state = to_insert; | |
1623 load_info.upload_size = progress.size(); | |
1624 load_info.upload_position = progress.position(); | |
1625 } | |
1626 | |
1627 if (info_map.empty()) | |
1628 return; | |
1629 | |
1630 BrowserThread::PostTask( | |
1631 BrowserThread::UI, FROM_HERE, | |
1632 base::Bind(&LoadInfoUpdateCallback, info_map)); | |
1633 } | |
1634 | |
1635 void ResourceDispatcherHostImpl::BlockRequestsForRoute(int child_id, | |
1636 int route_id) { | |
1637 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1638 ProcessRouteIDs key(child_id, route_id); | |
1639 DCHECK(blocked_loaders_map_.find(key) == blocked_loaders_map_.end()) << | |
1640 "BlockRequestsForRoute called multiple time for the same RVH"; | |
1641 blocked_loaders_map_[key] = new BlockedLoadersList(); | |
1642 } | |
1643 | |
1644 void ResourceDispatcherHostImpl::ResumeBlockedRequestsForRoute(int child_id, | |
1645 int route_id) { | |
1646 ProcessBlockedRequestsForRoute(child_id, route_id, false); | |
1647 } | |
1648 | |
1649 void ResourceDispatcherHostImpl::CancelBlockedRequestsForRoute(int child_id, | |
1650 int route_id) { | |
1651 ProcessBlockedRequestsForRoute(child_id, route_id, true); | |
1652 } | |
1653 | |
1654 void ResourceDispatcherHostImpl::ProcessBlockedRequestsForRoute( | |
1655 int child_id, | |
1656 int route_id, | |
1657 bool cancel_requests) { | |
1658 BlockedLoadersMap::iterator iter = blocked_loaders_map_.find( | |
1659 std::pair<int, int>(child_id, route_id)); | |
1660 if (iter == blocked_loaders_map_.end()) { | |
1661 // It's possible to reach here if the renderer crashed while an interstitial | |
1662 // page was showing. | |
1663 return; | |
1664 } | |
1665 | |
1666 BlockedLoadersList* loaders = iter->second; | |
1667 | |
1668 // Removing the vector from the map unblocks any subsequent requests. | |
1669 blocked_loaders_map_.erase(iter); | |
1670 | |
1671 for (BlockedLoadersList::iterator loaders_iter = loaders->begin(); | |
1672 loaders_iter != loaders->end(); ++loaders_iter) { | |
1673 linked_ptr<ResourceLoader> loader = *loaders_iter; | |
1674 ResourceRequestInfoImpl* info = loader->GetRequestInfo(); | |
1675 if (cancel_requests) { | |
1676 IncrementOutstandingRequestsMemoryCost(-1 * info->memory_cost(), | |
1677 info->GetChildID()); | |
1678 } else { | |
1679 StartLoading(info, loader); | |
1680 } | |
1681 } | |
1682 | |
1683 delete loaders; | |
1684 } | |
1685 | |
1686 ResourceDispatcherHostImpl::HttpAuthResourceType | |
1687 ResourceDispatcherHostImpl::HttpAuthResourceTypeOf(net::URLRequest* request) { | |
1688 // Use the same critera as for cookies to determine the sub-resource type | |
1689 // that is requesting to be authenticated. | |
1690 if (!request->first_party_for_cookies().is_valid()) | |
1691 return HTTP_AUTH_RESOURCE_TOP; | |
1692 | |
1693 if (net::RegistryControlledDomainService::SameDomainOrHost( | |
1694 request->first_party_for_cookies(), request->url())) | |
1695 return HTTP_AUTH_RESOURCE_SAME_DOMAIN; | |
1696 | |
1697 if (allow_cross_origin_auth_prompt()) | |
1698 return HTTP_AUTH_RESOURCE_ALLOWED_CROSS; | |
1699 | |
1700 return HTTP_AUTH_RESOURCE_BLOCKED_CROSS; | |
1701 } | |
1702 | |
1703 bool ResourceDispatcherHostImpl::allow_cross_origin_auth_prompt() { | |
1704 return allow_cross_origin_auth_prompt_; | |
1705 } | |
1706 | |
1707 bool ResourceDispatcherHostImpl::IsTransferredNavigation( | |
1708 const GlobalRequestID& id) const { | |
1709 ResourceLoader* loader = GetLoader(id); | |
1710 return loader ? loader->is_transferring() : false; | |
1711 } | |
1712 | |
1713 ResourceLoader* ResourceDispatcherHostImpl::GetLoader( | |
1714 const GlobalRequestID& id) const { | |
1715 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1716 | |
1717 LoaderMap::const_iterator i = pending_loaders_.find(id); | |
1718 if (i == pending_loaders_.end()) | |
1719 return NULL; | |
1720 | |
1721 return i->second.get(); | |
1722 } | |
1723 | |
1724 ResourceLoader* ResourceDispatcherHostImpl::GetLoader(int child_id, | |
1725 int request_id) const { | |
1726 return GetLoader(GlobalRequestID(child_id, request_id)); | |
1727 } | |
1728 | |
1729 } // namespace content | |
OLD | NEW |