summaryrefslogtreecommitdiffstats
path: root/ios/web/net/request_tracker_impl.h
blob: 45aaa0361b195c8309904c2e20dc8c4a432de5f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef IOS_WEB_NET_REQUEST_TRACKER_IMPL_H_
#define IOS_WEB_NET_REQUEST_TRACKER_IMPL_H_

#import <Foundation/Foundation.h>
#include <map>
#include <set>

#include "base/callback_forward.h"
#include "base/mac/scoped_nsobject.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#import "ios/net/request_tracker.h"
#import "ios/web/net/crw_request_tracker_delegate.h"
#include "ios/web/public/web_thread.h"
#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"

@class SSLCarrier;
@class CRWSSLCarrier;
class SSLErrorInfo;
struct TrackerCounts;

namespace content {
struct SSLStatus;
}

namespace net {
class HttpResponseHeaders;
class URLRequest;
class URLRequestContext;
class SSLInfo;
class X509Certificate;
}

namespace web {

class BrowserState;
class CertificatePolicyCache;

// Structure to capture the current state of a page.
struct PageCounts {
 public:
  PageCounts() : finished(0),
                 finished_bytes(0),
                 unfinished(0),
                 unfinished_no_estimate(0),
                 unfinished_no_estimate_bytes_done(0),
                 unfinished_estimated_bytes_left(0),
                 unfinished_estimate_bytes_done(0),
                 largest_byte_size_known(0) {
  };

