// Copyright 2013 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_RENDERER_HOST_FRAME_TREE_H_
#define CONTENT_BROWSER_RENDERER_HOST_FRAME_TREE_H_

#include <stdint.h>

#include <iterator>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>

#include "base/containers/queue.h"
#include "base/dcheck_is_on.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/safe_ref.h"
#include "base/memory/weak_ptr.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigator.h"
#include "content/browser/renderer_host/navigator_delegate.h"
#include "content/browser/renderer_host/render_frame_host_manager.h"
#include "content/browser/renderer_host/render_view_host_enums.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/frame/frame_owner_element_type.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom-forward.h"
#include "url/origin.h"

namespace blink {
namespace mojom {
class BrowserInterfaceBroker;
enum class TreeScopeType;
}  // namespace mojom

struct FramePolicy;
}  // namespace blink

namespace content {

class BrowserContext;
class PageDelegate;
class RenderFrameHostDelegate;
class RenderFrameProxyHost;
class RenderViewHostDelegate;
class RenderViewHostImpl;
class RenderFrameHostManager;
class RenderWidgetHostDelegate;
class SiteInstance;
class SiteInstanceGroup;

// Represents the frame tree for a page. With the exception of the main frame,
// all FrameTreeNodes will be created/deleted in response to frame attach and
// detach events in the DOM.
//
// The main frame's FrameTreeNode is special in that it is reused. This allows
// it to serve as an anchor for state that needs to persist across top-level
// page navigations.
//
// TODO(ajwong): Move NavigationController ownership to the main frame
// FrameTreeNode. Possibly expose access to it from here.
//
// This object is only used on the UI thread.
class CONTENT_EXPORT FrameTree {
 public:
  class NodeRange;

  class CONTENT_EXPORT NodeIterator {
   public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = FrameTreeNode*;
    using difference_type = std::ptrdiff_t;
    using pointer = FrameTreeNode**;
    using reference = FrameTreeNode*&;

    NodeIterator(const NodeIterator& other);
    ~NodeIterator();

    NodeIterator& operator++();
    // Advances the iterator and excludes the children of the current node
    NodeIterator& AdvanceSkippingChildren();

    bool operator==(const NodeIterator& rhs) const;

    FrameTreeNode* operator*() { return current_node_; }

   private:
    friend class FrameTreeTest;
    friend class NodeRange;

    NodeIterator(const std::vector<raw_ptr<FrameTreeNode, DanglingUntriaged>>&
                     starting_nodes,
                 const FrameTreeNode* root_of_subtree_to_skip,
                 bool should_descend_into_inner_trees,
                 bool include_delegate_nodes_for_inner_frame_trees);

    void AdvanceNode();

    // `current_node_` and `root_of_subtree_to_skip_` are not a raw_ptr<...> for
    // performance reasons (based on analysis of sampling profiler data and
    // tab_search:top100:2020).
    RAW_PTR_EXCLUSION FrameTreeNode* current_node_;
    RAW_PTR_EXCLUSION const FrameTreeNode* const root_of_subtree_to_skip_;

    const bool should_descend_into_inner_trees_;
    const bool include_delegate_nodes_for_inner_frame_trees_;
    base::circular_deque<raw_ptr<FrameTreeNode, DanglingUntriaged>> queue_;
  };

  class CONTENT_EXPORT NodeRange {
   public:
    NodeRange(const NodeRange&);
    ~NodeRange();

    NodeIterator begin();
    NodeIterator end();

   private:
    friend class FrameTree;

    NodeRange(const std::vector<raw_ptr<FrameTreeNode, DanglingUntriaged>>&
                  starting_nodes,
              const FrameTreeNode* root_of_subtree_to_skip,
              bool should_descend_into_inner_trees,
              bool include_delegate_nodes_for_inner_frame_trees);

    const std::vector<raw_ptr<FrameTreeNode, DanglingUntriaged>>
        starting_nodes_;
    const raw_ptr<const FrameTreeNode> root_of_subtree_to_skip_;
    const bool should_descend_into_inner_trees_;
    const bool include_delegate_nodes_for_inner_frame_trees_;
  };

