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 #ifndef IOS_WEB_NET_REQUEST_TRACKER_IMPL_H_
6 #define IOS_WEB_NET_REQUEST_TRACKER_IMPL_H_
8 #import <Foundation/Foundation.h>
12 #include "base/callback_forward.h"
13 #include "base/mac/scoped_nsobject.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/memory/weak_ptr.h"
17 #import "ios/net/request_tracker.h"
18 #import "ios/web/net/crw_request_tracker_delegate.h"
19 #include "ios/web/public/web_thread.h"
20 #include "net/url_request/url_request_context_getter.h"
33 class HttpResponseHeaders
;
35 class URLRequestContext
;
37 class X509Certificate
;
43 class CertificatePolicyCache
;
45 // Structure to capture the current state of a page.
48 PageCounts() : finished(0),
51 unfinished_no_estimate(0),
52 unfinished_no_estimate_bytes_done(0),
53 unfinished_estimated_bytes_left(0),
54 unfinished_estimate_bytes_done(0),
55 largest_byte_size_known(0) {
58 // Count of finished requests.
60 // Total bytes count dowloaded for all finished requests.
61 uint64_t finished_bytes
;
62 // Count of unfinished requests.
64 // Count of unfinished requests with unknown size.
65 uint64_t unfinished_no_estimate
;
66 // Total bytes count dowloaded for unfinished requests of unknown size.
67 uint64_t unfinished_no_estimate_bytes_done
;
68 // Count of unfinished requests with an estimated size.
69 uint64_t unfinished_estimated_bytes_left
;
70 // Total bytes count dowloaded for unfinished requests with an estimated size.
71 uint64_t unfinished_estimate_bytes_done
;
72 // Size of the request with the most bytes on the page.
73 uint64_t largest_byte_size_known
;
76 // RequestTrackerImpl captures and stores all the network requests that
77 // initiated from a particular tab. It only keeps the URLs and eventually, if
78 // available, the expected length of the result and the length of the received
79 // data so far as this is used to build a progress bar for a page.
80 // Note that the Request tracker has no notion of a page, it only tracks the
81 // requests by tab. In order for the tracker to know that a request is for a
82 // page or a subresource it is necessary for the tab to call StartPageLoad()
83 // with the URL of the page once it is known to avoid storing all the requests
86 // The consumer needs to implement the CRWRequestTrackerImplDelegate protocol
87 // and needs to call StartPageLoad() and FinishPageLoad() to indicate the page
88 // boundaries. StartPageLoad() will also have the side effect of clearing past
89 // requests from memory. The consumer is assumed to be on the UI thread at all
92 // RequestTrackerImpl objects are created and destroyed on the UI thread and
93 // must be owned by some other object on the UI thread by way of a
94 // scoped_refptr, as returned by the public static constructor method,
95 // CreateTrackerForRequestGroupID. All consumer API methods will be called
96 // through this pointer.
98 class RequestTrackerImpl
;
100 struct RequestTrackerImplTraits
{
101 static void Destruct(const RequestTrackerImpl
* t
);
104 class RequestTrackerImpl
105 : public base::RefCountedThreadSafe
<RequestTrackerImpl
,
106 RequestTrackerImplTraits
>,
107 public net::RequestTracker
{
109 #pragma mark Public Consumer API
110 // Consumer API methods should only be called on the UI thread.
112 // Create a new RequestTrackerImpl associated with a particular tab. The
113 // profile must be the one associated to the given tab. This method has to be
114 // called *once* per tab and needs to be called before triggering any network
115 // request. The caller of CreateTrackerForRequestGroupID owns the tracker, and
116 // this class also keeps a global map of all active trackers. When the owning
117 // object releases it, the class removes it from the global map.
118 static scoped_refptr
<RequestTrackerImpl
> CreateTrackerForRequestGroupID(
119 NSString
* request_group_id
,
120 BrowserState
* browser_state
,
121 net::URLRequestContextGetter
* context_getter
,
122 id
<CRWRequestTrackerDelegate
> delegate
);
124 // The network layer has no way to know which network request is the primary
125 // one for a page load. The tab knows, either because it initiated the page
126 // load via the URL or received a callback informing it of the page change.
127 // Every time this happens the tab should call this method to clear the
128 // resources tracked.
129 // This will forget all the finished requests made before this URL in history.
130 // user_info is to be used by the consumer to store more additional specific
131 // info about the page, as an URL is not unique.
132 void StartPageLoad(const GURL
& url
, id user_info
);
134 // In order to properly provide progress information the tracker needs to know
135 // when the page is fully loaded. |load_success| indicates if the page
136 // successfully loaded.
137 void FinishPageLoad(const GURL
& url
, bool load_success
);
139 // Tells the tracker that history.pushState() or history.replaceState()
140 // changed the page URL.
141 void HistoryStateChange(const GURL
& url
);
143 // Marks the tracker as closed. An owner must call this before the tracker is
144 // deleted. Once closed, no further calls will be made to the delegate.
147 // Call |callback| on the UI thread after any pending request cancellations
148 // have completed on the IO thread.
149 // This should be used to delete a profile for which all of the trackers
150 // that use the profile's request context are closed.
151 static void RunAfterRequestsCancel(const base::Closure
& callback
);
153 // Block until all pending IO thread activity has completed. This should only
154 // be used when Chrome is shutting down, and after all request trackers have
155 // had Close() called on them.
156 static void BlockUntilTrackersShutdown();
158 #pragma mark Client utility methods.
160 // Finds the tracker given the tab ID. As calling this method involves a lock
161 // it is expected that the provider will call it only once.
162 // Returns a weak pointer, which should only be dereferenced on the IO thread.
163 // Returns NULL if no tracker exists for |request_group_id|.
164 static RequestTrackerImpl
* GetTrackerForRequestGroupID(
165 NSString
* request_group_id
);
167 // Callback from the UI to allow or deny a particular certificate.
168 void ErrorCallback(CRWSSLCarrier
* carrier
, bool allow
);
170 // Utility method for clients to post tasks to the IO thread from the UI
172 void PostIOTask(const base::Closure
& task
);
174 // Utility method for clients to post tasks to the IO thread from the IO
176 void ScheduleIOTask(const base::Closure
& task
);
178 // Utility method for clients to conditionally post tasks to the UI thread
179 // from the IO thread. The task will not be posted if the request tracker
180 // is in the process of closing (thus it "is open").
181 void PostUITaskIfOpen(const base::Closure
& task
);
182 // Static version of the method, where |tracker| is a RequestTrackerImpl
183 // passed as a base::WeakPtr<RequestTracker>.
184 static void PostUITaskIfOpen(const base::WeakPtr
<RequestTracker
> tracker
,
185 const base::Closure
& task
);
187 // Sets the cache mode. Must be called from the UI thread.
188 void SetCacheModeFromUIThread(RequestTracker::CacheMode mode
);
190 #pragma mark Testing methods
192 void SetCertificatePolicyCacheForTest(web::CertificatePolicyCache
* cache
);
194 #pragma mark Accessors used by internal classes and network clients.
195 int identifier() { return identifier_
; }
196 bool has_mixed_content() { return has_mixed_content_
; }
198 // RequestTracker implementation.
199 void StartRequest(net::URLRequest
* request
) override
;
200 void CaptureHeaders(net::URLRequest
* request
) override
;
201 void CaptureExpectedLength(const net::URLRequest
* request
,
202 uint64_t length
) override
;
203 void CaptureReceivedBytes(const net::URLRequest
* request
,
204 uint64_t byte_count
) override
;
205 void CaptureCertificatePolicyCache(
206 const net::URLRequest
* request
,
207 const SSLCallback
& should_continue
) override
;
208 void StopRequest(net::URLRequest
* request
) override
;
209 void StopRedirectedRequest(net::URLRequest
* request
) override
;
210 void OnSSLCertificateError(const net::URLRequest
* request
,
211 const net::SSLInfo
& ssl_info
,
213 const SSLCallback
& should_continue
) override
;
214 net::URLRequestContext
* GetRequestContext() override
;
217 friend class base::RefCountedThreadSafe
<RequestTrackerImpl
>;
218 friend struct RequestTrackerImplTraits
;
220 #pragma mark Object lifecycle API
221 // Private. RequestTrackerImpls are created through
222 // CreateTrackerForRequestGroupID().
223 RequestTrackerImpl(NSString
* request_group_id
,
224 net::URLRequestContextGetter
* context_getter
,
225 id
<CRWRequestTrackerDelegate
> delegate
);
228 const scoped_refptr
<web::CertificatePolicyCache
>& policy_cache
);
230 // Private destructor because the object is reference counted. A no-op; the
231 // useful destruction work happens in Destruct().
232 ~RequestTrackerImpl() override
;
234 // Handles pre-destruction destruction tasks. This is invoked by
235 // RequestTrackerImplTraits::Destruct whenever the reference count of a
236 // RequestTrackerImpl is zero, and this will untimately delete the
237 // RequestTrackerImpl.
240 #pragma mark Private Provider API
241 // Private methods that implement provider API features. All are only called
244 // Called when something has changed (network load progress or SSL status)
245 // that the consumer should know about. Notifications are asynchronous and
249 // If no other notifications are pending, notifies the consumer of SSL status
250 // and load progress.
251 void StackNotification();
253 // Notify the consumer about the SSL status of this tracker's page load.
256 // If the counts is for a request currently waiting for the user to approve it
257 // will reevaluate the approval.
258 void EvaluateSSLCallbackForCounts(TrackerCounts
* counts
);
260 // Loop through all the requests waiting for approval and invoke
261 // |-evaluateSSLCallbackForCounts:| on all the ones with an |UNKNOWN|
263 void ReevaluateCallbacksForAllCounts();
265 // To cancel a rejected request due to a SSL issue.
266 void CancelRequestForCounts(TrackerCounts
* counts
);
268 // Estimate the page load progress. Returns -1 if the progress didn't change
269 // since the last time this method was invoked.
270 float EstimatedProgress();
272 // The URL change notification is often late, therefore the mixed content
273 // status and the certificate policies may need to be recomputed.
274 void RecomputeMixedContent(const TrackerCounts
* split_position
);
275 void RecomputeCertificatePolicy(const TrackerCounts
* split_position
);
277 // Remove all finished request up to the last instance of |url|. If url is not
278 // found, this will clear all the requests.
279 void TrimToURL(const GURL
& url
, id user_info
);
281 // Sets page_url_ to the new URL if it's a valid history state change (i.e.
282 // the URL's have the same origin) and if the tab is currently loading.
283 void HistoryStateChangeToURL(const GURL
& full_url
);
285 // Note that the page started by a call to Trim is no longer loading.
286 // |load_success| indicates if the page successfully loaded.
287 void StopPageLoad(const GURL
& url
, bool load_success
);
289 // Cancels all the requests in |live_requests_|.
290 void CancelRequests();
292 #pragma mark Private Consumer API
293 // Private methods that call into delegate methods.
295 // Notify* methods are posted to the UI thread by the provider API
298 // Has the delegate handle |headers| for |request_url|.
299 void NotifyResponseHeaders(net::HttpResponseHeaders
* headers
,
300 const GURL
& request_url
);
302 // Notifies the deleage of certificate use.
303 void NotifyCertificateUsed(net::X509Certificate
* certificate
,
304 const std::string
& host
,
305 net::CertStatus status
);
307 // Notifies the deleate of a load completion estimate.
308 void NotifyUpdatedProgress(float estimate
);
310 // Has the delegate clear SSL certificates.
311 void NotifyClearCertificates();
313 // Notifies the delegate of an SSL status update.
314 void NotifyUpdatedSSLStatus(base::scoped_nsobject
<CRWSSLCarrier
> carrier
);
316 // Calls the delegate method to present an SSL error interstitial.
317 void NotifyPresentSSLError(base::scoped_nsobject
<CRWSSLCarrier
> carrier
,
320 #pragma mark Internal utilities for task posting
321 // Posts |task| to |thread|. Must not be called from |thread|. If |thread| is
322 // the IO thread, silently returns if |is_closing_| is true.
323 void PostTask(const base::Closure
& task
, web::WebThread::ID thread
);
325 // Posts |block| to |thread|, safely passing in |caller| to |block|.
326 void PostBlock(id caller
, void (^block
)(id
), web::WebThread::ID thread
);
328 #pragma mark Other internal methods.
329 // Returns the current state of the page.
330 PageCounts
pageCounts();
332 // Like description, but cannot be called from any thread. It must be called
333 // only from the IO thread.
334 NSString
* UnsafeDescription();
336 // Generates a string unique to this RequestTrackerImpl to use with the
337 // CRWNetworkActivityIndicatorManager.
338 NSString
* GetNetworkActivityKey();
340 #pragma mark Non thread-safe fields, only accessed from the main thread.
341 // The RequestTrackerImpl delegate. All changes and access to this object
342 // should be done on the main thread.
343 id
<CRWRequestTrackerDelegate
> delegate_
; // Weak.
345 #pragma mark Non thread-safe fields, only accessed from the IO thread.
346 // All the tracked requests for the page, indexed by net::URLRequest (Cast as
347 // a void* to avoid the temptation of accessing it from the wrong thread).
348 // This map is not exhaustive: it is only meant to estimate the loading
349 // progress, and thus requests corresponding to old navigation events are not
351 std::map
<const void*, TrackerCounts
*> counts_by_request_
;
352 // All the live requests associated with the tracker.
353 std::set
<net::URLRequest
*> live_requests_
;
354 // A list of all the TrackerCounts, including the finished ones.
355 ScopedVector
<TrackerCounts
> counts_
;
356 // The system shall never allow the page load estimate to go back.
357 float previous_estimate_
;
358 // Index of the first request to consider for building the estimation.
359 unsigned int estimate_start_index_
;
360 // How many notifications are currently queued, to avoid notifying too often.
361 int notification_depth_
;
362 // The tracker containing the error currently presented to the user.
363 TrackerCounts
* current_ssl_error_
;
364 // Set to |YES| if the page has mixed content
365 bool has_mixed_content_
;
366 // Set to true if between TrimToURL and StopPageLoad.
368 // Set to true in TrimToURL if starting a new estimate round. Set to false by
369 // StartRequest once the new round is started.
370 bool new_estimate_round_
;
372 #pragma mark Other fields.
373 scoped_refptr
<web::CertificatePolicyCache
> policy_cache_
;
374 // If |true| all the requests should be static file requests, otherwise all
375 // the requests should be network requests. This is a constant initialized
376 // in the constructor and read in IO and UI threads.
377 const bool is_for_static_file_requests_
;
379 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter_
;
380 // Current page URL, as far as we know.
382 // Userinfo attached to the page, passed back by the delegate.
383 base::scoped_nsobject
<id
> user_info_
;
384 // A tracker identifier (a simple increasing number) used to store
387 // The string that identifies the tab this tracker serves. Used to index
389 base::scoped_nsobject
<NSString
> request_group_id_
;
390 // Flag to synchronize deletion and callback creation. Lives on the IO thread.
391 // True when this tracker has beed Close()d. If this is the case, no further
392 // references to it should be generated (for example by binding it into a
393 // callback), and the expectation is that it will soon be deleted.
399 #endif // IOS_WEB_NET_REQUEST_TRACKER_IMPL_H_