  // Count of finished requests.
  uint64_t finished;
  // Total bytes count dowloaded for all finished requests.
  uint64_t finished_bytes;
  // Count  of unfinished requests.
  uint64_t unfinished;
  // Count of unfinished requests with unknown size.
  uint64_t unfinished_no_estimate;
  // Total bytes count dowloaded for unfinished requests of unknown size.
  uint64_t unfinished_no_estimate_bytes_done;
  // Count of unfinished requests with an estimated size.
  uint64_t unfinished_estimated_bytes_left;
  // Total bytes count dowloaded for unfinished requests with an estimated size.
  uint64_t unfinished_estimate_bytes_done;
  // Size of the request with the most bytes on the page.
  uint64_t largest_byte_size_known;
};

// RequestTrackerImpl captures and stores all the network requests that
// initiated from a particular tab. It only keeps the URLs and eventually, if
// available, the expected length of the result and the length of the received
// data so far as this is used to build a progress bar for a page.
// Note that the Request tracker has no notion of a page, it only tracks the
// requests by tab. In order for the tracker to know that a request is for a
// page or a subresource it is necessary for the tab to call StartPageLoad()
// with the URL of the page once it is known to avoid storing all the requests
// forever.
//
// The consumer needs to implement the CRWRequestTrackerImplDelegate protocol
// and needs to call StartPageLoad() and FinishPageLoad() to indicate the page
// boundaries. StartPageLoad() will also have the side effect of clearing past
// requests from memory. The consumer is assumed to be on the UI thread at all
// times.
//
// RequestTrackerImpl objects are created and destroyed on the UI thread and
// must be owned by some other object on the UI thread by way of a
// scoped_refptr, as returned by the public static constructor method,
// CreateTrackerForRequestGroupID. All consumer API methods will be called
// through this pointer.

class RequestTrackerImpl;

struct RequestTrackerImplTraits {
  static void Destruct(const RequestTrackerImpl* t);
};

class RequestTrackerImpl
    : public base::RefCountedThreadSafe<RequestTrackerImpl,
                                        RequestTrackerImplTraits>,
      public net::RequestTracker {
 public:
#pragma mark Public Consumer API
  // Consumer API methods should only be called on the UI thread.

  // Create a new RequestTrackerImpl associated with a particular tab. The
  // profile must be the one associated to the given tab. This method has to be
  // called *once* per tab and needs to be called before triggering any network
  // request. The caller of CreateTrackerForRequestGroupID owns the tracker, and
  // this class also keeps a global map of all active trackers. When the owning
  // object releases it, the class removes it from the global map.
  static scoped_refptr<RequestTrackerImpl> CreateTrackerForRequestGroupID(
      NSString* request_group_id,
      BrowserState* browser_state,
      net::URLRequestContextGetter* context_getter,
      id<CRWRequestTrackerDelegate> delegate);

  // The network layer has no way to know which network request is the primary
  // one for a page load. The tab knows, either because it initiated the page
  // load via the URL or received a callback informing it of the page change.
  // Every time this happens the tab should call this method to clear the
  // resources tracked.
  // This will forget all the finished requests made before this URL in history.
  // user_info is to be used by the consumer to store more additional specific
  // info about the page, as an URL is not unique.
  void StartPageLoad(const GURL& url, id user_info);

  // In order to properly provide progress information the tracker needs to know
  // when the page is fully loaded. |load_success| indicates if the page
  // successfully loaded.
  void FinishPageLoad(const GURL& url, bool load_success);

  // Tells the tracker that history.pushState() or history.replaceState()
  // changed the page URL.
  void HistoryStateChange(const GURL& url);

  // Marks the tracker as closed. An owner must call this before the tracker is
  // deleted. Once closed, no further calls will be made to the delegate.
  void Close();

  // Call |callback| on the UI thread after any pending request cancellations
  // have completed on the IO thread.
  // This should be used to delete a profile for which all of the trackers
  // that use the profile's request context are closed.
  static void RunAfterRequestsCancel(const base::Closure& callback);

  // Block until all pending IO thread activity has completed. This should only
  // be used when Chrome is shutting down, and after all request trackers have
  // had Close() called on them.
  static void BlockUntilTrackersShutdown();

#pragma mark Client utility methods.

  // Finds the tracker given the tab ID. As calling this method involves a lock
  // it is expected that the provider will call it only once.
  // Returns a weak pointer, which should only be dereferenced on the IO thread.
  // Returns NULL if no tracker exists for |request_group_id|.
  static RequestTrackerImpl* GetTrackerForRequestGroupID(
      NSString* request_group_id);

  // Callback from the UI to allow  or deny a particular certificate.
  void ErrorCallback(CRWSSLCarrier* carrier, bool allow);

  // Utility method for clients to post tasks to the IO thread from the UI
  // thread.
  void PostIOTask(const base::Closure& task);

  // Utility method for clients to post tasks to the IO thread from the IO
  // thread.
  void ScheduleIOTask(const base::Closure& task);

  // Utility method for clients to conditionally post tasks to the UI thread
  // from the IO thread. The task will not be posted if the request tracker
  // is in the process of closing (thus it "is open").
  void PostUITaskIfOpen(const base::Closure& task);
  // Static version of the method, where |tracker| is a RequestTrackerImpl
  // passed as a base::WeakPtr<RequestTracker>.
  static void PostUITaskIfOpen(const base::WeakPtr<RequestTracker> tracker,
                               const base::Closure& task);

  // Sets the cache mode. Must be called from the UI thread.
  void SetCacheModeFromUIThread(RequestTracker::CacheMode mode);

#pragma mark Testing methods

  void SetCertificatePolicyCacheForTest(web::CertificatePolicyCache* cache);

#pragma mark Accessors used by internal classes and network clients.
  int identifier() { return identifier_; }
  bool has_mixed_content() { return has_mixed_content_; }

  // RequestTracker implementation.
  void StartRequest(net::URLRequest* request) override;
  void CaptureHeaders(net::URLRequest* request) override;
  void CaptureExpectedLength(const net::URLRequest* request,
                             uint64_t length) override;
  void CaptureReceivedBytes(const net::URLRequest* request,
                            uint64_t byte_count) override;
  void CaptureCertificatePolicyCache(
      const net::URLRequest* request,
      const SSLCallback& should_continue) override;
  void StopRequest(net::URLRequest* request) override;
  void StopRedirectedRequest(net::URLRequest* request) override;
  void OnSSLCertificateError(const net::URLRequest* request,
                             const net::SSLInfo& ssl_info,
                             bool recoverable,
                             const SSLCallback& should_continue) override;
  net::URLRequestContext* GetRequestContext() override;

 private:
  friend class base::RefCountedThreadSafe<RequestTrackerImpl>;
  friend struct RequestTrackerImplTraits;

#pragma mark Object lifecycle API
  // Private. RequestTrackerImpls are created through
  // CreateTrackerForRequestGroupID().
  RequestTrackerImpl(NSString* request_group_id,
                     net::URLRequestContextGetter* context_getter,
                     id<CRWRequestTrackerDelegate> delegate);

  void InitOnIOThread(
      const scoped_refptr<web::CertificatePolicyCache>& policy_cache);

  // Private destructor because the object is reference counted. A no-op; the
  // useful destruction work happens in Destruct().
  ~RequestTrackerImpl() override;

  // Handles pre-destruction destruction tasks. This is invoked by
  // RequestTrackerImplTraits::Destruct whenever the reference count of a
  // RequestTrackerImpl is zero, and this will untimately delete the
  // RequestTrackerImpl.
  void Destruct();

#pragma mark Private Provider API
  // Private methods that implement provider API features. All are only called
  // on the IO thread.

  // Called when something has changed (network load progress or SSL status)
  // that the consumer should know about. Notifications are asynchronous and
  // batched.
  void Notify();

  // If no other notifications are pending, notifies the consumer of SSL status
  // and load progress.
  void StackNotification();

  // Notify the consumer about the SSL status of this tracker's page load.
  void SSLNotify();

  // If the counts is for a request currently waiting for the user to approve it
  // will reevaluate the approval.
  void EvaluateSSLCallbackForCounts(TrackerCounts* counts);

  // Loop through all the requests waiting for approval and invoke
  // |-evaluateSSLCallbackForCounts:| on all the ones with an |UNKNOWN|
  // judgment.
  void ReevaluateCallbacksForAllCounts();

  // To cancel a rejected request due to a SSL issue.
  void CancelRequestForCounts(TrackerCounts* counts);

  // Estimate the page load progress. Returns -1 if the progress didn't change
  // since the last time this method was invoked.
  float EstimatedProgress();

  // The URL change notification is often late, therefore the mixed content
  // status and the certificate policies may need to be recomputed.
  void RecomputeMixedContent(const TrackerCounts* split_position);
  void RecomputeCertificatePolicy(const TrackerCounts* split_position);

  // Remove all finished request up to the last instance of |url|. If url is not
  // found, this will clear all the requests.
  void TrimToURL(const GURL& url, id user_info);

  // Sets page_url_ to the new URL if it's a valid history state change (i.e.
  // the URL's have the same origin) and if the tab is currently loading.
  void HistoryStateChangeToURL(const GURL& full_url);

  // Note that the page started by a call to Trim is no longer loading.
  // |load_success| indicates if the page successfully loaded.
  void StopPageLoad(const GURL& url, bool load_success);

  // Cancels all the requests in |live_requests_|.
  void CancelRequests();

#pragma mark Private Consumer API
  // Private methods that call into delegate methods.

  // Notify* methods are posted to the UI thread by the provider API
  // methods.

  // Has the delegate handle |headers| for |request_url|.
  void NotifyResponseHeaders(net::HttpResponseHeaders* headers,
                             const GURL& request_url);

  // Notifies the deleage of certificate use.
  void NotifyCertificateUsed(net::X509Certificate* certificate,
                             const std::string& host,
                             net::CertStatus status);

  // Notifies the deleate of a load completion estimate.
  void NotifyUpdatedProgress(float estimate);

  // Has the delegate clear SSL certificates.
  void NotifyClearCertificates();

  // Notifies the delegate of an SSL status update.
  void NotifyUpdatedSSLStatus(base::scoped_nsobject<CRWSSLCarrier> carrier);

  // Calls the delegate method to present an SSL error interstitial.
  void NotifyPresentSSLError(base::scoped_nsobject<CRWSSLCarrier> carrier,
                             bool recoverable);

#pragma mark Internal utilities for task posting
  // Posts |task| to |thread|. Must not be called from |thread|. If |thread| is
  // the IO thread, silently returns if |is_closing_| is true.
  void PostTask(const base::Closure& task, web::WebThread::ID thread);

  // Posts |block| to |thread|, safely passing in |caller| to |block|.
  void PostBlock(id caller, void (^block)(id), web::WebThread::ID thread);

#pragma mark Other internal methods.
  // Returns the current state of the page.
  PageCounts pageCounts();

  // Like description, but cannot be called from any thread. It must be called
  // only from the IO thread.
  NSString* UnsafeDescription();

  // Generates a string unique to this RequestTrackerImpl to use with the
  // CRWNetworkActivityIndicatorManager.
  NSString* GetNetworkActivityKey();

#pragma mark Non thread-safe fields, only accessed from the main thread.
  // The RequestTrackerImpl delegate. All changes and access to this object
  // should be done on the main thread.
  id<CRWRequestTrackerDelegate> delegate_;  // Weak.

#pragma mark Non thread-safe fields, only accessed from the IO thread.
  // All the tracked requests for the page, indexed by net::URLRequest (Cast as
  // a void* to avoid the temptation of accessing it from the wrong thread).
  // This map is not exhaustive: it is only meant to estimate the loading
  // progress, and thus requests corresponding to old navigation events are not
  // in it.
  std::map<const void*, TrackerCounts*> counts_by_request_;
  // All the live requests associated with the tracker.
  std::set<net::URLRequest*> live_requests_;
  // A list of all the TrackerCounts, including the finished ones.
  ScopedVector<TrackerCounts> counts_;
  // The system shall never allow the page load estimate to go back.
  float previous_estimate_;
  // Index of the first request to consider for building the estimation.
  unsigned int estimate_start_index_;
  // How many notifications are currently queued, to avoid notifying too often.
  int notification_depth_;
  // The tracker containing the error currently presented to the user.
  TrackerCounts* current_ssl_error_;
  // Set to |YES| if the page has mixed content
  bool has_mixed_content_;
  // Set to true if between TrimToURL and StopPageLoad.
  bool is_loading_;
  // Set to true in TrimToURL if starting a new estimate round. Set to false by
  // StartRequest once the new round is started.
  bool new_estimate_round_;

#pragma mark Other fields.
  scoped_refptr<web::CertificatePolicyCache> policy_cache_;
  // If |true| all the requests should be static file requests, otherwise all
  // the requests should be network requests. This is a constant initialized
  // in the constructor and read in IO and UI threads.
  const bool is_for_static_file_requests_;

  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
  // Current page URL, as far as we know.
  GURL page_url_;
  // Userinfo attached to the page, passed back by the delegate.
  base::scoped_nsobject<id> user_info_;
  // A tracker identifier (a simple increasing number) used to store
  // certificates.
  int identifier_;
  // The string that identifies the tab this tracker serves. Used to index
  // g_trackers.
  base::scoped_nsobject<NSString> request_group_id_;
  // Flag to synchronize deletion and callback creation. Lives on the IO thread.
  // True when this tracker has beed Close()d. If this is the case, no further
  // references to it should be generated (for example by binding it into a
  // callback), and the expectation is that it will soon be deleted.
  bool is_closing_;
};

}  // namespace web

#endif  // IOS_WEB_NET_REQUEST_TRACKER_IMPL_H_