  class CONTENT_EXPORT Delegate {
   public:
    // The FrameTree changed its LoadingState. This can be a transition between
    // not-loading and loading (in which case it will be accompanied by either a
    // DidStartLoading or DidStopLoading), or a transition between not showing
    // loading UI and showing loading UI while a navigation is in progress (in
    // which case it will be called without either DidStartLoading or
    // DidStopLoading).
    virtual void LoadingStateChanged(LoadingState new_state) = 0;

    // The FrameTree has started loading in `frame_tree_node`. Note that this
    // is only called when the FrameTree as a whole goes from not-loading to
    // loading. If a second FrameTreeNode begins loading, a new DidStartLoading
    // message will not be sent.
    virtual void DidStartLoading(FrameTreeNode* frame_tree_node) = 0;

    // The FrameTree has stopped loading. Sent only when all FrameTreeNodes have
    // stopped loading.
    virtual void DidStopLoading() = 0;

    // Returns the delegate's top loading tree, which should be used to infer
    // the values of loading-related states. The state of
    // IsLoadingIncludingInnerFrameTrees() is a WebContents level concept and
    // LoadingTree would return the frame tree to which loading events should be
    // directed.
    //
    // TODO(crbug.com/40202416): Remove this method and directly rely on
    // GetOutermostMainFrame() once guest views are migrated to MPArch.
    virtual FrameTree* LoadingTree() = 0;

    // Returns true when the active RenderWidgetHostView should be hidden.
    virtual bool IsHidden() = 0;

    // If the FrameTree using this delegate is an inner/nested FrameTree, then
    // there may be a FrameTreeNode in the outer FrameTree that is considered
    // our outer delegate FrameTreeNode. This method returns the outer delegate
    // FrameTreeNode ID if one exists. If we don't have a an outer delegate
    // FrameTreeNode, this method returns an invalid value.
    virtual FrameTreeNodeId GetOuterDelegateFrameTreeNodeId() = 0;

    // If the FrameTree using this delegate is an inner/nested FrameTree that
    // has not yet been attached to an outer FrameTreeNode, returns the parent
    // RenderFrameHost of the intended outer FrameTreeNode to which the inner
    // frame tree will be attached. This is usually the RenderFrameHost that is
    // the outer document once attachment occurs, however in the case of some
    // kinds of GuestView, the outer document may end up being a same-origin
    // subframe of the RenderFrameHost returned by this method (see the
    // `testNewWindowAttachInSubFrame` webview test for an example of this).
    // Otherwise, returns null.
    virtual RenderFrameHostImpl* GetProspectiveOuterDocument() = 0;

    // Set the `node` frame as focused in its own FrameTree as well as possibly
    // changing the focused frame tree in the case of inner/outer FrameTrees.
    virtual void SetFocusedFrame(FrameTreeNode* node,
                                 SiteInstanceGroup* source) = 0;

    // Returns this FrameTree's document picture-in-picture FrameTree if it has
    // one.
    virtual FrameTree* GetOwnedDocumentPictureInPictureFrameTree() = 0;

    // Returns this FrameTree's opener if this FrameTree represents a
    // document picture-in-picture window.
    virtual FrameTree* GetDocumentPictureInPictureOpenerFrameTree() = 0;

    // Called when the visibility of the RenderFrameProxyHost changes.
    // This method should only handle visibility for inner WebContents and
    // will eventually notify all the RenderWidgetHostViews belonging to that
    // WebContents. If this is not an inner WebContents or the inner WebContents
    // FrameTree root does not match `render_frame_proxy_host` FrameTreeNode it
    // should return false.
    virtual bool OnRenderFrameProxyVisibilityChanged(
        RenderFrameProxyHost* render_frame_proxy_host,
        blink::mojom::FrameVisibility visibility) = 0;

