#ifndef CONTENT_BROWSER_PRELOADING_PRERENDER_PRERENDER_HOST_H_
#define CONTENT_BROWSER_PRELOADING_PRERENDER_PRERENDER_HOST_H_
#include <memory>
#include <optional>
#include <string>
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/types/pass_key.h"
#include "content/browser/preloading/preload_serving_metrics_holder.h"
#include "content/browser/preloading/prerender/prerender_attributes.h"
#include "content/browser/preloading/prerender/prerender_final_status.h"
#include "content/browser/preloading/speculation_rules/speculation_rules_tags.h"
#include "content/browser/prerender_host_id.h"
#include "content/browser/renderer_host/frame_tree.h"
#include "content/browser/renderer_host/navigation_controller_delegate.h"
#include "content/common/content_export.h"
#include "content/public/browser/render_frame_host.h"
#include "net/http/http_no_vary_search_data.h"
#include "third_party/blink/public/mojom/navigation/navigation_params.mojom-forward.h"
#include "url/gurl.h"
namespace base {
class TimeDelta;
}
namespace blink {
class EnabledClientHints;
}
namespace network::mojom {
enum class WebClientHintsType;
}
namespace content {
class DevToolsPrerenderAttempt;
class FrameTreeNode;
class NavigationHandle;
class PrerenderCancellationReason;
class PrerenderHostRegistry;
class RenderFrameHostImpl;
class WebContentsImpl;
class CONTENT_EXPORT PrerenderHost {
public:
enum class ActivationNavigationParamsMatch {
kOk = 0,
kInitiatorFrameToken = 1,
kHttpRequestHeader = 2,
kCacheLoadFlags = 3,
kLoadFlags = 4,
kSkipServiceWorker = 5,
kMixedContentContextType = 6,
kIsFormSubmission = 7,
kSearchableFormUrl = 8,
kSearchableFormEncoding = 9,
kTrustTokenParams = 10,
kWebBundleToken = 11,
kRequestContextType = 12,
kImpressionHasValue = 13,
kInitiatorOrigin = 14,
kTransition = 15,
kNavigationType = 16,
kBaseUrlForDataUrl = 17,
kPostData = 18,
kStartedFromContextMenu = 19,
kInitiatorOriginTrialFeature = 20,
kHrefTranslate = 21,
kIsHistoryNavigationInNewChildFrame = 22,
kRequestDestination = 24,
kMaxValue = kRequestDestination,
};
enum class WaitingForHeadersStartedReason { kWithoutTimeout, kWithTimeout };
enum class WaitingForHeadersFinishedReason {
kNoVarySearchHeaderNotReceived = 1,
kNoVarySearchHeaderParseFailed = 2,
kHostDestroyed = 3,
kTimeoutElapsed = 4,
kMaybeNavigationCancelled = 5,
kNoVarySearchHeaderReceivedAndMatched = 6,
kNoVarySearchHeaderReceivedButNotMatched = 7,
kNoVarySearchHeaderReceivedButDefaultValue = 8,
kUnknownFailure = 9,
kPrerenderNavigationFailed = 10,
kMaxValue = kPrerenderNavigationFailed,
};
class Observer : public base::CheckedObserver {
public:
virtual void OnActivated() {}
virtual void OnHeadersReceived(NavigationHandle& navigation_handle) {}
virtual void OnWaitingForHeadersStarted(
NavigationHandle& navigation_handle,
WaitingForHeadersStartedReason reason) {}
virtual void OnWaitingForHeadersFinished(
WaitingForHeadersFinishedReason reason) {}
virtual void OnFailed(PrerenderFinalStatus status) {}
virtual void OnHostDestroyed(PrerenderFinalStatus status) {}
virtual void OnHostReused() {}
};
static PrerenderHost* GetFromFrameTreeNodeIfPrerendering(
FrameTreeNode& frame_tree_node);
static PrerenderHost& GetFromFrameTreeNode(FrameTreeNode& frame_tree_node);
static PrerenderHost& GetFromFrameTree(FrameTree* frame_tree);
static bool IsActivationHeaderMatch(
const net::HttpRequestHeaders& potential_activation_headers,
const net::HttpRequestHeaders& prerender_headers,
PrerenderCancellationReason& reaosn);
static bool AreHttpRequestHeadersCompatible(
const std::string& potential_activation_headers_str,
#if BUILDFLAG(IS_ANDROID)
const std::string& potential_activation_additional_headers_str,
#endif
const std::string& prerender_headers_str,
PreloadingTriggerType trigger_type,
const std::string& histogram_suffix,
bool allow_x_header_mismatch,
PrerenderCancellationReason& reason);
static void SetHostCreationCallbackForTesting(
base::OnceCallback<void(FrameTreeNodeId host_id)> callback);
PrerenderHost(std::unique_ptr<PrerenderHost> reuse_host,
const PrerenderAttributes& attributes,
WebContentsImpl& web_contents,
base::WeakPtr<PreloadingAttempt> attempt,
std::unique_ptr<DevToolsPrerenderAttempt> devtools_attempt);
~PrerenderHost();
PrerenderHost(const PrerenderHost&) = delete;
PrerenderHost& operator=(const PrerenderHost&) = delete;
PrerenderHost(PrerenderHost&&) = delete;
PrerenderHost& operator=(PrerenderHost&&) = delete;
NavigationControllerImpl& GetNavigationController() {
return GetFrameTree()->controller();
}
bool StartPrerendering();
void DidStartNavigation(NavigationHandle* navigation_handle);
void DidFinishNavigation(NavigationHandle* navigation_handle);
void ReadyToCommitNavigation(NavigationHandle* navigation_handle);
std::unique_ptr<StoredPage> Activate(NavigationRequest& navigation_request);
bool AreInitialPrerenderNavigationParamsCompatibleWithNavigation(
NavigationRequest& navigation_request,
PrerenderCancellationReason& reason);
bool IsFramePolicyCompatibleWithPrimaryFrameTree();
RenderFrameHostImpl* GetPrerenderedMainFrameHost();
FrameTree& GetPrerenderFrameTree();
void RecordFailedFinalStatus(base::PassKey<PrerenderHostRegistry>,
const PrerenderCancellationReason& reason);
void RecordActivation(NavigationRequest& navigation_request);
enum class LoadingOutcome {
kLoadingCompleted,
kPrerenderingCancelled,
};
LoadingOutcome WaitForLoadStopForTesting();
const GURL& GetInitialUrl() const;
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
void SetInitialNavigation(NavigationRequest* navigation);
std::optional<int64_t> GetInitialNavigationId() const;
std::optional<UrlMatchType> IsUrlMatch(const GURL& url) const;
bool IsNoVarySearchHintUrlMatch(const GURL& url) const;
bool IsUrlSameOrigin(const GURL& url) const;
bool IsUrlSameSite(const GURL& url) const;
bool IsReusable() const { return attributes_.allow_reuse; }
void OnAcceptClientHintChanged(
const url::Origin& origin,
const std::vector<network::mojom::WebClientHintsType>& client_hints_type);
void GetAllowedClientHintsOnPage(
const url::Origin& origin,
blink::EnabledClientHints* client_hints) const;
std::string GetHistogramSuffix() const;
std::optional<url::Origin> initiator_origin() const {
return attributes_.initiator_origin;
}
std::optional<base::UnguessableToken> initiator_devtools_navigation_token()
const {
return attributes_.initiator_devtools_navigation_token;
}
bool IsBrowserInitiated() { return attributes_.IsBrowserInitiated(); }
PrerenderHostId prerender_host_id() const { return prerender_host_id_; }
FrameTreeNodeId frame_tree_node_id() const { return frame_tree_node_id_; }
base::WeakPtr<WebContents> initiator_web_contents() {
return attributes_.initiator_web_contents;
}
FrameTreeNodeId initiator_frame_tree_node_id() const {
return attributes_.initiator_frame_tree_node_id;
}
int initiator_ukm_id() const { return attributes_.initiator_ukm_id; }
bool is_ready_for_activation() const { return is_ready_for_activation_; }
PreloadingTriggerType trigger_type() const {
return attributes_.trigger_type;
}
const std::string& embedder_histogram_suffix() const {
return attributes_.embedder_histogram_suffix;
}
std::optional<blink::mojom::SpeculationEagerness> eagerness() const {
return attributes_.GetEagerness();
}
base::WeakPtr<PreloadingAttempt> preloading_attempt() { return attempt_; }
const std::optional<net::HttpNoVarySearchData>& no_vary_search() const {
return no_vary_search_;
}
const std::optional<network::mojom::NoVarySearchParseError>&
no_vary_search_parse_error() const {
return no_vary_search_parse_error_;
}
const std::optional<net::HttpNoVarySearchData>& no_vary_search_hint() const {
return attributes_.no_vary_search_hint;
}
bool should_warm_up_compositor() const {
return attributes_.should_warm_up_compositor;
}
bool should_prepare_paint_tree() const {
return attributes_.should_prepare_paint_tree;
}
bool should_pause_javascript_execution() const {
return attributes_.prerender_action_type ==
blink::mojom::SpeculationAction::kPrerenderUntilScript;
}
blink::mojom::SpeculationAction speculation_action() const {
return attributes_.prerender_action_type;
}
bool IsInitialNavigation(const NavigationRequest& navigation_request) const;
bool were_headers_received() const { return were_headers_received_; }
bool host_reused() const { return host_reused_; }
base::TimeDelta WaitUntilHeadTimeout();
void OnWaitingForHeadersStarted(NavigationHandle& navigation_handle,
WaitingForHeadersStartedReason reason);
void OnWaitingForHeadersFinished(WaitingForHeadersFinishedReason reason);
bool AllowCrossOriginSubframeNavigation() const {
return allow_cross_origin_subframe_navigation_;
}
bool ShouldAbortNavigationBecausePrefetchUnavailable() const;
void AddAdditionalRequestHeaders(net::HttpRequestHeaders& headers,
FrameTreeNode& navigating_frame_tree_node);
void NotifyReused();
void OnWillBeCancelled(const PrerenderCancellationReason& reason);
const PreloadPipelineInfo& preload_pipeline_info() const {
return *attributes_.preload_pipeline_info.get();
}
bool IsInitiatorOverridingUserAgent();
base::WeakPtr<PrerenderHost> GetWeakPtr();
private:
class PrerenderFrameTreeDelegate : public FrameTree::Delegate,
public NavigationControllerDelegate {
public:
PrerenderFrameTreeDelegate(BrowserContext* browser_context,
WebContentsImpl& web_contents,
PrerenderHost& prerender_host);
void LoadingStateChanged(LoadingState new_state) override {}
void DidStartLoading(FrameTreeNode* frame_tree_node) override {}
void DidStopLoading() override;
bool IsHidden() override;
FrameTree* LoadingTree() override;
FrameTreeNodeId GetOuterDelegateFrameTreeNodeId() override;
RenderFrameHostImpl* GetProspectiveOuterDocument() override;
void SetFocusedFrame(FrameTreeNode* node,
SiteInstanceGroup* source) override;
FrameTree* GetOwnedDocumentPictureInPictureFrameTree() override;
FrameTree* GetDocumentPictureInPictureOpenerFrameTree() override;
bool OnRenderFrameProxyVisibilityChanged(
RenderFrameProxyHost* render_frame_proxy_host,
blink::mojom::FrameVisibility visibility) override;
void NotifyNavigationStateChangedFromController(
InvalidateTypes changed_flags) override {}
void NotifyBeforeFormRepostWarningShow() override {}
void NotifyNavigationEntryCommitted(
const LoadCommittedDetails& load_details) override {}
void NotifyNavigationEntryChanged(
const EntryChangedDetails& change_details) override {}
void NotifyNavigationListPruned(
const PrunedDetails& pruned_details) override {}
void NotifyNavigationEntriesDeleted() override {}
void ActivateAndShowRepostFormWarningDialog() override;
bool ShouldPreserveAbortedURLs() override;
void UpdateOverridingUserAgent() override {}
#if BUILDFLAG(ARKWEB_NETWORK_LOAD)
std::string NotifyNavigationRewriteUrl(
const std::string& original_url,
const std::string& referrer,
int transition_type,
bool is_key_request) override { return ""; }
#endif
#if BUILDFLAG(IS_ANDROID)
scoped_refptr<viz::RasterContextProvider> GetRasterContextProvider()
override;
gfx::ColorSpace GetOutputColorSpace(gfx::ContentColorUsage color_usage,
bool needs_alpha) override;
#endif
LoadingOutcome WaitForLoadStopForTesting();
~PrerenderFrameTreeDelegate() override;
private:
friend class PrerenderHost;
raw_ref<PrerenderHost> prerender_host_;
std::unique_ptr<FrameTree> frame_tree_;
base::OnceCallback<void(PrerenderHost::LoadingOutcome)>
on_wait_loading_finished_;
};
FrameTree* GetFrameTree() { return frame_tree_delegate_->frame_tree_.get(); }
void RecordFailedFinalStatusImpl(const PrerenderCancellationReason& reason);
void Cancel(PrerenderFinalStatus status);
void SetTriggeringOutcome(PreloadingTriggeringOutcome outcome);
void SetFailureReason(const PrerenderCancellationReason& reason);
ActivationNavigationParamsMatch
AreBeginNavigationParamsCompatibleWithNavigation(
const GURL& potential_activation_url,
const blink::mojom::BeginNavigationParams& potential_activation,
bool allow_partial_mismatch,
PrerenderCancellationReason& reason);
ActivationNavigationParamsMatch
AreCommonNavigationParamsCompatibleWithNavigation(
const blink::mojom::CommonNavigationParams& potential_activation,
bool allow_partial_mismatch);
void MaybeSetNoVarySearch(network::mojom::NoVarySearchWithParseError&
no_vary_search_with_parse_error);
const PrerenderAttributes attributes_;
const PrerenderHostId prerender_host_id_;
bool is_ready_for_activation_ = false;
FrameTreeNodeId frame_tree_node_id_;
std::optional<PrerenderFinalStatus> final_status_;
const std::string metric_suffix_;
base::ObserverList<Observer> observers_;
base::WeakPtr<PreloadingAttempt> attempt_;
std::unique_ptr<DevToolsPrerenderAttempt> devtools_attempt_;
blink::mojom::BeginNavigationParamsPtr begin_params_;
blink::mojom::CommonNavigationParamsPtr common_params_;
base::flat_map<url::Origin, std::vector<network::mojom::WebClientHintsType>>
client_hints_type_;
std::optional<int64_t> initial_navigation_id_;
const raw_ref<WebContentsImpl> web_contents_;
std::unique_ptr<PrerenderFrameTreeDelegate> frame_tree_delegate_;
std::optional<net::HttpNoVarySearchData> no_vary_search_;
std::optional<network::mojom::NoVarySearchParseError>
no_vary_search_parse_error_;
bool were_headers_received_ = false;
const bool host_reused_ = false;
std::unique_ptr<PreloadServingMetrics>
prerender_initial_preload_serving_metrics_;
bool allow_cross_origin_subframe_navigation_ = false;
base::WeakPtrFactory<PrerenderHost> weak_factory_{this};
};
}
#endif