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