    // Returns the PrerenderHostId hosting this FrameTree. Returns an invalid ID
    // when this FrameTree is not being prerendered.
    virtual PrerenderHostId GetPrerenderHostId() = 0;

    // If this FrameTree is hosted by a privileged WebContents (see //chrome's
    // PrivilegedWebContents), returns that WebContents' immutable feature id;
    // otherwise returns nullopt. Used to mark every frame the privileged
    // WebContents hosts with a privileged EmbedderIsolationInfo, which keeps it
    // isolated from ordinary web content. Delegates for inner frame trees
    // (fenced frames, guests) may return nullopt here; those frames instead
    // inherit the privileged bit from their outer document.
    virtual std::optional<int64_t> GetPrivilegedContentsFeatureId();

    // Returns true if this FrameTree is hosted by a WebContents that disallows
    // service worker control of the pages it hosts (see
    // WebContents::PrivilegedParams). Used to skip the service worker for the
    // main resource of navigations in such a WebContents.
    virtual bool DoesWebContentsDisallowServiceWorkerControl();
  };

  // Type of FrameTree instance.
  enum class Type {
    // This FrameTree is the primary frame tree for the WebContents, whose main
    // document URL is shown in the Omnibox.
    kPrimary,

    // This FrameTree is used to prerender a page in the background which is
    // invisible to the user.
    kPrerender,

    // This FrameTree is used to host the contents of a <fencedframe> element.
    //
    // Note that the FrameTree's Type should not be confused for
    // `RenderFrameHost::LifecycleState`. For example, when a <fencedframe> is
    // nested in a page in the bfcache, the FrameTree associated with the fenced
    // frame will be kFencedFrame, but the RenderFrameHosts inside of it will
    // have their lifecycle state indicate that they are bfcached.
    kFencedFrame,

    // This FrameTree is used to host the contents of a guest page. Guests are
    // kinds of embedded pages, but their semantics are mostly delegated outside
    // of the content/ layer. See components/guest_view/README.md.
    // The implementation of guests is being migrated from using a separate
    // WebContents to using this FrameTree Type. This type is used with the
    // `features::kGuestViewMPArch` flag.
    kGuest,
  };

  // A set of delegates are remembered here so that we can create
  // RenderFrameHostManagers.
  FrameTree(BrowserContext* browser_context,
            Delegate* delegate,
            NavigationControllerDelegate* navigation_controller_delegate,
            NavigatorDelegate* navigator_delegate,
            RenderFrameHostDelegate* render_frame_delegate,
            RenderViewHostDelegate* render_view_delegate,
            RenderWidgetHostDelegate* render_widget_delegate,
            RenderFrameHostManager::Delegate* manager_delegate,
            PageDelegate* page_delegate,
            Type type);

  FrameTree(const FrameTree&) = delete;
  FrameTree& operator=(const FrameTree&) = delete;

  ~FrameTree();

  // Initializes the main frame for this FrameTree. That is it creates the
  // initial RenderFrameHost in the root node's RenderFrameHostManager, and also
  // creates an initial NavigationEntry that potentially inherits
  // `opener_for_origin`'s origin in its NavigationController. This method will
  // call back into the delegates so it should only be called once they have
  // completed their initialization. Pass in frame_policy so that it can be set
  // in the root node's replication_state.
  // TODO(carlscab): It would be great if initialization could happened in the
  // constructor so we do not leave objects in a half initialized state.
  void Init(SiteInstanceImpl* main_frame_site_instance,
            bool renderer_initiated_creation,
            const std::string& main_frame_name,
            RenderFrameHostImpl* opener_for_origin,
            const blink::FramePolicy& frame_policy,
            const base::UnguessableToken& devtools_frame_token);

  Type type() const { return type_; }

  FrameTreeNode* root() { return &root_; }
  const FrameTreeNode* root() const { return &root_; }

  bool is_primary() const { return type_ == Type::kPrimary; }
  bool is_prerendering() const { return type_ == Type::kPrerender; }
  bool is_fenced_frame() const { return type_ == Type::kFencedFrame; }
  bool is_guest() const { return type_ == Type::kGuest; }

