QUIC - cleanup changes to sync chromium tree with internal source.
[chromium-blink-merge.git] / ios / web / net / request_tracker_impl.mm
blob4854fdaef076656c5b02d97fec71ca4c544c3cbb
1 // Copyright 2014 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 "ios/web/net/request_tracker_impl.h"
7 #include <pthread.h>
9 #include "base/containers/hash_tables.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/mac/bind_objc_block.h"
13 #include "base/mac/scoped_nsobject.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "base/synchronization/lock.h"
17 #import "ios/net/clients/crn_forwarding_network_client.h"
18 #import "ios/net/clients/crn_forwarding_network_client_factory.h"
19 #import "ios/web/crw_network_activity_indicator_manager.h"
20 #import "ios/web/history_state_util.h"
21 #import "ios/web/net/crw_request_tracker_delegate.h"
22 #include "ios/web/public/browser_state.h"
23 #include "ios/web/public/cert_store.h"
24 #include "ios/web/public/certificate_policy_cache.h"
25 #include "ios/web/public/ssl_status.h"
26 #include "ios/web/public/url_util.h"
27 #include "ios/web/public/web_thread.h"
28 #import "net/base/mac/url_conversions.h"
29 #include "net/base/net_errors.h"
30 #include "net/http/http_response_headers.h"
31 #include "net/url_request/url_request.h"
33 namespace {
35 struct EqualNSStrings {
36   bool operator()(const base::scoped_nsobject<NSString>& s1,
37                   const base::scoped_nsobject<NSString>& s2) const {
38     // Use a ternary due to the BOOL vs bool type difference.
39     return [s1 isEqualToString:s2] ? true : false;
40   }
43 struct HashNSString {
44   size_t operator()(const base::scoped_nsobject<NSString>& s) const {
45     return [s hash];
46   }
49 // A map of all RequestTrackerImpls for tabs that are:
50 // * Currently open
51 // * Recently closed waiting for all their network operations to finish.
52 // The code accesses this variable from two threads: the consumer is expected to
53 // always access it from the main thread, the provider is accessing it from the
54 // WebThread, a thread created by the UIWebView/CFURL. For this reason access to
55 // this variable must always gated by |g_trackers_lock|.
56 typedef base::hash_map<base::scoped_nsobject<NSString>,
57                        web::RequestTrackerImpl*,
58                        HashNSString, EqualNSStrings> TrackerMap;
60 TrackerMap* g_trackers = NULL;
61 base::Lock* g_trackers_lock = NULL;
62 pthread_once_t g_once_control = PTHREAD_ONCE_INIT;
64 // Flag, lock, and function to implement BlockUntilTrackersShutdown().
65 // |g_waiting_on_io_thread| is guarded by |g_waiting_on_io_thread_lock|;
66 // it is set to true when the shutdown wait starts, then a call to
67 // StopIOThreadWaiting is posted to the IO thread (enqueued after any pending
68 // request terminations) while the posting method loops over a check on the
69 // |g_waiting_on_io_thread|.
70 static bool g_waiting_on_io_thread = false;
71 base::Lock* g_waiting_on_io_thread_lock = NULL;
72 void StopIOThreadWaiting() {
73   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
74   base::AutoLock scoped_lock(*g_waiting_on_io_thread_lock);
75   g_waiting_on_io_thread = false;
78 // Initialize global state. Calls to this should be conditional on
79 // |g_once_control| (that is, this should only be called once, across all
80 // threads).
81 void InitializeGlobals() {
82   g_trackers = new TrackerMap;
83   g_trackers_lock = new base::Lock;
84   g_waiting_on_io_thread_lock = new base::Lock;
87 // Each request tracker get a unique increasing number, used anywhere an
88 // identifier is needed for tracker (e.g. storing certs).
89 int g_next_request_tracker_id = 0;
91 // IsIntranetHost logic and its associated kDot constant are lifted directly
92 // from content/browser/ssl/ssl_policy.cc. Unfortunately that particular file
93 // has way too many dependencies on content to be used on iOS.
94 static const char kDot = '.';
96 static bool IsIntranetHost(const std::string& host) {
97   const size_t dot = host.find(kDot);
98   return dot == std::string::npos || dot == host.length() - 1;
101 // Add |tracker| to |g_trackers| under |key|.
102 static void RegisterTracker(web::RequestTrackerImpl* tracker, NSString* key) {
103   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
104   pthread_once(&g_once_control, &InitializeGlobals);
105   {
106     base::scoped_nsobject<NSString> scoped_key([key copy]);
107     base::AutoLock scoped_lock(*g_trackers_lock);
108     DCHECK(!g_trackers->count(scoped_key));
109     (*g_trackers)[scoped_key] = tracker;
110   }
113 // Empty callback.
114 void DoNothing(bool flag) {}
116 }  // namespace
118 // The structure used to gather the information about the resources loaded.
119 struct TrackerCounts {
120  public:
121   TrackerCounts(const GURL& tracked_url, const net::URLRequest* tracked_request)
122       : url(tracked_url),
123         first_party_for_cookies_origin(
124             tracked_request->first_party_for_cookies().GetOrigin()),
125         request(tracked_request),
126         ssl_info(net::SSLInfo()),
127         ssl_judgment(web::CertPolicy::ALLOWED),
128         allowed_by_user(false),
129         expected_length(0),
130         processed(0),
131         done(false) {
132     DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
133     is_subrequest = tracked_request->first_party_for_cookies().is_valid() &&
134         tracked_request->url() != tracked_request->first_party_for_cookies();
135   };
137   // The resource url.
138   const GURL url;
139   // The origin of the url of the top level document of the resource. This is
140   // used to ignore request coming from an old document when detecting mixed
141   // content.
142   const GURL first_party_for_cookies_origin;
143   // The request associated with this struct. As a void* to prevent access from
144   // the wrong thread.
145   const void* request;
146   // SSLInfo for the request.
147   net::SSLInfo ssl_info;
148   // Is the SSL request blocked waiting for user choice.
149   web::CertPolicy::Judgment ssl_judgment;
150   // True if |ssl_judgment| is ALLOWED as the result of a user choice.
151   bool allowed_by_user;
152   // block to call to cancel or authorize a blocked request.
153   net::RequestTracker::SSLCallback ssl_callback;
154   // If known, the expected length of the resource in bytes.
155   uint64_t expected_length;
156   // Number of bytes loaded so far.
157   uint64_t processed;
158   // Set to true is the resource is fully loaded.
159   bool done;
160   // Set to true if the request has a main request set.
161   bool is_subrequest;
163   NSString* Description() {
164     NSString* spec = base::SysUTF8ToNSString(url.spec());
165     NSString* status = nil;
166     if (done) {
167       status = [NSString stringWithFormat:@"\t-- Done -- (%04qu) bytes",
168           processed];
169     } else if (!expected_length) {
170       status = [NSString stringWithFormat:@"\t>> Loading (%04qu) bytes",
171           processed];
172     } else {
173       status = [NSString stringWithFormat:@"\t>> Loading (%04qu/%04qu)",
174           processed, expected_length];
175     }
177     NSString* ssl = @"";
178     if (ssl_info.is_valid()) {
179       NSString* subject = base::SysUTF8ToNSString(
180           ssl_info.cert.get()->subject().GetDisplayName());
181       NSString* issuer = base::SysUTF8ToNSString(
182           ssl_info.cert.get()->issuer().GetDisplayName());
184       ssl = [NSString stringWithFormat:
185           @"\n\t\tcert for '%@' issued by '%@'", subject, issuer];
187       if (!net::IsCertStatusMinorError(ssl_info.cert_status)) {
188         ssl = [NSString stringWithFormat:@"%@ (status: %0xd)",
189             ssl, ssl_info.cert_status];
190       }
191     }
192     return [NSString stringWithFormat:@"%@\n\t\t%@%@", status, spec, ssl];
193   }
195   DISALLOW_COPY_AND_ASSIGN(TrackerCounts);
198 // A SSL carrier is used to transport SSL information to the UI via its
199 // encapsulation in a block. Once the object is constructed all public methods
200 // can be called from any thread safely. This object is designed so it is
201 // instantiated on the IO thread but may be accessed from the UI thread.
202 @interface CRWSSLCarrier : NSObject {
203  @private
204   scoped_refptr<web::RequestTrackerImpl> tracker_;
205   net::SSLInfo sslInfo_;
206   GURL url_;
207   web::SSLStatus status_;
210 // Designated initializer.
211 - (id)initWithTracker:(web::RequestTrackerImpl*)tracker
212                counts:(const TrackerCounts*)counts;
213 // URL of the request.
214 - (const GURL&)url;
215 // Returns a SSLStatus representing the state of the page. This assumes the
216 // target carrier is the main page request.
217 - (const web::SSLStatus&)sslStatus;
218 // Returns a SSLInfo with a reference to the certificate and SSL information.
219 - (const net::SSLInfo&)sslInfo;
220 // Callback method to allow or deny the request from going through.
221 - (void)errorCallback:(BOOL)flag;
222 // Internal method used to build the SSLStatus object. Called from the
223 // initializer to make sure it is invoked on the network thread.
224 - (void)buildSSLStatus;
225 @end
227 @implementation CRWSSLCarrier
229 - (id)initWithTracker:(web::RequestTrackerImpl*)tracker
230                counts:(const TrackerCounts*)counts {
231   self = [super init];
232   if (self) {
233     tracker_ = tracker;
234     url_ = counts->url;
235     sslInfo_ = counts->ssl_info;
236     [self buildSSLStatus];
237   }
238   return self;
241 - (const GURL&)url {
242   return url_;
245 - (const net::SSLInfo&)sslInfo {
246   return sslInfo_;
249 - (const web::SSLStatus&)sslStatus {
250   return status_;
253 - (void)errorCallback:(BOOL)flag {
254   base::scoped_nsobject<CRWSSLCarrier> scoped([self retain]);
255   web::WebThread::PostTask(web::WebThread::IO, FROM_HERE,
256                            base::Bind(&web::RequestTrackerImpl::ErrorCallback,
257                                       tracker_, scoped, flag));
260 - (void)buildSSLStatus {
261   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
262   if (!sslInfo_.is_valid())
263     return;
265   status_.cert_id = web::CertStore::GetInstance()->StoreCert(
266       sslInfo_.cert.get(), tracker_->identifier());
268   status_.cert_status = sslInfo_.cert_status;
269   if (status_.cert_status & net::CERT_STATUS_COMMON_NAME_INVALID) {
270     // CAs issue certificates for intranet hosts to everyone.  Therefore, we
271     // mark intranet hosts as being non-unique.
272     if (IsIntranetHost(url_.host())) {
273       status_.cert_status |= net::CERT_STATUS_NON_UNIQUE_NAME;
274     }
275   }
277   status_.security_bits = sslInfo_.security_bits;
278   status_.connection_status = sslInfo_.connection_status;
280   if (tracker_->has_mixed_content()) {
281     // TODO(noyau): In iOS there is no notion of resource type. The insecure
282     // content could be an image (DISPLAYED_INSECURE_CONTENT) or a script
283     // (RAN_INSECURE_CONTENT). The status of the page is different for both, but
284     // there is not enough information from UIWebView to differentiate the two
285     // cases.
286     status_.content_status = web::SSLStatus::DISPLAYED_INSECURE_CONTENT;
287   } else {
288     status_.content_status = web::SSLStatus::NORMAL_CONTENT;
289   }
291   if (!url_.SchemeIsCryptographic()) {
292     // Should not happen as the sslInfo is valid.
293     NOTREACHED();
294     status_.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
295   } else if (net::IsCertStatusError(status_.cert_status) &&
296              !net::IsCertStatusMinorError(status_.cert_status)) {
297     // Minor errors don't lower the security style to
298     // SECURITY_STYLE_AUTHENTICATION_BROKEN.
299     status_.security_style = web::SECURITY_STYLE_AUTHENTICATION_BROKEN;
300   } else {
301     // This page is secure.
302     status_.security_style = web::SECURITY_STYLE_AUTHENTICATED;
303   }
306 - (NSString*)description {
307   NSString* sslInfo = @"";
308   if (sslInfo_.is_valid()) {
309     switch (status_.security_style) {
310       case web::SECURITY_STYLE_UNKNOWN:
311       case web::SECURITY_STYLE_UNAUTHENTICATED:
312         sslInfo = @"Unexpected SSL state ";
313         break;
314       case web::SECURITY_STYLE_AUTHENTICATION_BROKEN:
315         sslInfo = @"Not secure ";
316         break;
317       case web::SECURITY_STYLE_WARNING:
318         sslInfo = @"Security warning";
319         break;
320       case web::SECURITY_STYLE_AUTHENTICATED:
321         if (status_.content_status ==
322             web::SSLStatus::DISPLAYED_INSECURE_CONTENT)
323           sslInfo = @"Mixed ";
324         else
325           sslInfo = @"Secure ";
326         break;
327     }
328   }
330   NSURL* url = net::NSURLWithGURL(url_);
332   return [NSString stringWithFormat:@"<%@%@>", sslInfo, url];
335 @end
337 namespace web {
339 #pragma mark Consumer API
341 // static
342 scoped_refptr<RequestTrackerImpl>
343 RequestTrackerImpl::CreateTrackerForRequestGroupID(
344     NSString* request_group_id,
345     BrowserState* browser_state,
346     net::URLRequestContextGetter* context_getter,
347     id<CRWRequestTrackerDelegate> delegate) {
348   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
349   DCHECK(request_group_id);
351   scoped_refptr<RequestTrackerImpl> tracker =
352       new RequestTrackerImpl(request_group_id, context_getter, delegate);
354   scoped_refptr<CertificatePolicyCache> policy_cache =
355       BrowserState::GetCertificatePolicyCache(browser_state);
356   DCHECK(policy_cache);
358   // Take care of the IO-thread init.
359   web::WebThread::PostTask(
360       web::WebThread::IO, FROM_HERE,
361       base::Bind(&RequestTrackerImpl::InitOnIOThread, tracker, policy_cache));
362   RegisterTracker(tracker.get(), request_group_id);
363   return tracker;
366 void RequestTrackerImpl::StartPageLoad(const GURL& url, id user_info) {
367   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
368   base::scoped_nsobject<id> scoped_user_info([user_info retain]);
369   web::WebThread::PostTask(
370       web::WebThread::IO, FROM_HERE,
371       base::Bind(&RequestTrackerImpl::TrimToURL, this, url, scoped_user_info));
374 void RequestTrackerImpl::FinishPageLoad(const GURL& url, bool load_success) {
375   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
376   web::WebThread::PostTask(
377       web::WebThread::IO, FROM_HERE,
378       base::Bind(&RequestTrackerImpl::StopPageLoad, this, url, load_success));
381 void RequestTrackerImpl::HistoryStateChange(const GURL& url) {
382   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
383   web::WebThread::PostTask(
384       web::WebThread::IO, FROM_HERE,
385       base::Bind(&RequestTrackerImpl::HistoryStateChangeToURL, this, url));
388 // Close is called when an owning object (a Tab or something that acts like
389 // it) is done with the RequestTrackerImpl. There may still be queued calls on
390 // the UI thread that will make use of the fields being cleaned-up here; they
391 // must ensure they they operate without crashing with the cleaned-up values.
392 void RequestTrackerImpl::Close() {
393   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
394   // Mark the tracker as closing on the IO thread. Note that because the local
395   // scoped_refptr here retains |this|, we a are guaranteed that destruiction
396   // won't begin until the block completes, and thus |is_closing_| will always
397   // be set before destruction begins.
398   scoped_refptr<RequestTrackerImpl> tracker = this;
399   web::WebThread::PostTask(web::WebThread::IO, FROM_HERE,
400                            base::BindBlock(^{
401                              tracker->is_closing_ = true;
402                              tracker->CancelRequests();
403                            }));
405   // Disable the delegate.
406   delegate_ = nil;
407   // The user_info is no longer needed.
408   user_info_.reset();
409   // Get rid of the stored certificates
410   web::CertStore::GetInstance()->RemoveCertsForGroup(identifier_);
413 // static
414 void RequestTrackerImpl::RunAfterRequestsCancel(const base::Closure& callback) {
415   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
416   // Post a no-op to the IO thread, and after that has executed, run |callback|.
417   // This ensures that |callback| runs after anything elese queued on the IO
418   // thread, in particular CancelRequest() calls made from closing trackers.
419   web::WebThread::PostTaskAndReply(web::WebThread::IO, FROM_HERE,
420                                    base::Bind(&base::DoNothing), callback);
423 // static
424 void RequestTrackerImpl::BlockUntilTrackersShutdown() {
425   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
426   {
427     base::AutoLock scoped_lock(*g_waiting_on_io_thread_lock);
428     g_waiting_on_io_thread = true;
429   }
430   web::WebThread::PostTask(web::WebThread::IO, FROM_HERE,
431                            base::Bind(&StopIOThreadWaiting));
433   // Poll endlessly until the wait flag is unset on the IO thread by
434   // StopIOThreadWaiting().
435   // (Consider instead having a hard time cap, like 100ms or so, after which
436   // we stop blocking. In that case this method would return a boolean
437   // indicating if the wait completed or not).
438   while (1) {
439     base::AutoLock scoped_lock(*g_waiting_on_io_thread_lock);
440     if (!g_waiting_on_io_thread)
441       return;
442     // Ensure that other threads have a chance to run even on a single-core
443     // devices.
444     pthread_yield_np();
445   }
448 #pragma mark Provider API
450 // static
451 RequestTrackerImpl* RequestTrackerImpl::GetTrackerForRequestGroupID(
452     NSString* request_group_id) {
453   DCHECK(request_group_id);
454   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
455   RequestTrackerImpl* tracker = nullptr;
456   TrackerMap::iterator map_it;
457   pthread_once(&g_once_control, &InitializeGlobals);
458   {
459     base::AutoLock scoped_lock(*g_trackers_lock);
460     map_it = g_trackers->find(
461         base::scoped_nsobject<NSString>([request_group_id copy]));
462     if (map_it != g_trackers->end())
463       tracker = map_it->second;
464   }
465   return tracker;
468 net::URLRequestContext* RequestTrackerImpl::GetRequestContext() {
469   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
470   return request_context_getter_->GetURLRequestContext();
473 void RequestTrackerImpl::StartRequest(net::URLRequest* request) {
474   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
475   DCHECK(!counts_by_request_.count(request));
476   DCHECK_EQ(is_for_static_file_requests_, request->url().SchemeIsFile());
478   bool addedRequest = live_requests_.insert(request).second;
479   if (!is_for_static_file_requests_ && addedRequest) {
480     NSString* networkActivityKey = GetNetworkActivityKey();
481     web::WebThread::PostTask(
482         web::WebThread::UI, FROM_HERE,
483         base::BindBlock(^{
484           [[CRWNetworkActivityIndicatorManager sharedInstance]
485               startNetworkTaskForGroup:networkActivityKey];
486         }));
487   }
489   if (new_estimate_round_) {
490     // Starting a new estimate round. Ignore the previous requests for the
491     // calculation.
492     counts_by_request_.clear();
493     estimate_start_index_ = counts_.size();
494     new_estimate_round_ = false;
495   }
496   const GURL& url = request->original_url();
497   TrackerCounts* counts = new TrackerCounts(
498       GURLByRemovingRefFromGURL(url), request);
499   counts_.push_back(counts);
500   counts_by_request_[request] = counts;
501   if (page_url_.SchemeIsCryptographic() && !url.SchemeIsCryptographic())
502     has_mixed_content_ = true;
503   Notify();
506 void RequestTrackerImpl::CaptureHeaders(net::URLRequest* request) {
507   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
508   if (is_closing_)
509     return;
511   if (!request->response_headers())
512     return;
514   scoped_refptr<net::HttpResponseHeaders> headers(request->response_headers());
515   web::WebThread::PostTask(
516       web::WebThread::UI, FROM_HERE,
517       base::Bind(&RequestTrackerImpl::NotifyResponseHeaders, this, headers,
518                  request->url()));
521 void RequestTrackerImpl::CaptureExpectedLength(const net::URLRequest* request,
522                                                uint64_t length) {
523   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
524   if (counts_by_request_.count(request)) {
525     TrackerCounts* counts = counts_by_request_[request];
526     DCHECK(!counts->done);
527     if (length < counts->processed) {
528       // Something is wrong with the estimate. Ignore it.
529       counts->expected_length = 0;
530     } else {
531       counts->expected_length = length;
532     }
533     Notify();
534   }
537 void RequestTrackerImpl::CaptureReceivedBytes(const net::URLRequest* request,
538                                               uint64_t byte_count) {
539   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
540   if (counts_by_request_.count(request)) {
541     TrackerCounts* counts = counts_by_request_[request];
542     DCHECK(!counts->done);
543     const net::SSLInfo& ssl_info = request->ssl_info();
544     if (ssl_info.is_valid())
545       counts->ssl_info = ssl_info;
546     counts->processed += byte_count;
547     if (counts->expected_length > 0 &&
548         counts->expected_length < counts->processed) {
549       // Something is wrong with the estimate, it is too low. Ignore it.
550       counts->expected_length = 0;
551     }
552     Notify();
553   }
556 void RequestTrackerImpl::StopRequest(net::URLRequest* request) {
557   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
559   int removedRequests = live_requests_.erase(request);
560   if (!is_for_static_file_requests_ && removedRequests > 0) {
561     NSString* networkActivityKey = GetNetworkActivityKey();
562     web::WebThread::PostTask(
563         web::WebThread::UI, FROM_HERE,
564         base::BindBlock(^{
565           [[CRWNetworkActivityIndicatorManager sharedInstance]
566               stopNetworkTaskForGroup:networkActivityKey];
567         }));
568   }
570   if (counts_by_request_.count(request)) {
571     StopRedirectedRequest(request);
572     Notify();
573   }
576 void RequestTrackerImpl::StopRedirectedRequest(net::URLRequest* request) {
577   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
579   int removedRequests = live_requests_.erase(request);
580   if (!is_for_static_file_requests_ && removedRequests > 0) {
581     NSString* networkActivityKey = GetNetworkActivityKey();
582     web::WebThread::PostTask(
583         web::WebThread::UI, FROM_HERE,
584         base::BindBlock(^{
585           [[CRWNetworkActivityIndicatorManager sharedInstance]
586               stopNetworkTaskForGroup:networkActivityKey];
587         }));
588   }
590   if (counts_by_request_.count(request)) {
591     TrackerCounts* counts = counts_by_request_[request];
592     DCHECK(!counts->done);
593     const net::SSLInfo& ssl_info = request->ssl_info();
594     if (ssl_info.is_valid())
595       counts->ssl_info = ssl_info;
596     counts->done = true;
597     counts_by_request_.erase(request);
598   }
601 void RequestTrackerImpl::CaptureCertificatePolicyCache(
602     const net::URLRequest* request,
603     const RequestTracker::SSLCallback& should_continue) {
604   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
605   std::string host = request->url().host();
606   CertPolicy::Judgment judgment = policy_cache_->QueryPolicy(
607       request->ssl_info().cert.get(), host, request->ssl_info().cert_status);
608   if (judgment == CertPolicy::UNKNOWN) {
609     // The request comes from the cache, and has been loaded even though the
610     // policy is UNKNOWN. Display the interstitial page now.
611     OnSSLCertificateError(request, request->ssl_info(), true, should_continue);
612     return;
613   }
615   // Notify the  delegate that a judgment has been used.
616   DCHECK(judgment == CertPolicy::ALLOWED);
617   if (counts_by_request_.count(request)) {
618     const net::SSLInfo& ssl_info = request->ssl_info();
619     TrackerCounts* counts = counts_by_request_[request];
620     counts->allowed_by_user = true;
621     if (ssl_info.is_valid())
622       counts->ssl_info = ssl_info;
623     web::WebThread::PostTask(
624         web::WebThread::UI, FROM_HERE,
625         base::Bind(&RequestTrackerImpl::NotifyCertificateUsed, this,
626                    ssl_info.cert, host, ssl_info.cert_status));
627   }
628   should_continue.Run(true);
631 void RequestTrackerImpl::OnSSLCertificateError(
632     const net::URLRequest* request,
633     const net::SSLInfo& ssl_info,
634     bool recoverable,
635     const RequestTracker::SSLCallback& should_continue) {
636   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
637   DCHECK(ssl_info.is_valid());
639   if (counts_by_request_.count(request)) {
640     TrackerCounts* counts = counts_by_request_[request];
642     DCHECK(!counts->done);
643     // Store the ssl error.
644     counts->ssl_info = ssl_info;
645     counts->ssl_callback = should_continue;
646     counts->ssl_judgment =
647         recoverable ? CertPolicy::UNKNOWN : CertPolicy::DENIED;
648     ReevaluateCallbacksForAllCounts();
649   }
652 void RequestTrackerImpl::ErrorCallback(CRWSSLCarrier* carrier, bool allow) {
653   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
654   DCHECK(policy_cache_);
656   if (allow) {
657     policy_cache_->AllowCertForHost([carrier sslInfo].cert.get(),
658                                     [carrier url].host(),
659                                     [carrier sslInfo].cert_status);
660     ReevaluateCallbacksForAllCounts();
661   }
662   current_ssl_error_ = NULL;
665 #pragma mark Client utility methods.
667 // TODO(marq): Convert all internal task-posting to use these.
668 void RequestTrackerImpl::PostUITaskIfOpen(const base::Closure& task) {
669   PostTask(task, web::WebThread::UI);
672 // static
673 void RequestTrackerImpl::PostUITaskIfOpen(
674     const base::WeakPtr<RequestTracker> tracker,
675     const base::Closure& task) {
676   if (!tracker)
677     return;
678   RequestTrackerImpl* tracker_impl =
679       static_cast<RequestTrackerImpl*>(tracker.get());
680   tracker_impl->PostUITaskIfOpen(task);
683 void RequestTrackerImpl::PostIOTask(const base::Closure& task) {
684   PostTask(task, web::WebThread::IO);
687 void RequestTrackerImpl::ScheduleIOTask(const base::Closure& task) {
688   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
689   web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, task);
692 void RequestTrackerImpl::SetCacheModeFromUIThread(
693     RequestTracker::CacheMode mode) {
694   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
695   web::WebThread::PostTask(
696       web::WebThread::IO, FROM_HERE,
697       base::Bind(&RequestTracker::SetCacheMode, this, mode));
700 #pragma mark Private Object Lifecycle API
702 RequestTrackerImpl::RequestTrackerImpl(
703     NSString* request_group_id,
704     net::URLRequestContextGetter* context_getter,
705     id<CRWRequestTrackerDelegate> delegate)
706     : delegate_(delegate),
707       previous_estimate_(0.0f),  // Not active by default.
708       estimate_start_index_(0),
709       notification_depth_(0),
710       current_ssl_error_(NULL),
711       has_mixed_content_(false),
712       is_loading_(false),
713       new_estimate_round_(true),
714       is_for_static_file_requests_([delegate isForStaticFileRequests]),
715       request_context_getter_(context_getter),
716       identifier_(++g_next_request_tracker_id),
717       request_group_id_([request_group_id copy]),
718       is_closing_(false) {
719   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
722 void RequestTrackerImpl::InitOnIOThread(
723     const scoped_refptr<CertificatePolicyCache>& policy_cache) {
724   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
725   Init();
726   DCHECK(policy_cache);
727   policy_cache_ = policy_cache;
730 RequestTrackerImpl::~RequestTrackerImpl() {
733 void RequestTrackerImplTraits::Destruct(const RequestTrackerImpl* t) {
734   // RefCountedThreadSafe assumes we can do all the destruct tasks with a
735   // const pointer, but we actually can't.
736   RequestTrackerImpl* inconstant_t = const_cast<RequestTrackerImpl*>(t);
737   if (web::WebThread::CurrentlyOn(web::WebThread::IO)) {
738     inconstant_t->Destruct();
739   } else {
740     // Use BindBlock rather than Bind to avoid creating another scoped_refpter
741     // to |this|. |inconstant_t| isn't retained by the block, but since this
742     // method is the mechanism by which all RequestTrackerImpl instances are
743     // destroyed, the object inconstant_t points to won't be deleted while
744     // the block is executing (and Destruct() itself will do the deleting).
745     web::WebThread::PostTask(web::WebThread::IO, FROM_HERE,
746                              base::BindBlock(^{
747                                inconstant_t->Destruct();
748                              }));
749   }
752 void RequestTrackerImpl::Destruct() {
753   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
754   DCHECK(is_closing_);
756   pthread_once(&g_once_control, &InitializeGlobals);
757   {
758     base::AutoLock scoped_lock(*g_trackers_lock);
759     g_trackers->erase(request_group_id_);
760   }
761   InvalidateWeakPtrs();
762   // Delete on the UI thread.
763   web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, base::BindBlock(^{
764                                                             delete this;
765                                                           }));
768 #pragma mark Other private methods
769 // TODO(marq): Reorder method implementations to match header and add grouping
770 // marks/comments.
772 void RequestTrackerImpl::Notify() {
773   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
774   if (is_closing_)
775     return;
776   // Notify() is called asynchronously, it runs later on the same
777   // thread. This is used to collate notifications together, avoiding
778   // blanketing the UI with a stream of information.
779   notification_depth_ += 1;
780   web::WebThread::PostTask(
781       web::WebThread::IO, FROM_HERE,
782       base::Bind(&RequestTrackerImpl::StackNotification, this));
785 void RequestTrackerImpl::StackNotification() {
786   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
787   if (is_closing_)
788     return;
790   // There is no point in sending the notification if there is another one
791   // already queued. This queue is processing very lightweight changes and
792   // should be exhausted very easily.
793   --notification_depth_;
794   if (notification_depth_)
795     return;
797   SSLNotify();
798   if (is_loading_) {
799     float estimate = EstimatedProgress();
800     if (estimate != -1.0f) {
801       web::WebThread::PostTask(
802           web::WebThread::UI, FROM_HERE,
803           base::Bind(&RequestTrackerImpl::NotifyUpdatedProgress, this,
804                      estimate));
805     }
806   }
809 void RequestTrackerImpl::SSLNotify() {
810   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
811   if (is_closing_)
812     return;
814   if (!counts_.size())
815     return;  // Nothing yet to notify.
817   if (!page_url_.SchemeIsCryptographic())
818     return;
820   const GURL page_origin = page_url_.GetOrigin();
821   ScopedVector<TrackerCounts>::iterator it;
822   for (it = counts_.begin(); it != counts_.end(); ++it) {
823     if (!(*it)->ssl_info.is_valid())
824       continue;  // No SSL info at this point in time on this tracker.
826     GURL request_origin = (*it)->url.GetOrigin();
827     if (request_origin != page_origin)
828       continue;  // Not interesting in the context of the page.
830     base::scoped_nsobject<CRWSSLCarrier> carrier(
831         [[CRWSSLCarrier alloc] initWithTracker:this counts:*it]);
832     web::WebThread::PostTask(
833         web::WebThread::UI, FROM_HERE,
834         base::Bind(&RequestTrackerImpl::NotifyUpdatedSSLStatus, this, carrier));
835     break;
836   }
839 void RequestTrackerImpl::NotifyResponseHeaders(
840     net::HttpResponseHeaders* headers,
841     const GURL& request_url) {
842   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
843   [delegate_ handleResponseHeaders:headers requestUrl:request_url];
846 void RequestTrackerImpl::NotifyCertificateUsed(
847     net::X509Certificate* certificate,
848     const std::string& host,
849     net::CertStatus status) {
850   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
851   [delegate_ certificateUsed:certificate forHost:host status:status];
854 void RequestTrackerImpl::NotifyUpdatedProgress(float estimate) {
855   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
856   [delegate_ updatedProgress:estimate];
859 void RequestTrackerImpl::NotifyClearCertificates() {
860   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
861   [delegate_ clearCertificates];
864 void RequestTrackerImpl::NotifyUpdatedSSLStatus(
865     base::scoped_nsobject<CRWSSLCarrier> carrier) {
866   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
867   [delegate_ updatedSSLStatus:[carrier sslStatus]
868                    forPageUrl:[carrier url]
869                      userInfo:user_info_];
872 void RequestTrackerImpl::NotifyPresentSSLError(
873     base::scoped_nsobject<CRWSSLCarrier> carrier,
874     bool recoverable) {
875   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
876   [delegate_ presentSSLError:[carrier sslInfo]
877                 forSSLStatus:[carrier sslStatus]
878                        onUrl:[carrier url]
879                  recoverable:recoverable
880                     callback:^(BOOL flag) {
881                          [carrier errorCallback:flag && recoverable];
882                     }];
885 void RequestTrackerImpl::EvaluateSSLCallbackForCounts(TrackerCounts* counts) {
886   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
887   DCHECK(policy_cache_);
889   // Ignore non-SSL requests.
890   if (!counts->ssl_info.is_valid())
891     return;
893   CertPolicy::Judgment judgment =
894       policy_cache_->QueryPolicy(counts->ssl_info.cert.get(),
895                                  counts->url.host(),
896                                  counts->ssl_info.cert_status);
898   if (judgment != CertPolicy::ALLOWED) {
899     // Apply some fine tuning.
900     // TODO(droger): This logic is duplicated from SSLPolicy. Sharing the code
901     // would be better.
902     switch (net::MapCertStatusToNetError(counts->ssl_info.cert_status)) {
903       case net::ERR_CERT_NO_REVOCATION_MECHANISM:
904         // Ignore this error.
905         judgment = CertPolicy::ALLOWED;
906         break;
907       case net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION:
908         // We ignore this error but it will show a warning status in the
909         // location bar.
910         judgment = CertPolicy::ALLOWED;
911         break;
912       case net::ERR_CERT_CONTAINS_ERRORS:
913       case net::ERR_CERT_REVOKED:
914       case net::ERR_CERT_INVALID:
915       case net::ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY:
916       case net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN:
917         judgment = CertPolicy::DENIED;
918         break;
919       case net::ERR_CERT_WEAK_SIGNATURE_ALGORITHM:
920       case net::ERR_CERT_COMMON_NAME_INVALID:
921       case net::ERR_CERT_DATE_INVALID:
922       case net::ERR_CERT_AUTHORITY_INVALID:
923         // Nothing. If DENIED it will stay denied. If UNKNOWN it will be
924         // shown to the user for decision.
925         break;
926       default:
927         NOTREACHED();
928         judgment = CertPolicy::DENIED;
929         break;
930     }
931   }
933   counts->ssl_judgment = judgment;
935   switch (judgment) {
936     case CertPolicy::UNKNOWN:
937     case CertPolicy::DENIED:
938       // Simply cancel the request.
939       CancelRequestForCounts(counts);
940       break;
941     case CertPolicy::ALLOWED:
942       counts->ssl_callback.Run(YES);
943       counts->ssl_callback = base::Bind(&DoNothing);
944       break;
945     default:
946       NOTREACHED();
947       // For now simply cancel the request.
948       CancelRequestForCounts(counts);
949       break;
950   }
953 void RequestTrackerImpl::ReevaluateCallbacksForAllCounts() {
954   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
955   if (is_closing_)
956     return;
958   ScopedVector<TrackerCounts>::iterator it;
959   for (it = counts_.begin(); it != counts_.end(); ++it) {
960     // Check if the value hasn't changed via a user action.
961     if ((*it)->ssl_judgment == CertPolicy::UNKNOWN)
962       EvaluateSSLCallbackForCounts(*it);
964     CertPolicy::Judgment judgment = (*it)->ssl_judgment;
965     if (judgment == CertPolicy::ALLOWED)
966       continue;
968     // SSL errors on subrequests are simply ignored. The call to
969     // EvaluateSSLCallbackForCounts() cancelled the request and nothing will
970     // restart it.
971     if ((*it)->is_subrequest)
972       continue;
974     if (!current_ssl_error_) {
975       // For the UNKNOWN and DENIED state the information should be pushed to
976       // the delegate. But only one at a time.
977       current_ssl_error_ = (*it);
978       base::scoped_nsobject<CRWSSLCarrier> carrier([[CRWSSLCarrier alloc]
979           initWithTracker:this counts:current_ssl_error_]);
980       web::WebThread::PostTask(
981           web::WebThread::UI, FROM_HERE,
982           base::Bind(&RequestTrackerImpl::NotifyPresentSSLError, this, carrier,
983                      judgment == CertPolicy::UNKNOWN));
984     }
985   }
988 void RequestTrackerImpl::CancelRequestForCounts(TrackerCounts* counts) {
989   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
990   // Cancel the request.
991   counts->done = true;
992   counts_by_request_.erase(counts->request);
993   counts->ssl_callback.Run(NO);
994   counts->ssl_callback = base::Bind(&DoNothing);
995   Notify();
998 PageCounts RequestTrackerImpl::pageCounts() {
999   DCHECK_GE(counts_.size(), estimate_start_index_);
1001   PageCounts page_counts;
1003   ScopedVector<TrackerCounts>::iterator it;
1004   for (it = counts_.begin() + estimate_start_index_;
1005        it != counts_.end(); ++it) {
1006     if ((*it)->done) {
1007       uint64_t size = (*it)->processed;
1008       page_counts.finished += 1;
1009       page_counts.finished_bytes += size;
1010       if (page_counts.largest_byte_size_known < size) {
1011         page_counts.largest_byte_size_known = size;
1012       }
1013     } else {
1014       page_counts.unfinished += 1;
1015       if ((*it)->expected_length) {
1016         uint64_t size = (*it)->expected_length;
1017         page_counts.unfinished_estimate_bytes_done += (*it)->processed;
1018         page_counts.unfinished_estimated_bytes_left += size;
1019         if (page_counts.largest_byte_size_known < size) {
1020           page_counts.largest_byte_size_known = size;
1021         }
1022       } else {
1023         page_counts.unfinished_no_estimate += 1;
1024         page_counts.unfinished_no_estimate_bytes_done += (*it)->processed;
1025       }
1026     }
1027   }
1029   return page_counts;
1032 float RequestTrackerImpl::EstimatedProgress() {
1033   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
1035   const PageCounts page_counts = pageCounts();
1037   // Nothing in progress and the last time was the same.
1038   if (!page_counts.unfinished && previous_estimate_ == 0.0f)
1039     return -1.0f;
1041   // First request.
1042   if (previous_estimate_ == 0.0f) {
1043     // start low.
1044     previous_estimate_ = 0.1f;
1045     return previous_estimate_;  // Return the just started status.
1046   }
1048   // The very simple case where everything is probably done and dusted.
1049   if (!page_counts.unfinished) {
1050     // Add 60%, and return. Another task is going to finish this.
1051     float bump = (1.0f - previous_estimate_) * 0.6f;
1052     previous_estimate_ += bump;
1053     return previous_estimate_;
1054   }
1056   // Calculate some ratios.
1057   // First the ratio of the finished vs the unfinished counts of resources
1058   // loaded.
1059   float unfinishedRatio =
1060       static_cast<float>(page_counts.finished) /
1061       static_cast<float>(page_counts.unfinished + page_counts.finished);
1063   // The ratio of bytes left vs bytes already downloaded for the resources where
1064   // no estimates of final size are known. For this ratio it is assumed the size
1065   // of a resource not downloaded yet is the maximum size of all the resources
1066   // seen so far.
1067   float noEstimateRatio = (!page_counts.unfinished_no_estimate_bytes_done) ?
1068       0.0f :
1069       static_cast<float>(page_counts.unfinished_no_estimate *
1070                          page_counts.largest_byte_size_known) /
1071           static_cast<float>(page_counts.finished_bytes +
1072                              page_counts.unfinished_no_estimate_bytes_done);
1074   // The ratio of bytes left vs bytes already downloaded for the resources with
1075   // available estimated size.
1076   float estimateRatio = (!page_counts.unfinished_estimated_bytes_left) ?
1077       noEstimateRatio :
1078       static_cast<float>(page_counts.unfinished_estimate_bytes_done) /
1079   static_cast<float>(page_counts.unfinished_estimate_bytes_done +
1080                      page_counts.unfinished_estimated_bytes_left);
1082   // Reassemble all of this.
1083   float total =
1084       0.1f +  // Minimum value.
1085       unfinishedRatio * 0.6f +
1086       estimateRatio * 0.3f;
1088   if (previous_estimate_ >= total)
1089     return -1.0f;
1091   // 10% of what's left.
1092   float maxBump = (1.0f - previous_estimate_) / 10.0f;
1093   // total is greater than previous estimate, need to bump the estimate up.
1094   if ((previous_estimate_ + maxBump) > total) {
1095     // Less than a 10% bump, bump to the new value.
1096     previous_estimate_ = total;
1097   } else {
1098     // Just bump by 10% toward the total.
1099     previous_estimate_ += maxBump;
1100   }
1102   return previous_estimate_;
1105 void RequestTrackerImpl::RecomputeMixedContent(
1106     const TrackerCounts* split_position) {
1107   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
1108   // Check if the mixed content before trimming was correct.
1109   if (page_url_.SchemeIsCryptographic() && has_mixed_content_) {
1110     bool old_url_has_mixed_content = false;
1111     const GURL origin = page_url_.GetOrigin();
1112     ScopedVector<TrackerCounts>::iterator it = counts_.begin();
1113     while (it != counts_.end() && *it != split_position) {
1114       if (!(*it)->url.SchemeIsCryptographic() &&
1115           origin == (*it)->first_party_for_cookies_origin) {
1116         old_url_has_mixed_content = true;
1117         break;
1118       }
1119       ++it;
1120     }
1121     if (!old_url_has_mixed_content) {
1122       // We marked the previous page with incorrect data about its mixed
1123       // content. Turns out that the elements that triggered that condition
1124       // where in fact in a subsequent page. Duh.
1125       // Resend a notification for the |page_url_| informing the upper layer
1126       // that the mixed content was a red herring.
1127       has_mixed_content_ = false;
1128       SSLNotify();
1129     }
1130   }
1133 void RequestTrackerImpl::RecomputeCertificatePolicy(
1134     const TrackerCounts* splitPosition) {
1135   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
1136   // Clear the judgments for the old URL.
1137   web::WebThread::PostTask(
1138       web::WebThread::UI, FROM_HERE,
1139       base::Bind(&RequestTrackerImpl::NotifyClearCertificates, this));
1140   // Report judgements for the new URL.
1141   ScopedVector<TrackerCounts>::const_reverse_iterator it;
1142   for (it = counts_.rbegin(); it != counts_.rend(); ++it) {
1143     TrackerCounts* counts = *it;
1144     if (counts->allowed_by_user) {
1145       std::string host = counts->url.host();
1146       web::WebThread::PostTask(
1147           web::WebThread::UI, FROM_HERE,
1148           base::Bind(&RequestTrackerImpl::NotifyCertificateUsed, this,
1149                      counts->ssl_info.cert, host,
1150                      counts->ssl_info.cert_status));
1151     }
1152     if (counts == splitPosition)
1153       break;
1154   }
1157 void RequestTrackerImpl::HistoryStateChangeToURL(const GURL& full_url) {
1158   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
1159   GURL url = GURLByRemovingRefFromGURL(full_url);
1161   if (is_loading_ &&
1162       web::history_state_util::IsHistoryStateChangeValid(url, page_url_)) {
1163     page_url_ = url;
1164   }
1167 void RequestTrackerImpl::TrimToURL(const GURL& full_url, id user_info) {
1168   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
1170   GURL url = GURLByRemovingRefFromGURL(full_url);
1172   // Locate the request with this url, if present.
1173   bool new_url_has_mixed_content = false;
1174   bool url_scheme_is_secure = url.SchemeIsCryptographic();
1175   ScopedVector<TrackerCounts>::const_reverse_iterator rit = counts_.rbegin();
1176   while (rit != counts_.rend() && (*rit)->url != url) {
1177     if (url_scheme_is_secure && !(*rit)->url.SchemeIsCryptographic() &&
1178         (*rit)->first_party_for_cookies_origin == url.GetOrigin()) {
1179       new_url_has_mixed_content = true;
1180     }
1181     ++rit;
1182   }
1184   // |split_position| will be set to the count for the passed url if it exists.
1185   TrackerCounts* split_position = NULL;
1186   if (rit != counts_.rend()) {
1187     split_position = (*rit);
1188   } else {
1189     // The URL was not found, everything will be trimmed. The mixed content
1190     // calculation is invalid.
1191     new_url_has_mixed_content = false;
1193     // In the case of a page loaded via a HTML5 manifest there is no page
1194     // boundary to be found. However the latest count is a request for a
1195     // manifest. This tries to detect this peculiar case.
1196     // This is important as if this request for the manifest is on the same
1197     // domain as the page itself this will allow retrieval of the SSL
1198     // information.
1199     if (url_scheme_is_secure && counts_.size()) {
1200       TrackerCounts* back = counts_.back();
1201       const GURL& back_url = back->url;
1202       if (back_url.SchemeIsCryptographic() &&
1203           back_url.GetOrigin() == url.GetOrigin() && !back->is_subrequest) {
1204         split_position = back;
1205       }
1206     }
1207   }
1208   RecomputeMixedContent(split_position);
1209   RecomputeCertificatePolicy(split_position);
1211   // Trim up to that element.
1212   ScopedVector<TrackerCounts>::iterator it = counts_.begin();
1213   while (it != counts_.end() && *it != split_position) {
1214     if (!(*it)->done) {
1215       // This is for an unfinished request on a previous page. We do not care
1216       // about those anymore. Cancel the request.
1217       if ((*it)->ssl_judgment == CertPolicy::UNKNOWN)
1218         CancelRequestForCounts(*it);
1219       counts_by_request_.erase((*it)->request);
1220     }
1221     it = counts_.erase(it);
1222   }
1224   has_mixed_content_ = new_url_has_mixed_content;
1225   page_url_ = url;
1226   user_info_.reset([user_info retain]);
1227   estimate_start_index_ = 0;
1228   is_loading_ = true;
1229   previous_estimate_ = 0.0f;
1230   new_estimate_round_ = true;
1231   ReevaluateCallbacksForAllCounts();
1232   Notify();
1235 void RequestTrackerImpl::StopPageLoad(const GURL& url, bool load_success) {
1236   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
1237   DCHECK(page_url_ == GURLByRemovingRefFromGURL(url));
1238   is_loading_ = false;
1241 #pragma mark Internal utilities for task posting
1243 void RequestTrackerImpl::PostTask(const base::Closure& task,
1244                                   web::WebThread::ID thread) {
1245   // Absolute sanity test: |thread| is one of {UI, IO}
1246   DCHECK(thread == web::WebThread::UI || thread == web::WebThread::IO);
1247   // Check that we're on the counterpart thread to the one we're posting to.
1248   DCHECK_CURRENTLY_ON_WEB_THREAD(
1249       thread == web::WebThread::IO ? web::WebThread::UI : web::WebThread::IO);
1250   // Don't post if the tracker is closing and we're on the IO thread.
1251   // (there should be no way to call anything from the UI thread if
1252   // the tracker is closing).
1253   if (is_closing_ && web::WebThread::CurrentlyOn(web::WebThread::IO))
1254     return;
1255   web::WebThread::PostTask(thread, FROM_HERE, task);
1258 #pragma mark Other internal methods.
1260 NSString* RequestTrackerImpl::UnsafeDescription() {
1261   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
1263   NSMutableArray* urls = [NSMutableArray array];
1264   ScopedVector<TrackerCounts>::iterator it;
1265   for (it = counts_.begin(); it != counts_.end(); ++it)
1266     [urls addObject:(*it)->Description()];
1268   return [NSString stringWithFormat:@"RequestGroupID %@\n%@\n%@",
1269                                     request_group_id_.get(),
1270                                     net::NSURLWithGURL(page_url_),
1271                                     [urls componentsJoinedByString:@"\n"]];
1274 NSString* RequestTrackerImpl::GetNetworkActivityKey() {
1275   return [NSString
1276       stringWithFormat:@"RequestTrackerImpl.NetworkActivityIndicatorKey.%@",
1277                        request_group_id_.get()];
1278   }
1280 void RequestTrackerImpl::CancelRequests() {
1281   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
1282   std::set<net::URLRequest*>::iterator it;
1283   // TODO(droger): When canceling the request, we should in theory make sure
1284   // that the NSURLProtocol client method |didFailWithError| is called,
1285   // otherwise the iOS system may wait indefinitely for the request to complete.
1286   // However, as we currently only cancel the requests when closing a tab, the
1287   // requests are all canceled by the system shortly after and nothing bad
1288   // happens.
1289   for (it = live_requests_.begin(); it != live_requests_.end(); ++it)
1290     (*it)->Cancel();
1292   int removedRequests = live_requests_.size();
1293   live_requests_.clear();
1294   if (!is_for_static_file_requests_ && removedRequests > 0) {
1295     NSString* networkActivityKey = GetNetworkActivityKey();
1296     web::WebThread::PostTask(
1297         web::WebThread::UI, FROM_HERE,
1298         base::BindBlock(^{
1299           [[CRWNetworkActivityIndicatorManager sharedInstance]
1300               clearNetworkTasksForGroup:networkActivityKey];
1301         }));
1302   }
1305 void RequestTrackerImpl::SetCertificatePolicyCacheForTest(
1306     web::CertificatePolicyCache* cache) {
1307   policy_cache_ = cache;
1310 }  // namespace web