// 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_STORED_PAGE_H_
#define CONTENT_BROWSER_RENDERER_HOST_STORED_PAGE_H_

#include <set>

#include "base/memory/raw_ptr.h"
#include "base/memory/safe_ref.h"
#include "content/browser/site_instance_group.h"
#include "content/public/browser/frame_tree_node_id.h"
#include "content/public/browser/site_instance.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/blink/public/mojom/page/page.mojom.h"

namespace content {
class RenderFrameHostImpl;
class RenderFrameProxyHost;
class RenderViewHostImpl;

// StoredPage contains a page which is not tied to a FrameTree. It holds the
// main RenderFrameHost together with RenderViewHosts and main document's
// proxies. It's used for storing pages in back/forward cache or when preparing
// prerendered pages for activation. It is possible that this class
// is constructed with a `RenderViewHostImpl` that may disappear when a
// outstanding subframe navigation completes. To make sure the references
// of this class to `RenderViewHostImpl` are not stale this class implements
// `SiteInstanceGroup::Observer` to monitor their destruction.
class StoredPage : public SiteInstanceGroup::Observer {
 public:
  using RenderFrameProxyHostMap =
      absl::flat_hash_map<SiteInstanceGroupId,
                          std::unique_ptr<RenderFrameProxyHost>>;

  using RenderViewHostImplSafeRefSet =
      std::set<base::SafeRef<RenderViewHostImpl>>;

  // A delegate class for various state change callbacks.
  class Delegate {
   public:
    // Callback to indicate when we've removed watching a RenderViewHostImpl.
    // This can happen when a RenderFrameProxyHost is no longer needed
    // and terminates.
    virtual void RenderViewHostNoLongerStored(RenderViewHostImpl* rvh) = 0;
  };

  StoredPage(std::unique_ptr<RenderFrameHostImpl> rfh,
             RenderFrameProxyHostMap proxy_hosts,
             RenderViewHostImplSafeRefSet render_view_hosts);
  ~StoredPage() override;

  void SetDelegate(Delegate* delegate);

  // SiteInstanceGroup::Observer overrides:
  void ActiveFrameCountIsZero(SiteInstanceGroup* site_instance_group) override;

  void SetPageRestoreParams(
      blink::mojom::PageRestoreParamsPtr page_restore_params) {
    page_restore_params_ = std::move(page_restore_params);
  }

  const blink::mojom::PageRestoreParamsPtr& page_restore_params() const {
    return page_restore_params_;
  }

  RenderFrameHostImpl* render_frame_host() { return render_frame_host_.get(); }

  const RenderViewHostImplSafeRefSet& render_view_hosts() const {
    return render_view_hosts_;
  }

  const StoredPage::RenderFrameProxyHostMap& proxy_hosts() const {
    return proxy_hosts_;
  }

  size_t proxy_hosts_size() { return proxy_hosts_.size(); }

  std::unique_ptr<RenderFrameHostImpl> TakeRenderFrameHost();

  // Must be called before `TakeProxyHosts()` or `TakeRenderViewHosts()`
  // is called.
  void PrepareToRestore();

  RenderFrameProxyHostMap TakeProxyHosts();
  RenderViewHostImplSafeRefSet TakeRenderViewHosts();

  void SetViewTransitionState(
      std::optional<blink::ViewTransitionState> view_transition_state);
  std::optional<blink::ViewTransitionState> TakeViewTransitionState();

  // The frame that was focused within this page when it was stored, so that the
  // focused frame can be restored on activation (e.g. for back-forward cache).
  // Invalid if no frame in this page was focused.
  void set_focused_frame_tree_node_id(FrameTreeNodeId id) {
    focused_frame_tree_node_id_ = id;
  }
  FrameTreeNodeId focused_frame_tree_node_id() const {
    return focused_frame_tree_node_id_;
  }

 private:
  void ClearAllObservers();

  bool cleared_observers_ = false;

  // The main document being stored.
  std::unique_ptr<RenderFrameHostImpl> render_frame_host_;

  // Proxies of the main document as seen by other processes.
  // Currently, we only store proxies for SiteInstanceGroups of all subframes on
  // the page, because pages using window.open and nested WebContents are
  // not cached.
  RenderFrameProxyHostMap proxy_hosts_;

  // RenderViewHosts belonging to the main frames, and its proxies (if any).
  // Note this includes all `RenderViewHost`s from inner frame trees as well.
  //
  // While RenderViewHostImpl(s) are in the BackForwardCache, they aren't
  // reused for pages outside the cache. This prevents us from having two
  // main frames, (one in the cache, one live), associated with a single
  // RenderViewHost.
  //
  // Keeping these here also prevents RenderFrameHostManager code from
  // unwittingly iterating over RenderViewHostImpls that are in the cache.
  RenderViewHostImplSafeRefSet render_view_hosts_;

  // Additional parameters to send with SetPageLifecycleState calls when
  // we're restoring a page from the back-forward cache.
  blink::mojom::PageRestoreParamsPtr page_restore_params_;

  raw_ptr<Delegate> delegate_ = nullptr;

  // View transition state to use when the page is activated, either via BFCache
  // activation or prerender activation.
  std::optional<blink::ViewTransitionState> view_transition_state_;

  // The frame focused within this page when it was stored. See the accessors
  // above.
  FrameTreeNodeId focused_frame_tree_node_id_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_STORED_PAGE_H_
