blob: a84a11d39ebcd095bf2d985588864b5e80b848ca [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_SERVICE_H_
#define CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_SERVICE_H_
#include <map>
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "content/browser/preloading/prefetch/prefetch_container.h"
#include "content/browser/preloading/prefetch/prefetch_status.h"
#include "content/browser/preloading/prefetch/prefetch_streaming_url_loader_status.h"
#include "content/common/content_export.h"
#include "content/public/browser/global_routing_id.h"
#include "net/cookies/canonical_cookie.h"
#include "net/url_request/redirect_info.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
namespace base {
class OneShotTimer;
} // namespace base
namespace network::mojom {
class NetworkContext;
class URLLoaderFactory;
} // namespace network::mojom
namespace content {
class BrowserContext;
class PrefetchOriginProber;
class PrefetchProxyConfigurator;
class PrefetchServiceDelegate;
class ServiceWorkerContext;
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class PrefetchRedirectResult {
kSuccessRedirectFollowed = 0,
kFailedNullPrefetch = 1,
kFailedRedirectsDisabled = 2,
kFailedInvalidMethod = 3,
kFailedInvalidResponseCode = 4,
kFailedInvalidChangeInNetworkContext = 5,
kFailedIneligible = 6,
kFailedInsufficientReferrerPolicy = 7,
kMaxValue = kFailedInsufficientReferrerPolicy,
};
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class PrefetchRedirectNetworkContextTransition {
kDefaultToDefault = 0,
kDefaultToIsolated = 1,
kIsolatedToDefault = 2,
kIsolatedToIsolated = 3,
kMaxValue = kIsolatedToIsolated,
};
// Manages all prefetches within a single BrowserContext. Responsible for
// checking the eligibility of the prefetch, making the network request for the
// prefetch, and provide prefetched resources to URL loader interceptor when
// needed.
class CONTENT_EXPORT PrefetchService {
public:
// |browser_context| must outlive this instance. In general this should always
// be true, since |PrefetchService| will be indirectly owned by
// |BrowserContext|.
static std::unique_ptr<PrefetchService> CreateIfPossible(
BrowserContext* browser_context);
static PrefetchService* GetFromFrameTreeNodeId(int frame_tree_node_id);
static void SetFromFrameTreeNodeIdForTesting(
int frame_tree_node_id,
std::unique_ptr<PrefetchService> prefetch_service);
explicit PrefetchService(BrowserContext* browser_context);
virtual ~PrefetchService();
PrefetchService(const PrefetchService&) = delete;
const PrefetchService& operator=(const PrefetchService&) = delete;
BrowserContext* GetBrowserContext() const { return browser_context_; }
PrefetchServiceDelegate* GetPrefetchServiceDelegate() const {
return delegate_.get();
}
PrefetchProxyConfigurator* GetPrefetchProxyConfigurator() const {
return prefetch_proxy_configurator_.get();
}
virtual PrefetchOriginProber* GetPrefetchOriginProber() const;
virtual void PrefetchUrl(base::WeakPtr<PrefetchContainer> prefetch_container);
// Called when a navigation to `url` that will be served by
// `prefetch_container` is likely to occur in the immediate future.
// |url| and |prefetch_container->GetURL()| might not be the same
// because of No-Vary-Search non-exact url match.
virtual void PrepareToServe(
const GURL& url,
base::WeakPtr<PrefetchContainer> prefetch_container);
// Finds the prefetch (if any) that can be used to serve a navigation to
// |url|, and then calls |on_prefetch_to_serve_ready| with that prefetch.
using OnPrefetchToServeReady = base::OnceCallback<void(
base::WeakPtr<PrefetchContainer> prefetch_to_serve)>;
void GetPrefetchToServe(const PrefetchContainer::Key& key,
OnPrefetchToServeReady on_prefetch_to_serve_ready);
// Copies any cookies in the isolated network context associated with
// |prefetch_container| to the default network context.
virtual void CopyIsolatedCookies(
base::WeakPtr<PrefetchContainer> prefetch_container);
// Removes the prefetch with the given |prefetch_container_key| from
// |all_prefetches_|.
void RemovePrefetch(const PrefetchContainer::Key& prefetch_container_key);
// Destroys the prefetch with the given |prefetch_container_key|. Called
// to remove a prefetch when making room for a new prefetch, and sets the
// status to |PrefetchStatus::kPrefetchEvicted| before destruction to record
// this.
void EvictPrefetch(const PrefetchContainer::Key& prefetch_container_key);
// Helper functions to control the behavior of the eligibility check when
// testing.
static void SetServiceWorkerContextForTesting(ServiceWorkerContext* context);
static void SetHostNonUniqueFilterForTesting(
bool (*filter)(base::StringPiece));
// Sets the URLLoaderFactory to be used during testing instead of the
// |PrefetchNetworkContext| associated with each |PrefetchContainer|. Note
// that this does not take ownership of |url_loader_factory|, and caller must
// keep ownership over the course of the test.
static void SetURLLoaderFactoryForTesting(
network::mojom::URLLoaderFactory* url_loader_factory);
// Sets the NetworkContext to use just for the proxy lookup. Note that this
// does not take ownership of |network_context|, and the caller must keep
// ownership over the course of the test.
static void SetNetworkContextForProxyLookupForTesting(
network::mojom::NetworkContext* network_context);
private:
// Checks whether the given |prefetch_container| is eligible for prefetch.
// Once the eligibility is determined then |result_callback| will be called
// with result and an optional status stating why the prefetch is not
// eligible.
using OnEligibilityResultCallback =
base::OnceCallback<void(base::WeakPtr<PrefetchContainer>,
bool eligible,
absl::optional<PrefetchStatus> status)>;
void CheckEligibilityOfPrefetch(
const GURL& url,
base::WeakPtr<PrefetchContainer> prefetch_container,
OnEligibilityResultCallback result_callback) const;
// Called after getting the existing cookies associated with
// |prefetch_container|. If there are any cookies, then the prefetch is not
// eligible.
void OnGotCookiesForEligibilityCheck(
const GURL& url,
base::WeakPtr<PrefetchContainer> prefetch_container,
OnEligibilityResultCallback result_callback,
const net::CookieAccessResultList& cookie_list,
const net::CookieAccessResultList& excluded_cookies) const;
// Starts the check for whether or not there is a proxy configured for the URL
// of |prefetch_container|. If there is an existing proxy, then the prefetch
// is not eligible.
void StartProxyLookupCheck(
const GURL& url,
base::WeakPtr<PrefetchContainer> prefetch_container,
OnEligibilityResultCallback result_callback) const;
// Called after looking up the proxy configuration for the URL of
// |prefetch_container|. If there is an existing proxy, then the prefetch is
// not eligible.
void OnGotProxyLookupResult(
base::WeakPtr<PrefetchContainer> prefetch_container,
OnEligibilityResultCallback result_callback,
bool has_proxy) const;
// Called once the eligibility of |prefetch_container| is determined. If the
// prefetch is eligible it is added to the queue to be prefetched. If it is
// not eligible, then we consider making it a decoy request.
void OnGotEligibilityResult(
base::WeakPtr<PrefetchContainer> prefetch_container,
bool eligible,
absl::optional<PrefetchStatus> status);
// Called once the eligibility of a redirect for a |prefetch_container| is
// determined. If its eligible, then the prefetch will continue, otherwise it
// is stopped.
void OnGotEligibilityResultForRedirect(
const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr redirect_head,
base::WeakPtr<PrefetchContainer> prefetch_container,
bool eligible,
absl::optional<PrefetchStatus> status);
// Starts the network requests for as many prefetches in |prefetch_queue_| as
// possible.
void Prefetch();
// Pops the first valid prefetch from |prefetch_queue_|. If there are no
// valid prefetches in the queue, then nullptr is returned. In this context,
// for a prefetch to be valid, it must not be null and it must be on a visible
// web contents.
base::WeakPtr<PrefetchContainer> PopNextPrefetchContainer();
// Once the network request for a prefetch starts, ownership is transferred
// from the referring |PrefetchDocumentManager| to |this|. After
// |PrefetchContainerLifetimeInPrefetchService| amount of time, the prefetch
// is deleted. Note that if |PrefetchContainerLifetimeInPrefetchService| is 0
// or less, then it is kept forever.
void TakeOwnershipOfPrefetch(
base::WeakPtr<PrefetchContainer> prefetch_container);
void ResetPrefetch(base::WeakPtr<PrefetchContainer> prefetch_container);
// Starts the given |prefetch_container|.
void StartSinglePrefetch(base::WeakPtr<PrefetchContainer> prefetch_container);
// Makes the network request for the given |prefetch_container| to the given
// |url|. This is called when initially starting a prefetch and when a
// redirect causes a change in network context and a new request needs to be
// made.
void MakePrefetchRequest(base::WeakPtr<PrefetchContainer> prefetch_container,
const GURL& url);
// Gets the URL loader for the given |prefetch_container|. If an override was
// set by |SetURLLoaderFactoryForTesting|, then that will be returned instead.
network::mojom::URLLoaderFactory* GetURLLoaderFactoryForCurrentPrefetch(
base::WeakPtr<PrefetchContainer> prefetch_container);
// Called when the request for |prefetch_container| is redirected.
void OnPrefetchRedirect(base::WeakPtr<PrefetchContainer> prefetch_container,
const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr redirect_head);
// Called when the response for |prefetch_container| has started. Based on
// |head|, returns a status to inform the |PrefetchStreamingURLLoader| whether
// the prefetch is servable. If servable, then |kHeadReceivedWaitingOnBody|
// will be returned, otherwise a valid failure status is returned.
PrefetchStreamingURLLoaderStatus OnPrefetchResponseStarted(
base::WeakPtr<PrefetchContainer> prefetch_container,
network::mojom::URLResponseHead* head);
// Called when the response for |prefetch_container| has completed when using
// the streaming URL loader. Only used if |PrefetchUseStreamingURLLoader| is
// true.
void OnPrefetchResponseCompleted(
base::WeakPtr<PrefetchContainer> prefetch_container,
const network::URLLoaderCompletionStatus& completion_status);
// Called when the cookies from |prefetch_conatiner| are read from the
// isolated network context and are ready to be written to the default network
// context.
void OnGotIsolatedCookiesForCopy(
base::WeakPtr<PrefetchContainer> prefetch_container,
const net::CookieAccessResultList& cookie_list,
const net::CookieAccessResultList& excluded_cookies);
// Helper function for |GetPrefetchToServe| to return |prefetch_container| via
// |on_prefetch_to_serve_ready|. Starts the cookie copy process for the given
// prefetch if needed, and updates its state.
void ReturnPrefetchToServe(
base::WeakPtr<PrefetchContainer> prefetch_container,
OnPrefetchToServeReady on_prefetch_to_serve_ready);
// Helper function for |GetPrefetchToServe| to wait for head of a
// potentially matching CL in order to decide if we can use it or not for
// the current navigation.
// Once we make the decision to use a prefetch, call |PrepareToServe| and
// |GetPrefetchToServe| again in order to enforce that prefetches that are
// served are served from |prefetches_ready_to_serve_|.
void WaitOnPrefetchToServeHead(
const PrefetchContainer::Key& key,
base::WeakPtr<PrefetchContainer> prefetch_container,
OnPrefetchToServeReady on_prefetch_to_serve_ready);
// Helper function for |GetPrefetchToServe| which identifies the
// |prefetch_container| that could potentially be served.
PrefetchContainer* FindPrefetchContainerToServe(
const PrefetchContainer::Key& key);
// Checks if there is a prefetch in |all_prefetches_| with the same URL as
// |prefetch_container| but from a different referring RenderFrameHost.
// Records the result to a UMA histogram.
void RecordExistingPrefetchWithMatchingURL(
base::WeakPtr<PrefetchContainer> prefetch_container) const;
void DumpPrefetchesForDebug() const;
raw_ptr<BrowserContext, DanglingUntriaged> browser_context_;
// Delegate provided by embedder that controls specific behavior of |this|.
// May be nullptr if embedder doesn't provide a delegate.
std::unique_ptr<PrefetchServiceDelegate> delegate_;
// The custom proxy configurator for Prefetch Proxy. Only used on prefetches
// that require the proxy.
std::unique_ptr<PrefetchProxyConfigurator> prefetch_proxy_configurator_;
// The origin prober class which manages all logic for origin probing.
std::unique_ptr<PrefetchOriginProber> origin_prober_;
// All prefetches associated with |this| regardless of ownership.
std::map<PrefetchContainer::Key, base::WeakPtr<PrefetchContainer>>
all_prefetches_;
// A FIFO queue of prefetches that have been confirmed to be eligible but have
// not started yet.
std::vector<base::WeakPtr<PrefetchContainer>> prefetch_queue_;
// The set of prefetches with in progress requests.
std::set<PrefetchContainer::Key> active_prefetches_;
// Prefetches owned by |this|. Once the network request for a prefetch is
// started, |this| takes ownership of the prefetch so the response can be used
// on future page loads. A timer of
// |PrefetchContainerLifetimeInPrefetchService| is set that deletes the
// prefetch. If |PrefetchContainerLifetimeInPrefetchService| zero or less,
// then, the prefetch is kept forever.
std::map<PrefetchContainer::Key,
std::pair<std::unique_ptr<PrefetchContainer>,
std::unique_ptr<base::OneShotTimer>>>
owned_prefetches_;
// The set of prefetches that are ready to serve. In order to be in this map,
// the prefetch must also be in |owned_prefetches_|, have a valid prefetched
// response, and have started the cookie copy process. A prefetch is added to
// this map when |PrepareToServe| is called on it, and once in this map, it
// can be returned by |GetPrefetchToServe|.
//
// Unlike other maps, the URL in `PrefetchContainer::Key` can be different
// from `PrefetchContainer::GetURL()` due to No-Vary-Search.
std::map<PrefetchContainer::Key, base::WeakPtr<PrefetchContainer>>
prefetches_ready_to_serve_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<PrefetchService> weak_method_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_SERVICE_H_