[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / content / browser / loader / resource_loader.cc
blob64f5e7b341ba6c365ebe3e2d7af950448fb0bd83
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.
5 #include "content/browser/loader/resource_loader.h"
7 #include "base/command_line.h"
8 #include "base/location.h"
9 #include "base/metrics/histogram.h"
10 #include "base/profiler/scoped_tracker.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/time/time.h"
14 #include "content/browser/appcache/appcache_interceptor.h"
15 #include "content/browser/child_process_security_policy_impl.h"
16 #include "content/browser/loader/cross_site_resource_handler.h"
17 #include "content/browser/loader/detachable_resource_handler.h"
18 #include "content/browser/loader/resource_loader_delegate.h"
19 #include "content/browser/loader/resource_request_info_impl.h"
20 #include "content/browser/service_worker/service_worker_request_handler.h"
21 #include "content/browser/ssl/ssl_client_auth_handler.h"
22 #include "content/browser/ssl/ssl_manager.h"
23 #include "content/browser/ssl/ssl_policy.h"
24 #include "content/common/ssl_status_serialization.h"
25 #include "content/public/browser/cert_store.h"
26 #include "content/public/browser/resource_context.h"
27 #include "content/public/browser/resource_dispatcher_host_login_delegate.h"
28 #include "content/public/browser/signed_certificate_timestamp_store.h"
29 #include "content/public/common/content_client.h"
30 #include "content/public/common/content_switches.h"
31 #include "content/public/common/process_type.h"
32 #include "content/public/common/resource_response.h"
33 #include "content/public/common/security_style.h"
34 #include "net/base/io_buffer.h"
35 #include "net/base/load_flags.h"
36 #include "net/http/http_response_headers.h"
37 #include "net/ssl/client_cert_store.h"
38 #include "net/url_request/redirect_info.h"
39 #include "net/url_request/url_request_status.h"
41 using base::TimeDelta;
42 using base::TimeTicks;
44 namespace content {
45 namespace {
47 void StoreSignedCertificateTimestamps(
48 const net::SignedCertificateTimestampAndStatusList& sct_list,
49 int process_id,
50 SignedCertificateTimestampIDStatusList* sct_ids) {
51 SignedCertificateTimestampStore* sct_store(
52 SignedCertificateTimestampStore::GetInstance());
54 for (auto iter = sct_list.begin(); iter != sct_list.end(); ++iter) {
55 const int sct_id(sct_store->Store(iter->sct.get(), process_id));
56 sct_ids->push_back(
57 SignedCertificateTimestampIDAndStatus(sct_id, iter->status));
61 void GetSSLStatusForRequest(const GURL& url,
62 const net::SSLInfo& ssl_info,
63 int child_id,
64 SSLStatus* ssl_status) {
65 DCHECK(ssl_info.cert);
67 int cert_id =
68 CertStore::GetInstance()->StoreCert(ssl_info.cert.get(), child_id);
70 SignedCertificateTimestampIDStatusList signed_certificate_timestamp_ids;
71 StoreSignedCertificateTimestamps(ssl_info.signed_certificate_timestamps,
72 child_id, &signed_certificate_timestamp_ids);
74 *ssl_status = SSLStatus(SSLPolicy::GetSecurityStyleForResource(
75 url, cert_id, ssl_info.cert_status),
76 cert_id, signed_certificate_timestamp_ids, ssl_info);
79 void PopulateResourceResponse(ResourceRequestInfoImpl* info,
80 net::URLRequest* request,
81 ResourceResponse* response) {
82 response->head.request_time = request->request_time();
83 response->head.response_time = request->response_time();
84 response->head.headers = request->response_headers();
85 request->GetCharset(&response->head.charset);
86 response->head.content_length = request->GetExpectedContentSize();
87 request->GetMimeType(&response->head.mime_type);
88 net::HttpResponseInfo response_info = request->response_info();
89 response->head.was_fetched_via_spdy = response_info.was_fetched_via_spdy;
90 response->head.was_npn_negotiated = response_info.was_npn_negotiated;
91 response->head.npn_negotiated_protocol =
92 response_info.npn_negotiated_protocol;
93 response->head.connection_info = response_info.connection_info;
94 response->head.was_fetched_via_proxy = request->was_fetched_via_proxy();
95 response->head.proxy_server = response_info.proxy_server;
96 response->head.socket_address = request->GetSocketAddress();
97 if (ServiceWorkerRequestHandler* handler =
98 ServiceWorkerRequestHandler::GetHandler(request)) {
99 handler->GetExtraResponseInfo(&response->head);
101 AppCacheInterceptor::GetExtraResponseInfo(
102 request, &response->head.appcache_id,
103 &response->head.appcache_manifest_url);
104 if (info->is_load_timing_enabled())
105 request->GetLoadTimingInfo(&response->head.load_timing);
107 if (request->ssl_info().cert.get()) {
108 SSLStatus ssl_status;
109 GetSSLStatusForRequest(request->url(), request->ssl_info(),
110 info->GetChildID(), &ssl_status);
111 response->head.security_info = SerializeSecurityInfo(ssl_status);
112 } else {
113 // We should not have any SSL state.
114 DCHECK(!request->ssl_info().cert_status);
115 DCHECK_EQ(request->ssl_info().security_bits, -1);
116 DCHECK_EQ(request->ssl_info().key_exchange_info, 0);
117 DCHECK(!request->ssl_info().connection_status);
121 } // namespace
123 ResourceLoader::ResourceLoader(scoped_ptr<net::URLRequest> request,
124 scoped_ptr<ResourceHandler> handler,
125 ResourceLoaderDelegate* delegate)
126 : deferred_stage_(DEFERRED_NONE),
127 request_(request.Pass()),
128 handler_(handler.Pass()),
129 delegate_(delegate),
130 is_transferring_(false),
131 times_cancelled_before_request_start_(0),
132 started_request_(false),
133 times_cancelled_after_request_start_(0),
134 weak_ptr_factory_(this) {
135 request_->set_delegate(this);
136 handler_->SetController(this);
139 ResourceLoader::~ResourceLoader() {
140 if (login_delegate_.get())
141 login_delegate_->OnRequestCancelled();
142 ssl_client_auth_handler_.reset();
144 // Run ResourceHandler destructor before we tear-down the rest of our state
145 // as the ResourceHandler may want to inspect the URLRequest and other state.
146 handler_.reset();
149 void ResourceLoader::StartRequest() {
150 if (delegate_->HandleExternalProtocol(this, request_->url())) {
151 CancelAndIgnore();
152 return;
155 // Give the handler a chance to delay the URLRequest from being started.
156 bool defer_start = false;
157 if (!handler_->OnWillStart(request_->url(), &defer_start)) {
158 Cancel();
159 return;
162 if (defer_start) {
163 deferred_stage_ = DEFERRED_START;
164 } else {
165 StartRequestInternal();
169 void ResourceLoader::CancelRequest(bool from_renderer) {
170 CancelRequestInternal(net::ERR_ABORTED, from_renderer);
173 void ResourceLoader::CancelAndIgnore() {
174 ResourceRequestInfoImpl* info = GetRequestInfo();
175 info->set_was_ignored_by_handler(true);
176 CancelRequest(false);
179 void ResourceLoader::CancelWithError(int error_code) {
180 CancelRequestInternal(error_code, false);
183 void ResourceLoader::MarkAsTransferring() {
184 CHECK(IsResourceTypeFrame(GetRequestInfo()->GetResourceType()))
185 << "Can only transfer for navigations";
186 is_transferring_ = true;
188 int child_id = GetRequestInfo()->GetChildID();
189 AppCacheInterceptor::PrepareForCrossSiteTransfer(request(), child_id);
190 ServiceWorkerRequestHandler* handler =
191 ServiceWorkerRequestHandler::GetHandler(request());
192 if (handler)
193 handler->PrepareForCrossSiteTransfer(child_id);
196 void ResourceLoader::CompleteTransfer() {
197 // Although CrossSiteResourceHandler defers at OnResponseStarted
198 // (DEFERRED_READ), it may be seeing a replay of events via
199 // MimeTypeResourceHandler, and so the request itself is actually deferred
200 // at a later read stage.
201 DCHECK(DEFERRED_READ == deferred_stage_ ||
202 DEFERRED_RESPONSE_COMPLETE == deferred_stage_);
203 DCHECK(is_transferring_);
205 // In some cases, a process transfer doesn't really happen and the
206 // request is resumed in the original process. Real transfers to a new process
207 // are completed via ResourceDispatcherHostImpl::UpdateRequestForTransfer.
208 int child_id = GetRequestInfo()->GetChildID();
209 AppCacheInterceptor::MaybeCompleteCrossSiteTransferInOldProcess(
210 request(), child_id);
211 ServiceWorkerRequestHandler* handler =
212 ServiceWorkerRequestHandler::GetHandler(request());
213 if (handler)
214 handler->MaybeCompleteCrossSiteTransferInOldProcess(child_id);
216 is_transferring_ = false;
217 GetRequestInfo()->cross_site_handler()->ResumeResponse();
220 ResourceRequestInfoImpl* ResourceLoader::GetRequestInfo() {
221 return ResourceRequestInfoImpl::ForRequest(request_.get());
224 void ResourceLoader::ClearLoginDelegate() {
225 login_delegate_ = NULL;
228 void ResourceLoader::OnReceivedRedirect(net::URLRequest* unused,
229 const net::RedirectInfo& redirect_info,
230 bool* defer) {
231 DCHECK_EQ(request_.get(), unused);
233 DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec();
234 DCHECK(request_->status().is_success());
236 ResourceRequestInfoImpl* info = GetRequestInfo();
238 if (info->GetProcessType() != PROCESS_TYPE_PLUGIN &&
239 !ChildProcessSecurityPolicyImpl::GetInstance()->
240 CanRequestURL(info->GetChildID(), redirect_info.new_url)) {
241 DVLOG(1) << "Denied unauthorized request for "
242 << redirect_info.new_url.possibly_invalid_spec();
244 // Tell the renderer that this request was disallowed.
245 Cancel();
246 return;
249 delegate_->DidReceiveRedirect(this, redirect_info.new_url);
251 if (delegate_->HandleExternalProtocol(this, redirect_info.new_url)) {
252 // The request is complete so we can remove it.
253 CancelAndIgnore();
254 return;
257 scoped_refptr<ResourceResponse> response(new ResourceResponse());
258 PopulateResourceResponse(info, request_.get(), response.get());
259 if (!handler_->OnRequestRedirected(redirect_info, response.get(), defer)) {
260 Cancel();
261 } else if (*defer) {
262 deferred_stage_ = DEFERRED_REDIRECT; // Follow redirect when resumed.
266 void ResourceLoader::OnAuthRequired(net::URLRequest* unused,
267 net::AuthChallengeInfo* auth_info) {
268 DCHECK_EQ(request_.get(), unused);
270 ResourceRequestInfoImpl* info = GetRequestInfo();
271 if (info->do_not_prompt_for_login()) {
272 request_->CancelAuth();
273 return;
276 // Create a login dialog on the UI thread to get authentication data, or pull
277 // from cache and continue on the IO thread.
279 DCHECK(!login_delegate_.get())
280 << "OnAuthRequired called with login_delegate pending";
281 login_delegate_ = delegate_->CreateLoginDelegate(this, auth_info);
282 if (!login_delegate_.get())
283 request_->CancelAuth();
286 void ResourceLoader::OnCertificateRequested(
287 net::URLRequest* unused,
288 net::SSLCertRequestInfo* cert_info) {
289 DCHECK_EQ(request_.get(), unused);
291 if (request_->load_flags() & net::LOAD_PREFETCH) {
292 request_->Cancel();
293 return;
296 DCHECK(!ssl_client_auth_handler_)
297 << "OnCertificateRequested called with ssl_client_auth_handler pending";
298 ssl_client_auth_handler_.reset(new SSLClientAuthHandler(
299 GetRequestInfo()->GetContext()->CreateClientCertStore(), request_.get(),
300 cert_info, this));
301 ssl_client_auth_handler_->SelectCertificate();
304 void ResourceLoader::OnSSLCertificateError(net::URLRequest* request,
305 const net::SSLInfo& ssl_info,
306 bool fatal) {
307 ResourceRequestInfoImpl* info = GetRequestInfo();
309 int render_process_id;
310 int render_frame_id;
311 if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_id))
312 NOTREACHED();
314 SSLManager::OnSSLCertificateError(
315 weak_ptr_factory_.GetWeakPtr(),
316 info->GetResourceType(),
317 request_->url(),
318 render_process_id,
319 render_frame_id,
320 ssl_info,
321 fatal);
324 void ResourceLoader::OnBeforeNetworkStart(net::URLRequest* unused,
325 bool* defer) {
326 DCHECK_EQ(request_.get(), unused);
328 // Give the handler a chance to delay the URLRequest from using the network.
329 if (!handler_->OnBeforeNetworkStart(request_->url(), defer)) {
330 Cancel();
331 return;
332 } else if (*defer) {
333 deferred_stage_ = DEFERRED_NETWORK_START;
337 void ResourceLoader::OnResponseStarted(net::URLRequest* unused) {
338 DCHECK_EQ(request_.get(), unused);
340 DVLOG(1) << "OnResponseStarted: " << request_->url().spec();
342 if (!request_->status().is_success()) {
343 ResponseCompleted();
344 return;
347 CompleteResponseStarted();
349 if (is_deferred())
350 return;
352 if (request_->status().is_success())
353 StartReading(false); // Read the first chunk.
354 else
355 ResponseCompleted();
358 void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) {
359 DCHECK_EQ(request_.get(), unused);
360 DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\""
361 << " bytes_read = " << bytes_read;
363 // bytes_read == -1 always implies an error.
364 if (bytes_read == -1 || !request_->status().is_success()) {
365 ResponseCompleted();
366 return;
369 CompleteRead(bytes_read);
371 // If the handler cancelled or deferred the request, do not continue
372 // processing the read. If cancelled, the URLRequest has already been
373 // cancelled and will schedule an erroring OnReadCompleted later. If deferred,
374 // do nothing until resumed.
376 // Note: if bytes_read is 0 (EOF) and the handler defers, resumption will call
377 // ResponseCompleted().
378 if (is_deferred() || !request_->status().is_success())
379 return;
381 if (bytes_read > 0) {
382 StartReading(true); // Read the next chunk.
383 } else {
384 // TODO(darin): Remove ScopedTracker below once crbug.com/475761 is fixed.
385 tracked_objects::ScopedTracker tracking_profile(
386 FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 ResponseCompleted()"));
388 // URLRequest reported an EOF. Call ResponseCompleted.
389 DCHECK_EQ(0, bytes_read);
390 ResponseCompleted();
394 void ResourceLoader::CancelSSLRequest(int error,
395 const net::SSLInfo* ssl_info) {
396 DCHECK_CURRENTLY_ON(BrowserThread::IO);
398 // The request can be NULL if it was cancelled by the renderer (as the
399 // request of the user navigating to a new page from the location bar).
400 if (!request_->is_pending())
401 return;
402 DVLOG(1) << "CancelSSLRequest() url: " << request_->url().spec();
404 if (ssl_info) {
405 request_->CancelWithSSLError(error, *ssl_info);
406 } else {
407 request_->CancelWithError(error);
411 void ResourceLoader::ContinueSSLRequest() {
412 DCHECK_CURRENTLY_ON(BrowserThread::IO);
414 DVLOG(1) << "ContinueSSLRequest() url: " << request_->url().spec();
416 request_->ContinueDespiteLastError();
419 void ResourceLoader::ContinueWithCertificate(net::X509Certificate* cert) {
420 DCHECK(ssl_client_auth_handler_);
421 ssl_client_auth_handler_.reset();
422 request_->ContinueWithCertificate(cert);
425 void ResourceLoader::CancelCertificateSelection() {
426 DCHECK(ssl_client_auth_handler_);
427 ssl_client_auth_handler_.reset();
428 request_->CancelWithError(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
431 void ResourceLoader::Resume() {
432 DCHECK(!is_transferring_);
434 DeferredStage stage = deferred_stage_;
435 deferred_stage_ = DEFERRED_NONE;
436 switch (stage) {
437 case DEFERRED_NONE:
438 NOTREACHED();
439 break;
440 case DEFERRED_START:
441 StartRequestInternal();
442 break;
443 case DEFERRED_NETWORK_START:
444 request_->ResumeNetworkStart();
445 break;
446 case DEFERRED_REDIRECT:
447 request_->FollowDeferredRedirect();
448 break;
449 case DEFERRED_READ:
450 base::ThreadTaskRunnerHandle::Get()->PostTask(
451 FROM_HERE, base::Bind(&ResourceLoader::ResumeReading,
452 weak_ptr_factory_.GetWeakPtr()));
453 break;
454 case DEFERRED_RESPONSE_COMPLETE:
455 base::ThreadTaskRunnerHandle::Get()->PostTask(
456 FROM_HERE, base::Bind(&ResourceLoader::ResponseCompleted,
457 weak_ptr_factory_.GetWeakPtr()));
458 break;
459 case DEFERRED_FINISH:
460 // Delay self-destruction since we don't know how we were reached.
461 base::ThreadTaskRunnerHandle::Get()->PostTask(
462 FROM_HERE, base::Bind(&ResourceLoader::CallDidFinishLoading,
463 weak_ptr_factory_.GetWeakPtr()));
464 break;
468 void ResourceLoader::Cancel() {
469 CancelRequest(false);
472 void ResourceLoader::StartRequestInternal() {
473 DCHECK(!request_->is_pending());
475 if (!request_->status().is_success()) {
476 return;
479 started_request_ = true;
480 request_->Start();
482 delegate_->DidStartRequest(this);
485 void ResourceLoader::CancelRequestInternal(int error, bool from_renderer) {
486 DVLOG(1) << "CancelRequestInternal: " << request_->url().spec();
488 ResourceRequestInfoImpl* info = GetRequestInfo();
490 // WebKit will send us a cancel for downloads since it no longer handles
491 // them. In this case, ignore the cancel since we handle downloads in the
492 // browser.
493 if (from_renderer && (info->IsDownload() || info->is_stream()))
494 return;
496 if (from_renderer && info->detachable_handler()) {
497 // TODO(davidben): Fix Blink handling of prefetches so they are not
498 // cancelled on navigate away and end up in the local cache.
499 info->detachable_handler()->Detach();
500 return;
503 // TODO(darin): Perhaps we should really be looking to see if the status is
504 // IO_PENDING?
505 bool was_pending = request_->is_pending();
507 if (login_delegate_.get()) {
508 login_delegate_->OnRequestCancelled();
509 login_delegate_ = NULL;
511 ssl_client_auth_handler_.reset();
513 if (!started_request_) {
514 times_cancelled_before_request_start_++;
515 } else {
516 times_cancelled_after_request_start_++;
519 request_->CancelWithError(error);
521 if (!was_pending) {
522 // If the request isn't in flight, then we won't get an asynchronous
523 // notification from the request, so we have to signal ourselves to finish
524 // this request.
525 base::ThreadTaskRunnerHandle::Get()->PostTask(
526 FROM_HERE, base::Bind(&ResourceLoader::ResponseCompleted,
527 weak_ptr_factory_.GetWeakPtr()));
531 void ResourceLoader::CompleteResponseStarted() {
532 ResourceRequestInfoImpl* info = GetRequestInfo();
533 scoped_refptr<ResourceResponse> response(new ResourceResponse());
534 PopulateResourceResponse(info, request_.get(), response.get());
536 delegate_->DidReceiveResponse(this);
538 // TODO(darin): Remove ScopedTracker below once crbug.com/475761 is fixed.
539 tracked_objects::ScopedTracker tracking_profile(
540 FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 OnResponseStarted()"));
542 bool defer = false;
543 if (!handler_->OnResponseStarted(response.get(), &defer)) {
544 Cancel();
545 } else if (defer) {
546 read_deferral_start_time_ = base::TimeTicks::Now();
547 deferred_stage_ = DEFERRED_READ; // Read first chunk when resumed.
551 void ResourceLoader::StartReading(bool is_continuation) {
552 int bytes_read = 0;
553 ReadMore(&bytes_read);
555 // If IO is pending, wait for the URLRequest to call OnReadCompleted.
556 if (request_->status().is_io_pending())
557 return;
559 if (!is_continuation || bytes_read <= 0) {
560 OnReadCompleted(request_.get(), bytes_read);
561 } else {
562 // Else, trigger OnReadCompleted asynchronously to avoid starving the IO
563 // thread in case the URLRequest can provide data synchronously.
564 base::ThreadTaskRunnerHandle::Get()->PostTask(
565 FROM_HERE,
566 base::Bind(&ResourceLoader::OnReadCompleted,
567 weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read));
571 void ResourceLoader::ResumeReading() {
572 DCHECK(!is_deferred());
574 if (!read_deferral_start_time_.is_null()) {
575 UMA_HISTOGRAM_TIMES("Net.ResourceLoader.ReadDeferral",
576 base::TimeTicks::Now() - read_deferral_start_time_);
577 read_deferral_start_time_ = base::TimeTicks();
579 if (request_->status().is_success()) {
580 StartReading(false); // Read the next chunk (OK to complete synchronously).
581 } else {
582 ResponseCompleted();
586 void ResourceLoader::ReadMore(int* bytes_read) {
587 DCHECK(!is_deferred());
589 // Make sure we track the buffer in at least one place. This ensures it gets
590 // deleted even in the case the request has already finished its job and
591 // doesn't use the buffer.
592 scoped_refptr<net::IOBuffer> buf;
593 int buf_size;
595 // TODO(darin): Remove ScopedTracker below once crbug.com/475761 is fixed.
596 tracked_objects::ScopedTracker tracking_profile2(
597 FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 OnWillRead()"));
599 if (!handler_->OnWillRead(&buf, &buf_size, -1)) {
600 Cancel();
601 return;
605 DCHECK(buf.get());
606 DCHECK(buf_size > 0);
608 request_->Read(buf.get(), buf_size, bytes_read);
610 // No need to check the return value here as we'll detect errors by
611 // inspecting the URLRequest's status.
614 void ResourceLoader::CompleteRead(int bytes_read) {
615 DCHECK(bytes_read >= 0);
616 DCHECK(request_->status().is_success());
618 // TODO(darin): Remove ScopedTracker below once crbug.com/475761 is fixed.
619 tracked_objects::ScopedTracker tracking_profile(
620 FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 OnReadCompleted()"));
622 bool defer = false;
623 if (!handler_->OnReadCompleted(bytes_read, &defer)) {
624 Cancel();
625 } else if (defer) {
626 deferred_stage_ =
627 bytes_read > 0 ? DEFERRED_READ : DEFERRED_RESPONSE_COMPLETE;
630 // Note: the request may still have been cancelled while OnReadCompleted
631 // returns true if OnReadCompleted caused request to get cancelled
632 // out-of-band. (In AwResourceDispatcherHostDelegate::DownloadStarting, for
633 // instance.)
636 void ResourceLoader::ResponseCompleted() {
637 DVLOG(1) << "ResponseCompleted: " << request_->url().spec();
638 RecordHistograms();
639 ResourceRequestInfoImpl* info = GetRequestInfo();
641 std::string security_info;
642 const net::SSLInfo& ssl_info = request_->ssl_info();
643 if (ssl_info.cert.get() != NULL) {
644 SSLStatus ssl_status;
645 GetSSLStatusForRequest(request_->url(), ssl_info, info->GetChildID(),
646 &ssl_status);
648 security_info = SerializeSecurityInfo(ssl_status);
651 bool defer = false;
653 // TODO(darin): Remove ScopedTracker below once crbug.com/475761 is fixed.
654 tracked_objects::ScopedTracker tracking_profile(
655 FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 OnResponseCompleted()"));
657 handler_->OnResponseCompleted(request_->status(), security_info, &defer);
659 if (defer) {
660 // The handler is not ready to die yet. We will call DidFinishLoading when
661 // we resume.
662 deferred_stage_ = DEFERRED_FINISH;
663 } else {
664 // This will result in our destruction.
665 CallDidFinishLoading();
669 void ResourceLoader::CallDidFinishLoading() {
670 delegate_->DidFinishLoading(this);
673 void ResourceLoader::RecordHistograms() {
674 ResourceRequestInfoImpl* info = GetRequestInfo();
676 if (info->GetResourceType() == RESOURCE_TYPE_PREFETCH) {
677 PrefetchStatus status = STATUS_UNDEFINED;
678 TimeDelta total_time = base::TimeTicks::Now() - request_->creation_time();
680 switch (request_->status().status()) {
681 case net::URLRequestStatus::SUCCESS:
682 if (request_->was_cached()) {
683 status = STATUS_SUCCESS_FROM_CACHE;
684 UMA_HISTOGRAM_TIMES("Net.Prefetch.TimeSpentPrefetchingFromCache",
685 total_time);
686 } else {
687 status = STATUS_SUCCESS_FROM_NETWORK;
688 UMA_HISTOGRAM_TIMES("Net.Prefetch.TimeSpentPrefetchingFromNetwork",
689 total_time);
691 break;
692 case net::URLRequestStatus::CANCELED:
693 status = STATUS_CANCELED;
694 UMA_HISTOGRAM_TIMES("Net.Prefetch.TimeBeforeCancel", total_time);
695 break;
696 case net::URLRequestStatus::IO_PENDING:
697 case net::URLRequestStatus::FAILED:
698 status = STATUS_UNDEFINED;
699 break;
702 UMA_HISTOGRAM_ENUMERATION("Net.Prefetch.Pattern", status, STATUS_MAX);
706 } // namespace content