  Delegate* delegate() { return delegate_; }

  // Delegates for various objects.  These can be kept centrally on the
  // FrameTree because they are expected to be the same for all frames on a
  // given FrameTree.
  RenderFrameHostDelegate* render_frame_delegate() {
    return render_frame_delegate_;
  }
  RenderViewHostDelegate* render_view_delegate() {
    return render_view_delegate_;
  }
  RenderWidgetHostDelegate* render_widget_delegate() {
    return render_widget_delegate_;
  }
  RenderFrameHostManager::Delegate* manager_delegate() {
    return manager_delegate_;
  }
  PageDelegate* page_delegate() { return page_delegate_; }

  // Iterate over all RenderViewHosts, including speculative RenderViewHosts.
  // See `speculative_render_view_host_` for more details.
  void ForEachRenderViewHost(
      base::FunctionRef<void(RenderViewHostImpl*)> on_host);

  // Speculative RenderViewHost accessors.
  RenderViewHostImpl* speculative_render_view_host() const {
    return speculative_render_view_host_.get();
  }
  void set_speculative_render_view_host(
      base::WeakPtr<RenderViewHostImpl> render_view_host) {
    speculative_render_view_host_ = render_view_host;
  }

  // Moves `speculative_render_view_host_` to `render_view_host_map_`. This
  // should be called every time a main-frame same-SiteInstanceGroup speculative
  // RenderFrameHost gets swapped in and becomes the active RenderFrameHost.
  // This overwrites the previous RenderViewHost for the SiteInstanceGroup in
  // `render_view_host_map_`, if one exists.
  void MakeSpeculativeRVHCurrent();

  // Returns the FrameTreeNode with the given |frame_tree_node_id| if it is part
  // of this FrameTree.
  FrameTreeNode* FindByID(FrameTreeNodeId frame_tree_node_id);

  // Returns the FrameTreeNode with the given renderer-specific |routing_id|.
  FrameTreeNode* FindByRoutingID(int process_id, int routing_id);

  // Returns the first frame in this tree with the given |name|, or the main
  // frame if |name| is empty.
  // Note that this does NOT support pseudo-names like _self, _top, and _blank,
  // nor searching other FrameTrees (unlike blink::WebView::findFrameByName).
  FrameTreeNode* FindByName(const std::string& name);

  // Returns a range to iterate over all FrameTreeNodes in the frame tree in
  // breadth-first traversal order.
  NodeRange Nodes();

  // Returns a range to iterate over all FrameTreeNodes in a subtree of the
  // frame tree, starting from |subtree_root|.
  NodeRange SubtreeNodes(FrameTreeNode* subtree_root);

  // Returns a range to iterate over all FrameTreeNodes in this frame tree, as
  // well as any FrameTreeNodes of inner frame trees. Note that this includes
  // inner frame trees of inner WebContents as well.
  NodeRange NodesIncludingInnerTreeNodes();

  // Returns a range to iterate over all FrameTreeNodes in a subtree, starting
  // from, but not including |parent|, as well as any FrameTreeNodes of inner
  // frame trees. Note that this includes inner frame trees of inner WebContents
  // as well. If `include_delegate_nodes_for_inner_frame_trees` is true the
  // delegate RenderFrameHost owning the inner frame tree will also be returned.
  static NodeRange SubtreeAndInnerTreeNodes(
      RenderFrameHostImpl* parent,
      bool include_delegate_nodes_for_inner_frame_trees = false);

