blob: c7b43212fcc59818ed52e0508a4a64ebe956c61f [file] [log] [blame]
// 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 <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "base/callback_forward.h"
#import "base/mac/scoped_nsobject.h"
#include "base/memory/ref_counted.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;
struct TrackerCounts;
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);
// 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);
#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);
#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);
#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();
#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_;
// A list of all the TrackerCounts, including the finished ones.
std::vector<std::unique_ptr<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_;
// 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_;
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_