// Copyright 2020 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_SERVICE_WORKER_SERVICE_WORKER_REGISTRY_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTRY_H_

#include <memory>
#include <optional>

#include "base/containers/flat_set.h"
#include "base/containers/lru_cache.h"
#include "base/containers/unique_ptr_adapters.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 "components/services/storage/public/cpp/buckets/bucket_info.h"
#include "components/services/storage/public/cpp/quota_error_or.h"
#include "components/services/storage/public/mojom/service_worker_storage_control.mojom.h"
#include "components/services/storage/service_worker/service_worker_storage.h"
#include "content/browser/renderer_host/policy_container_host.h"
#include "content/browser/service_worker/service_worker_registration.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "storage/browser/quota/storage_policy_observer.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_ancestor_frame_type.mojom.h"

namespace blink {
class StorageKey;
}  // namespace blink

namespace storage {
class QuotaManagerProxy;
class SpecialStoragePolicy;
}  // namespace storage

namespace content {

class ServiceWorkerContextCore;
class ServiceWorkerVersion;
struct PolicyContainerPolicies;

class ServiceWorkerRegistryTest;
FORWARD_DECLARE_TEST(ServiceWorkerRegistryTest, StoragePolicyChange);

CONTENT_EXPORT BASE_DECLARE_FEATURE(
    kServiceWorkerMergeFindRegistrationForClientUrl);

// Manages in-memory representation of service worker registrations
// (i.e., ServiceWorkerRegistration) including installing and uninstalling
// registrations. Owned by ServiceWorkerContextCore and has the same lifetime of
// the owner. Lives on the UI thread.
//
// Communicates with ServiceWorkerStorageControl via a mojo remote to
// store/retrieve registrations to/from persistent storage. Most methods of this
// class call relevant mojo methods. Callbacks passed to these methods are
// invoked after mojo calls complete.
//
// The implementation of ServiceWorkerStorageControl lives in the Storage
// Service. This means that the mojo remote can be disconnected when the
// Storage Service crashes. To avoid a situation where callbacks never get
// invoked this class has a recovery mechanism. Upon detecting a disconnection,
// this tries to reconnect to ServiceWorkerStorageControl then retry all
// inflight mojo calls.
class CONTENT_EXPORT ServiceWorkerRegistry {
 public:
  using ResourceList =
      std::vector<storage::mojom::ServiceWorkerResourceRecordPtr>;
  using RegistrationList =
      std::vector<storage::mojom::ServiceWorkerRegistrationDataPtr>;
  using FindRegistrationCallback = base::OnceCallback<void(
      blink::ServiceWorkerStatusCode status,
      scoped_refptr<ServiceWorkerRegistration> registration)>;
  using GetRegisteredStorageKeysCallback = base::OnceCallback<void(
      const std::vector<blink::StorageKey>& storage_keys)>;
  using GetRegistrationsCallback = base::OnceCallback<void(
      blink::ServiceWorkerStatusCode status,
      const std::vector<scoped_refptr<ServiceWorkerRegistration>>&
          registrations)>;
  using GetRegistrationsInfosCallback = base::OnceCallback<void(
      blink::ServiceWorkerStatusCode status,
      const std::vector<ServiceWorkerRegistrationInfo>& registrations)>;
  using GetUserDataCallback =
      base::OnceCallback<void(const std::vector<std::string>& data,
                              blink::ServiceWorkerStatusCode status)>;
  using GetUserKeysAndDataCallback = base::OnceCallback<void(
      blink::ServiceWorkerStatusCode status,
      const base::flat_map<std::string, std::string>& data_map)>;
  using GetUserDataForAllRegistrationsCallback = base::OnceCallback<void(
      const std::vector<std::pair<int64_t, std::string>>& user_data,
      blink::ServiceWorkerStatusCode status)>;
  using GetStorageUsageForStorageKeyCallback =
      base::OnceCallback<void(blink::ServiceWorkerStatusCode status,
                              int64_t usage)>;
  using StatusCallback =
      base::OnceCallback<void(blink::ServiceWorkerStatusCode status)>;

  enum class Purpose { kNotForNavigation, kNavigation };

  ServiceWorkerRegistry(ServiceWorkerContextCore& context,
                        storage::QuotaManagerProxy* quota_manager_proxy,
                        storage::SpecialStoragePolicy* special_storage_policy,
                        base::TimeTicks start_time);