  // Adds a new child frame to the frame tree. |process_id| is required to
  // disambiguate |new_routing_id|, and it must match the process of the
  // |parent| node. Otherwise no child is added and this method returns nullptr.
  // |interface_provider_receiver| is the receiver end of the InterfaceProvider
  // interface through which the child RenderFrame can access Mojo services
  // exposed by the corresponding RenderFrameHost. The caller takes care of
  // sending the client end of the interface down to the
  // RenderFrame. |policy_container_bind_params|, if not null, is used for
  // binding Blink's policy container to the new RenderFrameHost's
  // PolicyContainerHost. This is only needed if this frame is the result of the
  // CreateChildFrame mojo call, which also delivers the
  // |policy_container_bind_params|. |is_dummy_frame_for_inner_tree| is true if
  // the added frame is only to serve as a placeholder for an inner frame tree
  // (e.g. fenced frames) and will not have a live RenderFrame of its
  // own.
  FrameTreeNode* AddFrame(
      RenderFrameHostImpl* parent,
      int process_id,
      int new_routing_id,
      mojo::PendingAssociatedRemote<mojom::Frame> frame_remote,
      mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
          browser_interface_broker_receiver,
      blink::mojom::PolicyContainerBindParamsPtr policy_container_bind_params,
      mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterfaceProvider>
          associated_interface_provider_receiver,
      blink::mojom::TreeScopeType scope,
      const std::string& frame_name,
      const std::string& frame_unique_name,
      bool is_created_by_script,
      const blink::LocalFrameToken& frame_token,
      const base::UnguessableToken& devtools_frame_token,
      const blink::DocumentToken& document_token,
      const blink::FramePolicy& frame_policy,
      const blink::mojom::FrameOwnerProperties& frame_owner_properties,
      bool was_discarded,
      blink::FrameOwnerElementType owner_type,
      bool is_dummy_frame_for_inner_tree,
      std::unique_ptr<base::UnguessableToken> sandbox_origin_token = nullptr);

  // Removes a frame from the frame tree. |child|, its children, and objects
  // owned by their RenderFrameHostManagers are immediately deleted. The root
  // node cannot be removed this way.
  void RemoveFrame(FrameTreeNode* child);

  // This method walks the entire frame tree and creates RenderFrameProxyHosts
  // as needed. Proxies are not created if suitable proxies already exist. See
  // below for special handling of |source|. Otherwise proxies are created for
  // the given |site_instance_group| in each node.
  //
  // |source| may be null if there is no node navigating in this frame tree
  // (such as when this is called for an opener's frame tree), in which case no
  // nodes are skipped for RenderFrameProxyHost creation. Otherwise, a proxy is
  // temporarily created for |source| in cross-SiteInstanceGroup cases (to allow
  // a remote-to-local swap to the new RenderFrameHost in |source|), but the
  // subtree rooted at source is skipped.
  //
  // |source_new_browsing_context_state| is the BrowsingContextState used by the
  // speculative frame host, which may differ from the BrowsingContextState in
  // |source| during cross-origin cross- browsing-instance navigations.
  //
  // |navigation_metrics_token| is a token identifying the navigation for which
  // these proxies are being created, if any. It allows metrics code and trace
  // events to tie together different IPCs and events pertaining to a particular
  // navigation. It's nullopt for non-navigation cases such as creating proxies
  // for a new subframe.
  void CreateProxiesForSiteInstanceGroup(
      FrameTreeNode* source,
      SiteInstanceGroup* site_instance_group,
      const scoped_refptr<BrowsingContextState>&
          source_new_browsing_context_state,
      const std::optional<base::UnguessableToken>& navigation_metrics_token);

  // Convenience accessor for the main frame's RenderFrameHostImpl.
  RenderFrameHostImpl* GetMainFrame() const;

  // Returns the focused frame.
  FrameTreeNode* GetFocusedFrame();

  // Sets the focused frame to |node|.  |source| identifies the
  // SiteInstanceGroup that initiated this focus change.  If this FrameTree has
  // SiteInstanceGroups other than |source|, those SiteInstanceGroups will be
  // notified about the new focused frame.   Note that |source| may differ from
  // |node|'s current SiteInstanceGroup (e.g., this happens for cross-process
  // window.focus() calls).
  void SetFocusedFrame(FrameTreeNode* node, SiteInstanceGroup* source);

