| 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 |