  // For re-creating the registry from the old one. This is called when
  // something went wrong during storage access.
  ServiceWorkerRegistry(ServiceWorkerContextCore& context,
                        ServiceWorkerRegistry& old_registry);

  ~ServiceWorkerRegistry();

  // Creates a new in-memory representation of registration. Can be null when
  // storage is disabled. This method must be called after storage is
  // initialized.
  using NewRegistrationCallback = base::OnceCallback<void(
      scoped_refptr<ServiceWorkerRegistration> registration)>;
  void CreateNewRegistration(
      blink::mojom::ServiceWorkerRegistrationOptions options,
      const blink::StorageKey& key,
      blink::mojom::AncestorFrameType ancestor_frame_type,
      NewRegistrationCallback callback);

  // Create a new instance of ServiceWorkerVersion which is associated with the
  // given |registration|. Can be null when storage is disabled. This method
  // must be called after storage is initialized.
  //
  // |creator_network_restrictions_id| is the network restrictions ID of the
  // creator (e.g., frame), which is used to subject the Service Worker script
  // fetch itself to the creator's connection allowlists.
  // |network_restrictions_id| is a unique token identifying this worker's own
  // network restrictions in the network service, which is used for the worker's
  // subsequent subresource requests.
  using NewVersionCallback =
      base::OnceCallback<void(scoped_refptr<ServiceWorkerVersion> version)>;
  void CreateNewVersion(
      scoped_refptr<ServiceWorkerRegistration> registration,
      const GURL& script_url,
      blink::mojom::ScriptType script_type,
      const std::optional<base::UnguessableToken>&
          creator_network_restrictions_id,
      const std::optional<base::UnguessableToken>& network_restrictions_id,
      const PolicyContainerPolicies& creator_policies,
      NewVersionCallback callback);

  // Finds registration for `client_url`, `scope`, or `registration_id` with the
  // associated `key`. The Find methods will find stored and initially
  // installing registrations. Returns blink::ServiceWorkerStatusCode::kOk with
  // non-null registration if registration is found, or returns
  // blink::ServiceWorkerStatusCode::kErrorNotFound if no
  // matching registration is found.  The FindRegistrationForScope method is
  // guaranteed to return asynchronously. However, the methods to find
  // for `client_url` or `registration_id` may complete immediately
  // (the callback may be called prior to the method returning) or
  // asynchronously.
  void FindRegistrationForClientUrl(Purpose purpose,
                                    const GURL& client_url,
                                    const blink::StorageKey& key,
                                    FindRegistrationCallback callback);
  void FindRegistrationForScope(const GURL& scope,
                                const blink::StorageKey& key,
                                FindRegistrationCallback callback);
  // These FindRegistrationForId() methods look up live registrations and may
  // return a "findable" registration without looking up storage. A registration
  // is considered as "findable" when the registration is stored or in the
  // installing state.
  void FindRegistrationForId(int64_t registration_id,
                             const blink::StorageKey& key,
                             FindRegistrationCallback callback);
  // Generally |FindRegistrationForId| should be used to look up a registration
  // by |registration_id| since it's more efficient. But if a |registration_id|
  // is all that is available this method can be used instead.
  // Like |FindRegistrationForId| this method may complete immediately (the
  // callback may be called prior to the method returning) or asynchronously.
  void FindRegistrationForIdOnly(int64_t registration_id,
                                 FindRegistrationCallback callback);
  // Returns all stored and installing registrations for a given StorageKey.
  void GetRegistrationsForStorageKey(const blink::StorageKey& key,
                                     GetRegistrationsCallback callback);
  // Reads the total resource size stored in the storage for a given storage
  // key.
  void GetStorageUsageForStorageKey(
      const blink::StorageKey& key,
      GetStorageUsageForStorageKeyCallback callback);

  // Returns info about all stored and initially installing registrations.
  // TODO(crbug.com/807440,1055677): Consider removing this method. Getting all
  // registrations at once might not be a good idea.
  void GetAllRegistrationsInfos(GetRegistrationsInfosCallback callback);
  ServiceWorkerRegistration* GetUninstallingRegistration(
      const GURL& scope,
      const blink::StorageKey& key);
  std::vector<scoped_refptr<ServiceWorkerRegistration>>
  GetUninstallingRegistrationsForStorageKey(const blink::StorageKey& key);

  // Commits |registration| with the installed but not activated |version|
  // to storage, overwriting any pre-existing registration data for the scope.
  // A pre-existing version's script resources remain available if that version
  // is live. ServiceWorkerStorage::PurgeResources() should be called when it's
  // OK to delete them.
  void StoreRegistration(ServiceWorkerRegistration* registration,
                         ServiceWorkerVersion* version,
                         StatusCallback callback);