  // Creates a RenderViewHostImpl for a given |site_instance| in the tree.
  //
  // The RenderFrameHostImpls and the RenderFrameProxyHosts will share ownership
  // of this object.
  // `create_case` indicates whether or not the RenderViewHost being created is
  // speculative or not. It should only be registered with the FrameTree if it
  // is not speculative.
  // `frame_sink_id` is optionally set only if we're creating a speculative
  // RenderViewHost. If set, it implies we're reusing the compositor from the
  // previous RenderViewHost.
  scoped_refptr<RenderViewHostImpl> CreateRenderViewHost(
      SiteInstanceGroup* site_instance_group,
      int32_t main_frame_routing_id,
      bool renderer_initiated_creation,
      scoped_refptr<BrowsingContextState> main_browsing_context_state,
      CreateRenderViewHostCase create_case,
      std::optional<viz::FrameSinkId> frame_sink_id);

  // Returns the existing RenderViewHost for a new RenderFrameHost.
  // There should always be such a RenderViewHost, because the main frame
  // RenderFrameHost for each SiteInstance should be created before subframes.
  // Note that this will never return `speculative_render_view_host_`. If that
  // is needed, call `speculative_render_view_host()` instead.
  scoped_refptr<RenderViewHostImpl> GetRenderViewHost(SiteInstanceGroup* group);

  using RenderViewHostMapId = base::IdType32<class RenderViewHostMap>;

  // Returns the ID used for the RenderViewHost associated with
  // |site_instance_group|.
  RenderViewHostMapId GetRenderViewHostMapId(
      SiteInstanceGroup* site_instance_group) const;

  // Registers a RenderViewHost so that it can be reused by other frames
  // whose SiteInstance maps to the same RenderViewHostMapId.
  //
  // This method does not take ownership of|rvh|.
  //
  // NOTE: This method CHECK fails if a RenderViewHost is already registered for
  // |rvh|'s SiteInstance.
  //
  // ALSO NOTE: After calling RegisterRenderViewHost, UnregisterRenderViewHost
  // *must* be called for |rvh| when it is destroyed or put into the
  // BackForwardCache, to prevent FrameTree::CreateRenderViewHost from trying to
  // reuse it.
  void RegisterRenderViewHost(RenderViewHostMapId id, RenderViewHostImpl* rvh);

  // Unregisters the RenderViewHostImpl that's available for reuse for a
  // particular RenderViewHostMapId. NOTE: This method CHECK fails if it is
  // called for a |render_view_host| that is not currently set for reuse.
  void UnregisterRenderViewHost(RenderViewHostMapId id,
                                RenderViewHostImpl* render_view_host);

  // This is called when the frame is about to be removed and started to run
  // unload handlers.
  void FrameUnloading(FrameTreeNode* frame);

  // This is only meant to be called by FrameTreeNode. Triggers calling
  // the listener installed by SetFrameRemoveListener.
  void FrameRemoved(FrameTreeNode* frame);

  void NodeLoadingStateChanged(FrameTreeNode& node,
                               LoadingState previous_frame_tree_loading_state);
  void DidCancelLoading();

  // Returns this FrameTree's total load progress. If the `root_` FrameTreeNode
  // is navigating returns `blink::kInitialLoadProgress`.
  double GetLoadProgress();

  // Returns true if at least one of the nodes in this frame tree or nodes in
  // any inner frame tree of the same WebContents is loading.
  // `exclude_ad_subframes` indicates whether ad subframes are excluded from the
  // query.
  //
  // Note: For top-level navigation, even if only ad subframes remain loading,
  // the main frame is still considered loading by this function. See comments
  // on `WebContents::IsLoadingExcludingAdSubframes()`.
  //
  // TODO(crbug.com/461821799): Expand this to work with top-level navigation.
  bool IsLoadingIncludingInnerFrameTrees(
      bool exclude_ad_subframes = false) const;

