| // Copyright 2020 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_PRERENDER_PRERENDER_HOST_H_ |
| #define CONTENT_BROWSER_PRELOADING_PRERENDER_PRERENDER_HOST_H_ |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/memory/raw_ref.h" |
| #include "base/observer_list.h" |
| #include "base/observer_list_types.h" |
| #include "base/types/pass_key.h" |
| #include "content/browser/preloading/prerender/prerender_attributes.h" |
| #include "content/browser/preloading/prerender/prerender_final_status.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 "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/blink/public/mojom/navigation/navigation_params.mojom-forward.h" |
| #include "url/gurl.h" |
| |
| namespace blink { |
| class EnabledClientHints; |
| } // namespace blink |
| |
| namespace network::mojom { |
| enum class WebClientHintsType; |
| } // namespace network::mojom |
| |
| namespace content { |
| |
| class FrameTreeNode; |
| class PrerenderHostRegistry; |
| class RenderFrameHostImpl; |
| class WebContentsImpl; |
| class PrerenderCancellationReason; |
| |
| // Prerender2: |
| // PrerenderHost creates a new FrameTree in WebContents associated with the page |
| // that triggered prerendering and starts prerendering. Then NavigationRequest |
| // is expected to find this host from PrerenderHostRegistry and activate the |
| // prerendered page upon navigation. This is created per request from a renderer |
| // process via SpeculationHostImpl or will directly be created for |
| // browser-initiated prerendering (this code path is not implemented yet). This |
| // is owned by PrerenderHostRegistry. |
| class CONTENT_EXPORT PrerenderHost : public FrameTree::Delegate, |
| public NavigationControllerDelegate { |
| public: |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. This enum corresponds to |
| // PrerenderActivationNavigationParamsMatch in |
| // tools/metrics/histograms/enums.xml |
| 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, |
| // kReferrerPolicy = 23, Obsolete |
| kRequestDestination = 24, |
| kMaxValue = kRequestDestination, |
| }; |
| |
| // Observes a triggered prerender. Note that the observer should overlive the |
| // prerender host instance, or be removed properly upon destruction. |
| class Observer : public base::CheckedObserver { |
| public: |
| // Called on the page activation. |
| virtual void OnActivated() {} |
| |
| // Called from the PrerenderHost's destructor. The observer should drop any |
| // reference to the host. |
| virtual void OnHostDestroyed(PrerenderFinalStatus status) {} |
| }; |
| |
| // Returns the PrerenderHost that the given `frame_tree_node` is in, if it is |
| // being prerendered. Note that this function returns a nullptr if the |
| // prerender has been canceled. |
| // TODO(https://crbug.com/1355279): Always return a non-null ptr if the |
| // frame_tree_node is in a prerendering tree. |
| static PrerenderHost* GetPrerenderHostFromFrameTreeNode( |
| FrameTreeNode& frame_tree_node); |
| |
| PrerenderHost(const PrerenderAttributes& attributes, |
| WebContentsImpl& web_contents, |
| base::WeakPtr<PreloadingAttempt> attempt); |
| ~PrerenderHost() override; |
| |
| PrerenderHost(const PrerenderHost&) = delete; |
| PrerenderHost& operator=(const PrerenderHost&) = delete; |
| PrerenderHost(PrerenderHost&&) = delete; |
| PrerenderHost& operator=(PrerenderHost&&) = delete; |
| |
| // FrameTree::Delegate |
| |
| // TODO(https://crbug.com/1199682): Correctly handle load events. Ignored for |
| // now as it confuses WebContentsObserver instances because they can not |
| // distinguish between the different FrameTrees. |
| |
| void LoadingStateChanged(LoadingState new_state) override {} |
| void DidStartLoading(FrameTreeNode* frame_tree_node) override {} |
| void DidStopLoading() override; |
| bool IsHidden() override; |
| FrameTree* LoadingTree() override; |
| int GetOuterDelegateFrameTreeNodeId() override; |
| RenderFrameHostImpl* GetProspectiveOuterDocument() override; |
| bool IsPortal() override; |
| void SetFocusedFrame(FrameTreeNode* node, SiteInstanceGroup* source) override; |
| |
| // NavigationControllerDelegate |
| 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; |
| WebContents* DeprecatedGetWebContents() override; |
| void UpdateOverridingUserAgent() override {} |
| |
| NavigationControllerImpl& GetNavigationController() { |
| return frame_tree_->controller(); |
| } |
| |
| // Returns false if prerendering hasn't been started. |
| bool StartPrerendering(); |
| |
| // Called from PrerenderHostRegistry::DidStartNavigation(). It may reset |
| // `is_ready_for_activation_` flag when the main frame navigation happens in |
| // a prerendered page. |
| void DidStartNavigation(NavigationHandle* navigation_handle); |
| |
| // Called from PrerenderHostRegistry::DidFinishNavigation(). If the navigation |
| // request is for the main frame and doesn't have an error, then the host will |
| // be ready for activation. |
| void DidFinishNavigation(NavigationHandle* navigation_handle); |
| |
| // Activates the prerendered page and returns StoredPage containing the page. |
| // This must be called after this host gets ready for activation. |
| std::unique_ptr<StoredPage> Activate(NavigationRequest& navigation_request); |
| |
| // Returns true if the navigation params that were used in the initial |
| // prerender navigation (i.e., in StartPrerendering()) match the navigation |
| // params in `navigation_request`. This function can be used to determine |
| // whether `navigation_request` may be eligible to activate this |
| // PrerenderHost. |
| bool AreInitialPrerenderNavigationParamsCompatibleWithNavigation( |
| NavigationRequest& navigation_request); |
| |
| bool IsFramePolicyCompatibleWithPrimaryFrameTree(); |
| |
| // Returns the main RenderFrameHost of the prerendered page. |
| // This must be called after StartPrerendering() and before Activate(). |
| RenderFrameHostImpl* GetPrerenderedMainFrameHost(); |
| |
| // Returns the frame tree for the prerendered page `this` is hosting. |
| FrameTree& GetPrerenderFrameTree(); |
| |
| // Tells the reason of the destruction of this host. PrerenderHostRegistry |
| // uses this before abandoning the host. Exposed to PrerenderHostRegistry |
| // only. |
| void RecordFailedFinalStatus(base::PassKey<PrerenderHostRegistry>, |
| const PrerenderCancellationReason& reason); |
| |
| // Called by PrerenderHostRegistry to report that this prerender host is |
| // successfully activated. |
| void RecordActivation(NavigationRequest& navigation_request); |
| |
| enum class LoadingOutcome { |
| kLoadingCompleted, |
| kPrerenderingCancelled, |
| }; |
| |
| // Waits until the page load finishes. Returns the loading status indicating |
| // how the operation was finished. |
| LoadingOutcome WaitForLoadStopForTesting(); |
| |
| const GURL& GetInitialUrl() const; |
| |
| void AddObserver(Observer* observer); |
| void RemoveObserver(Observer* observer); |
| |
| // The initial navigation is set by the PrerenderNavigationThrottle |
| // when the PrerenderHost is first navigated. |
| void SetInitialNavigation(NavigationRequest* navigation); |
| absl::optional<int64_t> GetInitialNavigationId() const; |
| |
| // Returns true if the given `url` indicates the same destination to the |
| // initial_url. |
| bool IsUrlMatch(const GURL& url) const; |
| |
| // Called when the prerender pages asks the client to change the Accept Client |
| // Hints. The instruction applies to the prerendering page before activation, |
| // and will be persisted to the global setting upon activation. |
| void OnAcceptClientHintChanged( |
| const url::Origin& origin, |
| const std::vector<network::mojom::WebClientHintsType>& client_hints_type); |
| |
| // Updates the given `client_hints`. |
| void GetAllowedClientHintsOnPage( |
| const url::Origin& origin, |
| blink::EnabledClientHints* client_hints) const; |
| |
| // Returns absl::nullopt iff prerendering is initiated by the browser (not by |
| // a renderer using Speculation Rules API). |
| absl::optional<url::Origin> initiator_origin() const { |
| return attributes_.initiator_origin; |
| } |
| |
| absl::optional<base::UnguessableToken> initiator_devtools_navigation_token() |
| const { |
| return attributes_.initiator_devtools_navigation_token; |
| } |
| |
| const GURL& prerendering_url() const { return attributes_.prerendering_url; } |
| |
| bool IsBrowserInitiated() { return attributes_.IsBrowserInitiated(); } |
| |
| int frame_tree_node_id() const { return frame_tree_node_id_; } |
| |
| base::WeakPtr<WebContents> initiator_web_contents() { |
| return attributes_.initiator_web_contents; |
| } |
| |
| int 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_; } |
| |
| const absl::optional<PrerenderFinalStatus>& final_status() const { |
| return final_status_; |
| } |
| |
| PrerenderTriggerType trigger_type() const { return attributes_.trigger_type; } |
| const std::string& embedder_histogram_suffix() const { |
| return attributes_.embedder_histogram_suffix; |
| } |
| |
| base::WeakPtr<PreloadingAttempt> preloading_attempt() { return attempt_; } |
| |
| private: |
| void RecordFailedFinalStatusImpl(const PrerenderCancellationReason& reason); |
| |
| // Asks the registry to cancel prerendering. |
| void Cancel(PrerenderFinalStatus status); |
| |
| // Sets the PreloadingTriggeringOutcome, PreloadingEligibility, |
| // PreloadingFailureReason for PreloadingAttempt associated with this |
| // PrerenderHost. |
| void SetTriggeringOutcome(PreloadingTriggeringOutcome outcome); |
| void SetEligibility(PreloadingEligibility eligibility); |
| void SetFailureReason(PrerenderFinalStatus status); |
| |
| ActivationNavigationParamsMatch |
| AreBeginNavigationParamsCompatibleWithNavigation( |
| const blink::mojom::BeginNavigationParams& potential_activation); |
| ActivationNavigationParamsMatch |
| AreCommonNavigationParamsCompatibleWithNavigation( |
| const blink::mojom::CommonNavigationParams& potential_activation); |
| |
| const PrerenderAttributes attributes_; |
| |
| // Indicates if this PrerenderHost is ready for activation. |
| bool is_ready_for_activation_ = false; |
| |
| // The ID of the root node of the frame tree for the prerendered page `this` |
| // is hosting. Since PrerenderHost has 1:1 correspondence with FrameTree, |
| // this is also used for the ID of this PrerenderHost. |
| int frame_tree_node_id_ = RenderFrameHost::kNoFrameTreeNodeId; |
| |
| absl::optional<PrerenderFinalStatus> final_status_; |
| |
| base::ObserverList<Observer> observers_; |
| |
| // Stores the attempt corresponding to this prerender to log various metrics. |
| // We use a WeakPtr here to avoid inadvertent UAF. `attempt_` can get deleted |
| // before `PrerenderHostRegistry::DeleteAbandonedHosts` is scheduled. |
| base::WeakPtr<PreloadingAttempt> attempt_; |
| // Navigation parameters for the navigation which loaded the main document of |
| // the prerendered page, copied immediately after BeginNavigation when |
| // throttles are created. They will be compared with the navigation parameters |
| // of the potential activation when attempting to reserve the prerender host |
| // for a navigation. |
| blink::mojom::BeginNavigationParamsPtr begin_params_; |
| blink::mojom::CommonNavigationParamsPtr common_params_; |
| |
| // Stores the client hints type that applies to this page. |
| base::flat_map<url::Origin, std::vector<network::mojom::WebClientHintsType>> |
| client_hints_type_; |
| |
| // Holds the navigation ID for the main frame initial navigation. |
| absl::optional<int64_t> initial_navigation_id_; |
| |
| // WebContents where this prerenderer is embedded. Keeping a reference is safe |
| // as WebContentsImpl owns PrerenderHostRegistry, which in turn owns |
| // PrerenderHost. |
| const raw_ref<WebContentsImpl> web_contents_; |
| |
| // Used for testing, this closure is only set when waiting a page to be either |
| // loaded for prerendering. |frame_tree_| provides us with a trigger for when |
| // the page is loaded. |
| base::OnceCallback<void(PrerenderHost::LoadingOutcome)> |
| on_wait_loading_finished_; |
| |
| // Frame tree created for the prerenderer to load the page and prepare it for |
| // a future activation. During activation, the prerendered page will be taken |
| // out from |frame_tree_| and moved over to |web_contents_|'s primary frame |
| // tree, while |frame_tree_| will be deleted. |
| std::unique_ptr<FrameTree> frame_tree_; |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_PRELOADING_PRERENDER_PRERENDER_HOST_H_ |