  // Deletes the registration data for `registration`. The live registration is
  // still findable via GetUninstallingRegistration(), and versions are usable
  // because their script resources have not been deleted. After calling this,
  // the caller should later:
  // - Call NotifyDoneUninstallingRegistration() to let registry know the
  //   uninstalling operation is done.
  // - If it no longer wants versions to be usable, call
  //   ServiceWorkerStorage::PurgeResources() to delete their script resources.
  // If these aren't called, on the next profile session the cleanup occurs.
  void DeleteRegistration(scoped_refptr<ServiceWorkerRegistration> registration,
                          StatusCallback callback);

  // Intended for use only by ServiceWorkerRegisterJob and
  // ServiceWorkerRegistration.
  void NotifyInstallingRegistration(ServiceWorkerRegistration* registration);
  void NotifyDoneInstallingRegistration(ServiceWorkerRegistration* registration,
                                        ServiceWorkerVersion* version,
                                        blink::ServiceWorkerStatusCode status);
  void NotifyDoneUninstallingRegistration(
      ServiceWorkerRegistration* registration,
      ServiceWorkerRegistration::Status new_status);

  // Wrapper functions of ServiceWorkerStorage. These wrappers provide error
  // recovering mechanism when database operations fail.
  void UpdateToActiveState(int64_t registration_id,
                           const blink::StorageKey& key,
                           StatusCallback callback);
  void UpdateLastUpdateCheckTime(int64_t registration_id,
                                 const blink::StorageKey& key,
                                 base::Time last_update_check_time,
                                 StatusCallback callback);
  void UpdateNavigationPreloadEnabled(int64_t registration_id,
                                      const blink::StorageKey& key,
                                      bool enable,
                                      StatusCallback callback);
  void UpdateNavigationPreloadHeader(int64_t registration_id,
                                     const blink::StorageKey& key,
                                     const std::string& value,
                                     StatusCallback callback);
  void UpdateFetchHandlerType(
      int64_t registration_id,
      const blink::StorageKey& key,
      blink::mojom::ServiceWorkerFetchHandlerType fetch_handler_type,
      StatusCallback callback);
  void UpdateResourceSha256Checksums(
      int64_t registration_id,
      const blink::StorageKey& key,
      const base::flat_map<int64_t, std::string>& updated_sha256_checksums,
      StatusCallback callback);
  void StoreUncommittedResourceId(int64_t resource_id,
                                  const blink::StorageKey& key);
  void DoomUncommittedResource(int64_t resource_id);
  void GetUserData(int64_t registration_id,
                   const std::vector<std::string>& keys,
                   GetUserDataCallback callback);
  void GetUserDataByKeyPrefix(int64_t registration_id,
                              const std::string& key_prefix,
                              GetUserDataCallback callback);
  void GetUserKeysAndDataByKeyPrefix(int64_t registration_id,
                                     const std::string& key_prefix,
                                     GetUserKeysAndDataCallback callback);
  void StoreUserData(
      int64_t registration_id,
      const blink::StorageKey& key,
      const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
      StatusCallback callback);
  void ClearUserData(int64_t registration_id,
                     const std::vector<std::string>& keys,
                     StatusCallback callback);
  void ClearUserDataByKeyPrefixes(int64_t registration_id,
                                  const std::vector<std::string>& key_prefixes,
                                  StatusCallback callback);
  void ClearUserDataForAllRegistrationsByKeyPrefix(
      const std::string& key_prefix,
      StatusCallback callback);
  void GetUserDataForAllRegistrations(
      const std::string& key,
      GetUserDataForAllRegistrationsCallback callback);
  void GetUserDataForAllRegistrationsByKeyPrefix(
      const std::string& key_prefix,
      GetUserDataForAllRegistrationsCallback callback);

  // Returns a set of storage keys which have at least one stored registration.
  // The set doesn't include installing/uninstalling/uninstalled registrations.
  void GetRegisteredStorageKeys(GetRegisteredStorageKeysCallback callback);

  // Returns false only when we are sure that there's no service worker
  // registrations for the given storage key. If we are not sure, this function
  // must return true to suggest that we need to check the service worker
  // registration specifically (e.g. by calling FindRegistrationForClientUrl()).
  // This function is a lot faster than FindRegistrationForClientUrl().
  bool MaybeHasRegistrationForStorageKey(const blink::StorageKey& key);

