// Copyright 2023 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_LOADER_RESOURCE_CACHE_MANAGER_H_
#define CONTENT_BROWSER_LOADER_RESOURCE_CACHE_MANAGER_H_

#include <map>
#include <memory>
#include <set>

#include "base/containers/unique_ptr_adapters.h"
#include "base/memory/weak_ptr.h"
#include "content/browser/process_lock.h"
#include "content/common/content_export.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_frame_host.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/mojom/loader/resource_cache.mojom.h"
#include "url/origin.h"

namespace content {

class NavigationRequest;
class RenderFrameHostImpl;
class ResourceCacheHostObserver;

// Used as a key to group ResourceCache.
struct ResourceCacheKey {
  ResourceCacheKey(const ProcessLock& process_lock, const url::Origin& origin);

  bool operator==(const ResourceCacheKey& rhs) const;
  bool operator<(const ResourceCacheKey& rhs) const;

  const ProcessLock process_lock;
  const url::Origin origin;
};

// Manages ResourceCache. Refer to
// //third_party/blink/public/mojom/loader/resource_cache.mojom for what
// ResourceCache does.
// ResourceCacheManager maintains mojo connections to ResourceCache instances,
// which live in renderers, keyed with ProcessLock and origin. Upon navigation
// commit, it tries to find or create a mojo remote to ResourceCache when there
// is no active ResourceCache for the newly committing document for the given
// ProcessLock/origin. The ResourceCache will be created and hosted by another
// existing renderer, not the one that we're about to commit to. The remote
// might also be used for subsequent navigation commits to pass the (cloned)
// remote to another frame in a new renderer that has the same
// ProcessLock/origin. When a remote is disconnected, ResourceCacheManager tries
// to find a renderer to host a ResourceCache for the same ProcessLock/origin.
//
// ResourceCacheManager and ResourceCache exist only to record histograms.
// These shouldn't plumb resources among renderers.
// Exists per profile (i.e. owned by StoragePartition).
class CONTENT_EXPORT ResourceCacheManager {
 public:
  ResourceCacheManager();
  ~ResourceCacheManager();

  // Initializes `pending_remote` when an existing renderer has the same
  // process lock and the renderer has a ResourceCache with the same
  // Process lock/origin as the navigation. If the existing renderer
  // doesn't host a ResourceCache, creates a ResourceCache in an existing
  // renderer and initializes `pending_remote` with it. If no matching renderer
  // exists, `pending_remote` stays uninitialized.
  // `navigation_request` must be about to commit navigation.
  void MaybeInitializeResourceCacheRemoteOnCommitNavigation(
      mojo::PendingRemote<blink::mojom::ResourceCache>& pending_remote,
      NavigationRequest& navigation_request);

  // Called when a RenderFrameHostImpl became ineligible to host a
  // ResourceCache, e.g., the renderer is deleted or is stored in BFCache.
  void RenderFrameHostBecameIneligible(RenderFrameHostImpl& render_frame_host);

  // Called when lifecycle state of `render_frame_host` has changed.
  void RenderFrameHostStateChanged(RenderFrameHostImpl& render_frame_host,
                                   RenderFrameHost::LifecycleState old_state,
                                   RenderFrameHost::LifecycleState new_state);

  // Destroys a host observer.
  void DestroyHostObserver(ResourceCacheHostObserver* observer);

  // Returns true when a frame associated with `render_frame_host` is
  // hosting a ResourceCache.
  bool IsRenderFrameHostHostingRemoteCache(
      RenderFrameHostImpl& render_frame_host);

 private:
  struct ResourceCacheEntry {
    ResourceCacheEntry(mojo::Remote<blink::mojom::ResourceCache> remote,
                       GlobalRenderFrameHostId render_frame_host_id);
    ~ResourceCacheEntry();

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

    mojo::Remote<blink::mojom::ResourceCache> remote;
    GlobalRenderFrameHostId render_frame_host_id;
  };

  // Inserts `render_frame_host_id` to `non_hosting_frame_hosts_`.
  void InsertNonHostingRenderFrameId(
      const ResourceCacheKey& key,
      GlobalRenderFrameHostId render_frame_host_id);

  // Looks for an active ResourceCache. If not found, tries to create a new
  // ResourceCache in a renderer. Returns a RenderFrameHostImpl that hosts the
  // ResourceCache. Returns nullptr when no resource cache is/ found nor
  // created.
  RenderFrameHostImpl* FindOrMaybeCreateResourceCache(
      const ResourceCacheKey& key);

  // Makes `render_frame_host` host a ResourceCache.
  void HostResourceCache(RenderFrameHostImpl& render_frame_host,
                         const ResourceCacheKey& key);

  // Creates blink::mojom::ResourceCache interface from `render_frame_host`.
  mojo::Remote<blink::mojom::ResourceCache> CreateResourceCacheRemote(
      RenderFrameHostImpl& render_frame_host,
      const ResourceCacheKey& key);

  // Called when a mojo remote to a ResourceCache is disconnected, to pick
  // a new renderer to host a ResourceCache for the `key`.
  void OnRemoteCacheDisconnected(const ResourceCacheKey& key,
                                 GlobalRenderFrameHostId render_frame_host_id);

  // Contains active remote caches.
  std::map<ResourceCacheKey, ResourceCacheEntry> remote_caches_;

  // Keeps track of renderers that don't host ResourceCache. Used to select
  // a new host for a ResourceCache when a renderer that hosted the
  // ResourceCache is disconnected.
  std::map<ResourceCacheKey, std::set<GlobalRenderFrameHostId>>
      non_hosting_frame_hosts_;

  // A set of WebContentsObservers to keep track of lifecycle state changes of
  // RenderFrameHosts that host ResourceCaches. Used to handle lifecycle state
  // changes. If a RenderFrameHost becomes inactive (e.g. pending deletion,
  // BFCached), the host becomes ineligible to host a ResourceCache. If the
  // RenderFrameHost becomes active again, the RenderFrameHost becomes eligible
  // to use/host a ResourceCache.
  std::set<std::unique_ptr<ResourceCacheHostObserver>,
           base::UniquePtrComparator>
      host_observers_;

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

}  // namespace content

#endif  // CONTENT_BROWSER_LOADER_RESOURCE_CACHE_MANAGER_H_