// Copyright 2021 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_BROWSING_CONTEXT_STATE_H_
#define CONTENT_BROWSER_RENDERER_HOST_BROWSING_CONTEXT_STATE_H_

#include "base/feature_list.h"
#include "base/memory/ref_counted.h"
#include "base/memory/safe_ref.h"
#include "content/browser/renderer_host/render_frame_proxy_host.h"
#include "content/browser/site_instance_group.h"
#include "content/public/browser/browsing_instance_id.h"
#include "third_party/blink/public/mojom/frame/frame_replication_state.mojom-forward.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"

namespace features {
// Currently there are two paths - legacy code, in which BrowsingContextState
// will be 1:1 with FrameTreeNode, allowing us to move proxy storage to it as a
// no-op, and a new path hidden behind a feature flag, which will create a new
// BrowsingContextState for cross-BrowsingInstance navigations.

CONTENT_EXPORT extern const base::Feature
    kNewBrowsingContextStateOnBrowsingContextGroupSwap;

enum class BrowsingContextStateImplementationType {
  kLegacyOneToOneWithFrameTreeNode,
  kSwapForCrossBrowsingInstanceNavigations,
};

CONTENT_EXPORT BrowsingContextStateImplementationType GetBrowsingContextMode();
}  // namespace features

namespace content {

class RenderFrameHostImpl;
class SiteInstanceImpl;

// BrowsingContextState is intended to store all state associated with a given
// browsing context (BrowsingInstance in the code, as defined in the HTML spec
// (https://html.spec.whatwg.org/multipage/browsers.html#browsing-context),
// in particular RenderFrameProxyHosts and FrameReplicationState. Each
// RenderFrameHost will have an associated BrowsingContextState (which never
// changes), but each BrowsingContextState can be shared between multiple
// RenderFrameHosts for the same frame/FrameTreeNode.

// BrowsingContextState is responsible for proxy storage and
// RenderFrameHostManager is responsible for connecting different
// BrowsingContextStates and creating proxies for appropriate SiteInstances.

// A new BCS will be created when a new RenderFrameHost is created for a new
// frame or a speculative RFH is created for a cross-BrowsingInstance (browsing
// context group in the spec) navigation (speculative RFHs created in the same
// BrowsingInstance will use the same BrowsingContextState as the old
// RenderFrameHost). For pages stored in bfcache and used for prerendering
// activations, BrowsingContextState will travel automatically together with the
// RenderFrameHost.

// Note: "browsing context" is an HTML spec term (close to a "frame") and it's
// different from content::BrowserContext, which represents a "browser profile".

// TODO(crbug.com/1270671): Currently it's under implementation and there are
// two different modes, controlled by a flag: kLegacyOneToOneWithFrameTreeNode,
// where BrowsingContextState is 1:1 with FrameTreeNode and exists for the
// duration of the FrameTreeNode lifetime, and
// kSwapForCrossBrowsingInstanceNavigations intended state with the behaviour
// described above, tied to the lifetime of the RenderFrameHostImpl.
// kLegacyOneToOneWithFrameTreeNode is currently enabled and will be removed
// once the functionality gated behind kSwapForCrossBrowsingInstanceNavigations
// is implemented.
class CONTENT_EXPORT BrowsingContextState
    : public base::RefCounted<BrowsingContextState>,
      public SiteInstanceGroup::Observer {
 public:
  using RenderFrameProxyHostMap =
      std::unordered_map<SiteInstanceGroupId,
                         std::unique_ptr<RenderFrameProxyHost>,
                         SiteInstanceGroupId::Hasher>;

  // Currently browsing_instance_id| will be null iff the legacy mode is
  // enabled, as is the legacy mode BrowsingContextState is 1:1 with
  // FrameTreeNode and therefore doesn't have a dedicated associated
  // BrowsingInstance.
  // TODO(crbug.com/1270671): Make |browsing_instance_id| non-optional when the
  // legacy path is removed.
  BrowsingContextState(blink::mojom::FrameReplicationStatePtr replication_state,
                       RenderFrameHostImpl* parent,
                       absl::optional<BrowsingInstanceId> browsing_instance_id);

  // Returns a const reference to the map of proxy hosts. The keys are
  // SiteInstanceGroup IDs, the values are RenderFrameProxyHosts.
  const RenderFrameProxyHostMap& proxy_hosts() const { return proxy_hosts_; }

  RenderFrameProxyHostMap& proxy_hosts() { return proxy_hosts_; }

  // Returns true if this is a main BrowsingContextState. True if and only if
  // this BrowsingContextState doesn't have a parent.
  bool is_main_frame() const { return !parent_; }

  const blink::mojom::FrameReplicationState& current_replication_state() const {
    return *replication_state_;
  }

  const std::string& frame_name() const { return replication_state_->name; }

  // Returns the currently active frame policy for this frame, including the
  // sandbox flags which were present at the time the document was loaded, and
  // the permissions policy container policy, which is set by the iframe's
  // allowfullscreen, allowpaymentrequest, and allow attributes, along with the
  // origin of the iframe's src attribute (which may be different from the URL
  // of the document currently loaded into the frame). This does not include
  // policy changes that have been made by updating the containing iframe
  // element attributes since the frame was last navigated; use
  // pending_frame_policy() for those.
  const blink::FramePolicy& effective_frame_policy() const {
    return replication_state_->frame_policy;
  }

  // Returns the sandbox flags currently in effect for this frame. This includes
  // flags inherited from parent frames, the currently active flags from the
  // <iframe> element hosting this frame, as well as any flags set from a
  // Content-Security-Policy HTTP header. This does not include flags that have
  // have been updated in an <iframe> element but have not taken effect yet; use
  // pending_frame_policy() for those. To see the flags which will take effect
  // on navigation (which does not include the CSP-set flags), use
  // effective_frame_policy().
  network::mojom::WebSandboxFlags active_sandbox_flags() const {
    return replication_state_->active_sandbox_flags;
  }

  void set_frame_name(const std::string& unique_name, const std::string& name) {
    replication_state_->unique_name = unique_name;
    replication_state_->name = name;
  }

  void set_has_active_user_gesture(bool has_active_user_gesture) {
    replication_state_->has_active_user_gesture = has_active_user_gesture;
  }

  // All proxies except outer delegate proxies should belong to the same
  // BrowsingInstance as their BrowsingContextState. See the comment for the
  // CHECK inside BrowsingContextState::GetRenderFrameProxyHost for more
  // details. All proxy accessing/creating/deleting functionality assumes the
  // same BrowsingInstance. However, in very select cases (i.e. outer
  // delegates), the proxies will not have the same BrowsingInstance. As such,
  // we use this enum to specify whether or not we need to check for a
  // BrowsingInstance match when creating/deleting or accessing proxies from
  // this BrowsingContextState.
  enum class ProxyAccessMode {
    kRegular,
    kAllowOuterDelegate,
  };

  RenderFrameProxyHost* GetRenderFrameProxyHost(
      SiteInstanceGroup* site_instance_group,
      ProxyAccessMode proxy_access_mode = ProxyAccessMode::kRegular) const;

  // Returns the number of RenderFrameProxyHosts for this frame.
  size_t GetProxyCount();

  // Set the current name and notify proxies about the update.
  void SetFrameName(const std::string& name, const std::string& unique_name);

  // Set the current origin and notify proxies about the update.
  void SetCurrentOrigin(const url::Origin& origin,
                        bool is_potentially_trustworthy_unique_origin);

  // Sets the current insecure request policy, and notifies proxies about the
  // update.
  void SetInsecureRequestPolicy(blink::mojom::InsecureRequestPolicy policy);

  // Sets the current set of insecure urls to upgrade, and notifies proxies
  // about the update.
  void SetInsecureNavigationsSet(
      const std::vector<uint32_t>& insecure_navigations_set);

  // Sets the sticky user activation status and notifies proxies about the
  // update.
  void OnSetHadStickyUserActivationBeforeNavigation(bool value);

  // Sets whether this is an ad frame and notifies the proxies about the update.
  void SetIsAdFrame(bool is_ad_frame);

  // Delete a RenderFrameProxyHost owned by this object.
  void DeleteRenderFrameProxyHost(
      SiteInstanceGroup* site_instance_group,
      ProxyAccessMode proxy_access_mode = ProxyAccessMode::kRegular);

  // SiteInstanceGroup::Observer
  void ActiveFrameCountIsZero(SiteInstanceGroup* site_instance_group) override;
  void RenderProcessGone(SiteInstanceGroup* site_instance_group,
                         const ChildProcessTerminationInfo& info) override;

  // Set the frame_policy provided in function parameter as active frame policy,
  // while leaving the FrameTreeNode::pending_frame_policy_ untouched. This
  // functionality is used on FrameTreeNode initialization, where it is
  // associated with a RenderFrameHost. Returns a boolean indicating whether
  // there was an update to the FramePolicy.
  bool CommitFramePolicy(const blink::FramePolicy& frame_policy);

  // Updates the active sandbox flags in this frame, in response to a
  // Content-Security-Policy header adding additional flags, in addition to
  // those given to this frame by its parent, or in response to the
  // Permissions-Policy header being set. Usually this will be when we create
  // WebContents with an opener. Note that on navigation, these updates will be
  // cleared, and the flags in the pending frame policy will be applied to the
  // frame. The old document's frame policy should therefore not impact the new
  // document's frame policy.
  // Returns true iff this operation has changed state of either sandbox flags
  // or permissions policy.
  bool UpdateFramePolicyHeaders(
      network::mojom::WebSandboxFlags sandbox_flags,
      const blink::ParsedPermissionsPolicy& parsed_header);

  // Notify all of the proxies about the updated FramePolicy, excluding the
  // parent, as it will already know.
  void SendFramePolicyUpdatesToProxies(SiteInstanceGroup* parent_group,
                                       const blink::FramePolicy& frame_policy);

  // Create a RenderFrameProxyHost owned by this object. This
  // RenderFrameProxyHost represents the browsing context in this site instance.
  // TODO(crbug.com/1270671): Currently we pass a FrameTreeNode because it is
  // required for the constructor to RenderFrameProxyHost. However, the stored
  // reference to FrameTreeNode should be replaced by a BrowsingContextState
  // instead; FrameTreeNode will need to be removed from here as well.
  RenderFrameProxyHost* CreateRenderFrameProxyHost(
      SiteInstanceImpl* site_instance,
      const scoped_refptr<RenderViewHostImpl>& rvh,
      FrameTreeNode* frame_tree_node,
      ProxyAccessMode proxy_access_mode = ProxyAccessMode::kRegular,
      const blink::RemoteFrameToken& frame_token = blink::RemoteFrameToken());

  // Called on the RFHM of the inner WebContents to create a
  // RenderFrameProxyHost in its outer WebContents's SiteInstance,
  // |outer_contents_site_instance|.
  RenderFrameProxyHost* CreateOuterDelegateProxy(
      SiteInstanceImpl* outer_contents_site_instance,
      FrameTreeNode* frame_tree_node,
      const blink::RemoteFrameToken& frame_token);

  // Called on an inner WebContents that's being detached from its outer
  // WebContents. This will delete the proxy in the
  // |outer_contents_site_instance_group|.
  void DeleteOuterDelegateProxy(
      SiteInstanceGroup* outer_contents_site_instance_group);

  // Deletes any proxy hosts associated with this node. Used during destruction
  // of WebContentsImpl.
  void ResetProxyHosts();

  // Notification methods to tell this RenderFrameHostManager that the frame it
  // is responsible for has started or stopped loading a document.
  void OnDidStartLoading();
  void OnDidStopLoading();

  // Notify proxies that an opener has been updated.
  void UpdateOpener(SiteInstanceGroup* source_site_instance_group);

  void OnDidUpdateFrameOwnerProperties(
      const blink::mojom::FrameOwnerProperties& properties);

  void ExecuteRemoteFramesBroadcastMethod(
      base::RepeatingCallback<void(RenderFrameProxyHost*)> callback,
      SiteInstanceGroup* group_to_skip,
      RenderFrameProxyHost* outer_delegate_proxy);

  using TraceProto = perfetto::protos::pbzero::BrowsingContextState;
  // Write a representation of this object into a trace.
  void WriteIntoTrace(perfetto::TracedProto<TraceProto> proto) const;

  base::SafeRef<BrowsingContextState> GetSafeRef();

 protected:
  friend class base::RefCounted<BrowsingContextState>;

  virtual ~BrowsingContextState();

 private:
  RenderFrameProxyHost* GetRenderFrameProxyHostImpl(
      SiteInstanceGroup* site_instance_group,
      ProxyAccessMode proxy_access_mode) const;

  // Proxy hosts for this browsing context in various renderer processes, keyed
  // by SiteInstanceGroup ID.
  RenderFrameProxyHostMap proxy_hosts_;

  // Track information that needs to be replicated to processes that have
  // proxies for this frame.
  blink::mojom::FrameReplicationStatePtr replication_state_;

  // Parent document of this BrowsingContextState, might be null if this is a
  // main frame BrowsingContextState.
  const raw_ptr<RenderFrameHostImpl> parent_;

  // ID of the BrowsingInstance to which this BrowsingContextState belongs.
  // Currently browsing_instance_id| will be null iff the legacy mode is
  // enabled, as is the legacy mode BrowsingContextState is 1:1 with
  // FrameTreeNode and therefore doesn't have a dedicated associated
  // BrowsingInstance.
  // TODO(crbug.com/1270671): Make |browsing_instance_id| non-optional when the
  // legacy path is removed.
  const absl::optional<BrowsingInstanceId> browsing_instance_id_;

  base::WeakPtrFactory<BrowsingContextState> weak_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_BROWSING_CONTEXT_STATE_H_