// Copyright 2012 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_SECURITY_CPSP_CHILD_PROCESS_SECURITY_POLICY_IMPL_H_
#define CONTENT_BROWSER_SECURITY_CPSP_CHILD_PROCESS_SECURITY_POLICY_IMPL_H_

#include <memory>
#include <string>
#include <string_view>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/singleton.h"
#include "base/metrics/field_trial_params.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "content/browser/can_commit_status.h"
#include "content/browser/isolated_origin_util.h"
#include "content/browser/isolation_context.h"
#include "content/browser/origin_agent_cluster_isolation_state.h"
#include "content/common/content_export.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/child_process_id.h"
#include "storage/common/file_system/file_system_types.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
#include "url/origin.h"

class GURL;

namespace base {
class FilePath;
}  // namespace base

namespace network {
class ResourceRequestBody;
}  // namespace network

namespace storage {
class FileSystemContext;
class FileSystemURL;
}  // namespace storage

namespace content {

class BrowserContext;
class IsolationContext;
class ProcessLock;
struct UrlInfo;

// Determines how the experimental Rust ChildProcessSecurityPolicy
// implementation should be enabled.
enum class RustPolicy {
  // The Rust ChildProcessSecurityPolicy implementation is not used, and only
  // the legacy C++ implementation is used.
  kCppOnly,
  // The Rust ChildProcessSecurityPolicy implementation is used, and the legacy
  // C++ implementation is not used.
  kRustOnly,
  // Both Rust and C++ ChildProcessSecurityPolicy implementations run in
  // parallel, and runtime checks ensure that they match.
  kRustAndCpp,
};

CONTENT_EXPORT extern const base::FeatureParam<RustPolicy> kRustPolicyParam;

// Which feature flag a given Rust migration function depends on. Using an enum
// rather than base::Feature directly allows us to limit to just the valid
// feature flags for this Rust migration.
enum class CpspRustFeature {
  // Identifies cases that depend on the main ChildProcessSecurityPolicy Rust
  // feature for global state: features::kChildProcessSecurityPolicyRust.
  kMain,
  // Identifies cases that depend on the per-process ChildProcessSecurityPolicy
  // Rust feature for ProcessState:
  // features::kChildProcessSecurityPolicyRustProcessState.
  kProcessState,
};

// Note: This class's implementation is migrating to Rust in
// https://crbug.com/482216433. Existing functions will be replaced with
// wrappers that can forward to a Rust implementation or a C++ implementation
// (named with a _Cpp suffix), based on the following feature flags:
//   --enable-features=ChildProcessSecurityPolicyRust for only running the Rust
//     implementations.
//   --enable-features=ChildProcessSecurityPolicyRust:policy/rust-and-cpp for
//     running both implementations and ensuring their results match. By
//     default, only the C++ implementations are used.
// The _Cpp implementations and the wrapper functions will be removed after
// the Rust features have launched.
class CONTENT_EXPORT ChildProcessSecurityPolicyImpl
    : public ChildProcessSecurityPolicy {
 public:
  // Handle used to access the security state for a specific process.
  //
  // Objects that require the process state to be preserved beyond the
  // lifetime of the RenderProcessHostImpl should hold an instance of this
  // object and use it to answer security policy questions. (e.g. Mojo services
  // created by RPHI that can receive calls after RPHI destruction). This
  // object should only be called on the UI and IO threads.
  //
  // Note: Some security methods, like CanAccessDataForOrigin(), require
  // information from the BrowserContext to make its decisions. These methods
  // will fall back to failsafe values if called after BrowserContext
  // destruction. Callers should be prepared to gracefully handle this or
  // ensure that they don't make any calls after BrowserContext destruction.
  class CONTENT_EXPORT Handle {
   public:
    Handle();
    Handle(Handle&&);
    Handle(const Handle&) = delete;
    ~Handle();

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

    // Create a new instance of Handle, holding another reference to the same
    // process ID as the current one.
    Handle Duplicate();

    // Returns true if this object has a valid process ID.
    // Returns false if this object was created with the default constructor,
    // the contents of this object was transferred to another Handle via
    // std::move(), or ChildProcessSecurityPolicyImpl::CreateHandle()
    // created this object after the process has already been destructed.
    bool is_valid() const;

    // Before servicing a child process's request to upload a file to the web,
    // the browser should call this method to determine whether the process has
    // the capability to upload the requested file.
    bool CanReadFile(const base::FilePath& file);

    // Explicit permissions checks for FileSystemURL specified files.
    bool CanReadFileSystemFile(const storage::FileSystemURL& url);
    bool CanWriteFileSystemFile(const storage::FileSystemURL& url);
    bool CanCreateFileSystemFile(const storage::FileSystemURL& url);
    bool CanDeleteFileSystemFile(const storage::FileSystemURL& url);
    bool CanMoveFileSystemFile(const storage::FileSystemURL& src_url,
                               const storage::FileSystemURL& dest_url);
    bool CanCopyFileSystemFile(const storage::FileSystemURL& src_url,
                               const storage::FileSystemURL& dest_url);

    // Returns true if the process is permitted to read and modify the data for
    // the given `origin`. For more details, see
    // ChildProcessSecurityPolicy::CanAccessDataForOrigin().
    bool CanAccessDataForOrigin(const url::Origin& origin);

    // Returns the original `child_id` used to create the handle.
    ChildProcessId child_id() { return child_id_; }

   private:
    friend class ChildProcessSecurityPolicyImpl;
    // |child_id| - The ID of the process that this Handle is being created
    // for, or ChildProcessHost::kInvalidUniqueID if an invalid handle is being
    // created.
    // |duplicating_handle| - True if the handle is being created by a
    // Duplicate() call. Otherwise false. This is used to trigger special
    // behavior for handle duplication that is not allowed for Handles created
    // by other means.
    Handle(ChildProcessId child_id, bool duplicating_handle);

    // The ID of the child process that this handle is associated with or
    // ChildProcessHost::kInvalidUniqueID if the handle is no longer valid.
    ChildProcessId child_id_;
  };

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

  // Object can only be created through GetInstance() so the constructor is
  // private.
  ~ChildProcessSecurityPolicyImpl() override;

  static ChildProcessSecurityPolicyImpl* GetInstance();

  // ChildProcessSecurityPolicy implementation.
  void RegisterWebSafeScheme(const std::string& scheme) override;
  void RegisterWebSafeScheme_Cpp(const std::string& scheme);
  void RegisterWebSafeIsolatedScheme(const std::string& scheme) override;
  void RegisterWebSafeIsolatedScheme_Cpp(const std::string& scheme);
  bool IsWebSafeScheme(const std::string& scheme) override;
  bool IsWebSafeScheme_Cpp(const std::string& scheme);
  void GrantReadFile(ChildProcessId child_id,
                     const base::FilePath& file) override;
  void GrantCreateReadWriteFile(int child_id,
                                const base::FilePath& file) override;
  void GrantCopyInto(int child_id, const base::FilePath& dir) override;
  void GrantDeleteFrom(int child_id, const base::FilePath& dir) override;
  // TODO(crbug.com/379869738) Remove this method and add the ChildProcessId
  // version to the public API instead when usages are ported.
  void GrantReadFileSystem(int child_id,
                           const std::string& filesystem_id) override;
  void GrantReadFileSystem(ChildProcessId child_id,
                           const std::string& filesystem_id);
  void GrantWriteFileSystem(int child_id,
                            const std::string& filesystem_id) override;
  void GrantCreateFileForFileSystem(int child_id,
                                    const std::string& filesystem_id) override;
  void GrantCreateReadWriteFileSystem(
      int child_id,
      const std::string& filesystem_id) override;
  void GrantCopyIntoFileSystem(int child_id,
                               const std::string& filesystem_id) override;
  void GrantDeleteFromFileSystem(int child_id,
                                 const std::string& filesystem_id) override;
  void GrantCommitOrigin(int child_id, const url::Origin& origin) override;
  void GrantRequestOrigin(int child_id, const url::Origin& origin) override;
  void GrantCommitScheme(int child_id, const std::string& scheme) override;
  void GrantRequestScheme(int child_id, const std::string& scheme) override;
  bool CanRequestURL(int child_id, const GURL& url) override;
  bool CanRequestURL(ChildProcessId child_id, const GURL& url);
  bool CanReadFile(ChildProcessId child_id,
                   const base::FilePath& file) override;
  bool CanCreateReadWriteFile(int child_id,
                              const base::FilePath& file) override;
  bool CanReadFileSystem(int child_id,
                         const std::string& filesystem_id) override;
  bool CanReadWriteFileSystem(int child_id,
                              const std::string& filesystem_id) override;
  bool CanCopyIntoFileSystem(int child_id,
                             const std::string& filesystem_id) override;
  bool CanDeleteFromFileSystem(int child_id,
                               const std::string& filesystem_id) override;
  bool HasWebUIBindings(int child_id) override;
  void GrantSendMidiMessage(int child_id) override;
  void GrantSendMidiMessage_Cpp(int child_id);
  void GrantSendMidiSysExMessage(int child_id) override;
  void GrantSendMidiSysExMessage_Cpp(int child_id);
  bool CanAccessDataForOrigin(int child_id, const url::Origin& origin) override;
  bool HostsOrigin(int child_id, const url::Origin& origin) override;
  void AddFutureIsolatedOrigins(
      std::string_view origins_list,
      IsolatedOriginSource source,
      BrowserContext* browser_context = nullptr) override;
  void AddFutureIsolatedOrigins(
      const std::vector<url::Origin>& origins,
      IsolatedOriginSource source,
      BrowserContext* browser_context = nullptr) override;
  bool IsGloballyIsolatedOriginForTesting(const url::Origin& origin) override;
  std::vector<url::Origin> GetIsolatedOrigins(
      std::optional<IsolatedOriginSource> source = std::nullopt,
      BrowserContext* browser_context = nullptr) override;
  bool IsIsolatedSiteFromSource(const url::Origin& origin,
                                IsolatedOriginSource source) override;
  void ClearIsolatedOriginsForTesting() override;

  // Centralized internal implementation of site isolation enforcements,
  // including CanAccessDataForOrigin and HostsOrigin. It supports the following
  // types of access checks, in order of increasing strictness:
  enum class AccessType {
    // Whether the process can commit a navigation to an origin, allowing a
    // document with that origin to be hosted in this process. This is
    // specifically about whether a particular new origin may be introduced
    // into a given process.
    //
    // This access type can only be used on the UI thread, because it involves
    // jail and citadel checks which require UI thread data structures.
    kCanCommitNewOrigin,
    // Whether the process has previously committed a document or instantiated a
    // worker with the particular origin. This can be used to verify whether a
    // particular origin can be used as an initiator or source origin, e.g. in
    // postMessage or other IPCs sent from this process. Unlike
    // kCanCommitNewOrigin, this check assumes that the origin must already
    // exist in the process. Because a document/worker destruction may race with
    // processing legitimate IPCs on behalf of `origin`, this check also allows
    // the case where an origin has been hosted by the process in the past, but
    // not necessarily now.
    //
    // This access type can be used on any thread.
    kHostsOrigin,
    // Whether the process can access data belonging to an origin already
    // committed in the process, such as passwords, localStorage, or cookies.
    // Similarly to kHostsOrigin, this check assumes that the origin must
    // already
    // exist in the process, but it is more strict for certain kinds of
    // processes that aren't supposed to access any data. For example, sandboxed
    // frame processes (which contain only opaque origins) or PDF processes
    // cannot access data for any origin.
    //
    // This access type can be used on any thread.
    kCanAccessDataForCommittedOrigin,
  };
  bool CanAccessOrigin(int child_id,
                       const url::Origin& origin,
                       AccessType access_type);

  // Determines if the combination of origin, url and web_exposed_isolation_info
  // bundled in `url_info` are safe to commit to the process associated with
  // `child_id`.
  //
  // Returns CAN_COMMIT_ORIGIN_AND_URL if it is safe to commit `url_info` origin
  // and `url_info`'s url combination to the process associated with `child_id`.
  // Returns CANNOT_COMMIT_URL if `url_info` url is not safe to commit.
  // Returns CANNOT_COMMIT_ORIGIN if `url_info` origin is not safe to commit.
  CanCommitStatus CanCommitOriginAndUrl(
      int child_id,
      const IsolationContext& isolation_context,
      const UrlInfo& url_info);

  // Whether the process is allowed to commit a document from the given URL.
  // This is more restrictive than CanRequestURL, since CanRequestURL allows
  // requests that might lead to cross-process navigations or external protocol
  // handlers. Used primarily as a helper for CanCommitOriginAndUrl and thus not
  // exposed publicly.
  bool CanCommitURL(int child_id, const GURL& url);

  // This function will check whether |origin| requires process isolation
  // within |isolation_context|, and if so, it will return true and put the
  // most specific matching isolated origin into |result|.
  //
  // Such origins may be registered with the --isolate-origins command-line
  // flag, via features::IsolateOrigins, via an IsolateOrigins enterprise
  // policy, or by a content/ embedder using
  // ContentBrowserClient::GetOriginsRequiringDedicatedProcess().
  //
  // If |origin| does not require process isolation, this function will return
  // false, and |result| will be a unique origin. This means that neither
  // |origin|, nor any origins for which |origin| is a subdomain, have been
  // registered as isolated origins.
  //
  // For example, if both https://isolated.com/ and
  // https://bar.foo.isolated.com/ are registered as isolated origins, then the
  // values returned in |result| are:
  //   https://isolated.com/             -->  https://isolated.com/
  //   https://foo.isolated.com/         -->  https://isolated.com/
  //   https://bar.foo.isolated.com/     -->  https://bar.foo.isolated.com/
  //   https://baz.bar.foo.isolated.com/ -->  https://bar.foo.isolated.com/
  //   https://unisolated.com/           -->  (unique origin)
  //
  // |isolation_context| is used to determine which origins are isolated in
  // this context.  For example, isolated origins that are dynamically added
  // will only affect future BrowsingInstances.
  bool GetMatchingProcessIsolatedOrigin(
      const IsolationContext& isolation_context,
      const url::Origin& origin,
      bool requests_origin_keyed_process,
      url::Origin* result);

  // Removes any state associated with `browsing_instance_id`.
  void RemoveAllStateForBrowsingInstance(
      const BrowsingInstanceId& browsing_instance_id);

  // Registers |origin| isolation state in the BrowsingInstance associated
  // with |isolation_context|.
  //
  // |oac_isolation_state| is the Origin-Agent-Cluster to register for the
  // origin. It contains values describing both the logical isolation (i.e.
  // agent cluster separation in the renderer process) and the process isolation
  // that can be triggered by the Origin-Agent-Cluster header, the
  // kOriginKeyedProcessesByDefault feature and the
  // kOriginAgentClusterDefaultEnabled feature.
  //
  // If |origin| has already been registered as isolated for the same
  // BrowsingInstance, then nothing will be changed by this call.
  void AddOriginAgentClusterStateForBrowsingInstance(
      const IsolationContext& isolation_context,
      const url::Origin& origin,
      const OriginAgentClusterIsolationState& oac_isolation_state);
  void AddOriginAgentClusterStateForBrowsingInstance_Cpp(
      const BrowsingInstanceId& browsing_instance_id,
      const url::Origin& origin,
      const OriginAgentClusterIsolationState& oac_isolation_state,
      const OriginAgentClusterIsolationState& default_isolation_state);

  // Adds `origin` to the IsolatedOrigins list for only the BrowsingInstance of
  // `isolation_context`, without isolating all subdomains. For use when the
  // isolation is triggered by COOP headers.
  void AddCoopIsolatedOriginForBrowsingInstance(
      const IsolationContext& isolation_context,
      const url::Origin& origin,
      IsolatedOriginSource source);

  // This function checks whether |origin| has opted-in to logical or process
  // isolation (via the Origin-Agent-Cluster header), with respect to the
  // current state of the |isolation_context|. It is different from
  // IsIsolatedOrigin() in that it only deals with Origin-Agent-Cluster
  // isolation status, whereas IsIsolatedOrigin() considers all possible
  // mechanisms for requesting isolation. It checks for two things:
  // 1) whether |origin| already is assigned to a SiteInstance in the
  //    |isolation_context| by being tracked in
  //    |origin_agent_cluster_by_browsing_instance_|, in which case we follow
  //    the same policy, or
  // 2) if it's not currently tracked as described above, whether |origin| is
  //    currently requesting isolation via |requested_isolation_state|.
  OriginAgentClusterIsolationState DetermineOriginAgentClusterIsolation(
      const IsolationContext& isolation_context,
      const url::Origin& origin,
      const OriginAgentClusterIsolationState& requested_isolation_state);

  // This function adds |origin| to a list of origins that have explicitly
  // requested an Origin-Agent-Cluster state (either opting in or opting out) in
  // the given |browser_context|. Returns true if |origin| was newly added to
  // the list, or false if it had already been recorded or it is not eligible
  // for origin isolation.
  bool RecordOriginAgentClusterRequestIfNew(BrowserContext* browser_context,
                                            const url::Origin& origin);
  bool RecordOriginAgentClusterRequestIfNew_Cpp(BrowserContext* browser_context,
                                                const url::Origin& origin);

  // A version of GetMatchingProcessIsolatedOrigin that takes in both the
  // |origin| and the |site_url| that |origin| corresponds to.  |site_url| is
  // the key by which |origin| will be looked up in |isolated_origins_| within
  // |isolation_context|; this function allows it to be passed in when it is
  // already known to avoid recomputing it internally.
  bool GetMatchingProcessIsolatedOrigin(
      const IsolationContext& isolation_context,
      const url::Origin& origin,
      bool requests_origin_keyed_process,
      const GURL& site_url,
      url::Origin* result);

  // A version of GetMatchingProcessIsolatedOrigin that only checks the
  // list of isolated origins (e.g. command-line or dynamically registered
  // ones) and bypasses any checks for origin-keyed agent clusters (OAC).
  bool GetMatchingProcessIsolatedOriginFromLegacyOriginList(
      const IsolationContext& isolation_context,
      const url::Origin& origin,
      const GURL& site_url,
      url::Origin* result);

  // Stores the v8-optimization state for the passed-in `browsing_instance_id`
  // and `process_lock_origin` if the state isn't already cached.
  void AddV8OptimizationDisabledStateForOriginIfNotCached(
      const BrowsingInstanceId& browsing_instance_id,
      const url::Origin& process_lock_origin,
      bool are_v8_optimizations_disabled);
  void AddV8OptimizationDisabledStateForOriginIfNotCached_Cpp(
      const BrowsingInstanceId& browsing_instance_id,
      const url::Origin& process_lock_origin,
      bool are_v8_optimizations_disabled);

  // Returns whether v8-optimization should be disabled for the passed-in
  // (`browsing_instance_id`, `process_lock_origin`) pair. Returns std::nullopt
  // if there is no cached v8-optimization verdict.
  std::optional<bool> LookupAreV8OptimizationsDisabled(
      const BrowsingInstanceId& browsing_instance_id,
      const url::Origin& process_lock_origin);
  std::optional<bool> LookupAreV8OptimizationsDisabled_Cpp(
      const BrowsingInstanceId& browsing_instance_id,
      const url::Origin& process_lock_origin);

  // Returns if |child_id| can read all of the |files|.
  bool CanReadAllFiles(ChildProcessId child_id,
                       const std::vector<base::FilePath>& files);

  // Validate that |child_id| in |file_system_context| is allowed to access
  // data in the POST body specified by |body|.  Can be called on any thread.
  bool CanReadRequestBody(
      ChildProcessId child_id,
      const storage::FileSystemContext* file_system_context,
      const scoped_refptr<network::ResourceRequestBody>& body);

  // Validate that `process` is allowed to access data in the POST body
  // specified by |body|.  Has to be called on the UI thread.
  bool CanReadRequestBody(
      RenderProcessHost* process,
      const scoped_refptr<network::ResourceRequestBody>& body);

  // Grants the network service the capability to upload a specific file on
  // the browser's behalf. The owner_token ties the lifetime of the grant to
  // an object (e.g. SimpleURLLoader) so it can be revoked when the object is
  // destroyed.
  void GrantFileForBrowserUpload(const base::UnguessableToken& owner_token,
                                 const base::FilePath& file);
  void GrantFileForBrowserUpload_Cpp(const base::UnguessableToken& owner_token,
                                     const base::FilePath& file);

  // Revokes all file accesses previously granted to the specific owner_token.
  void RevokeFileForBrowserUpload(const base::UnguessableToken& owner_token);
  void RevokeFileForBrowserUpload_Cpp(
      const base::UnguessableToken& owner_token);

  // Verifies whether the browser process has granted the network service
  // permission to upload the given file.
  bool CanReadFileForBrowserUpload(const base::FilePath& file);
  bool CanReadFileForBrowserUpload_Cpp(const base::FilePath& file);

  // Pseudo schemes are treated differently than other schemes because they
  // cannot be requested like normal URLs.  There is no mechanism for revoking
  // pseudo schemes.
  void RegisterPseudoScheme(const std::string& scheme);
  void RegisterPseudoScheme_Cpp(const std::string& scheme);

  // Returns true iff |scheme| has been registered as pseudo scheme.
  bool IsPseudoScheme(const std::string& scheme);
  bool IsPseudoScheme_Cpp(const std::string& scheme);

  // Upon creation, child processes should register themselves by calling this
  // this method exactly once. This call must be made on the UI thread.
  void Add(ChildProcessId child_id, BrowserContext* browser_context);

  // Helper method for unit tests that calls Add() and
  // LockProcess() with an "allow_any_site" lock. This ensures that the process
  // policy is always in a state where it is valid to call
  // CanAccessDataForOrigin().
  void AddForTesting(ChildProcessId child_id, BrowserContext* browser_context);

  // Upon destruction, child processes should unregister themselves by calling
  // this method exactly once. This call must be made on the UI thread.
  //
  // Note: Pre-Remove() permissions remain in effect on the IO thread until
  // the task posted to the IO thread by this call runs and removes the entry
  // from |pending_remove_state_|.
  // This UI -> IO task sequence ensures that any pending tasks, on the IO
  // thread, for this |child_id| are allowed to run before access is completely
  // revoked.
  void Remove(ChildProcessId child_id);

  // Whenever the browser processes commands the child process to commit a URL,
  // it should call this method to grant the child process the capability to
  // commit anything from the URL's origin, along with permission to request all
  // URLs of the same scheme.
  void GrantCommitURL(int child_id, const GURL& url);

  // Whenever the browser process drops a file icon on a tab, it should call
  // this method to grant the child process the capability to request this one
  // file:// URL (or content:// URL in android), but not all urls of the file://
  // scheme.
  void GrantRequestOfSpecificFile(ChildProcessId child_id,
                                  const base::FilePath& file);

#if BUILDFLAG(IS_CHROMEOS)
  // Grants the child process the capability to request a specific external file
  // URL, but not all URLs of the same scheme.
  void GrantRequestOfExternalFileUrl(ChildProcessId child_id, const GURL& url);

  // Grants the child process the capability to commit a specific externalfile
  // URL.
  void GrantCommitOfExternalFileUrl(ChildProcessId child_id, const GURL& url);
#endif

  // Revokes all permissions granted to the given file.
  void RevokeAllPermissionsForFile(ChildProcessId child_id,
                                   const base::FilePath& file);

  // Grant the child process the ability to use Web UI Bindings.
  void GrantWebUIBindings(int child_id, BindingsPolicySet bindings);

  // Some APIs for Android WebView and <webview> tags allow bypassing some
  // security checks, such as which URLs are allowed to commit. This method
  // grants that ability to any document with an origin used with these APIs,
  // because the exemption is needed for about:blank frames that inherit the
  // same origin.
  //
  // For safety, this is limited to opaque origins used with LoadDataWithBaseURL
  // in unlocked processes, as well as file origins used with
  // allow_universal_access_from_file_urls.
  //
  // Note that LoadDataWithBaseURL can be used with non-opaque origins as well,
  // but in that case the bypass is only allowed for the document and not the
  // entire origin, to prevent other code in the origin from bypassing checks.
  void GrantOriginCheckExemptionForWebView(int child_id,
                                           const url::Origin& origin);

  // Returns whether the given opaque or file origin was granted an exemption
  // due to Android WebView and <webview> APIs, allowing its documents to bypass
  // certain URL and origin checks.
  bool HasOriginCheckExemptionForWebView(int child_id,
                                         const url::Origin& origin);

  // Explicit permissions checks for FileSystemURL specified files.
  bool CanReadFileSystemFile(ChildProcessId child_id,
                             const storage::FileSystemURL& filesystem_url);
  bool CanWriteFileSystemFile(ChildProcessId child_id,
                              const storage::FileSystemURL& filesystem_url);
  bool CanCreateFileSystemFile(ChildProcessId child_id,
                               const storage::FileSystemURL& filesystem_url);
  bool CanCreateReadWriteFileSystemFile(
      ChildProcessId child_id,
      const storage::FileSystemURL& filesystem_url);
  bool CanCopyIntoFileSystemFile(ChildProcessId child_id,
                                 const storage::FileSystemURL& filesystem_url);
  bool CanDeleteFileSystemFile(ChildProcessId child_id,
                               const storage::FileSystemURL& filesystem_url);
  bool CanMoveFileSystemFile(ChildProcessId child_id,
                             const storage::FileSystemURL& src_url,
                             const storage::FileSystemURL& dest_url);
  bool CanCopyFileSystemFile(ChildProcessId child_id,
                             const storage::FileSystemURL& src_url,
                             const storage::FileSystemURL& dest_url);

  // Notifies process state of |child_id| about the IsolationContext it will
  // host.  The main side effect is proper setting of the lowest
  // BrowsingInstanceId associated with the process state.
  void IncludeIsolationContext(int child_id,
                               const IsolationContext& isolation_context);

  // Sets the process identified by |child_id| as only permitted to access data
  // for the origin specified by |site_info|'s process_lock_url(). Most callers
  // should use RenderProcessHostImpl::SetProcessLock instead of calling this
  // directly. |isolation_context| provides the context, such as
  // BrowsingInstance, from which this process locked was created. This
  // information is used when making isolation decisions for this process, such
  // as determining which isolated origins pertain to it. |is_process_used|
  // indicates whether any content has been loaded in the process already.
  void LockProcess(const IsolationContext& isolation_context,
                   ChildProcessId child_id,
                   bool is_process_used,
                   const ProcessLock& process_lock);

  // Testing helper method that generates a lock_url from |url| and then
  // calls LockProcess() with that lock URL.
  void LockProcessForTesting(const IsolationContext& isolation_context,
                             ChildProcessId child_id,
                             const GURL& url);

  // Retrieves the current ProcessLock of process |child_id|.  Returns an empty
  // lock if the process does not exist or if it is not locked.
  ProcessLock GetProcessLock(ChildProcessId child_id);

  // TODO(crbug.com/379869738) Remove this method when usages are ported.
  ProcessLock GetProcessLock(int child_id);

  // Register FileSystem type and permission policy which should be used
  // for the type.  The |policy| must be a bitwise-or'd value of
  // storage::FilePermissionPolicy.
  void RegisterFileSystemPermissionPolicy(storage::FileSystemType type,
                                          int policy);
  void RegisterFileSystemPermissionPolicy_Cpp(storage::FileSystemType type,
                                              int policy);

  // Returns true if sending MIDI messages is allowed.
  bool CanSendMidiMessage(ChildProcessId child_id);
  bool CanSendMidiMessage_Cpp(ChildProcessId child_id);

  // Returns true if sending system exclusive (SysEx) MIDI messages is allowed.
  bool CanSendMidiSysExMessage(ChildProcessId child_id);
  bool CanSendMidiSysExMessage_Cpp(ChildProcessId child_id);

  // Remove all isolated origins associated with |browser_context| and clear any
  // pointers that may reference |browser_context|.  This is
  // typically used when |browser_context| is being destroyed and assumes that
  // no processes are running or will run for that profile; this makes the
  // isolated origin removal safe.  Note that |browser_context| cannot be null;
  // i.e., isolated origins that apply globally to all profiles cannot
  // currently be removed, since that is not safe to do at runtime.
  void RemoveStateForBrowserContext(const BrowserContext& browser_context);

  // Check whether |origin| requires origin-wide process isolation within
  // |isolation_context|.
  //
  // Subdomains of an isolated origin are considered part of that isolated
  // origin.  Thus, if https://isolated.foo.com/ had been added as an isolated
  // origin, this will return true for https://isolated.foo.com/,
  // https://bar.isolated.foo.com/, or https://baz.bar.isolated.foo.com/; and
  // it will return false for https://foo.com/ or https://unisolated.foo.com/.
  //
  // |isolation_context| is used to determine which origins are isolated in
  // this context.  For example, isolated origins that are dynamically added
  // will only affect future BrowsingInstances. |origin_requests_isolation| may
  // be true during navigation requests, and allows us to correctly determine
  // isolation status for an origin that may not have had its isolation status
  // recorded in the BrowsingInstance yet.
  bool IsIsolatedOrigin(const IsolationContext& isolation_context,
                        const url::Origin& origin,
                        bool origin_requests_isolation);

  // Removes a previously added isolated origin, currently only used in tests.
  //
  // TODO(alexmos): Exposing this more generally will require extra care, such
  // as ensuring that there are no active SiteInstances in that origin.
  void RemoveIsolatedOriginForTesting(const url::Origin& origin);

  // Returns false for redirects that must be blocked no matter which renderer
  // process initiated the request (if any).
  // Note: Checking CanRedirectToURL is not enough. CanRequestURL(child_id, url)
  //       represents a stricter subset. It must also be used for
  //       renderer-initiated navigations.
  bool CanRedirectToURL(const GURL& url);

  // Sets "killed_process_origin_lock" crash key with lock info for the
  // process associated with |child_id|.
  void LogKilledProcessOriginLock(int child_id);

  // Creates a Handle object for a specific child process ID.
  //
  // This handle can be used to extend the lifetime of policy state beyond the
  // Remove() call for |child_id|. This should be used by objects that can
  // outlive the RenderProcessHostImpl object associated with |child_id| and
  // need to be able to make policy decisions after RPHI destruction. (e.g. Mojo
  // services created by RPHI)
  //
  // Returns a valid Handle if |child_id| is present in |process_states_|.
  // Otherwise it returns a Handle that returns false for all policy checks.
  Handle CreateHandle(ChildProcessId child_id);

  // TODO(crbug.com/379869738) Remove this method when usages are ported.
  inline Handle CreateHandle(int child_id) {
    return CreateHandle(ChildProcessId::FromUnsafeValue(child_id));
  }

  // Returns true if we have seen an explicit Origin-Agent-Cluster header
  // (either opt-in or opt-out) for this |origin| in the given |browser_context|
  // before in any BrowsingInstance.
  bool HasOriginEverRequestedOriginAgentClusterValue(
      BrowserContext* browser_context,
      const url::Origin& origin);
  bool HasOriginEverRequestedOriginAgentClusterValue_Cpp(
      const base::UnguessableToken& browser_context_id,
      const url::Origin& origin);

  // Records |origin| as having the default isolation state for the
  // BrowsingInstance specified by |isolation_context|, if we need to track it
  // and it's not already in the list.
  //
  // |is_global_walk_or_frame_removal| should be set to true during the global
  // walk that is triggered when |origin| first requests an Origin-Agent-Cluster
  // state, so that the function can skip safety checks that will be unnecessary
  // during the global walk. It is also set to true if this function is called
  // when removing a FrameNavigationEntry, since that entry won't be available
  // to any subsequent global walks.
  void RecordDefaultOriginAgentClusterOriginIfNew(
      const IsolationContext& isolation_context,
      const url::Origin& origin,
      bool is_global_walk_or_frame_removal);
  void RecordDefaultOriginAgentClusterOriginIfNew_Cpp(
      const BrowsingInstanceId& browsing_instance_id,
      const base::UnguessableToken& browser_context_id,
      const url::Origin& origin,
      const OriginAgentClusterIsolationState& oac_isolation_state,
      bool is_global_walk_or_frame_removal);

  // Add `origin` to the list of committed origins for the process identified by
  // `child_id`. An attempt to add the same origin more than once is safely
  // ignored. Note that there is currently no way to revoke an origin once it
  // has been committed, even if all associated documents and workers go away.
  // This might need to be revisited in the future if the list of committed
  // origins grows too large.
  void AddCommittedOrigin(int child_id, const url::Origin& origin);

  // Allows tests to modify the delay in cleaning up BrowsingInstanceIds. If the
  // delay is set to zero, cleanup happens immediately.
  void SetBrowsingInstanceCleanupDelayForTesting(int64_t delay_in_seconds) {
    browsing_instance_cleanup_delay_ = base::Seconds(delay_in_seconds);
  }

  // Allows tests to query the number of BrowsingInstanceIds associated with a
  // child process.
  size_t BrowsingInstanceIdCountForTesting(ChildProcessId child_id);

  void ClearRegisteredSchemeForTesting(const std::string& scheme);
  void ClearRegisteredSchemeForTesting_Cpp(const std::string& scheme);

  // Clears and re-registers the default web-safe and pseudo schemes. Used to
  // reset state between unit tests.
  void ResetRegisteredSchemesForTesting();

  // Checks if the provided `url` matches any committed origin in the process
  // `child_id`. Currently only exposed for testing, since normally this check
  // happens within CanAccessMaybeOpaqueOrigin().
  bool MatchesCommittedOriginForTesting(ChildProcessId child_id,
                                        const GURL& url,
                                        bool url_is_for_precursor_origin);

  // Exposes LookupOriginAgentClusterState() for tests.
  std::optional<OriginAgentClusterIsolationState>
  LookupOriginAgentClusterStateForTesting(
      const BrowsingInstanceId& browsing_instance_id,
      const url::Origin& origin);

  // For legacy isolated origin tests, this helper returns the number of
  // `IsolatedOriginEntry` entries that match the provided `origin`. An origin
  // can have multiple entries when it's isolated in several BrowserContexts.
  // Only counts precise origin matches, without subdomain matching.
  int GetIsolatedOriginEntryCountForTesting(const url::Origin& origin);

 private:
  friend class ChildProcessSecurityPolicyInProcessBrowserTest;
  friend class ChildProcessSecurityPolicyTest;
  friend class ChildProcessSecurityPolicyImpl::Handle;
  FRIEND_TEST_ALL_PREFIXES(ChildProcessSecurityPolicyInProcessBrowserTest,
                           NoLeak);
  FRIEND_TEST_ALL_PREFIXES(ChildProcessSecurityPolicyTest, FilePermissions);
  FRIEND_TEST_ALL_PREFIXES(ChildProcessSecurityPolicyTest,
                           IsolateAllSuborigins);
  FRIEND_TEST_ALL_PREFIXES(ChildProcessSecurityPolicyTest,
                           IsWebSafeIsolatedSchemeTest);
  FRIEND_TEST_ALL_PREFIXES(
      ChildProcessSecurityPolicyTest_NoOriginKeyedProcessesByDefault,
      WildcardAndNonWildcardOrigins);
  FRIEND_TEST_ALL_PREFIXES(
      ChildProcessSecurityPolicyTest_NoOriginKeyedProcessesByDefault,
      WildcardAndNonWildcardEmbedded);
  FRIEND_TEST_ALL_PREFIXES(ChildProcessSecurityPolicyTest,
                           ParseIsolatedOrigins);
  FRIEND_TEST_ALL_PREFIXES(ChildProcessSecurityPolicyTest, WildcardDefaultPort);
  FRIEND_TEST_ALL_PREFIXES(ChildProcessSecurityPolicyTest,
                           MatchesCommittedOrigin);
  FRIEND_TEST_ALL_PREFIXES(
      ChildProcessSecurityPolicyTest,
      AddOriginAgentClusterStateForBrowsingInstanceConsistency);
  FRIEND_TEST_ALL_PREFIXES(
      ChildProcessSecurityPolicyTest,
      RecordDefaultOriginAgentClusterOriginIfNewConsistency);

  class ProcessState;

  using SchemeSet = absl::flat_hash_set<std::string>;
  using FileSystemPermissionPolicyMap =
      absl::flat_hash_map<storage::FileSystemType, int>;

  // Data structure that tracks ProcessState for each RenderProcessHost based
  // on ChildProcessId. A registered ProcessState is guaranteed to exist both
  // while the RenderProcessHost exists and until all of the
  // ChildProcessSecurityPolicy::Handles for the process have gone away, as
  // tracked by reference counting within this class.
  //
  // The ProcessState can only be modified while the RenderProcessHost exists,
  // so that no new permissions can be granted after it is deleted. Queries for
  // the state can continue to be safely serviced until the Handles are gone.
  //
  // All ProcessState query functions should use `GetProcessStateForQuery` to
  // look up ProcessState. This approach looks for the state in both maps and
  // returns a const ProcessState that won't allow mutation.
  //
  // All ProcessState mutator functions must use `GetProcessStateForMutation`
  // to look up ProcessState, which is enforced by the compiler because
  // `GetProcessStateForQuery` is the only other accessor to the internal maps
  // and returns a const ProcessState.
  //
  // This can be accessed from any thread, because the only instance of this
  // class is guarded by ChildProcessSecurityPolicyImpl::lock_.
  class CONTENT_EXPORT ProcessStateMaps {
   public:
    ProcessStateMaps();
    ~ProcessStateMaps();

    // Registers a new ProcessState for `child_id`. Crashes if this ID has
    // already been registered.
    void CreateStateForProcess(ChildProcessId child_id,
                               BrowserContext* browser_context);

    // Gets the ProcessState object associated with `child_id`, for callers
    // that want to query but not modify the state. See
    // `GetProcessStateForMutation` for callers that want to modify the state.
    //
    // This function consults both the live `process_state_` map and the
    // `pending_remove_state_` map, to ensure queries can access state both
    // while the RenderProcessHost exists and for a short time afterwards, as
    // long as any ChildProcessSecurityPolicy::Handles exist. This allows
    // queries to succeed on other threads until they hear about the process's
    // deletion.
    //
    // Note: Returned object is only valid for the duration the caller holds
    // `lock_`.
    const ProcessState* GetProcessStateForQuery(ChildProcessId child_id);

    // Gets the ProcessState object associated with `child_id`, for callers
    // that want to modify the state. Callers that only want to query the state
    // must not use this, and should use `GetProcessStateForQuery` instead.
    //
    // This function only consults the live `process_state_` map and not the
    // `pending_remove_state_` map, to ensure that ProcessState can only be
    // modified while the RenderProcessHost still exists.
    //
    // Note: Returned object is only valid for the duration the caller holds
    // `lock_`.
    ProcessState* GetProcessStateForMutation(ChildProcessId child_id);

    // Updates reference counts for `child_id` both when the process is
    // registered and when a Handle is created. If `duplicating_handle` is
    // false, then this will fail and return false if the RenderProcessHost has
    // already been destroyed.
    bool AddProcessReference(ChildProcessId child_id, bool duplicating_handle);

    // Updates reference counts for `child_id` when the RenderProcessHost or any
    // of its Handles are destroyed. When all have been destroyed, this cleans
    // up the ProcessState from the `pending_remove_state_` map.
    void RemoveProcessReference(ChildProcessId child_id);

    // Helper function for CPSPI::RemoveAllStateForBrowsingInstance.
    void RemoveStateForBrowsingInstance(
        const BrowsingInstanceId browsing_instance_id);

    // Helper function for CPSPI::RemoveStateForBrowserContext.
    void ClearBrowserContextIfMatches(const BrowserContext& browser_context);

    // When the RenderProcessHost with `child_id` is deleted, this function
    // transitions the ProcessState to `pending_remove_state_`, which continues
    // to be used for queries until all Handles have been deleted. No changes
    // should be made to the ProcessState after this transition.
    void PrepareToRemoveState(ChildProcessId child_id);

    // When the RenderProcessHost and all Handles for `child_id` have been
    // deleted, this function removes its ProcessState from ProcessStateMaps
    // entirely. This assumes PrepareToRemoveState has been called already.
    //
    // Note: This runs on the IO thread, to allow time for any pending IO thread
    // tasks to run after the last references for the process have gone away.
    void CompletePendingStateRemoval(ChildProcessId child_id);

    // Returns how many ProcessStates are registered in `process_state_`
    // (omitting those in `pending_remove_state_`).
    size_t GetSizeForTesting();

   private:
    using ProcessStateMap =
        absl::flat_hash_map<ChildProcessId, std::unique_ptr<ProcessState>>;

    // This map holds a ProcessState for each child process, while its
    // RenderProcessHost exists. The key for the map is the ID of the
    // RenderProcessHost. The ProcessState objects are owned by this class and
    // are protected by ChildProcessSecurityPolicy's |lock_|. References to
    // them must not escape ChildProcessSecurityPolicy.
    ProcessStateMap process_state_;

    // This map holds the ProcessState for a child process after its
    // RenderProcessHost is deleted, when Remove() is called on the UI thread.
    // An entry stays in this map until all corresponding
    // ChildProcessSecurityPolicy::Handles are deleted, and then until a task
    // has run on the IO thread. This is necessary to provide consistent
    // security decisions and avoid races between the UI & IO threads during
    // child process shutdown. This separate map is used to preserve
    // ProcessState info AND prevent mutation of that state after Remove() is
    // called.
    ProcessStateMap pending_remove_state_;

    // Contains a mapping between child process ID and the number of outstanding
    // references that want to keep the ProcessState for each process alive.
    // ChildProcessSecurityPolicy and the Handles that it creates increment and
    // decrement the counts in this map. A ProcessState object for a process is
    // only destroyed when its count goes to zero.
    absl::flat_hash_map<ChildProcessId, int> process_reference_counts_;
  };

  // This class holds an isolated origin along with information such as which
  // BrowsingInstances and profile it applies to.  See |isolated_origins_|
  // below for more details.
  class CONTENT_EXPORT IsolatedOriginEntry {
   public:
    IsolatedOriginEntry(const url::Origin& origin,
                        bool applies_to_future_browsing_instances,
                        BrowsingInstanceId browsing_instance_id,
                        const base::UnguessableToken& browser_context_id,
                        bool isolate_all_subdomains,
                        IsolatedOriginSource source);
    // Copyable and movable.
    IsolatedOriginEntry(const IsolatedOriginEntry& other);
    IsolatedOriginEntry& operator=(const IsolatedOriginEntry& other);
    IsolatedOriginEntry(IsolatedOriginEntry&& other);
    IsolatedOriginEntry& operator=(IsolatedOriginEntry&& other);
    ~IsolatedOriginEntry();

    // Allow this class to be used as a key in STL.
    bool operator<(const IsolatedOriginEntry& other) const {
      return std::tie(origin_, applies_to_future_browsing_instances_,
                      browsing_instance_id_, browser_context_id_,
                      isolate_all_subdomains_, source_) <
             std::tie(other.origin_,
                      other.applies_to_future_browsing_instances_,
                      other.browsing_instance_id_, other.browser_context_id_,
                      other.isolate_all_subdomains_, source_);
    }

    bool operator==(const IsolatedOriginEntry& other) const {
      return origin_ == other.origin_ &&
             applies_to_future_browsing_instances_ ==
                 other.applies_to_future_browsing_instances_ &&
             browsing_instance_id_ == other.browsing_instance_id_ &&
             browser_context_id_ == other.browser_context_id_ &&
             isolate_all_subdomains_ == other.isolate_all_subdomains_ &&
             source_ == other.source_;
    }

    // True if this isolated origin applies globally to all profiles.
    bool AppliesToAllBrowserContexts() const;

    // True if (1) this entry is associated with the same profile as
    // |browser_context_id|, or (2) this entry applies to all profiles.  May be
    // used on UI or IO threads.
    bool MatchesProfile(const base::UnguessableToken& browser_context_id) const;

    // True if this entry applies to the BrowsingInstance specified by
    // `browsing_instance_id`.  See `applies_to_future_browsing_instances_` and
    // `browsing_instance_id_` for more details.
    bool MatchesBrowsingInstance(BrowsingInstanceId browsing_instance_id) const;

    const url::Origin& origin() const { return origin_; }

    // See the declaration of `applies_to_future_browsing_instances_` for
    // details.
    bool applies_to_future_browsing_instances() const {
      return applies_to_future_browsing_instances_;
    }

    // See the declaration of `browsing_instance_id_` for details.
    BrowsingInstanceId browsing_instance_id() const {
      return browsing_instance_id_;
    }

    const base::UnguessableToken& browser_context_id() const {
      return browser_context_id_;
    }

    bool isolate_all_subdomains() const { return isolate_all_subdomains_; }

    IsolatedOriginSource source() const { return source_; }

   private:
    url::Origin origin_;

    // If this is false, the origin is isolated only in the BrowsingInstance
    // specified by `browsing_instance_id_`.  If this is true, the origin is
    // isolated in all BrowsingInstances that have an ID equal to or
    // greater than `browsing_instance_id_`.
    bool applies_to_future_browsing_instances_;

    // Specifies which BrowsingInstance(s) this IsolatedOriginEntry applies to.
    // When `applies_to_future_browsing_instances_` is false, this refers to a
    // specific BrowsingInstance.  Otherwise, it specifies the minimum
    // BrowsingInstance ID, and the origin is isolated in all
    // BrowsingInstances with IDs greater than or equal to this value.
    BrowsingInstanceId browsing_instance_id_;

    // Optional information about the profile where the isolated origin
    // applies. This may only be used on the UI thread. If this is empty,
    // then the isolated origin applies globally to all profiles.
    base::UnguessableToken browser_context_id_;

    // True if origins at this or lower level should be treated as distinct
    // isolated origins, effectively isolating all domains below a given domain,
    // e.g. if the origin is https://foo.com and isolate_all_subdomains_ is
    // true, then https://bar.foo.com, https://qux.bar.foo.com and all
    // subdomains of the form https://<<any pattern here>>.foo.com are
    // considered isolated origins.
    bool isolate_all_subdomains_;

    // This tracks the source of each isolated origin entry, e.g., to
    // distinguish those that should be displayed to the user from those that
    // should not.  See https://crbug.com/920911.
    IsolatedOriginSource source_;
  };

  // Obtain an instance of ChildProcessSecurityPolicyImpl via GetInstance().
  ChildProcessSecurityPolicyImpl();
  friend struct base::DefaultSingletonTraits<ChildProcessSecurityPolicyImpl>;

  // Determines if certain permissions were granted for a file to given child
  // process. |permissions| is an internally defined bit-set.
  bool ChildProcessHasPermissionsForFile(ChildProcessId child_id,
                                         const base::FilePath& file,
                                         int permissions)
      EXCLUSIVE_LOCKS_REQUIRED(lock_);

  // Grant a particular permission set for a file. |permissions| is an
  // internally defined bit-set.
  void GrantPermissionsForFile(ChildProcessId child_id,
                               const base::FilePath& file,
                               int permissions);

  // Grants access permission to the given isolated file system
  // identified by |filesystem_id|.  See comments for
  // ChildProcessSecurityPolicy::GrantReadFileSystem() for more details.
  void GrantPermissionsForFileSystem(ChildProcessId child_id,
                                     const std::string& filesystem_id,
                                     int permission);

  // Determines if certain permissions were granted for a file. |permissions|
  // is an internally defined bit-set.
  bool HasPermissionsForFile(ChildProcessId child_id,
                             const base::FilePath& file,
                             int permissions);

  // Determines if certain permissions were granted for a file in FileSystem
  // API. |permissions| is an internally defined bit-set.
  bool HasPermissionsForFileSystemFile(
      ChildProcessId child_id,
      const storage::FileSystemURL& filesystem_url,
      int permissions);

  // Helper function for `HasPermissionsForFileSystemFile`, which looks for any
  // permission policy granted to `type` in `file_system_policy_map_`. Returns
  // false if `type` was not found in the map, and otherwise writes any granted
  // permission policy to the `policy` mutable ref out parameter (which is
  // necessary for Rust FFI) and returns true.
  // TODO(crbug.com/482216433): Return a `std::optional<int>` once Rust CXX
  // supports it in https://github.com/dtolnay/cxx/issues/87, or when switching
  // to Crubit.
  bool FindPermissionPolicyForFileSystemType(storage::FileSystemType type,
                                             int& policy);
  bool FindPermissionPolicyForFileSystemType_Cpp(storage::FileSystemType type,
                                                 int& policy);

  // Determines if certain permissions were granted for a file system.
  // |permissions| is an internally defined bit-set.
  bool HasPermissionsForFileSystem(ChildProcessId child_id,
                                   const std::string& filesystem_id,
                                   int permission);

  // Convert a list of comma separated isolated origins in |pattern_list|,
  // specified either as wildcard origins, non-wildcard origins or a mix of the
  // two into IsolatedOriginPatterns, suitable for addition via
  // AddFutureIsolatedOrigins().
  static std::vector<IsolatedOriginPattern> ParseIsolatedOrigins(
      std::string_view pattern_list);

  void AddFutureIsolatedOrigins(
      const std::vector<IsolatedOriginPattern>& patterns,
      IsolatedOriginSource source,
      BrowserContext* browser_context = nullptr);

  // Internal helper used for adding a particular isolated origin.  See
  // IsolatedOriginEntry for descriptions of various parameters.
  void AddIsolatedOriginInternal(BrowserContext* browser_context,
                                 const url::Origin& origin,
                                 bool applies_to_future_browsing_instances,
                                 BrowsingInstanceId browsing_instance_id,
                                 bool isolate_all_subdomains,
                                 IsolatedOriginSource source)
      EXCLUSIVE_LOCKS_REQUIRED(isolated_origins_lock_);

  bool AddProcessReference(ChildProcessId child_id, bool duplicating_handle);
  void RemoveProcessReference(ChildProcessId child_id);

  // Internal helper for RemoveAllStateForBrowsingInstance().
  void RemoveAllStateForBrowsingInstanceInternal(
      const BrowsingInstanceId browsing_instance_id);

  // Helper for RemoveAllStateForBrowsingInstanceInternal().
  void EraseV8OptimizationState(const BrowsingInstanceId& browsing_instance_id);
  void EraseV8OptimizationState_Cpp(
      const BrowsingInstanceId& browsing_instance_id);
  void EraseOriginAgentClusterState(
      const BrowsingInstanceId& browsing_instance_id);
  void EraseOriginAgentClusterState_Cpp(
      const BrowsingInstanceId& browsing_instance_id);

  // Creates the value to place in the "killed_process_origin_lock" crash key
  // based on the contents of |process_state|.
  static std::string GetKilledProcessOriginLock(
      const ProcessState* process_state);

  // Creates the value to place in the "committed_origins" crash key
  // based on the contents of |process_state|.
  static std::string GetCommittedOriginsForCrashKey(
      const ProcessState* process_state);

  // Helper for CanAccessMaybeOpaqueOrigin, to perform two security checks:
  //  - Jail check: a process locked to a particular site shouldn't access data
  //    belonging to other sites.
  //  - Citadel check: a process not locked to any site shouldn't access data
  //    belonging to sites that require a dedicated process.
  //
  // These checks are performed by comparing the actual ProcessLock of the
  // process represented by `child_id` and `process_state` to an expected
  // ProcessLock computed from `url`, which takes into account factors such as
  // whether `url` should be site-isolated or origin-isolated (or not isolated,
  // e.g. on Android). Determining site-vs-origin isolation is non-trivial: the
  // answer may differ depending on BrowsingInstance (e.g., OriginAgentCluster
  // might require origin isolation only for certain BrowsingInstances), so all
  // BrowsingInstances hosting in the process must be consulted.
  //
  // This function returns true only if both Jail and Citadel checks pass. On
  // failure, it also populates `out_failure_reason` with debugging information
  // about the cause of the failure, as well as `out_expected_process_lock` with
  // what the process lock was expected to be (e.g., to be used in crash keys).
  //
  // This function must be called on the UI thread while already holding
  // `lock_`, and it is only valid to use for AccessType::kCanCommitNewOrigin.
  bool PerformJailAndCitadelChecks(ChildProcessId child_id,
                                   const ProcessState& process_state,
                                   const GURL& url,
                                   bool url_is_precursor_of_opaque_origin,
                                   ProcessLock& out_expected_process_lock,
                                   std::string& out_failure_reason)
      EXCLUSIVE_LOCKS_REQUIRED(lock_);

  // Helper for public CanAccessOrigin overloads.
  bool CanAccessMaybeOpaqueOrigin(ChildProcessId child_id,
                                  const GURL& url,
                                  bool url_is_precursor_of_opaque_origin,
                                  AccessType access_type);

  // Helper used by CanAccessOrigin to impose additional restrictions on a
  // sandboxed process locked to `process_lock`.
  bool IsAccessAllowedForSandboxedProcess(const ProcessLock& process_lock,
                                          const GURL& url,
                                          bool url_is_for_opaque_origin,
                                          AccessType access_type);

  // Helper used by CanAccessOrigin to impose additional restrictions on a
  // process that only hosts PDF documents.
  bool IsAccessAllowedForPdfProcess(AccessType access_type);

  // Helper to register the default web-safe and pseudo schemes.
  void RegisterDefaultSchemes();

  // Utility function to simplify lookups for OriginAgentClusterIsolationState
  // values by origin.
  std::optional<OriginAgentClusterIsolationState> LookupOriginAgentClusterState(
      const BrowsingInstanceId& browsing_instance_id,
      const url::Origin& origin)
      EXCLUSIVE_LOCKS_REQUIRED(origin_agent_cluster_lock_);
  std::optional<OriginAgentClusterIsolationState>
  LookupOriginAgentClusterState_Cpp(
      const BrowsingInstanceId& browsing_instance_id,
      const url::Origin& origin)
      EXCLUSIVE_LOCKS_REQUIRED(origin_agent_cluster_lock_);

  // Helper used by CanCommitURL() to check if `scheme` can be committed in any
  // process.
  bool CanCommitSchemeInAnyProcess(const std::string& scheme);
  bool CanCommitSchemeInAnyProcess_Cpp(const std::string& scheme);

  // Helpers to remove all origins that have ever requested a particular OAC
  // state in `browser_context`.
  void RemoveOriginAgentClusterRequestsForBrowserContext(
      const BrowserContext& browser_context);
  void RemoveOriginAgentClusterRequestsForBrowserContext_Cpp(
      const BrowserContext& browser_context);

  // You must acquire this lock before reading or writing any members of this
  // class, except for isolated_origins_, schemes_okay_to_*, and
  // pseudo_schemes_, which use their own locks.  You must not block while
  // holding this lock.
  base::Lock lock_;

  // These schemes are allow-listed for all child processes in various contexts.
  // These sets are protected by |schemes_lock_| rather than |lock_|.
  base::Lock schemes_lock_;
  SchemeSet schemes_okay_to_commit_in_any_process_ GUARDED_BY(schemes_lock_);
  SchemeSet schemes_okay_to_request_in_any_process_ GUARDED_BY(schemes_lock_);

  // These schemes do not actually represent retrievable URLs.  For example,
  // the the URLs in the "about" scheme are aliases to other URLs.  This set is
  // protected by |schemes_lock_|.
  SchemeSet pseudo_schemes_ GUARDED_BY(schemes_lock_);

  // Tracks all per-process ProcessStates, both while the RenderProcessHost
  // exists and can be modified, and after it has been deleted until all of the
  // corresponding ChildProcessSecurityPolicy::Handles are gone (when the state
  // can be queried but should not be modified).
  ProcessStateMaps process_states_ GUARDED_BY(lock_);

  FileSystemPermissionPolicyMap file_system_policy_map_ GUARDED_BY(lock_);

  // You must acquire this lock before reading or writing isolated_origins_.
  // You must not block while holding this lock.
  //
  // It is allowed to hold both |lock_| and |isolated_origins_lock_|, but in
  // this case, |lock_| should always be acquired first to prevent deadlock.
  base::Lock isolated_origins_lock_ ACQUIRED_AFTER(lock_);

  // Tracks origins for which the entire origin should be treated as a site
  // when making process model decisions, rather than the origin's scheme and
  // eTLD+1. Each of these origins requires a dedicated process.  This set is
  // protected by |isolated_origins_lock_|.
  //
  // The origins are stored in a map indexed by a site URL computed for each
  // origin.  For example, adding https://foo.com, https://bar.foo.com, and
  // https://www.bar.com would result in the following structure:
  //   https://foo.com -> { https://foo.com, https://bar.foo.com }
  //   https://bar.com -> { https://www.bar.com }
  // This organization speeds up lookups of isolated origins. The site can be
  // found in O(log n) time, and the corresponding list of origins to search
  // using the expensive DoesOriginMatchIsolatedOrigin() comparison is
  // typically small.
  //
  // Each origin entry stores information about:
  //   1. Which BrowsingInstances it applies to.  This is a combination of a
  //      BrowsingInstance ID |browsing_instance_id_| and a bool flag
  //      |applies_to_future_browsing_instances_| stored in in each origin's
  //      IsolatedOriginEntry.  When |applies_to_future_browsing_instances_| is
  //      true, the origin will be isolated in all BrowsingInstances with
  //      IDs equal to or greater than |browsing_instance_id_|. When
  //      |applies_to_future_browsing_instances_| is false, the origin will be
  //      isolated only in a single BrowsingInstance with ID
  //      |browsing_instance_id_|.
  //   2. Optionally, which BrowserContext (profile) it applies to.  When the
  //      |browser_context| field in the IsolatedOriginEntry is non-null, a
  //      particular isolated origin entry only applies to that BrowserContext.
  //      Note that the same origin may be isolated in different profiles,
  //      possibly with different BrowsingInstance ID cut-offs.  For example:
  //        https://foo.com -> { [https://test.foo.com profile1 4],
  //                             [https://test.foo.com profile2 7] }
  //      represents https://test.foo.com being isolated in profile1
  //      with BrowsingInstance ID 4, and also in profile2 with
  //      BrowsingInstance ID 7.
  base::flat_map<GURL, std::vector<IsolatedOriginEntry>> isolated_origins_
      GUARDED_BY(isolated_origins_lock_);

  // TODO(wjmaclean): Move these lists into a per-BrowserContext container, to
  // prevent any record of sites visible in one profile from being visible to
  // another profile.
  base::Lock origin_agent_cluster_lock_;
  // The set of all origins that have ever explicitly requested an
  // Origin-Agent-Cluster state (either opting in or opting out), keyed by
  // BrowserContext's UniqueToken(). This allows us to know which origins need
  // to be tracked when using default isolation in any given BrowsingInstance.
  // Origins requesting an Origin-Agent-Cluster state, if successful, are marked
  // as isolated or not via DetermineOriginAgentClusterIsolation's checking
  // |requested_isolation_state|. Each BrowserContext's state is tracked
  // separately so that timing attacks do not reveal whether an origin has been
  // visited in another (e.g., incognito) BrowserContext. In general, the state
  // of other BrowsingInstances is not observable outside such timing side
  // channels.
  base::flat_map<base::UnguessableToken, base::flat_set<url::Origin>>
      origin_agent_cluster_opt_ins_and_outs_
          GUARDED_BY(origin_agent_cluster_lock_);

  // A map to track origins that have been isolated via Origin-Agent-Cluster
  // within a given BrowsingInstance, or that have been loaded in a
  // BrowsingInstance without isolation, but that have requested an
  // Origin-Agent-Cluster state in at least one other BrowsingInstance. Origins
  // loaded without isolation are tracked to make sure we don't try to isolate
  // the origin in the associated BrowsingInstance at a later time, in order to
  // keep the isolation consistent over the lifetime of the BrowsingInstance.
  //
  // Note that this map does not currently distinguish between a non-sandboxed
  // origin and a precursor of a sandboxed origin, even though that's not
  // technically necessary. See https://crbug.com/446157743 and
  // https://crbug.com/40910871.
  base::flat_map<BrowsingInstanceId,
                 base::flat_map<url::Origin, OriginAgentClusterIsolationState>>
      origin_agent_cluster_states_by_browsing_instance_
          GUARDED_BY(origin_agent_cluster_lock_);

  base::Lock are_v8_optimizations_disabled_lock_;

  // A map of BrowsingInstances and process-lock-origins to v8-optimization
  // verdicts. The purpose of the map is to ensure that changes in the return
  // value of ContentBrowserClient::AreV8OptimizationsDisabledForSite() only
  // affect process reuse decisions for future BrowsingInstances.
  base::flat_map<BrowsingInstanceId, base::flat_map<url::Origin, bool>>
      are_v8_optimizations_disabled_map_
          GUARDED_BY(are_v8_optimizations_disabled_lock_);

  // When we are notified a BrowsingInstance has destructed, delay cleanup by
  // this amount to allow outstanding IO thread requests to complete. May be set
  // to different values in tests. Note: the value is chosen to be slightly
  // longer than the KeepAliveHandleFactory delay of 30 seconds, with the aim of
  // covering the maximum time needed by any IncrementKeepAliveRefCount callers.
  // TODO(wjmaclean): we know the IncrementKeepAliveRefCount API needs
  // improvement, and with it the BrowsingInstance cleanup here can also be
  // improved.
  base::TimeDelta browsing_instance_cleanup_delay_;

  // Tracks files that the browser process has granted permission to the
  // network service to upload on the user's behalf.
  //
  // Each file path maps to a list of tokens representing the active requests
  // that have been granted access to this file. A token is added when a request
  // is created, and it is removed from all associated file paths when the
  // request is destroyed. Access is allowed as long as the file is present in
  // this map.
  absl::flat_hash_map<base::FilePath, std::vector<base::UnguessableToken>>
      browser_granted_files_ GUARDED_BY(lock_);
};

}  // namespace content

#endif  // CONTENT_BROWSER_SECURITY_CPSP_CHILD_PROCESS_SECURITY_POLICY_IMPL_H_
