| // 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_CONTAINER_H_ |
| #define CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_CONTAINER_H_ |
| |
| #include <optional> |
| #include <utility> |
| |
| #include "base/memory/raw_ref.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/time/time.h" |
| #include "content/browser/preloading/prefetch/prefetch_probe_result.h" |
| #include "content/browser/preloading/prefetch/prefetch_status.h" |
| #include "content/browser/preloading/prefetch/prefetch_streaming_url_loader_common_types.h" |
| #include "content/browser/preloading/prefetch/prefetch_type.h" |
| #include "content/browser/preloading/speculation_host_devtools_observer.h" |
| #include "content/common/content_export.h" |
| #include "content/public/browser/global_routing_id.h" |
| #include "content/public/browser/preloading.h" |
| #include "content/public/browser/preloading_data.h" |
| #include "net/http/http_no_vary_search_data.h" |
| #include "services/metrics/public/cpp/ukm_source_id.h" |
| #include "third_party/blink/public/common/tokens/tokens.h" |
| #include "url/gurl.h" |
| |
| namespace network { |
| namespace mojom { |
| class CookieManager; |
| } // namespace mojom |
| } // namespace network |
| |
| namespace content { |
| |
| class BrowserContext; |
| class PrefetchCookieListener; |
| class PrefetchDocumentManager; |
| class PrefetchNetworkContext; |
| class PrefetchResponseReader; |
| class PrefetchService; |
| class PrefetchServingPageMetricsContainer; |
| class PrefetchStreamingURLLoader; |
| class PreloadingAttempt; |
| class ProxyLookupClientImpl; |
| class RenderFrameHost; |
| class RenderFrameHostImpl; |
| |
| // Holds the relevant size information of the prefetched response. The struct is |
| // installed onto `PrefetchContainer`, and gets passed into |
| // `PrefetchFromStringURLLoader` to notify the associated `URLLoaderClient` of |
| // the actual size of the response, as `PrefetchFromStringURLLoader` is not |
| // aware of the prefetched request. |
| struct PrefetchResponseSizes { |
| int64_t encoded_data_length; |
| int64_t encoded_body_length; |
| int64_t decoded_body_length; |
| }; |
| |
| // This class contains the state for a request to prefetch a specific URL. |
| // |
| // A `PrefetchContainer` can have multiple `PrefetchContainer::SinglePrefetch`es |
| // and `PrefetchStreamingURLLoader`s to support redirects. Each |
| // `PrefetchContainer::SinglePrefetch` in `redirect_chain_` corresponds to a |
| // single redirect hop, while a single `PrefetchStreamingURLLoader` can receive |
| // multiple redirect hops unless network context switching is needed. |
| // |
| // For example: |
| // |
| // |PrefetchStreamingURLLoader A-----| |PrefetchStreamingURLLoader B ---------| |
| // HandleRedirect - HandleRedirect - HandleRedirect - ReceiveResponse-Finish |
| // |SinglePrefetch0| |SinglePrefetch1| |SinglePrefetch2| |SinglePrefetch3-----| |
| // |
| // While prefetching (see methods named like "ForCurrentPrefetch" or |
| // "ToPrefetch"), `SinglePrefetch`es and `PrefetchStreamingURLLoader`s (among |
| // other members) are added and filled. The steps for creating these objects and |
| // associating with each other span multiple classes/methods: |
| // |
| // 1. A new `PrefetchContainer::SinglePrefetch` and thus a new |
| // `PrefetchResponseReader` is created and added to `redirect_chain_`. |
| // This is done either in: |
| // - `PrefetchContainer` constructor [for an initial request], or |
| // - `AddRedirectHop()` [for a redirect]. |
| // |
| // 2. The new `PrefetchResponseReader` (created at Step 1, referenced as |
| // `GetResponseReaderForCurrentPrefetch()`) is associated with the |
| // `PrefetchStreamingURLLoader` to be used. |
| // This is done either in (see the indirect call sites of |
| // `PrefetchStreamingURLLoader::SetResponseReader()`): |
| // - `PrefetchService::StartSinglePrefetch()` [initial request] or |
| // - `PrefetchService::OnGotEligibilityForRedirect()` [redirect]. |
| // A new `PrefetchStreamingURLLoader` is also created if needed in |
| // `PrefetchService::MakePrefetchRequest()`. |
| class CONTENT_EXPORT PrefetchContainer { |
| public: |
| // Ctor used for renderer-initiated prefetch. |
| PrefetchContainer( |
| RenderFrameHostImpl& referring_render_frame_host, |
| const blink::DocumentToken& referring_document_token, |
| const GURL& url, |
| const PrefetchType& prefetch_type, |
| const blink::mojom::Referrer& referrer, |
| std::optional<net::HttpNoVarySearchData> no_vary_search_expected, |
| base::WeakPtr<PrefetchDocumentManager> prefetch_document_manager, |
| base::WeakPtr<PreloadingAttempt> attempt = nullptr); |
| |
| // Ctor used for browser-initiated prefetch. |
| // We can pass the referring origin of prefetches via `referring_origin` if |
| // necessary. When `std::nullopt` is passed, the referring origin will be |
| // opaque. |
| PrefetchContainer( |
| WebContents& referring_web_contents, |
| const GURL& url, |
| const PrefetchType& prefetch_type, |
| const blink::mojom::Referrer& referrer, |
| const std::optional<url::Origin>& referring_origin, |
| std::optional<net::HttpNoVarySearchData> no_vary_search_expected, |
| base::WeakPtr<PreloadingAttempt> attempt = nullptr); |
| |
| ~PrefetchContainer(); |
| |
| PrefetchContainer(const PrefetchContainer&) = delete; |
| PrefetchContainer& operator=(const PrefetchContainer&) = delete; |
| |
| // Defines the key to uniquely identify a prefetch. |
| // DocumentToken will be provided from initiating documents on |
| // renderer-initaited prefetches, while it will be nullopt when |
| // browser-initiated ones. |
| // Please see the doc on crbug.com/40946257 for more context. |
| // TODO(crbug.com/40942681): If kPrefetchBrowserInitiatedTriggers is enabeld, |
| // NetworkIsolationKey will be used instead of DocumentToken. |
| class CONTENT_EXPORT Key { |
| public: |
| Key() = delete; |
| Key(net::NetworkIsolationKey nik, GURL prefetch_url); |
| Key(std::optional<blink::DocumentToken> referring_document_token, |
| GURL prefetch_url); |
| ~Key(); |
| |
| Key(const Key&); |
| |
| bool operator==(const Key& rhs) const = default; |
| bool operator<(const Key& rhs) const { |
| if (referring_document_token_or_nik_ != |
| rhs.referring_document_token_or_nik_) { |
| return referring_document_token_or_nik_ < |
| rhs.referring_document_token_or_nik_; |
| } |
| return prefetch_url_ < rhs.prefetch_url_; |
| } |
| |
| const GURL& prefetch_url() const { return prefetch_url_; } |
| |
| Key WithNewUrl(const GURL& new_url) const { |
| return absl::visit([&](const auto& e) { return Key(e, new_url); }, |
| referring_document_token_or_nik_); |
| } |
| |
| bool NonUrlPartIsSame(const Key& other) const { |
| return referring_document_token_or_nik_ == |
| other.referring_document_token_or_nik_; |
| } |
| |
| private: |
| friend CONTENT_EXPORT std::ostream& operator<<(std::ostream& ostream, |
| const Key& prefetch_key); |
| |
| const absl::variant<std::optional<blink::DocumentToken>, |
| net::NetworkIsolationKey> |
| referring_document_token_or_nik_; |
| const GURL prefetch_url_; |
| }; |
| |
| const Key& GetPrefetchContainerKey() const { return key_; } |
| |
| // The ID of the RenderFrameHost that triggered the prefetch. |
| const GlobalRenderFrameHostId& GetReferringRenderFrameHostId() const { |
| return referring_render_frame_host_id_; |
| } |
| bool HasSameReferringURLForMetrics(const PrefetchContainer& other) const; |
| |
| // The initial URL that was requested to be prefetched. |
| const GURL& GetURL() const { return key_.prefetch_url(); } |
| |
| // The current URL being fetched. |
| GURL GetCurrentURL() const; |
| |
| // The previous URL, if this has been redirected. Invalid to call otherwise. |
| GURL GetPreviousURL() const; |
| |
| // The type of this prefetch. Controls how the prefetch is handled. |
| const PrefetchType& GetPrefetchType() const { return prefetch_type_; } |
| |
| // Whether this prefetch is initiated by renderer processes. |
| // Currently this is equivalent to whether the trigger type is Speculation |
| // Rules or not. |
| bool IsRendererInitiated() const; |
| |
| // The origin and that initiates the prefetch request. |
| const url::Origin& GetReferringOrigin() const { return referring_origin_; } |
| |
| // Whether or not an isolated network context is required to the next |
| // prefetch. |
| bool IsIsolatedNetworkContextRequiredForCurrentPrefetch() const; |
| |
| // Whether or not an isolated network context is required for the previous |
| // redirect hop of the given url. |
| bool IsIsolatedNetworkContextRequiredForPreviousRedirectHop() const; |
| |
| base::WeakPtr<PrefetchResponseReader> GetResponseReaderForCurrentPrefetch(); |
| |
| // Whether or not the prefetch proxy would be required to fetch the given url |
| // based on |prefetch_type_|. |
| bool IsProxyRequiredForURL(const GURL& url) const; |
| |
| const network::ResourceRequest* GetResourceRequest() const { |
| return resource_request_.get(); |
| } |
| void MakeResourceRequest(const net::HttpRequestHeaders& additional_headers); |
| |
| // Updates |referrer_| after a redirect. |
| void UpdateReferrer( |
| const GURL& new_referrer_url, |
| const network::mojom::ReferrerPolicy& new_referrer_policy); |
| |
| const std::optional<net::HttpNoVarySearchData>& GetNoVarySearchHint() const { |
| return no_vary_search_hint_; |
| } |
| |
| base::WeakPtr<PrefetchContainer> GetWeakPtr() { |
| return weak_method_factory_.GetWeakPtr(); |
| } |
| |
| // The status of the current prefetch. Note that |HasPrefetchStatus| will be |
| // initially false until |SetPrefetchStatus| is called. |SetPrefetchStatus| |
| // also sets |attempt_| PreloadingTriggeringOutcome and |
| // PreloadingFailureReason. It is only safe to call after |
| // `OnEligibilityCheckComplete`. |
| void SetPrefetchStatus(PrefetchStatus prefetch_status); |
| bool HasPrefetchStatus() const { return prefetch_status_.has_value(); } |
| PrefetchStatus GetPrefetchStatus() const; |
| |
| // The state enum of the current prefetch, to replace `PrefetchStatus`. |
| // https://crbug.com/1494771 |
| // Design doc for PrefetchContainer state transitions: |
| // https://docs.google.com/document/d/1dK4mAVoRrgTVTGdewthI_hA8AHirgXW8k6BmpK9gnBE/edit?usp=sharing |
| enum class LoadState { |
| // --- Phase 1. [Initial state] |
| kNotStarted, |
| |
| // --- Phase 2. The eligibility check for the initial request has completed |
| // and `PreloadingAttempt::SetEligibility()` has been called. |
| |
| // Found eligible. |
| kEligible, |
| |
| // [Final state] Found ineligible. `redirect_chain_[0].eligibility_` |
| // contains the reason for being ineligible. |
| kFailedIneligible, |
| |
| // --- Phase 3. PrefetchService::StartSinglePrefetch() has been called and |
| // the holdback check has completed. |
| |
| // [Final state] Not heldback. |
| // |
| // On this state, refer to `PrefetchResponseReader`s for detailed |
| // prefetching state and servability. |
| // |
| // Also, refer to `attempt_` for triggering outcome and failure reasons for |
| // metrics. |
| // `PreloadingAttempt::SetFailureReason()` can be only called on this state. |
| // Note that these states of `attempt_` don't directly affect |
| // `PrefetchResponseReader`'s servability. |
| // (e.g. `PrefetchResponseReader::GetServableState()` can be still |
| // `kServable` even if `attempt_` has a failure). |
| kStarted, |
| |
| // [Final state] Heldback due to `PreloadingAttempt::ShouldHoldback()`. |
| kFailedHeldback, |
| }; |
| void SetLoadState(LoadState prefetch_status); |
| LoadState GetLoadState() const; |
| |
| // Controls ownership of the |ProxyLookupClientImpl| used during the |
| // eligibility check. |
| void TakeProxyLookupClient( |
| std::unique_ptr<ProxyLookupClientImpl> proxy_lookup_client); |
| std::unique_ptr<ProxyLookupClientImpl> ReleaseProxyLookupClient(); |
| |
| // Whether or not the prefetch was determined to be eligibile. |
| void OnEligibilityCheckComplete(PreloadingEligibility eligibility); |
| bool IsInitialPrefetchEligible() const; |
| |
| // Adds a the new URL to |redirect_chain_|. |
| void AddRedirectHop(const net::RedirectInfo& redirect_info); |
| |
| // The length of the redirect chain for this prefetch. |
| size_t GetRedirectChainSize() const { return redirect_chain_.size(); } |
| |
| // Whether this prefetch is a decoy. Decoy prefetches will not store the |
| // response, and not serve any prefetched resources. |
| void SetIsDecoy(bool is_decoy) { is_decoy_ = is_decoy; } |
| bool IsDecoy() const { return is_decoy_; } |
| |
| // Whether this prefetch is potentially contaminated by cross-site state. |
| // If so, it may need special handling for privacy. |
| // See https://crbug.com/1439246. |
| bool IsCrossSiteContaminated() const { return is_cross_site_contaminated_; } |
| void MarkCrossSiteContaminated(); |
| |
| // Allows for |PrefetchCookieListener|s to be reigsitered for |
| // `GetCurrentSinglePrefetchToPrefetch()`. |
| void RegisterCookieListener(network::mojom::CookieManager* cookie_manager); |
| void StopAllCookieListeners(); |
| |
| // The network context used to make network requests for the next prefetch. |
| PrefetchNetworkContext* GetOrCreateNetworkContextForCurrentPrefetch(); |
| |
| // Closes idle connections for all elements in |network_contexts_|. |
| void CloseIdleConnections(); |
| |
| // Set the currently prefetching |PrefetchStreamingURLLoader|. |
| void SetStreamingURLLoader( |
| base::WeakPtr<PrefetchStreamingURLLoader> streaming_loader); |
| |
| // Returns the URL loader being used for prefetching the current redirect hop. |
| // This method should be used during prefetching and shouldn't be called for |
| // serving purpose. |
| const base::WeakPtr<PrefetchStreamingURLLoader>& GetStreamingURLLoader() |
| const; |
| |
| bool IsStreamingURLLoaderDeletionScheduledForTesting() const; |
| |
| // Returns the PrefetchResponseReader of the prefetched non-redirect response |
| // if already received its head. Ruturns nullptr otherwise. |
| const PrefetchResponseReader* GetNonRedirectResponseReader() const; |
| // Returns the head of the prefetched non-redirect response if already |
| // received. Ruturns nullptr otherwise. |
| const network::mojom::URLResponseHead* GetNonRedirectHead() const; |
| |
| // Clears |streaming_loader_| and cancels its loading, if any of its |
| // corresponding `PrefetchResponseReader` does NOT start serving. Currently |
| // this itself doesn't mark `this` as failed and thus can leave `this` |
| // stalled. Therefore, call this method only if `this` can be no longer used |
| // for serving, e.g. on the destructor or when |
| // `HaveDefaultContextCookiesChanged()` is true. |
| // TODO(crbug.com/40064891): For callsites outside the destructor, remove the |
| // call or mark `this` as failed, because the current behavior (== existing |
| // behavior, previously as `ResetAllStreamingURLLoaders()`) might potentially |
| // cause issues when there are multiple navigations using `this` concurrently. |
| void CancelStreamingURLLoaderIfNotServing(); |
| |
| // The |PrefetchDocumentManager| that requested |this|. |
| PrefetchDocumentManager* GetPrefetchDocumentManager() const; |
| |
| // Called when |PrefetchService::GetPrefetchToServe| and |
| // |PrefetchService::ReturnPrefetchToServe| with |this|. |
| void OnGetPrefetchToServe(bool blocked_until_head); |
| void OnReturnPrefetchToServe(bool served, const GURL& navigated_url); |
| |
| // Returns whether or not this prefetch has been considered to serve for a |
| // navigation in the past. If it has, then it shouldn't be used for any future |
| // navigations. |
| bool HasPrefetchBeenConsideredToServe() const; |
| |
| // Called when |PrefetchService::OnPrefetchComplete| is called for the |
| // prefetch. This happens when |loader_| fully downloads the requested |
| // resource. |
| void OnPrefetchComplete( |
| const network::URLLoaderCompletionStatus& completion_status); |
| |
| enum class ServableState { |
| // Not servable nor should block until head received. |
| kNotServable, |
| |
| // Servable. |
| kServable, |
| |
| // |PrefetchService| should block until the head of |this| is |
| // received on a navigation to a matching URL. |
| kShouldBlockUntilHeadReceived, |
| }; |
| |
| // Note: Even if this returns `kServable`, `CreateRequestHandler()` can still |
| // fail (returning null handler) due to final checks. See also the comment for |
| // `PrefetchResponseReader::CreateRequestHandler()`. |
| ServableState GetServableState(base::TimeDelta cacheable_duration) const; |
| |
| // Starts blocking `PrefetchMatchResolver` until non-redirect response header |
| // is determined or timeouted. `on_maybe_determined_head_callback` will be |
| // called when |
| // |
| // - `PrefetchStreamingURLLoader` succeeded/failed to fetch non-redirect |
| // response header. |
| // - The argument `timeout` is positive and timeouted. |
| // - `PrefetchContainer` dtor if `kPrefetchUnblockOnCancel` enabled. |
| void StartBlockUntilHead(base::OnceCallback<void(PrefetchContainer&)> |
| on_maybe_determined_head_callback, |
| base::TimeDelta timeout); |
| // Called when non-redirect response header is determined, i.e. |
| // `GetNonRedirectHead()` becomes immutable. |
| // |
| // This method must be called at most once in the lifecycle of |
| // `PrefetchContainer`. |
| void OnDeterminedHead(); |
| // Unblocks waiting `PrefetchMatchResolver`. |
| // |
| // This method can be called multiple times. |
| void UnblockPrefetchMatchResolver(); |
| |
| void StartTimeoutTimer(base::TimeDelta timeout, |
| base::OnceClosure on_timeout_callback); |
| |
| // Returns the time between the prefetch request was sent and the time the |
| // response headers were received. Not set if the prefetch request hasn't been |
| // sent or the response headers haven't arrived. |
| std::optional<base::TimeDelta> GetPrefetchHeaderLatency() const { |
| return header_latency_; |
| } |
| |
| // Allow for the serving page to metrics when changes to the prefetch occur. |
| void SetServingPageMetrics(base::WeakPtr<PrefetchServingPageMetricsContainer> |
| serving_page_metrics_container); |
| void UpdateServingPageMetrics(); |
| |
| // Returns request id to be used by DevTools and test utilities. |
| const std::string& RequestId() const { return request_id_; } |
| |
| // Sets DevTools observer |
| void SetDevToolsObserver( |
| base::WeakPtr<SpeculationHostDevToolsObserver> devtools_observer) { |
| devtools_observer_ = std::move(devtools_observer); |
| } |
| |
| // Returns DevTool observer |
| const base::WeakPtr<SpeculationHostDevToolsObserver>& GetDevToolsObserver() |
| const { |
| return devtools_observer_; |
| } |
| |
| const std::optional<PrefetchResponseSizes>& GetPrefetchResponseSizes() const { |
| return prefetch_response_sizes_; |
| } |
| |
| bool HasPreloadingAttempt() { return !!attempt_; } |
| base::WeakPtr<PreloadingAttempt> preloading_attempt() { return attempt_; } |
| |
| // Simulates a prefetch container that has started its request. It sets the |
| //`attempt_` to the correct state: `PreloadingEligibility::kEligible`, |
| // `PreloadingHoldbackStatus::kAllowed` and |
| // `PreloadingTriggeringOutcome::kReady`. |
| void SimulateAttemptAtRequestStartForTest(); |
| // Simulates a prefetch container that reaches the interceptor. Similar to |
| // |SimulateAttemptAtRequestStartForTest| but also marks the prefetch as |
| // completed. |
| void SimulateAttemptAtInterceptorForTest(); |
| void DisablePrecogLoggingForTest() { attempt_ = nullptr; } |
| |
| const std::optional<net::HttpNoVarySearchData>& GetNoVarySearchData() const { |
| return no_vary_search_data_; |
| } |
| // Sets `no_vary_search_data_` from `GetHead()`. Exposed for tests. |
| // RenderFrameHost is being used on no_vary_search::ProcessHead() to put |
| // message to DevTools console and can be null. |
| void MaybeSetNoVarySearchData(RenderFrameHost* rfh); |
| |
| // Called when cookies changes are detected via |
| // `HaveDefaultContextCookiesChanged()`, either for `this` or other |
| // `PrefetchContainer`s under the same `PrefetchMatchResolver`. |
| void OnCookiesChanged(); |
| |
| class SinglePrefetch; |
| |
| // A `Reader` represents the current state of serving. |
| // The `Reader` methods all operate on the currently *serving* |
| // `SinglePrefetch`, which is the element in |redirect_chain_| at index |
| // |index_redirect_chain_to_serve_|. |
| // |
| // This works like `base::WeakPtr<PrefetchContainer>` plus additional states, |
| // so check that the reader is valid (e.g. `if (reader)`) before calling other |
| // methods (except for `Clone()`). |
| // |
| // TODO(crbug.com/40064891): Allow multiple Readers for a PrefetchContainer. |
| // This might need ownership/lifetime changes of `Reader` and further cleaning |
| // up the dependencies between `PrefetchContainer` and `Reader`. |
| class CONTENT_EXPORT Reader final { |
| public: |
| Reader(); |
| |
| Reader(base::WeakPtr<PrefetchContainer> prefetch_container, |
| size_t index_redirect_chain_to_serve); |
| |
| Reader(const Reader&) = delete; |
| Reader& operator=(const Reader&) = delete; |
| |
| Reader(Reader&&); |
| Reader& operator=(Reader&&); |
| |
| ~Reader(); |
| |
| PrefetchContainer* GetPrefetchContainer() const { |
| return prefetch_container_.get(); |
| } |
| Reader Clone() const; |
| |
| // Returns true if `this` is valid. |
| // Do not call methods below if false. |
| explicit operator bool() const { return GetPrefetchContainer(); } |
| |
| // Methods redirecting to `prefetch_container_`. |
| PrefetchContainer::ServableState GetServableState( |
| base::TimeDelta cacheable_duration) const; |
| bool HasPrefetchStatus() const; |
| PrefetchStatus GetPrefetchStatus() const; |
| |
| // Returns whether the Reader reached the end. If true, the methods below |
| // shouldn't be called, because the current `SinglePrefetch` doesn't exist. |
| bool IsEnd() const; |
| |
| // Whether or not an isolated network context is required to serve. |
| bool IsIsolatedNetworkContextRequiredToServe() const; |
| |
| PrefetchNetworkContext* GetCurrentNetworkContextToServe() const; |
| |
| bool HaveDefaultContextCookiesChanged() const; |
| |
| // Before a prefetch can be served, any cookies added to the isolated |
| // network context must be copied over to the default network context. These |
| // functions are used to check and update the status of this process, as |
| // well as record metrics about how long this process takes. |
| bool HasIsolatedCookieCopyStarted() const; |
| bool IsIsolatedCookieCopyInProgress() const; |
| void OnIsolatedCookieCopyStart() const; |
| void OnIsolatedCookiesReadCompleteAndWriteStart() const; |
| void OnIsolatedCookieCopyComplete() const; |
| void OnInterceptorCheckCookieCopy() const; |
| void SetOnCookieCopyCompleteCallback(base::OnceClosure callback) const; |
| |
| // Called with the result of the probe. If the probing feature is enabled, |
| // then a probe must complete successfully before the prefetch can be |
| // served. |
| void OnPrefetchProbeResult(PrefetchProbeResult probe_result) const; |
| |
| // Checks if the given URL matches the the URL that can be served next. |
| bool DoesCurrentURLToServeMatch(const GURL& url) const; |
| |
| // Returns the URL that can be served next. |
| const GURL& GetCurrentURLToServe() const; |
| |
| // Gets the current PrefetchResponseReader. |
| base::WeakPtr<PrefetchResponseReader> |
| GetCurrentResponseReaderToServeForTesting(); |
| |
| // Called when one element of |redirect_chain_| is served and the next |
| // element can now be served. |
| void AdvanceCurrentURLToServe() { index_redirect_chain_to_serve_++; } |
| |
| // Returns the `SinglePrefetch` to be served next. |
| const SinglePrefetch& GetCurrentSinglePrefetchToServe() const; |
| |
| // See the comment for `PrefetchResponseReader::CreateRequestHandler()`. |
| PrefetchRequestHandler CreateRequestHandler(); |
| |
| // See the corresponding functions on `PrefetchResponseReader`. |
| // These apply to the current `SinglePrefetch` (and so, may change as the |
| // prefetch advances through a redirect change). |
| bool VariesOnCookieIndices() const; |
| bool MatchesCookieIndices( |
| base::span<const std::pair<std::string, std::string>> cookies) const; |
| |
| private: |
| base::WeakPtr<PrefetchContainer> prefetch_container_; |
| |
| // The index of the element in |prefetch_container_.redirect_chain_| that |
| // can be served. |
| size_t index_redirect_chain_to_serve_ = 0; |
| }; |
| |
| Reader CreateReader(); |
| |
| bool IsExactMatch(const GURL& url) const; |
| bool IsNoVarySearchHeaderMatch(const GURL& url) const; |
| |
| bool is_in_dtor() const { return is_in_dtor_; } |
| |
| protected: |
| friend class PrefetchContainerTestBase; |
| |
| // Updates metrics based on the result of the prefetch request. |
| void UpdatePrefetchRequestMetrics( |
| const std::optional<network::URLLoaderCompletionStatus>& |
| completion_status, |
| const network::mojom::URLResponseHead* head); |
| |
| private: |
| PrefetchContainer( |
| const GlobalRenderFrameHostId& referring_render_frame_host_id, |
| const url::Origin& referring_origin, |
| const std::optional<size_t>& referring_url_hash, |
| const PrefetchContainer::Key& key, |
| const PrefetchType& prefetch_type, |
| const blink::mojom::Referrer& referrer, |
| std::optional<net::HttpNoVarySearchData> no_vary_search_hint, |
| base::WeakPtr<PrefetchDocumentManager> prefetch_document_manager, |
| base::WeakPtr<BrowserContext> browser_context, |
| ukm::SourceId ukm_source_id, |
| base::WeakPtr<PreloadingAttempt> attempt, |
| std::optional<base::UnguessableToken> initiator_devtools_navigation_token, |
| bool is_javascript_enabed); |
| |
| // Update |prefetch_status_| and report prefetch status to |
| // DevTools without updating TriggeringOutcome. |
| void SetPrefetchStatusWithoutUpdatingTriggeringOutcome( |
| PrefetchStatus prefetch_status); |
| |
| // Add client hints headers to a request bound for |origin|. |
| void AddClientHintsHeaders(const url::Origin& origin, |
| net::HttpRequestHeaders* request_headers); |
| // Add X-Client-Data request header to a request. |
| void AddXClientDataHeader(network::ResourceRequest& request); |
| |
| // Returns the `SinglePrefetch` to be prefetched next. This is the last |
| // element in `redirect_chain_`, because, during prefetching from the network, |
| // we push back `SinglePrefetch`s to `redirect_chain_` and access the latest |
| // redirect hop. |
| SinglePrefetch& GetCurrentSinglePrefetchToPrefetch() const; |
| |
| // Returns the `SinglePrefetch` for the redirect leg before |
| // `GetCurrentSinglePrefetchToPrefetch()`. This must be called only if `this` |
| // has redirect(s). |
| const SinglePrefetch& GetPreviousSinglePrefetchToPrefetch() const; |
| |
| // Returns "Sec-Purpose" header value for a prefetch request to `request_url`. |
| const char* GetSecPurposeHeaderValue(const GURL& request_url) const; |
| |
| // The ID of the RenderFrameHost/Document that triggered the prefetch. |
| // This will be empty when browser-initiated prefetch. |
| const GlobalRenderFrameHostId referring_render_frame_host_id_; |
| |
| // The origin and URL that initiates the prefetch request. |
| // For renderer-initiated prefetch, this is calculated by referring |
| // RenderFrameHost's LastCommittedOrigin. For browser-initiated prefetch, this |
| // is sometimes explicitly passed via ctor, otherwise opaque origin. |
| const url::Origin referring_origin_; |
| // Used by metrics for equality checks, only works for renderer-initiated |
| // triggers. |
| const std::optional<size_t> referring_url_hash_; |
| |
| // The key used to match this PrefetchContainer, including the URL that was |
| // requested to prefetch. |
| const PrefetchContainer::Key key_; |
| |
| // The type of this prefetch. This controls some specific details about how |
| // the prefetch is handled, including whether an isolated network context or |
| // the default network context is used to perform the prefetch, whether or |
| // not the preftch proxy is used, and whether or not subresources are |
| // prefetched. |
| PrefetchType prefetch_type_; |
| |
| // The referrer to use for the request. |
| blink::mojom::Referrer referrer_; |
| |
| // Information about the current prefetch request. Updated when a redirect is |
| // encountered, whether or not the direct can be processed by the same URL |
| // loader or requires the instantiation of a new loader. |
| std::unique_ptr<network::ResourceRequest> resource_request_; |
| |
| // The No-Vary-Search response data, parsed from the actual response header |
| // (`GetHead()`). |
| // Unless this is set, `no_vary_search` helpers don't perform No-Vary-Search |
| // matching for `this`, even if `GetHead()` has No-Vary-Search headers. |
| std::optional<net::HttpNoVarySearchData> no_vary_search_data_; |
| |
| // The No-Vary-Search hint of the prefetch, which is specified by the |
| // speculation rules and can be different from actual `no_vary_search_data_`. |
| const std::optional<net::HttpNoVarySearchData> no_vary_search_hint_; |
| |
| // The |PrefetchDocumentManager| that requested |this|. |
| // This will be nullptr when the prefetch is initiated by browser. |
| base::WeakPtr<PrefetchDocumentManager> prefetch_document_manager_; |
| |
| // The |BrowserContext| in which this is being run. |
| base::WeakPtr<BrowserContext> browser_context_; |
| |
| // The current status, if any, of the prefetch. |
| // TODO(crbug.com/40075414): Use `load_state_` instead for non-metrics |
| // purpose. |
| std::optional<PrefetchStatus> prefetch_status_; |
| |
| // The current status of the prefetch. |
| LoadState load_state_ = LoadState::kNotStarted; |
| |
| // Looks up the proxy settings in the default network context all URLs in |
| // |redirect_chain_|. |
| std::unique_ptr<ProxyLookupClientImpl> proxy_lookup_client_; |
| |
| // Whether this prefetch is a decoy or not. If the prefetch is a decoy then |
| // any prefetched resources will not be served. |
| bool is_decoy_ = false; |
| |
| // The redirect chain resulting from prefetching |GetURL()|. |
| std::vector<std::unique_ptr<SinglePrefetch>> redirect_chain_; |
| |
| // The network contexts used for this prefetch. They key corresponds to the |
| // |is_isolated_network_context_required| param of the |
| // |PrefetchNetworkContext|. |
| std::map<bool, std::unique_ptr<PrefetchNetworkContext>> network_contexts_; |
| |
| // The currently prefetching streaming URL loader, prefetching the last |
| // element of `redirect_chain_`. Multiple streaming URL loaders can be used in |
| // the event a redirect causes a change in the network context, but here only |
| // one (=last) `PrefetchStreamingURLLoader` is kept here, because when |
| // switching the network context and `PrefetchStreamingURLLoader`s, the old |
| // `PrefetchStreamingURLLoader` is scheduled for deletion and then the new |
| // `PrefetchStreamingURLLoader` is set here. |
| base::WeakPtr<PrefetchStreamingURLLoader> streaming_loader_; |
| |
| ukm::SourceId ukm_source_id_; |
| |
| // The sizes information of the prefetched response. |
| std::optional<PrefetchResponseSizes> prefetch_response_sizes_; |
| |
| // The amount of time it took for the prefetch to complete. |
| std::optional<base::TimeDelta> fetch_duration_; |
| |
| // The amount of time it took for the headers to be received. |
| std::optional<base::TimeDelta> header_latency_; |
| |
| // Whether or not a navigation to this prefetch occurred. |
| bool navigated_to_ = false; |
| |
| // The result of probe when checked on navigation. |
| std::optional<PrefetchProbeResult> probe_result_; |
| |
| // If set, this prefetch's timing might be affected by cross-site state, so |
| // further processing may need to affect how the response is processed to make |
| // inferences about this logic less practical. |
| bool is_cross_site_contaminated_ = false; |
| |
| // Reference to metrics related to the page that considered using this |
| // prefetch. |
| base::WeakPtr<PrefetchServingPageMetricsContainer> |
| serving_page_metrics_container_; |
| |
| // Request identifier used by DevTools and test utilities. |
| std::string request_id_; |
| |
| // Weak pointer to DevTools observer |
| base::WeakPtr<SpeculationHostDevToolsObserver> devtools_observer_; |
| |
| // `PreloadingAttempt` is used to track the lifecycle of the preloading event, |
| // and reports various statuses to UKM dashboard. It is initialised along with |
| // `this`, and destroyed when `WCO::DidFinishNavigation` is fired. |
| // `attempt_`'s eligibility is set in `OnEligibilityCheckComplete`, and its |
| // holdback status, triggering outcome and failure reason are set in |
| // `SetPrefetchStatus`. |
| base::WeakPtr<PreloadingAttempt> attempt_; |
| |
| // A DevTools token used to identify initiator document if the prefetch is |
| // triggered by SpeculationRules. |
| std::optional<base::UnguessableToken> initiator_devtools_navigation_token_ = |
| std::nullopt; |
| |
| // The time at which |PrefetchService| started blocking until the head of |
| // |this| was received. |
| std::optional<base::TimeTicks> blocked_until_head_start_time_; |
| |
| // A timer used to limit the maximum amount of time that a navigation can be |
| // blocked waiting for the head of this prefetch to be received. |
| std::unique_ptr<base::OneShotTimer> block_until_head_timer_; |
| |
| // Callback for non-blocking call `StartBlockUntilHead()`. |
| // |
| // TODO(crbug.com/353490734): Remove it. |
| base::OnceCallback<void(PrefetchContainer&)> |
| on_maybe_determined_head_callback_; |
| |
| std::unique_ptr<base::OneShotTimer> timeout_timer_; |
| |
| // Whether JavaScript is on in this contents (or was, when this prefetch |
| // started). This affects Client Hints behavior. Per-origin settings are |
| // handled later, according to |
| // |ClientHintsControllerDelegate::IsJavaScriptAllowed|. |
| bool is_javascript_enabled_ = false; |
| |
| // True iff the destructor was called. |
| bool is_in_dtor_ = false; |
| |
| base::WeakPtrFactory<PrefetchContainer> weak_method_factory_{this}; |
| }; |
| |
| // For debug logs. |
| CONTENT_EXPORT std::ostream& operator<<( |
| std::ostream& ostream, |
| const PrefetchContainer& prefetch_container); |
| |
| CONTENT_EXPORT std::ostream& operator<<( |
| std::ostream& ostream, |
| const PrefetchContainer::Key& prefetch_key); |
| |
| CONTENT_EXPORT std::ostream& operator<<( |
| std::ostream& ostream, |
| PrefetchContainer::ServableState servable_state); |
| |
| CONTENT_EXPORT std::ostream& operator<<(std::ostream& ostream, |
| PrefetchContainer::LoadState state); |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_CONTAINER_H_ |