  // This method waits for service worker registrations to be initialized, and
  // depends on |on_registrations_initialized_| and |registrations_initialized_|
  // which are called in InitializeRegisteredOrigins().
  void WaitForRegistrationsInitializedForTest();

  // Performs internal storage cleanup. Operations to the storage in the past
  // (e.g. deletion) are usually recorded in disk for a certain period until
  // compaction happens. This method wipes them out to ensure that the deleted
  // entries and other traces like log files are removed.
  void PerformStorageCleanup(base::OnceClosure callback);

  // Disables the internal storage to prepare for error recovery.
  void PrepareForDeleteAndStartOver();
  // Deletes this registry and internal storage, then starts over for error
  // recovery.
  void DeleteAndStartOver(StatusCallback callback);

  mojo::Remote<storage::mojom::ServiceWorkerStorageControl>&
  GetRemoteStorageControl();

  // Binds a ServiceWorkerStorageControl.
  void BindStorageControl(
      mojo::PendingReceiver<storage::mojom::ServiceWorkerStorageControl>
          receiver);

  // Call storage::mojom::ServiceWorkerStorageControl::Disable() immediately.
  // This method sends an IPC message without using the queuing mechanism.
  void DisableStorageForTesting(base::OnceClosure callback);

 private:
  friend class ServiceWorkerRegistryTest;
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerRegistryTest, StoragePolicyChange);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerRegistryTest,
                           RetryInflightCalls_ApplyPolicyUpdates);

  void Start(base::TimeTicks start_time);
  void FindRegistrationForIdInternal(
      int64_t registration_id,
      const std::optional<blink::StorageKey>& key,
      FindRegistrationCallback callback);
  ServiceWorkerRegistration* FindInstallingRegistrationForClientUrl(
      const GURL& client_url,
      const blink::StorageKey& key);
  ServiceWorkerRegistration* FindInstallingRegistrationForScope(
      const GURL& scope,
      const blink::StorageKey& key);
  ServiceWorkerRegistration* FindInstallingRegistrationForId(
      int64_t registration_id);

  scoped_refptr<ServiceWorkerRegistration> GetOrCreateRegistration(
      const storage::mojom::ServiceWorkerRegistrationData& data,
      const ResourceList& resources,
      mojo::PendingRemote<storage::mojom::ServiceWorkerLiveVersionRef>
          version_reference);

  void CreateNewRegistrationWithBucketInfo(
      blink::mojom::ServiceWorkerRegistrationOptions options,
      const blink::StorageKey& key,
      blink::mojom::AncestorFrameType ancestor_frame_type,
      NewRegistrationCallback callback,
      storage::QuotaErrorOr<storage::BucketInfo> result);

  // Looks up live registrations and returns an optional value which may contain
  // a "findable" registration. See the implementation of this method for
  // what "findable" means and when a registration is returned.
  std::optional<scoped_refptr<ServiceWorkerRegistration>>
  FindFromLiveRegistrationsForId(int64_t registration_id);

  void DoomUncommittedResources(const std::vector<int64_t>& resource_ids);
  void DidFindRegistrationForClientUrl(
      const GURL& client_url,
      const blink::StorageKey& key,
      int64_t trace_event_id,
      FindRegistrationCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus database_status,
      storage::mojom::ServiceWorkerFindRegistrationResultPtr result,
      const std::optional<std::vector<GURL>>& scopes);
  void RunFindRegistrationCallbacks(
      const GURL& client_url,
      const blink::StorageKey& key,
      scoped_refptr<ServiceWorkerRegistration> registration,
      blink::ServiceWorkerStatusCode status);
  void DidFindRegistrationForScope(
      FindRegistrationCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus database_status,
      storage::mojom::ServiceWorkerFindRegistrationResultPtr result);
  void DidFindRegistrationForId(
      int64_t registration_id,
      FindRegistrationCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus database_status,
      storage::mojom::ServiceWorkerFindRegistrationResultPtr result);
  void DidGetRegistrationsForStorageKey(
      GetRegistrationsCallback callback,
      const blink::StorageKey& key_filter,
      storage::mojom::ServiceWorkerDatabaseStatus database_status,
      std::vector<storage::mojom::ServiceWorkerFindRegistrationResultPtr>
          entries);
  void DidGetAllRegistrations(
      GetRegistrationsInfosCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus database_status,
      RegistrationList registration_data_list);
  void DidGetStorageUsageForStorageKey(
      GetStorageUsageForStorageKeyCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus database_status,
      int64_t usage);
  void DidStoreRegistration(
      int64_t stored_registration_id,
      uint64_t stored_resources_total_size_bytes,
      const GURL& stored_scope,
      const blink::StorageKey& key,
      StatusCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus database_status,
      uint64_t deleted_resources_size);
  void NotifyRegistrationStored(int64_t stored_registration_id,
                                uint64_t stored_resources_total_size_bytes,
                                const GURL& stored_scope,
                                const blink::StorageKey& key,
                                StatusCallback callback);
  void DidDeleteRegistration(
      int64_t registration_id,
      const GURL& stored_scope,
      const blink::StorageKey& key,
      StatusCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus database_status,
      uint64_t deleted_resources_size,
      storage::mojom::ServiceWorkerStorageStorageKeyState storage_key_state);
  void NotifyRegistrationDeletedForStorageKey(
      int64_t registration_id,
      const GURL& stored_scope,
      const blink::StorageKey& key,
      storage::mojom::ServiceWorkerStorageStorageKeyState storage_key_state,
      StatusCallback callback);
  void DidUpdateRegistration(
      StatusCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus status);
  void DidUpdateToActiveState(
      const blink::StorageKey& key,
      StatusCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus status);
  void DidWriteUncommittedResourceIds(
      const blink::StorageKey& key,
      storage::mojom::ServiceWorkerDatabaseStatus status);
  void DidDoomUncommittedResourceIds(
      storage::mojom::ServiceWorkerDatabaseStatus status);
  void DidGetUserData(GetUserDataCallback callback,
                      storage::mojom::ServiceWorkerDatabaseStatus status,
                      const std::vector<std::string>& data);
  void DidGetUserKeysAndData(
      GetUserKeysAndDataCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus status,
      const base::flat_map<std::string, std::string>& data_map);
  void DidStoreUserData(StatusCallback callback,
                        const blink::StorageKey& key,
                        storage::mojom::ServiceWorkerDatabaseStatus status);
  void DidClearUserData(StatusCallback callback,
                        storage::mojom::ServiceWorkerDatabaseStatus status);
  void DidGetUserDataForAllRegistrations(
      GetUserDataForAllRegistrationsCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus status,
      std::vector<storage::mojom::ServiceWorkerUserDataPtr> entries);

  void DidGetNewRegistrationId(
      blink::mojom::ServiceWorkerRegistrationOptions options,
      const blink::StorageKey& key,
      blink::mojom::AncestorFrameType ancestor_frame_type,
      NewRegistrationCallback callback,
      int64_t registration_id);

  void DidGetNewVersionId(
      scoped_refptr<ServiceWorkerRegistration> registration,
      const GURL& script_url,
      blink::mojom::ScriptType script_type,
      const std::optional<base::UnguessableToken>&
          creator_network_restrictions_id,
      const std::optional<base::UnguessableToken>& network_restrictions_id,
      const PolicyContainerPolicies& creator_policies,
      NewVersionCallback callback,
      int64_t version_id,
      mojo::PendingRemote<storage::mojom::ServiceWorkerLiveVersionRef>
          version_reference);

  void ScheduleDeleteAndStartOver();
  void DidDeleteAndStartOver(
      StatusCallback callback,
      storage::mojom::ServiceWorkerDatabaseStatus status);

  void DidGetRegisteredStorageKeys(GetRegisteredStorageKeysCallback callback,
                                   const std::vector<blink::StorageKey>& keys);
  void DidPerformStorageCleanup(base::OnceClosure callback);
  void DidDisable();
  void DidApplyPolicyUpdates(
      storage::mojom::ServiceWorkerDatabaseStatus status);
  void DidGetRegisteredStorageKeysOnStartup(
      base::TimeTicks start_time,
      const std::vector<blink::StorageKey>& storage_keys);

  // This is used as a callback of GetRegisteredStorageKeys when initialising to
  // store a list of storage keys that have registered service workers.
  void DidGetRegisteredStorageKeysOnStartupDeprecated(
      base::TimeTicks start_time,
      const std::vector<blink::StorageKey>& storage_keys);

  void SetRegisteredStorageKeys(
      const std::vector<blink::StorageKey>& storage_keys);

  void ApplyPolicyUpdates(
      std::vector<storage::mojom::StoragePolicyUpdatePtr> policy_updates);
  bool ShouldPurgeOnShutdownForTesting(const blink::StorageKey& key);

  void OnRemoteStorageDisconnected();

  void DidRecover();

  // Represents an inflight mojo remote call. Used to support retry.
  class InflightCall {
   public:
    virtual ~InflightCall() = default;
    virtual void Run() = 0;
  };

  template <typename...>
  friend class InflightCallWithInvoker;

  void StartRemoteCall(std::unique_ptr<InflightCall> call);
  void FinishRemoteCall(const InflightCall* call);

  void ClearAllInternalCache();
  void ClearInternalCacheForStorageKey(const blink::StorageKey& storage_key);

  storage::ServiceWorkerStorage::StorageSharedBuffer& storage_shared_buffer() {
    // storage_shared_buffer_  always exists.
    return *storage_shared_buffer_;
  }

  // A helper function to call a mojo remote call that will automatically be
  // reissued if the mojo::Remote becomes disconnected. To allow the call to be
  // dispatched multiple times, all arguments must be either be:
  //
  // - passed by const reference
  // - copyable
  // - or clonable via `mojo::Clone()`.
  //
  // Example:
  //
  //   (in mojom)
  //   Foo(int64 arg1, int64 arg2) => (ServiceWorkerDatabaseStatus status);
  //
  //   CreateInvokerAndStartRemoteCall(
  //       &storage::mojom::ServiceWorkerStorageControl::Foo,
  //       base::BindOnce(&ServiceWorkerRegistry::DidFoo,
  //                      weak_factory_.GetWeakPtr(),
  //                      std::move(callback)),
  //       arg1, arg2);
  //
  //   void ServiceWorkerRegistry::DidFoo(
  //       FooCallback callback,
  //       storage::mojom::ServiceWorkerDatabaseStatus status) {
  //     // ...
  //   }
  template <typename Functor, typename... Args, typename... CallbackArgs>
  void CreateInvokerAndStartRemoteCall(
      Functor&& f,
      base::OnceCallback<void(CallbackArgs...)> callback,
      Args&&... args);

  // The ServiceWorkerContextCore object must outlive this.
  const raw_ref<ServiceWorkerContextCore> context_;

  // This is a direct communication channel between this ServiceWorkerRegistry
  // in the UI thread and the ServiceWorkerStorage in the thread pool.
  // This must not be null.
  scoped_refptr<storage::ServiceWorkerStorage::StorageSharedBuffer>
      storage_shared_buffer_;

  mojo::Remote<storage::mojom::ServiceWorkerStorageControl>
      remote_storage_control_;

  bool is_storage_disabled_ = false;

  // A set of StorageKeys that have at least one registration.
  std::set<blink::StorageKey> registered_storage_keys_;
  bool registrations_initialized_ = false;
  base::OnceClosure on_registrations_initialized_for_test_;

  // TODO(crbug.com/40103974): Consider moving QuotaManagerProxy to
  // ServiceWorkerStorage once QuotaManager gets mojofied.
  const scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
  const scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy_;
  std::optional<storage::StoragePolicyObserver> storage_policy_observer_;

  // For finding registrations being installed or uninstalled.
  using RegistrationRefsById =
      std::map<int64_t, scoped_refptr<ServiceWorkerRegistration>>;
  RegistrationRefsById installing_registrations_;
  RegistrationRefsById uninstalling_registrations_;

  // Indicates whether recovery process should be scheduled.
  bool should_schedule_delete_and_start_over_ = true;

  // Stores in-flight FindRegistrationForClientUrl callbacks to merge duplicate
  // requests.
  base::flat_map<std::pair<GURL, blink::StorageKey>,
                 std::vector<FindRegistrationCallback>>
      find_registration_callbacks_;

  // ServiceWorker registration scope cache to skip calling
  // FindRegistrationForClientUrl mojo function (https://crbug.com/1411197).
  base::LRUCache<blink::StorageKey, std::set<GURL>> registration_scope_cache_;

  // Live registration's `registration_id` cache to skip calling
  // FindRegistrationForClientUrl mojo function (https://crbug.com/1446216). The
  // key is a pair of {registration_scope, storage_key}.
  base::LRUCache<std::pair<GURL, blink::StorageKey>, int64_t>
      registration_id_cache_;

  enum class ConnectionState {
    kNormal,
    kRecovering,
  };
  ConnectionState connection_state_ = ConnectionState::kNormal;
  size_t recovery_retry_counts_ = 0;

  base::flat_set<std::unique_ptr<InflightCall>, base::UniquePtrComparator>
      inflight_calls_;

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

}  // namespace content

#endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTRY_H_