  // Returns the LoadingState for the FrameTree as a whole, indicating whether
  // a load is in progress, as well as whether loading UI should be shown.
  // `exclude_ad_subframes` indicates whether ad subframes are excluded from the
  // query.
  LoadingState GetLoadingState(bool exclude_ad_subframes = false) const;

  // Set page-level focus in all SiteInstances involved in rendering
  // this FrameTree, not including the current main frame's
  // SiteInstance. The focus update will be sent via the main frame's proxies
  // in those SiteInstances.
  void ReplicatePageFocus(bool is_focused);

  // Updates page-level focus for this FrameTree in the subframe renderer
  // identified by |group|.
  void SetPageFocus(SiteInstanceGroup* group, bool is_focused);

  // Walks the current frame tree and registers any origins matching
  // `previously_visited_origin`, either the last committed origin of a
  // RenderFrameHost or the origin associated with a NavigationRequest that has
  // been assigned to a SiteInstance, as having the default origin isolation
  // state. This is only necessary when `previously_visited_origin` is seen with
  // an OriginAgentCluster header explicitly requesting something other than the
  // default.
  void RegisterExistingOriginAsHavingDefaultIsolation(
      const url::Origin& previously_visited_origin,
      NavigationRequest* navigation_request_to_exclude);

  const NavigationControllerImpl& controller() const {
    return navigator_.controller();
  }
  NavigationControllerImpl& controller() { return navigator_.controller(); }

  Navigator& navigator() { return navigator_; }

  // Another page accessed the initial empty main document, which means it
  // is no longer safe to display a pending URL without risking a URL spoof.
  void DidAccessInitialMainDocument();

  bool has_accessed_initial_main_document() const {
    return has_accessed_initial_main_document_;
  }

  void ResetHasAccessedInitialMainDocument() {
    has_accessed_initial_main_document_ = false;
  }

  bool IsHidden() const { return delegate_->IsHidden(); }

  // LoadingTree returns the following for different frame trees to direct
  // loading related events. Please see FrameTree::Delegate::LoadingTree for
  // more comments.
  // - For prerender frame tree -> returns the frame tree itself.
  // - For fenced frame and primary frame tree -> returns
  // the delegate's primary frame tree.
  FrameTree* LoadingTree();

  // Stops all ongoing navigations in each of the nodes of this FrameTree.
  void StopLoading();

  // Prepares this frame tree for destruction, cleaning up the internal state
  // and firing the appropriate events like FrameDeleted.
  // Must be called before FrameTree is destroyed.
  void Shutdown();

  bool IsBeingDestroyed() const { return is_being_destroyed_; }

  base::SafeRef<FrameTree> GetSafeRef();

  // Walks up the FrameTree chain and focuses the FrameTreeNode where
  // each inner FrameTree is attached.
  void FocusOuterFrameTrees();

  // Discards the frame tree. The root frame is transitioned to an empty
  // document in blink and BFCache entries are cleared. The tree is configured
  // to reload when activated.
  void Discard(base::OnceClosure on_discarded_cb = base::NullCallback());

 private:
  friend class FrameTreeTest;
  FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplBrowserTest, RemoveFocusedFrame);
  FRIEND_TEST_ALL_PREFIXES(FencedFrameMPArchBrowserTest, NodesForIsLoading);
  FRIEND_TEST_ALL_PREFIXES(RenderFrameHostManagerTest,
                           CreateRenderViewAfterProcessKillAndClosedProxy);

  // Returns a range to iterate over all FrameTreeNodes in the frame tree in
  // breadth-first traversal order, skipping the subtree rooted at
  // |node|, but including |node| itself.
  NodeRange NodesExceptSubtree(FrameTreeNode* node);

  // Returns all FrameTreeNodes in this frame tree, as well as any
  // FrameTreeNodes of inner frame trees. Note that this doesn't include inner
  // frame trees of inner delegates. This is used to find the aggregate
  // IsLoading value for a frame tree.
  //
  // TODO(crbug.com/1261928, crbug.com/1261928): Remove this method and directly
  // rely on GetOutermostMainFrame() and NodesIncludingInnerTreeNodes() once
  // guest views are migrated to MPArch.
  std::vector<FrameTreeNode*> CollectNodesForIsLoading();

  const raw_ptr<Delegate> delegate_;

  // These delegates are installed into all the RenderViewHosts and
  // RenderFrameHosts that we create.
  raw_ptr<RenderFrameHostDelegate> render_frame_delegate_;
  raw_ptr<RenderViewHostDelegate> render_view_delegate_;
  raw_ptr<RenderWidgetHostDelegate> render_widget_delegate_;
  raw_ptr<RenderFrameHostManager::Delegate> manager_delegate_;
  raw_ptr<PageDelegate> page_delegate_;

  // The Navigator object responsible for managing navigations on this frame
  // tree. Each FrameTreeNode will default to using it for navigation tasks in
  // the frame.
  Navigator navigator_;

  // A map to store RenderViewHosts, keyed by SiteInstanceGroup ID.
  // This map does not cover all RenderViewHosts in a FrameTree. See
  // `speculative_render_view_host_`.
  using RenderViewHostMap =
      std::unordered_map<RenderViewHostMapId, RenderViewHostImpl*>;
  // Map of RenderViewHostMapId to RenderViewHost. This allows us to look up the
  // RenderViewHost for a given SiteInstance when creating RenderFrameHosts.
  // Each RenderViewHost maintains a refcount and is deleted when there are no
  // more RenderFrameHosts or RenderFrameProxyHosts using it.
  RenderViewHostMap render_view_host_map_;

  // A speculative RenderViewHost is created for all speculative cross-page
  // same-SiteInstanceGroup RenderFrameHosts. When the corresponding
  // RenderFrameHost gets committed and becomes the current RenderFrameHost,
  // `speculative_render_view_host_` will be moved to `render_view_host_map_`,
  // overwriting the previous RenderViewHost of the same SiteInstanceGroup, if
  // applicable. This field will also be reset at that time, or if the
  // speculative RenderFrameHost gets deleted.
  //
  // For any given FrameTree, there will be at most one
  // `speculative_render_view_host_`, because only main-frame speculative
  // RenderFrameHosts have speculative RenderViewHosts, and there is at most one
  // such RenderFrameHost per FrameTree at a time.
  // This is a WeakPtr, since the RenderViewHost is owned by the
  // RenderFrameHostImpl, not the FrameTree. This implies that if the owning
  // RenderFrameHostImpl gets deleted, this will too.
  //
  // This supports but is independent of RenderDocument, which introduces cases
  // where there may be more than one RenderViewHost per SiteInstanceGroup, such
  // as cross-page same-SiteInstanceGroup navigations. The speculative
  // RenderFrameHost has an associated RenderViewHost, but it cannot be put in
  // `render_view_host_map_` when it is created, as the existing RenderViewHost
  // will be incorrectly overwritten.
  // TODO(crbug.com/40228869): Expand support to include cross-SiteInstanceGroup
  // main-frame navigations, so all main-frame navigations use speculative
  // RenderViewHost.
  base::WeakPtr<RenderViewHostImpl> speculative_render_view_host_;

  // Indicates type of frame tree.
  const Type type_;

  FrameTreeNodeId focused_frame_tree_node_id_;

  // Whether the initial empty page has been accessed by another page, making it
  // unsafe to show the pending URL. Usually false unless another window tries
  // to modify the blank page.  Always false after the first commit.
  bool has_accessed_initial_main_document_ = false;

  bool is_being_destroyed_ = false;

  // Whether Shutdown() was called.
  bool was_shut_down_ = false;

  // The root FrameTreeNode.
  //
  // Note: It is common for a node to test whether it's the root node, via the
  // `root()` method, even while `root_` is running its destructor.
  // For that reason, we want to destroy |root_| before any other fields.
  FrameTreeNode root_;

  base::WeakPtrFactory<FrameTree> weak_ptr_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_FRAME_TREE_H_
