// 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.

#include "content/browser/storage_partition_impl.h"

#include <stdint.h>

#include <functional>
#include <memory>
#include <optional>
#include <utility>
#include <variant>
#include <vector>

#include "base/barrier_callback.h"
#include "base/barrier_closure.h"
#include "base/check_deref.h"
#include "base/command_line.h"
#include "base/dcheck_is_on.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/functional/concurrent_closures.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/not_fatal_until.h"
#include "base/numerics/safe_conversions.h"
#include "base/observer_list.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequence_local_storage_slot.h"
#include "base/timer/elapsed_timer.h"
#include "base/types/optional_util.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "components/leveldb_proto/public/proto_database_provider.h"
#include "components/services/storage/privileged/cpp/bucket_client_info.h"
#include "components/services/storage/privileged/mojom/indexed_db_control.mojom.h"
#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "components/services/storage/public/cpp/constants.h"
#include "components/services/storage/public/cpp/filesystem/filesystem_impl.h"
#include "components/services/storage/public/mojom/filesystem/directory.mojom.h"
#include "components/services/storage/public/mojom/storage_service.mojom.h"
#include "components/services/storage/storage_service_impl.h"
#include "components/variations/net/variations_http_headers.h"
#include "content/browser/background_fetch/background_fetch_context.h"
#include "content/browser/blob_storage/blob_registry_wrapper.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/bluetooth/bluetooth_allowed_devices_map.h"
#include "content/browser/broadcast_channel/broadcast_channel_service.h"
#include "content/browser/browsing_data/clear_site_data_handler.h"
#include "content/browser/browsing_data/storage_partition_code_cache_data_remover.h"
#include "content/browser/buckets/bucket_manager.h"
#include "content/browser/cache_storage/cache_storage_control_wrapper.h"
#include "content/browser/code_cache/generated_code_cache.h"
#include "content/browser/code_cache/generated_code_cache_context.h"
#include "content/browser/cookie_store/cookie_store_manager.h"
#include "content/browser/declarative_performance_observer/declarative_performance_observer_store.h"
#include "content/browser/devtools/devtools_background_services_context_impl.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/devtools/devtools_url_loader_interceptor.h"
#include "content/browser/file_system/browser_file_system_helper.h"
#include "content/browser/file_system_access/file_system_access_manager_impl.h"
#include "content/browser/font_access/font_access_manager.h"
#include "content/browser/gpu/gpu_disk_cache_factory.h"
#include "content/browser/guest_page_holder_impl.h"
#include "content/browser/host_zoom_level_context.h"
#include "content/browser/indexed_db/indexed_db_control_wrapper.h"
#include "content/browser/loader/keep_alive_url_loader_service.h"
#include "content/browser/loader/reconnectable_url_loader_factory.h"
#include "content/browser/loader/subresource_proxying_url_loader_service.h"
#include "content/browser/loader/url_loader_factory_utils.h"
#include "content/browser/locks/lock_manager.h"
#include "content/browser/navigation_or_document_handle.h"
#include "content/browser/network/shared_dictionary_util.h"
#include "content/browser/network_context_client_base_impl.h"
#include "content/browser/network_service_instance_impl.h"
#include "content/browser/notifications/platform_notification_context_impl.h"
#include "content/browser/payments/payment_app_context_impl.h"
#include "content/browser/preloading/prerender/prerender_final_status.h"
#include "content/browser/push_messaging/push_messaging_context.h"
#include "content/browser/quota/quota_context.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/navigation_state_keep_alive.h"
#include "content/browser/service_worker/service_worker_client.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/ssl/ssl_client_auth_handler.h"
#include "content/browser/ssl/ssl_error_handler.h"
#include "content/browser/ssl_private_key_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/worker_host/shared_worker_service_impl.h"
#include "content/common/features.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/cookie_access_details.h"
#include "content/public/browser/dom_storage_context.h"
#include "content/public/browser/login_delegate.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/permission_result.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/runtime_feature_state/runtime_feature_state_document_data.h"
#include "content/public/browser/service_process_host.h"
#include "content/public/browser/session_storage_usage_info.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/browser/storage_notification_service.h"
#include "content/public/browser/storage_partition_config.h"
#include "content/public/browser/storage_usage_info.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/child_process_id_util.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/features.h"
#include "net/base/net_errors.h"
#include "net/cookies/cookie_setting_override.h"
#include "net/disk_cache/buildflags.h"
#include "net/ssl/client_cert_store.h"
#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
#include "services/network/public/cpp/constants.h"
#include "services/network/public/cpp/cors/origin_access_list.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/ip_address_space_util.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "services/network/public/cpp/url_loader_factory_builder.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"
#include "services/network/public/mojom/cookie_access_observer.mojom.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/public/mojom/device_bound_sessions.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/shared_dictionary_access_observer.mojom.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"
#include "services/network/public/mojom/url_loader_network_service_observer.mojom.h"
#include "sql/database.h"
#include "storage/browser/blob/blob_url_registry.h"
#include "storage/browser/quota/quota_client_type.h"
#include "storage/browser/quota/quota_features.h"
#include "storage/browser/quota/quota_manager.h"
#include "storage/browser/quota/quota_manager_impl.h"
#include "storage/browser/quota/quota_settings.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/navigation/preloading_headers.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom-shared.h"
#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
#include "url/scheme_host_port.h"

#if BUILDFLAG(IS_ANDROID)
#include "content/public/browser/android/java_interfaces.h"
#include "net/android/http_auth_negotiate_android.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#endif  // BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
#include "content/browser/media/cdm_storage_common.h"
#include "content/browser/media/cdm_storage_manager.h"
#include "content/public/browser/cdm_storage_data_model.h"
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)

using CookieDeletionFilter = network::mojom::CookieDeletionFilter;
using CookieDeletionFilterPtr = network::mojom::CookieDeletionFilterPtr;

namespace content {

namespace {

using Type = StoragePartitionImpl::ContextType;

const storage::QuotaSettings* g_test_quota_settings;

// Timeout after which the
// History.ClearBrowsingData.Duration.SlowTasks180sStoragePartition histogram is
// recorded.
const base::TimeDelta kSlowTaskTimeout = base::Seconds(180);

// If true, Storage Service instances will always be started in-process.
bool g_force_in_process_storage_service = false;

mojo::Remote<storage::mojom::StorageService>& GetStorageServiceRemoteStorage() {
  // NOTE: This use of sequence-local storage is only to ensure that the Remote
  // only lives as long as the UI-thread sequence, since the UI-thread sequence
  // may be torn down and reinitialized e.g. between unit tests.
  static base::SequenceLocalStorageSlot<
      mojo::Remote<storage::mojom::StorageService>>
      remote_slot;
  return remote_slot.GetOrCreateValue();
}

void RunInProcessStorageService(
    mojo::PendingReceiver<storage::mojom::StorageService> receiver) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  static base::SequenceLocalStorageSlot<
      std::unique_ptr<storage::StorageServiceImpl>>
      service_storage_slot;
  service_storage_slot.GetOrCreateValue() =
      std::make_unique<storage::StorageServiceImpl>(std::move(receiver),
                                                    /*io_task_runner=*/nullptr);
}

#if !BUILDFLAG(IS_ANDROID)
void BindStorageServiceFilesystemImpl(
    const base::FilePath& directory_path,
    mojo::PendingReceiver<storage::mojom::Directory> receiver) {
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<storage::FilesystemImpl>(
          directory_path, storage::FilesystemImpl::ClientType::kUntrusted),
      std::move(receiver));
}
#endif

mojo::Remote<storage::mojom::StorageService>& GetStorageServiceRemote() {
  mojo::Remote<storage::mojom::StorageService>& remote =
      GetStorageServiceRemoteStorage();
  if (!remote) {
#if !BUILDFLAG(IS_ANDROID)
    const base::FilePath sandboxed_data_dir =
        GetContentClient()
            ->browser()
            ->GetSandboxedStorageServiceDataDirectory();
    const bool single_process_mode =
        base::CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kSingleProcess);
    const bool oop_storage_enabled = !sandboxed_data_dir.empty() &&
                                     !single_process_mode &&
                                     !g_force_in_process_storage_service;
    if (oop_storage_enabled) {
      DCHECK(sandboxed_data_dir.IsAbsolute())
          << "Storage Service data directory must be an absolute path, but \""
          << sandboxed_data_dir << "\" is not an absolute path.";
      remote = ServiceProcessHost::Launch<storage::mojom::StorageService>(
          ServiceProcessHost::Options()
              .WithDisplayName("Storage Service")
              .Pass());
      remote.reset_on_disconnect();

      // Provide the service with an API it can use to access filesystem
      // contents *only* within the embedder's specified data directory.
      mojo::PendingRemote<storage::mojom::Directory> directory;
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskPriority::USER_VISIBLE})
          ->PostTask(FROM_HERE,
                     base::BindOnce(
                         &BindStorageServiceFilesystemImpl, sandboxed_data_dir,
                         directory.InitWithNewPipeAndPassReceiver()));
      remote->SetDataDirectory(sandboxed_data_dir, std::move(directory));
    } else
#endif  // !BUILDFLAG(IS_ANDROID)
    {
      GetIOThreadTaskRunner({})->PostTask(
          FROM_HERE, base::BindOnce(&RunInProcessStorageService,
                                    remote.BindNewPipeAndPassReceiver()));
    }

    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kEnableAggressiveDOMStorageFlushing)) {
      remote->EnableAggressiveDomStorageFlushing();
    }
  }
  return remote;
}

void OnClearedCookies(base::OnceClosure callback, uint32_t num_deleted) {
  // The final callback needs to happen from UI thread.
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    GetUIThreadTaskRunner({})->PostTask(
        FROM_HERE,
        base::BindOnce(&OnClearedCookies, std::move(callback), num_deleted));
    return;
  }

  std::move(callback).Run();
}

void ClearedGpuCache(base::OnceClosure callback) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    GetUIThreadTaskRunner({})->PostTask(
        FROM_HERE, base::BindOnce(&ClearedGpuCache, std::move(callback)));
    return;
  }
  std::move(callback).Run();
}

void OnLocalStorageUsageInfo(
    const scoped_refptr<DOMStorageContextWrapper>& dom_storage_context,
    const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
    StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher,
    bool perform_storage_cleanup,
    const base::Time delete_begin,
    const base::Time delete_end,
    base::OnceClosure callback,
    const std::vector<StorageUsageInfo>& infos) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  base::OnceClosure done_callback =
      perform_storage_cleanup
          ? base::BindOnce(
                &DOMStorageContextWrapper::PerformLocalStorageCleanup,
                dom_storage_context, std::move(callback))
          : std::move(callback);

  base::ConcurrentClosures concurrent;
  for (const StorageUsageInfo& info : infos) {
    if (storage_key_matcher &&
        !storage_key_matcher.Run(info.storage_key,
                                 special_storage_policy.get())) {
      continue;
    }

    if (info.last_modified >= delete_begin &&
        info.last_modified <= delete_end) {
      dom_storage_context->DeleteLocalStorage(info.storage_key,
                                              concurrent.CreateClosure());
    }
  }
  std::move(concurrent).Done(std::move(done_callback));
}

void OnSessionStorageUsageInfo(
    const scoped_refptr<DOMStorageContextWrapper>& dom_storage_context,
    StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
    bool perform_storage_cleanup,
    base::OnceClosure callback,
    const std::vector<SessionStorageUsageInfo>& infos) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  base::OnceClosure done_callback =
      perform_storage_cleanup
          ? base::BindOnce(
                &DOMStorageContextWrapper::PerformSessionStorageCleanup,
                dom_storage_context, std::move(callback))
          : std::move(callback);

  base::ConcurrentClosures concurrent;
  for (const SessionStorageUsageInfo& info : infos) {
    if (storage_key_matcher && !storage_key_matcher.Run(info.storage_key)) {
      continue;
    }
    dom_storage_context->DeleteSessionStorage(info, concurrent.CreateClosure());
  }
  std::move(concurrent).Done(std::move(done_callback));
}

void ClearQuotaManagedData(
    scoped_refptr<storage::QuotaManager> quota_manager,
    const base::Time begin,
    const base::Time end,
    storage::QuotaClientTypes quota_client_types,
    const blink::StorageKey& storage_key,
    scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
    StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher,
    bool perform_storage_cleanup,
    base::OnceClosure callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  auto on_got_modified_buckets = base::BindOnce(
      [](scoped_refptr<storage::QuotaManager> quota_manager,
         scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
         StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher,
         std::optional<blink::StorageKey> storage_key,
         storage::QuotaClientTypes quota_client_types,
         bool perform_storage_cleanup, base::OnceClosure callback,
         const std::set<storage::BucketLocator>& buckets) {
        DCHECK_CURRENTLY_ON(BrowserThread::IO);
        if (buckets.empty()) {
          std::move(callback).Run();
          return;
        }

        // Collect buckets that pass the filters.
        std::vector<storage::BucketLocator> buckets_to_delete;
        for (const storage::BucketLocator& bucket : buckets) {
          if (storage_key.has_value() && bucket.storage_key != *storage_key) {
            continue;
          }

          if (storage_key_matcher &&
              !storage_key_matcher.Run(bucket.storage_key,
                                       special_storage_policy.get())) {
            continue;
          }

          buckets_to_delete.push_back(bucket);
        }

        base::OnceClosure done_callback =
            perform_storage_cleanup
                ? base::BindOnce(&storage::QuotaManager::PerformStorageCleanup,
                                 quota_manager, quota_client_types,
                                 std::move(callback))
                : std::move(callback);

        base::RepeatingClosure barrier = base::BarrierClosure(
            buckets_to_delete.size(), std::move(done_callback));

        for (const storage::BucketLocator& bucket : buckets_to_delete) {
          quota_manager->DeleteBucketData(
              bucket, quota_client_types,
              base::IgnoreArgs<blink::mojom::QuotaStatusCode>(barrier));
        }
      },
      quota_manager, std::move(special_storage_policy),
      std::move(storage_key_matcher),
      storage_key.origin().opaque() ? std::nullopt
                                    : std::make_optional(storage_key),
      std::move(quota_client_types), perform_storage_cleanup,
      std::move(callback));

  GetIOThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(&storage::QuotaManager::GetBucketsModifiedBetween,
                     std::move(quota_manager), begin, end,
                     std::move(on_got_modified_buckets)));
}

void ClearLocalStorage(
    const scoped_refptr<DOMStorageContextWrapper>& dom_storage_context,
    const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
    StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher,
    const blink::StorageKey& storage_key,
    bool perform_storage_cleanup,
    const base::Time begin,
    const base::Time end,
    base::OnceClosure callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  if (!storage_key.origin().opaque()) {
    bool can_delete =
        !storage_key_matcher ||
        storage_key_matcher.Run(storage_key, special_storage_policy.get());
    if (can_delete) {
      dom_storage_context->DeleteLocalStorage(storage_key, std::move(callback));
    } else {
      std::move(callback).Run();
    }
    return;
  }

  dom_storage_context->GetLocalStorageUsage(
      base::BindOnce(&OnLocalStorageUsageInfo, dom_storage_context,
                     special_storage_policy, std::move(storage_key_matcher),
                     perform_storage_cleanup, begin, end, std::move(callback)));
}

void ClearSessionStorage(
    const scoped_refptr<DOMStorageContextWrapper>& dom_storage_context,
    StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
    bool perform_storage_cleanup,
    base::OnceClosure callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  dom_storage_context->GetSessionStorageUsage(
      base::BindOnce(&OnSessionStorageUsageInfo, dom_storage_context,
                     std::move(storage_key_matcher), perform_storage_cleanup,
                     std::move(callback)));
}

// LoginHandlerDelegate manages HTTP auth. It is self-owning and deletes itself
// when the credentials are resolved or the AuthChallengeResponder is cancelled.
class LoginHandlerDelegate {
 public:
  LoginHandlerDelegate(
      mojo::PendingRemote<network::mojom::AuthChallengeResponder>
          auth_challenge_responder,
      WebContents* web_contents,
      content::BrowserContext* browser_context,
      const net::AuthChallengeInfo& auth_info,
      bool is_request_for_primary_main_frame_navigation,
      bool is_request_for_navigation,
      const network::OriginatingProcessId& process_id,
      base::StrictNumeric<int32_t> request_id,
      const GURL& url,
      scoped_refptr<net::HttpResponseHeaders> response_headers,
      bool first_auth_attempt,
      FrameTreeNodeId frame_tree_node_id)
      : auth_challenge_responder_(std::move(auth_challenge_responder)),
        auth_info_(auth_info),
        request_id_(process_id, request_id),
        is_request_for_primary_main_frame_navigation_(
            is_request_for_primary_main_frame_navigation),
        is_request_for_navigation_(is_request_for_navigation),
        creating_login_delegate_(false),
        url_(url),
        response_headers_(std::move(response_headers)),
        first_auth_attempt_(first_auth_attempt),
        web_contents_(web_contents ? web_contents->GetWeakPtr() : nullptr),
        browser_context_(browser_context->GetWeakPtr()),
        frame_tree_node_id_(frame_tree_node_id) {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    auth_challenge_responder_.set_disconnect_handler(base::BindOnce(
        &LoginHandlerDelegate::OnRequestCancelled, base::Unretained(this)));

    DevToolsURLLoaderInterceptor::HandleAuthRequest(
        request_id_, auth_info_,
        base::BindOnce(&LoginHandlerDelegate::ContinueAfterInterceptor,
                       weak_factory_.GetWeakPtr()));
  }

 private:
  void OnRequestCancelled() {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    // This will destroy `login_handler_io_` on the IO thread and, if needed,
    // inform the delegate.
    delete this;
  }

  void ContinueAfterInterceptor(
      bool use_fallback,
      const std::optional<net::AuthCredentials>& auth_credentials) {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    DCHECK(!(use_fallback && auth_credentials.has_value()));
    if (!use_fallback) {
      OnAuthCredentials(auth_credentials);
      return;
    }

    if (!browser_context_) {
      OnAuthCredentials(std::nullopt);
      return;
    }

    if (web_contents_) {
      CHECK_EQ(web_contents_->GetBrowserContext(), browser_context_.get());
    }

    FrameTreeNode* frame_tree_node =
        FrameTreeNode::GloballyFindByID(frame_tree_node_id_);
    GuestPageHolder* guest = nullptr;
    if (frame_tree_node) {
      guest = GuestPageHolderImpl::FromRenderFrameHost(
          *frame_tree_node->current_frame_host());
    }

    // WeakPtr is not strictly necessary here due to OnRequestCancelled.
    creating_login_delegate_ = true;
    login_delegate_ = GetContentClient()->browser()->CreateLoginDelegate(
        auth_info_, web_contents_.get(), browser_context_.get(), request_id_,
        is_request_for_primary_main_frame_navigation_,
        is_request_for_navigation_, url_, response_headers_,
        first_auth_attempt_, guest,
        base::BindOnce(&LoginHandlerDelegate::OnAuthCredentials,
                       weak_factory_.GetWeakPtr()));
    creating_login_delegate_ = false;
    if (!login_delegate_) {
      OnAuthCredentials(std::nullopt);
      return;
    }
  }

  void OnAuthCredentials(
      const std::optional<net::AuthCredentials>& auth_credentials) {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    // CreateLoginDelegate must not call the callback reentrantly. For
    // robustness, detect this mistake.
    CHECK(!creating_login_delegate_);
    auth_challenge_responder_->OnAuthCredentials(auth_credentials);
    delete this;
  }

  mojo::Remote<network::mojom::AuthChallengeResponder>
      auth_challenge_responder_;
  net::AuthChallengeInfo auth_info_;
  const content::GlobalRequestID request_id_;
  bool is_request_for_primary_main_frame_navigation_;
  bool is_request_for_navigation_;
  bool creating_login_delegate_;
  GURL url_;
  const scoped_refptr<net::HttpResponseHeaders> response_headers_;
  bool first_auth_attempt_;
  base::WeakPtr<WebContents> web_contents_;
  base::WeakPtr<BrowserContext> browser_context_;
  std::unique_ptr<LoginDelegate> login_delegate_;
  FrameTreeNodeId frame_tree_node_id_;
  base::WeakPtrFactory<LoginHandlerDelegate> weak_factory_{this};
};

// This class lives on the UI thread. It is self-owned and will delete itself
// after any of the SSLClientAuthHandler::Delegate methods are invoked (or when
// a mojo connection error occurs).
class SSLClientAuthDelegate : public SSLClientAuthHandler::Delegate {
 public:
  SSLClientAuthDelegate(
      mojo::PendingRemote<network::mojom::ClientCertificateResponder>
          client_cert_responder_remote,
      BrowserContext* browser_context,
      int process_id,
      base::WeakPtr<WebContents> web_contents,
      const scoped_refptr<net::SSLCertRequestInfo>& cert_info)
      : client_cert_responder_(std::move(client_cert_responder_remote)),
        ssl_client_auth_handler_(std::make_unique<SSLClientAuthHandler>(
            GetContentClient()->browser()->CreateClientCertStore(
                browser_context),
            browser_context->GetWeakPtr(),
            process_id,
            web_contents,
            std::move(cert_info.get()),
            this)) {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    DCHECK(client_cert_responder_);
    client_cert_responder_.set_disconnect_handler(base::BindOnce(
        &SSLClientAuthDelegate::DeleteSelf, base::Unretained(this)));
    ssl_client_auth_handler_->SelectCertificate();
  }

  ~SSLClientAuthDelegate() override { DCHECK_CURRENTLY_ON(BrowserThread::UI); }

  void DeleteSelf() { delete this; }

  // SSLClientAuthHandler::Delegate:
  void CancelCertificateSelection() override {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    client_cert_responder_->CancelRequest();
    DeleteSelf();
  }

  // SSLClientAuthHandler::Delegate:
  void ContinueWithCertificate(
      scoped_refptr<net::X509Certificate> cert,
      scoped_refptr<net::SSLPrivateKey> private_key) override {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);

    if (cert && private_key) {
      mojo::PendingRemote<network::mojom::SSLPrivateKey> ssl_private_key;

      mojo::MakeSelfOwnedReceiver(
          std::make_unique<SSLPrivateKeyImpl>(private_key),
          ssl_private_key.InitWithNewPipeAndPassReceiver());

      client_cert_responder_->ContinueWithCertificate(
          cert, private_key->GetProviderName(),
          private_key->GetAlgorithmPreferences(), std::move(ssl_private_key));
    } else {
      client_cert_responder_->ContinueWithoutCertificate();
    }

    DeleteSelf();
  }

 private:
  mojo::Remote<network::mojom::ClientCertificateResponder>
      client_cert_responder_;
  std::unique_ptr<SSLClientAuthHandler> ssl_client_auth_handler_;
};

void CallCancelRequest(
    mojo::PendingRemote<network::mojom::ClientCertificateResponder>
        client_cert_responder_remote) {
  DCHECK(client_cert_responder_remote);
  mojo::Remote<network::mojom::ClientCertificateResponder>
      client_cert_responder(std::move(client_cert_responder_remote));
  client_cert_responder->CancelRequest();
}

// Cancels prerendering if `navigation_or_document` is in a prerendered frame
// tree, using `final_status` as the cancellation reason. Returns true if
// cancelled.
bool CancelIfPrerendering(NavigationOrDocumentHandle* navigation_or_document,
                          PrerenderFinalStatus final_status) {
  FrameTreeNode* frame_tree_node = nullptr;
  // `navigation_or_document` can be null for `kSharedOrServiceWorkerContext`.
  if (!navigation_or_document) {
    return false;
  }
  auto* navigation_request = navigation_or_document->GetNavigationRequest();
  if (navigation_request) {
    frame_tree_node = navigation_request->frame_tree_node();
  }
  auto* render_frame_host = navigation_or_document->GetDocument();
  if (render_frame_host) {
    frame_tree_node = FrameTreeNode::From(render_frame_host);
  }
  if (!frame_tree_node) {
    return false;
  }

  auto* web_contents = WebContentsImpl::FromFrameTreeNode(frame_tree_node);
  return web_contents->CancelPrerendering(frame_tree_node, final_status);
}

class SSLErrorDelegate : public SSLErrorHandler::Delegate {
 public:
  explicit SSLErrorDelegate(network::mojom::URLLoaderNetworkServiceObserver::
                                OnSSLCertificateErrorCallback response)
      : response_(std::move(response)) {}
  ~SSLErrorDelegate() override = default;
  void CancelSSLRequest(int error, const net::SSLInfo* ssl_info) override {
    base::UmaHistogramTimes("Content.SslError.CallbackDuration",
                            base::TimeTicks::Now() - startTime_);
    std::move(response_).Run(error);
    delete this;
  }
  void ContinueSSLRequest() override {
    base::UmaHistogramTimes("Content.SslError.CallbackDuration",
                            base::TimeTicks::Now() - startTime_);
    std::move(response_).Run(net::OK);
    delete this;
  }
  base::WeakPtr<SSLErrorDelegate> GetWeakPtr() {
    return weak_factory_.GetWeakPtr();
  }

 private:
  base::TimeTicks startTime_ = base::TimeTicks::Now();
  network::mojom::URLLoaderNetworkServiceObserver::OnSSLCertificateErrorCallback
      response_;
  base::WeakPtrFactory<SSLErrorDelegate> weak_factory_{this};
};

#if BUILDFLAG(IS_ANDROID)
void FinishGenerateNegotiateAuthToken(
    std::unique_ptr<net::android::HttpAuthNegotiateAndroid> auth_negotiate,
    std::unique_ptr<std::string> auth_token,
    std::unique_ptr<net::HttpAuthPreferences> prefs,
    network::mojom::NetworkContextClient::
        OnGenerateHttpNegotiateAuthTokenCallback callback,
    int result) {
  std::move(callback).Run(result, *auth_token);
}
#endif

// If both `storage_key_matcher` and `storage_key_policy_matcher` are null, this
// should return a null callback that indicates all StorageKeys should match.
// Otherwise, returns true if StorageKey matches both `storage_key_matcher` and
// `storage_key_policy_matcher` if not null.
StoragePartition::StorageKeyPolicyMatcherFunction
CombineStorageKeyMatcherFunctions(
    StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
    StoragePartition::StorageKeyPolicyMatcherFunction
        storage_key_policy_matcher) {
  if (storage_key_matcher.is_null() && storage_key_policy_matcher.is_null()) {
    return base::NullCallback();
  }

  return base::BindRepeating(
      [](StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
         StoragePartition::StorageKeyPolicyMatcherFunction
             storage_key_policy_matcher,
         const blink::StorageKey& storage_key,
         storage::SpecialStoragePolicy* policy) -> bool {
        return (!storage_key_matcher || storage_key_matcher.Run(storage_key)) &&
               (!storage_key_policy_matcher ||
                storage_key_policy_matcher.Run(storage_key, policy));
      },
      std::move(storage_key_matcher), std::move(storage_key_policy_matcher));
}

// Conceptually, many downstream interfaces don't need to know about the
// complexity of callers into StoragePartition, so this function reduces the API
// surface to something simple and generic. It is designed to be used by
// callsites in ClearDataImpl.
//
// Precondition: `storage_key_matcher`/`storage_key_policy_matcher` and
// `storage_key` cannot both be set. If all of `storage_key_matcher`,
// `storage_key_policy_matcher` and `storage_key` are null/empty, this should
// return a null callback that indicates all StorageKeys should match. This is
// an optimization for backends to efficiently clear all data.
//
// TODO(csharrison, mek): Right now, this is only used by a small set of storage
// backends, e.g. aggregation service. We should consider moving some of the
// backends to use this if they can, and additionally we should consider
// rethinking this approach if / when storage backends move out of process
// (see crbug.com/1016065 for initial work here).
StoragePartition::StorageKeyMatcherFunction CreateGenericStorageKeyMatcher(
    const blink::StorageKey& storage_key,
    StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
    StoragePartition::StorageKeyPolicyMatcherFunction
        storage_key_policy_matcher,
    scoped_refptr<storage::SpecialStoragePolicy> policy) {
  const bool storage_key_origin_empty = storage_key.origin().opaque();
  DCHECK(storage_key_origin_empty || storage_key_matcher.is_null());
  DCHECK(storage_key_origin_empty || storage_key_policy_matcher.is_null());

  if (storage_key_origin_empty && storage_key_matcher.is_null() &&
      storage_key_policy_matcher.is_null()) {
    return base::NullCallback();
  }

  if (storage_key_matcher || storage_key_policy_matcher) {
    return base::BindRepeating(
        [](StoragePartition::StorageKeyPolicyMatcherFunction
               storage_key_policy_matcher,
           scoped_refptr<storage::SpecialStoragePolicy> policy,
           const blink::StorageKey& storage_key) -> bool {
          DCHECK(!storage_key_policy_matcher.is_null());
          return storage_key_policy_matcher.Run(storage_key, policy.get());
        },
        CombineStorageKeyMatcherFunctions(
            std::move(storage_key_matcher),
            std::move(storage_key_policy_matcher)),
        std::move(policy));
  }
  DCHECK(!storage_key_origin_empty);
  return base::BindRepeating(std::equal_to<const blink::StorageKey&>(),
                             storage_key);
}

std::vector<GlobalRenderFrameHostId> GetRoutingIdsForOrigin(
    StoragePartitionImpl* storage_partition,
    const url::Origin& origin) {
  scoped_refptr<ServiceWorkerContextWrapper> service_worker_context =
      storage_partition->GetServiceWorkerContext();

  return *service_worker_context->GetWindowClientFrameRoutingIds(
      blink::StorageKey::CreateFirstParty(origin));
}

// Resulting decision for a local network access permission check.
enum class LocalNetworkAccessDecision {
  // The request should be blocked immediately (e.g., in fenced frames or
  // prerendering main frames).
  kBlock,
  // The request is default-allowed without checking permissions (e.g., primary
  // main frame navigations).
  kAllow,
  // The request should proceed to permission checking using the resolved `rfh`
  // (if non-null) or worker context.
  kProceed,
};

// Outcome of resolving local network access context for a network request.
struct LocalNetworkAccessResolution {
  LocalNetworkAccessDecision decision;
  // The RenderFrameHost associated with the request context, if available.
  // Null for worker contexts or when no specific frame applies.
  raw_ptr<RenderFrameHost> rfh = nullptr;
};

// Resolves the frame context and initial decision for local network access
// requests. Shared helper between Web LNA
// (OnLocalNetworkAccessPermissionRequired) and Platform LNA
// (OnPlatformLocalNetworkPermissionRequired).
LocalNetworkAccessResolution ResolveLocalNetworkAccess(
    const StoragePartitionImpl::URLLoaderNetworkContext& context) {
  // Three different cases are handled here depending on the request context:
  //   1. Document context (ContextType::kRenderFrameHostContext) covers fetch()
  //      and subresource requests. These should check for existing permission
  //      state, and if the state is ASK trigger the permission prompt. These
  //      should also handle being delegated into subframe documents.
  //   2. Navigation context (ContextType::kNavigationRequestContext) covers
  //      all navigations. If the navigation is in a subframe, these should
  //      check for existing permission state, and if the state is ASK trigger
  //      the permission prompt. Nested subframes should be allowed iff
  //      permission policy delegated the permission into the embedding frame.
  //   3. Worker context (ContextType::kSharedOrServiceWorkerContext) covers
  //      requests from service workers and shared workers. These may not have
  //      an existing document around. These should check for the permission
  //      state, but NOT trigger the permission prompt.

  // Currently requesting the Local Network Access permission is restricted to
  // subresource requests and subframe navigation requests.

  if (!context.navigation_or_document()) {
    // Workers and background tasks do not resolve to a RenderFrameHost, but we
    // return kProceed here to let downstream handlers apply their own rules:
    // - Web LNA: Evaluates worker permissions via PermissionController.
    // - Platform LNA: Requires a UI tab (WebContents) to display dialogs. Since
    //   background workers lack a UI tab, requests are safely denied early.
    //   (See TODO below for potential future support via frame window IDs).
    return {LocalNetworkAccessDecision::kProceed, nullptr};
  }

  // Case 1: Document context (covers fetch() and subresource requests).
  if (RenderFrameHost* rfh = context.navigation_or_document()->GetDocument()) {
    // Get the document that is making the request.
    return {LocalNetworkAccessDecision::kProceed, rfh};
  }

  // Case 2: Navigation context.
  if (auto* request =
          context.navigation_or_document()->GetNavigationRequest()) {
    // Currently the LNA permission only applies to subframe navigations.
    // See content/browser/renderer_host/local_network_access_util.cc for
    // current feature state to policy mapping logic.
    //
    // For other types of navigation, we either default-allow or default-block
    // local network requests:
    //  - Primary main frame: default-allow.
    //  - Guest view main frame: default-allow.
    //  - Prerender: default-block.
    //  - Fenced frame: default-block. (See crbug.com/409303581.)
    switch (request->GetNavigatingFrameType()) {
      case FrameType::kPrimaryMainFrame:
      case FrameType::kGuestMainFrame:
        return {LocalNetworkAccessDecision::kAllow};
      case FrameType::kFencedFrameRoot:
      case FrameType::kPrerenderMainFrame:
        return {LocalNetworkAccessDecision::kBlock};
      case FrameType::kSubframe: {
        // Get the document that initiated the navigation. Can be nullptr if
        // the initiator has gone away, in which case we should just block the
        // navigation.
        if (!request->GetInitiatorFrameToken().has_value()) {
          return {LocalNetworkAccessDecision::kBlock};
        }

        RenderFrameHost* initiator_rfh =
            RenderFrameHost::FromFrameToken(GlobalRenderFrameHostToken(
                request->GetInitiatorProcessId(),
                request->GetInitiatorFrameToken().value()));

        if (!initiator_rfh) {
          return {LocalNetworkAccessDecision::kBlock};
        }

        return {LocalNetworkAccessDecision::kProceed, initiator_rfh};
      }
    }
  }

  // Fallback if neither document nor navigation request is available.
  return {LocalNetworkAccessDecision::kBlock};
}

}  // namespace

// Static.
storage::QuotaClientTypes StoragePartitionImpl::GenerateQuotaClientTypes(
    uint32_t remove_mask) {
  storage::QuotaClientTypes quota_client_types;

  if (remove_mask & StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS) {
    quota_client_types.insert(storage::QuotaClientType::kFileSystem);
  }
  if (remove_mask & StoragePartition::REMOVE_DATA_MASK_INDEXEDDB) {
    quota_client_types.insert(storage::QuotaClientType::kIndexedDatabase);
  }
  if (remove_mask & StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS) {
    quota_client_types.insert(storage::QuotaClientType::kServiceWorker);
  }
  if (remove_mask & StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE) {
    quota_client_types.insert(storage::QuotaClientType::kServiceWorkerCache);
  }
  if (remove_mask & StoragePartition::REMOVE_DATA_MASK_BACKGROUND_FETCH) {
    quota_client_types.insert(storage::QuotaClientType::kBackgroundFetch);
  }
  return quota_client_types;
}

// static
void StoragePartitionImpl::ForceInProcessStorageServiceForTesting() {
  g_force_in_process_storage_service = true;
}

// Helper for deleting all sorts of data from a partition, keeps track of
// deletion status.
//
// StoragePartitionImpl creates an instance of this class to keep track of
// data deletion progress. Deletion requires deleting multiple bits of data
// (e.g. cookies, local storage, session storage etc.) and hopping between UI
// and IO thread. An instance of this class is created in the beginning of
// deletion process (StoragePartitionImpl::ClearDataImpl) and the instance is
// forwarded and updated on each (sub) deletion's callback. The instance is
// finally destroyed when deletion completes (and `callback` is invoked).
class StoragePartitionImpl::DataDeletionHelper {
 public:
  DataDeletionHelper(uint32_t remove_mask, base::OnceClosure callback)
      : remove_mask_(remove_mask), callback_(std::move(callback)) {}

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

  ~DataDeletionHelper() = default;

  void ClearData(
      const blink::StorageKey& storage_key,
      BrowsingDataFilterBuilder* filter_builder,
      StorageKeyPolicyMatcherFunction storage_key_policy_matcher,
      CookieDeletionFilterPtr cookie_deletion_filter,
      const base::FilePath& path,
      DOMStorageContextWrapper* dom_storage_context,
      storage::QuotaManager* quota_manager,
      storage::SpecialStoragePolicy* special_storage_policy,
      storage::FileSystemContext* filesystem_context,
      network::mojom::CookieManager* cookie_manager,
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
      CdmStorageManager* cdm_storage_manager,
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
      network::mojom::DeviceBoundSessionManager* device_bound_session_manager,
      KeepAliveURLLoaderService* keep_alive_url_loader_service,
      DeclarativePerformanceObserverStore*
          declarative_performance_observer_store,
      bool perform_storage_cleanup,
      const base::Time begin,
      const base::Time end);

 private:
  // For debugging purposes. Please add new deletion tasks at the end.
  // This enum is recorded in a histogram, so don't change or reuse ids.
  // LINT.IfChange(TracingDataType)
  enum class TracingDataType {
    kSynchronous = 1,
    kCookies = 2,
    kQuota = 3,
    kLocalStorage = 4,
    kSessionStorage = 5,
    kShaderCache = 6,         // Deprecated in favor of using kGpuCache.
    kPluginPrivate = 7,       // Deprecated.
    kConversions = 8,         // Deprecated.
    kAggregationService = 9,  // Deprecated.
    kSharedStorage = 10,
    kGpuCache = 11,
    kPrivateAggregation = 12,  // Deprecated.
    kInterestGroups = 13,      // Deprecated
    kCdmStorage = 14,
    kDeviceBoundSessions = 15,
    kDeclarativePerformanceObserver = 16,
    kMaxValue = kDeclarativePerformanceObserver,
  };
  // LINT.ThenChange(//tools/metrics/histograms/metadata/history/enums.xml:StoragePartitionRemoverTasks)

  base::OnceClosure CreateTaskCompletionClosure(TracingDataType data_type);

  void OnTaskComplete(TracingDataType data_type,
                      int tracing_id);  // Callable on any thread.
  void RecordUnfinishedSubTasks();

  uint32_t remove_mask_;

  // Accessed on UI thread.
  base::OnceClosure callback_;
  // Accessed on UI thread.
  std::set<TracingDataType> pending_tasks_;

  base::WeakPtrFactory<StoragePartitionImpl::DataDeletionHelper> weak_factory_{
      this};
};

class StoragePartitionImpl::ServiceWorkerCookieAccessObserver
    : public network::mojom::CookieAccessObserver {
 public:
  explicit ServiceWorkerCookieAccessObserver(
      StoragePartitionImpl* storage_partition)
      : storage_partition_(storage_partition) {}

 private:
  void Clone(mojo::PendingReceiver<network::mojom::CookieAccessObserver>
                 observer) override {
    storage_partition_->service_worker_cookie_observers_.Add(
        std::make_unique<ServiceWorkerCookieAccessObserver>(storage_partition_),
        std::move(observer));
  }

  void OnCookiesAccessed(std::vector<network::mojom::CookieAccessDetailsPtr>
                             details_vector) override {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    // TODO(https://crbug.com/423472677): Group multiple cookie access details
    // with the same RenderFrameHost. This UMA metrics is to decide whether to
    // optimize this method or not.
    base::UmaHistogramCounts1M(
        "Cookie.OnCookiesAccessed.BatchSize.CookieAccessObserver",
        details_vector.size());
    std::optional<base::ElapsedTimer> timer;
    if (base::ShouldRecordSubsampledMetric(0.01)) {
      timer.emplace();
    }
    for (auto& details : details_vector) {
      for (GlobalRenderFrameHostId frame_id : GetRoutingIdsForOrigin(
               storage_partition_, url::Origin::Create(details->url))) {
        if (RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(frame_id)) {
          std::vector<network::mojom::CookieAccessDetailsPtr> details_list;
          details_list.reserve(1);
          details_list.push_back(mojo::Clone(details));
          rfh->NotifyCookiesAccessed(
              std::move(details_list),
              CookieAccessDetails::Source::kNonNavigation);
        }
      }
    }
    if (timer) {
      base::UmaHistogramCustomMicrosecondsTimes(
          "Browser.CookieAccessObserver.StoragePartitionImpl.Duration."
          "Subsampled",
          timer->Elapsed(), base::Microseconds(1), base::Seconds(1), 100);
    }
  }

  // `storage_partition_` owns this object via UniqueReceiverSet
  // (service_worker_cookie_observers_).
  raw_ptr<StoragePartitionImpl> storage_partition_;
};

class StoragePartitionImpl::ServiceWorkerTrustTokenAccessObserver
    : public network::mojom::TrustTokenAccessObserver {
 public:
  explicit ServiceWorkerTrustTokenAccessObserver(
      StoragePartitionImpl* storage_partition)
      : storage_partition_(storage_partition) {}

 private:
  void Clone(mojo::PendingReceiver<network::mojom::TrustTokenAccessObserver>
                 observer) override {
    storage_partition_->service_worker_trust_token_observers_.Add(
        std::make_unique<ServiceWorkerTrustTokenAccessObserver>(
            storage_partition_),
        std::move(observer));
  }

  void OnTrustTokensAccessed(
      network::mojom::TrustTokenAccessDetailsPtr details) override {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);

    url::Origin origin;
    switch (details->which()) {
      case network::mojom::TrustTokenAccessDetails::Tag::kIssuance:
        origin = details->get_issuance()->origin;
        break;
      case network::mojom::TrustTokenAccessDetails::Tag::kRedemption:
        origin = details->get_redemption()->origin;
        break;
      case network::mojom::TrustTokenAccessDetails::Tag::kSigning:
        origin = details->get_signing()->origin;
        break;
    }

    for (GlobalRenderFrameHostId frame_id :
         GetRoutingIdsForOrigin(storage_partition_, origin)) {
      if (RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(frame_id)) {
        rfh->OnTrustTokensAccessed(mojo::Clone(details));
      }
    }
  }

  // `storage_partition_` owns this object via UniqueReceiverSet
  // (service_worker_trust_token_observers_).
  raw_ptr<StoragePartitionImpl> storage_partition_;
};

class StoragePartitionImpl::ServiceWorkerSharedDictionaryAccessObserver
    : public network::mojom::SharedDictionaryAccessObserver {
 public:
  explicit ServiceWorkerSharedDictionaryAccessObserver(
      StoragePartitionImpl* storage_partition)
      : storage_partition_(storage_partition) {}

 private:
  void Clone(
      mojo::PendingReceiver<network::mojom::SharedDictionaryAccessObserver>
          observer) override {
    storage_partition_->service_worker_shared_dictionary_observers_.Add(
        std::make_unique<ServiceWorkerSharedDictionaryAccessObserver>(
            storage_partition_),
        std::move(observer));
  }

  void OnSharedDictionaryAccessed(
      network::mojom::SharedDictionaryAccessDetailsPtr details) override {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    for (GlobalRenderFrameHostId frame_id : GetRoutingIdsForOrigin(
             storage_partition_, details->isolation_key.frame_origin())) {
      if (RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(frame_id)) {
        rfh->OnSharedDictionaryAccessed(mojo::Clone(details));
      }
    }
  }

  // `storage_partition_` owns this object via UniqueReceiverSet
  // (service_worker_shared_dictionary_observers_).
  raw_ptr<StoragePartitionImpl> storage_partition_;
};

class StoragePartitionImpl::ServiceWorkerDeviceBoundSessionAccessObserver
    : public network::mojom::DeviceBoundSessionAccessObserver {
 public:
  explicit ServiceWorkerDeviceBoundSessionAccessObserver(
      StoragePartitionImpl* storage_partition)
      : storage_partition_(storage_partition) {}

 private:
  void Clone(
      mojo::PendingReceiver<network::mojom::DeviceBoundSessionAccessObserver>
          observer) override {
    storage_partition_->service_worker_device_bound_session_observers_.Add(
        std::make_unique<ServiceWorkerDeviceBoundSessionAccessObserver>(
            storage_partition_),
        std::move(observer));
  }

  void OnDeviceBoundSessionAccessed(
      const net::device_bound_sessions::SessionAccess& access) override {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    for (GlobalRenderFrameHostId frame_id : GetRoutingIdsForOrigin(
             storage_partition_,
             url::Origin::Create(access.session_key.site.GetURL()))) {
      if (RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(frame_id)) {
        rfh->OnDeviceBoundSessionAccessed(access);
      }
    }
  }

  // `storage_partition_` owns this object via UniqueReceiverSet
  // (service_worker_device_bound_session_observers_).
  raw_ptr<StoragePartitionImpl> storage_partition_;
};

struct StoragePartitionImpl::NetworkContextOwner {
  mojo::Remote<network::mojom::NetworkContext> network_context;
};

StoragePartitionImpl::StoragePartitionImpl(
    BrowserContext* browser_context,
    const StoragePartitionConfig& config,
    const base::FilePath& partition_path,
    const base::FilePath& relative_partition_path,
    storage::SpecialStoragePolicy* special_storage_policy)
    : performance_scenarios::MatchingScenarioObserver(
          performance_scenarios::kDefaultIdleScenarios),
      browser_context_(browser_context),
      partition_path_(partition_path),
      config_(config),
      relative_partition_path_(relative_partition_path),
      special_storage_policy_(special_storage_policy),
      network_context_owner_(std::make_unique<NetworkContextOwner>()),
      deletion_helpers_running_(0) {
#if BUILDFLAG(ENABLE_DISK_CACHE_SQL_BACKEND)
  // To run a database checkpoint when the browser is idle, the SQL disk cache
  // uses PerformanceScenarioObserverList to detect the idle state.
  if (base::FeatureList::IsEnabled(
          net::features::kDiskCacheBackendExperiment) &&
      net::features::kDiskCacheBackendParam.Get() ==
          net::features::DiskCacheBackend::kSql) {
    // `observer_list` may be null in tests.
    if (auto observer_list =
            performance_scenarios::PerformanceScenarioObserverList::GetForScope(
                performance_scenarios::ScenarioScope::kGlobal)) {
      performance_scenario_observation_.Observe(observer_list.get());
    }
  }
#endif  // ENABLE_DISK_CACHE_SQL_BACKEND
}

bool StoragePartitionImpl::ShouldClearSessionStorageOnStartup() const {
  return browser_context_->ShouldClearSessionStorageOnStartup();
}

StoragePartitionImpl::~StoragePartitionImpl() {
#if DCHECK_IS_ON()
  DCHECK(on_browser_context_will_be_destroyed_called_);
#endif
  browser_context_ = nullptr;

  if (GetFileSystemContext()) {
    GetFileSystemContext()->Shutdown();
  }

  if (GetDOMStorageContext()) {
    GetDOMStorageContext()->Shutdown();
  }

  if (GetServiceWorkerContext()) {
    GetServiceWorkerContext()->Shutdown();
  }

  if (GetBackgroundSyncContext()) {
    GetBackgroundSyncContext()->Shutdown();
  }

  if (GetBackgroundFetchContext()) {
    GetBackgroundFetchContext()->Shutdown();
  }

  if (GetGeneratedCodeCacheContext()) {
    GetGeneratedCodeCacheContext()->Shutdown();
  }
}

void StoragePartitionImpl::OnBrowserContextWillBeDestroyed() {
#if DCHECK_IS_ON()
  on_browser_context_will_be_destroyed_called_ = true;
#endif

  // Shut down service worker and shared worker machinery because these can keep
  // RenderProcessHosts and SiteInstances alive, and the codebase assumes these
  // are destroyed before the BrowserContext is destroyed.
  GetServiceWorkerContext()->Shutdown();
  GetSharedWorkerService()->Shutdown();

  // These hold raw pointers to objects that are about to be destroyed, before
  // this object is destroyed. Shut them down now to avoid dangling pointers.
  if (GetFileSystemAccessManager()) {
    GetFileSystemAccessManager()->Shutdown();
  }

  if (GetContentIndexContext()) {
    GetContentIndexContext()->Shutdown();
  }

  if (GetPlatformNotificationContext()) {
    GetPlatformNotificationContext()->Shutdown();
  }

  if (keep_alive_url_loader_service_) {
    keep_alive_url_loader_service_->Shutdown();
  }

  if (declarative_performance_observer_store_) {
    declarative_performance_observer_store_->Close();
  }
}

void StoragePartitionImpl::RestrictNetworkForIdsInNetworkContext(
    const std::map<base::UnguessableToken, network::ConnectionAllowlists>&
        ids_to_allowlists,
    base::OnceClosure callback) {
  std::vector<network::mojom::IdAndAllowlistedPatternsPtr> dest_vector;
  for (const auto& pair : ids_to_allowlists) {
    CHECK(pair.first != network::GetNoOpNetworkRestrictionsId(),
          base::NotFatalUntil::M165);
    CHECK(pair.first != network::GetTODONetworkRestrictionsId(),
          base::NotFatalUntil::M165);

    // Create a new Mojo struct pointer for each map entry.
    auto id_and_patterns = network::mojom::IdAndAllowlistedPatterns::New();
    id_and_patterns->network_restrictions_id = pair.first;
    id_and_patterns->allowlists = pair.second;
    dest_vector.push_back(std::move(id_and_patterns));
  }
  GetNetworkContext()->RestrictNetworkForIds(std::move(dest_vector),
                                             std::move(callback));

  // Save IDs and allowlisted URLs in `StoragePartitionImpl`. When there is a
  // crash of `NetworkService`, the network restriction IDs of
  // `NetworkContext` will be restored using this.
  for (const auto& pair : ids_to_allowlists) {
    network_restrictions_ids_.insert_or_assign(pair.first, pair.second);
  }
}

void StoragePartitionImpl::ClearNetworkRestrictionsAfterDelay(
    const std::vector<base::UnguessableToken>& network_restrictions_ids) {
  GetUIThreadTaskRunner({})->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(
          &StoragePartitionImpl::ClearNetworkRestrictionsAfterDelayCallback,
          weak_factory_.GetWeakPtr(), network_restrictions_ids),
      clear_network_restrictions_delay_);
}

void StoragePartitionImpl::ClearNetworkRestrictionsAfterDelayCallback(
    const std::vector<base::UnguessableToken>& network_restrictions_ids) {
  GetNetworkContext()->ClearNetworkRestrictions(network_restrictions_ids);

  for (const auto& network_restrictions_id : network_restrictions_ids) {
    network_restrictions_ids_.erase(network_restrictions_id);
  }

  clear_network_restrictions_callback_for_testing_.Run();  // IN-TEST
}

// static
std::unique_ptr<StoragePartitionImpl> StoragePartitionImpl::Create(
    BrowserContext* context,
    const StoragePartitionConfig& config,
    const base::FilePath& relative_partition_path) {
  // Ensure that these methods are called on the UI thread, except for
  // unittests where a UI thread might not have been created.
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
         !BrowserThread::IsThreadInitialized(BrowserThread::UI));

  base::FilePath partition_path =
      context->GetPath().Append(relative_partition_path);

  return base::WrapUnique(new StoragePartitionImpl(
      context, config, partition_path, relative_partition_path,
      context->GetSpecialStoragePolicy()));
}

void StoragePartitionImpl::Initialize(
    StoragePartitionImpl* fallback_for_blob_urls) {
  // Ensure that these methods are called on the UI thread, except for
  // unittests where a UI thread might not have been created.
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
         !BrowserThread::IsThreadInitialized(BrowserThread::UI));
  DCHECK(!initialized_);
  initialized_ = true;
  SCOPED_UMA_HISTOGRAM_TIMER("Storage.StoragePartition.InitializeDuration");
  base::ElapsedTimer step_timer;

  // All of the clients have to be created and registered with the
  // QuotaManager prior to the QuotaManager being used. We do them
  // all together here prior to handing out a reference to anything
  // that utilizes the QuotaManager.
  const bool report_static_storage_quota =
      GetContentClient()
          ->browser()
          ->GetOverrideValueForStaticStorageQuota(browser_context_)
          .value_or(base::FeatureList::IsEnabled(
              storage::features::kStaticStorageQuota));

  quota_context_ = base::MakeRefCounted<QuotaContext>(
      is_in_memory(), partition_path_,
      browser_context_->GetSpecialStoragePolicy(),
      base::BindRepeating(&StoragePartitionImpl::GetQuotaSettings,
                          weak_factory_.GetWeakPtr()),
      report_static_storage_quota);
  quota_manager_ = quota_context_->quota_manager();
  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy =
      quota_manager_->proxy();

  StorageNotificationService* storage_notification_service =
      browser_context_->GetStorageNotificationService();
  if (storage_notification_service) {
    // The weak ptr associated with the pressure notification callback will be
    // created and evaluated by a task runner on the UI thread, as confirmed by
    // the DCHECK's above, ensuring that the task runner does not attempt to run
    // the callback in the case that the storage notification service is already
    // destructed.
    quota_manager_->SetStoragePressureCallback(
        storage_notification_service
            ->CreateThreadSafePressureNotificationCallback());
  }

  // Each consumer is responsible for registering its QuotaClient during
  // its construction.
  filesystem_context_ = CreateFileSystemContext(
      browser_context_, partition_path_, is_in_memory(), quota_manager_proxy);
  base::UmaHistogramTimes(
      "Storage.StoragePartition.InitializeDuration.QuotaAndFileSystem",
      step_timer.Elapsed());
  step_timer = base::ElapsedTimer();

  dom_storage_context_ = DOMStorageContextWrapper::Create(
      this, browser_context_->GetSpecialStoragePolicy());

  lock_manager_ = std::make_unique<LockManager<storage::BucketId>>();


  scoped_refptr<ChromeBlobStorageContext> blob_context =
      ChromeBlobStorageContext::GetFor(browser_context_);

  file_system_access_manager_ =
      base::MakeRefCounted<FileSystemAccessManagerImpl>(
          filesystem_context_, blob_context,
          browser_context_->GetFileSystemAccessPermissionContext(),
          browser_context_->IsOffTheRecord());

  mojo::PendingRemote<storage::mojom::FileSystemAccessContext>
      file_system_access_context;
  file_system_access_manager_->BindInternalsReceiver(
      file_system_access_context.InitWithNewPipeAndPassReceiver());
  base::FilePath path = is_in_memory() ? base::FilePath() : partition_path_;
  indexed_db_control_wrapper_ =
      std::make_unique<indexed_db::IndexedDBControlWrapper>(
          path, browser_context_->GetSpecialStoragePolicy(),
          quota_manager_proxy,
          ChromeBlobStorageContext::GetRemoteFor(browser_context_),
          std::move(file_system_access_context), GetIOThreadTaskRunner({}));

  cache_storage_control_wrapper_ = std::make_unique<CacheStorageControlWrapper>(
      GetIOThreadTaskRunner({}), path,
      browser_context_->GetSpecialStoragePolicy(), quota_manager_proxy,
      ChromeBlobStorageContext::GetRemoteFor(browser_context_));
  base::UmaHistogramTimes(
      "Storage.StoragePartition.InitializeDuration.DOMStorageAndIndexedDB",
      step_timer.Elapsed());
  step_timer = base::ElapsedTimer();

  service_worker_context_ = new ServiceWorkerContextWrapper(browser_context_);
  service_worker_context_->set_storage_partition(this);

  dedicated_worker_service_ = std::make_unique<DedicatedWorkerServiceImpl>();

  shared_worker_service_ =
      std::make_unique<SharedWorkerServiceImpl>(this, service_worker_context_);

  push_messaging_context_ = std::make_unique<PushMessagingContext>(
      browser_context_, service_worker_context_);

  host_zoom_level_context_.reset(new HostZoomLevelContext(
      browser_context_->CreateZoomLevelDelegate(partition_path_)));

  platform_notification_context_ = new PlatformNotificationContextImpl(
      path, browser_context_, service_worker_context_);
  platform_notification_context_->Initialize();

  devtools_background_services_context_ =
      std::make_unique<DevToolsBackgroundServicesContextImpl>(
          browser_context_, service_worker_context_);

  content_index_context_ = base::MakeRefCounted<ContentIndexContextImpl>(
      browser_context_, service_worker_context_);

  background_fetch_context_ = base::MakeRefCounted<BackgroundFetchContext>(
      weak_factory_.GetWeakPtr(), service_worker_context_, quota_manager_proxy,
      *devtools_background_services_context_.get());

  background_sync_context_ = base::MakeRefCounted<BackgroundSyncContextImpl>();
  background_sync_context_->Init(service_worker_context_,
                                 *devtools_background_services_context_.get());

  payment_app_context_ = new PaymentAppContextImpl();
  payment_app_context_->Init(service_worker_context_);

  broadcast_channel_service_ = std::make_unique<BroadcastChannelService>();

  bluetooth_allowed_devices_map_ =
      std::make_unique<BluetoothAllowedDevicesMap>();

  shared_url_loader_factory_for_browser_process_ = std::make_unique<
      ReconnectableURLLoaderFactoryForIOThreadWrapper>(base::BindRepeating(
      &StoragePartitionImpl::CreateURLLoaderFactoryForBrowserProcessInternal,
      GetWeakPtr()));
  shared_url_loader_factory_for_browser_process_->factory_for_io_thread()
      ->Initialize();

  service_worker_context_->Init(path, quota_manager_proxy.get(),
                                browser_context_->GetSpecialStoragePolicy(),
                                blob_context.get());

  blob_url_registry_ = std::make_unique<storage::BlobUrlRegistry>(
      fallback_for_blob_urls
          ? fallback_for_blob_urls->GetBlobUrlRegistry()->AsWeakPtr()
          : nullptr);

  blob_registry_ = BlobRegistryWrapper::Create(blob_context);

  subresource_proxying_url_loader_service_ =
      std::make_unique<SubresourceProxyingURLLoaderService>(browser_context_);

  if (blink::features::IsKeepAliveURLLoaderServiceEnabled()) {
    keep_alive_url_loader_service_ =
        std::make_unique<KeepAliveURLLoaderService>(this);
  }

  cookie_store_manager_ =
      std::make_unique<CookieStoreManager>(service_worker_context_);
  // Unit tests use the LoadAllSubscriptions() callback to crash early if
  // restoring the CookieManagerStore's state from ServiceWorkerStorage fails.
  // Production and browser tests rely on CookieStoreManager's well-defined
  // behavior when restoring the state fails.
  cookie_store_manager_->LoadAllSubscriptions(base::DoNothing());
  base::UmaHistogramTimes(
      "Storage.StoragePartition.InitializeDuration.ServiceWorkerContext",
      step_timer.Elapsed());
  step_timer = base::ElapsedTimer();

  bucket_manager_ = std::make_unique<BucketManager>(this);

  base::UmaHistogramTimes(
      "Storage.StoragePartition.InitializeDuration.BackgroundTasks",
      step_timer.Elapsed());
  step_timer = base::ElapsedTimer();

  GeneratedCodeCacheSettings settings =
      GetContentClient()->browser()->GetGeneratedCodeCacheSettings(
          browser_context_);

  // For Incognito mode, we should not persist anything on the disk so
  // we do not create a code cache. Caching the generated code in memory
  // is not useful, since V8 already maintains one copy in memory.
  if (!is_in_memory() && settings.enabled()) {
    generated_code_cache_context_ =
        base::MakeRefCounted<GeneratedCodeCacheContext>();

    base::FilePath code_cache_path;
    if (config_.partition_domain().empty()) {
      code_cache_path = settings.path().AppendASCII("Code Cache");
    } else {
      // For site isolated partitions use the config directory.
      code_cache_path = settings.path()
                            .Append(relative_partition_path_)
                            .AppendASCII("Code Cache");
    }
    DCHECK_GE(settings.size_in_bytes(), 0);
    GetGeneratedCodeCacheContext()->Initialize(code_cache_path,
                                               settings.size_in_bytes());
  }

  font_access_manager_ = FontAccessManager::Create();

#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
  if (is_in_memory()) {
    // Pass an empty path if in_memory so that CdmStorage.db is not stored on
    // disk.
    cdm_storage_manager_ =
        std::make_unique<CdmStorageManager>(base::FilePath());
  } else {
    cdm_storage_manager_ = std::make_unique<CdmStorageManager>(
        partition_path_.Append(kCdmStorageDatabaseFileName));
  }
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)


  if (base::FeatureList::IsEnabled(
          blink::features::kDeclarativePerformanceObserver)) {
    declarative_performance_observer_store_ =
        std::make_unique<DeclarativePerformanceObserverStore>(is_in_memory(),
                                                              path);
  }
  base::UmaHistogramTimes(
      "Storage.StoragePartition.InitializeDuration.CodeCacheAndInterestGroup",
      step_timer.Elapsed());
}

void StoragePartitionImpl::ResetSessionStorageConnections() {
  for (const auto& client : dom_storage_clients_) {
    client.second->ResetSessionStorageConnections();
  }
}

void StoragePartitionImpl::ResetLocalStorageConnections() {
  for (const auto& client : dom_storage_clients_) {
    client.second->ResetLocalStorageConnections();
  }
}

const StoragePartitionConfig& StoragePartitionImpl::GetConfig() const {
  return config_;
}

const base::FilePath& StoragePartitionImpl::GetPath() const {
  return partition_path_;
}

const std::string& StoragePartitionImpl::GetPartitionDomain() const {
  return config_.partition_domain();
}

network::mojom::NetworkContext* StoragePartitionImpl::GetNetworkContext() {
  DCHECK(initialized_);
  if (!network_context_owner_->network_context.is_bound()) {
    InitNetworkContext();
  }
  return network_context_owner_->network_context.get();
}

bool StoragePartitionImpl::IsNetworkContextInitialized() {
  return network_context_owner_->network_context.is_bound();
}

cert_verifier::mojom::CertVerifierServiceUpdater*
StoragePartitionImpl::GetCertVerifierServiceUpdater() {
  DCHECK(initialized_);
  if (!cert_verifier_service_updater_.is_bound()) {
    InitNetworkContext();
  }
  return cert_verifier_service_updater_.get();
}

scoped_refptr<network::SharedURLLoaderFactory>
StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcess() {
  CHECK(shared_url_loader_factory_for_browser_process_);
  return shared_url_loader_factory_for_browser_process_->factory();
}

std::unique_ptr<network::PendingSharedURLLoaderFactory>
StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcessIOThread() {
  CHECK(shared_url_loader_factory_for_browser_process_);
  return shared_url_loader_factory_for_browser_process_->factory_for_io_thread()
      ->CloneForIOThread();
}

network::mojom::CookieManager*
StoragePartitionImpl::GetCookieManagerForBrowserProcess() {
  DCHECK(initialized_);
  // Create the CookieManager as needed.
  if (!cookie_manager_for_browser_process_ ||
      !cookie_manager_for_browser_process_.is_connected()) {
    // Reset `cookie_manager_for_browser_process_` before binding it again.
    cookie_manager_for_browser_process_.reset();
    GetNetworkContext()->GetCookieManager(
        cookie_manager_for_browser_process_.BindNewPipeAndPassReceiver());
  }
  return cookie_manager_for_browser_process_.get();
}

void StoragePartitionImpl::CreateRestrictedCookieManager(
    network::mojom::RestrictedCookieManagerRole role,
    const url::Origin& origin,
    const net::IsolationInfo& isolation_info,
    bool is_service_worker,
    int process_id,
    int routing_id,
    bool prefer_bound_cookie_context,
    net::CookieSettingOverrides cookie_setting_overrides,
    net::CookieSettingOverrides devtools_cookie_setting_overrides,
    mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver,
    mojo::PendingRemote<network::mojom::CookieAccessObserver> cookie_observer) {
  DCHECK(initialized_);
  if (!GetContentClient()->browser()->WillCreateRestrictedCookieManager(
          role, browser_context_, origin, isolation_info, is_service_worker,
          process_id, routing_id, prefer_bound_cookie_context, &receiver)) {
    GetNetworkContext()->GetRestrictedCookieManager(
        std::move(receiver), role, origin, isolation_info,
        cookie_setting_overrides, devtools_cookie_setting_overrides,
        prefer_bound_cookie_context, std::move(cookie_observer));
  }
}

void StoragePartitionImpl::CreateTrustTokenQueryAnswerer(
    mojo::PendingReceiver<network::mojom::TrustTokenQueryAnswerer> receiver,
    const url::Origin& top_frame_origin) {
  DCHECK(initialized_);
  GetNetworkContext()->GetTrustTokenQueryAnswerer(std::move(receiver),
                                                  top_frame_origin);
}

storage::QuotaManager* StoragePartitionImpl::GetQuotaManager() {
  DCHECK(initialized_);
  return quota_manager_.get();
}

storage::QuotaManagerProxy* StoragePartitionImpl::GetQuotaManagerProxy() {
  DCHECK(initialized_);
  return quota_manager_->proxy();
}

BackgroundSyncContextImpl* StoragePartitionImpl::GetBackgroundSyncContext() {
  DCHECK(initialized_);
  return background_sync_context_.get();
}

storage::FileSystemContext* StoragePartitionImpl::GetFileSystemContext() {
  DCHECK(initialized_);
  return filesystem_context_.get();
}

DOMStorageContextWrapper* StoragePartitionImpl::GetDOMStorageContext() {
  DCHECK(initialized_);
  return dom_storage_context_.get();
}

storage::mojom::LocalStorageControl*
StoragePartitionImpl::GetLocalStorageControl() {
  DCHECK(initialized_);
  return GetDOMStorageContext()->GetLocalStorageControl();
}

LockManager<storage::BucketId>* StoragePartitionImpl::GetLockManager() {
  DCHECK(initialized_);
  return lock_manager_.get();
}


storage::mojom::IndexedDBControl& StoragePartitionImpl::GetIndexedDBControl() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  return indexed_db_control_wrapper_->GetIndexedDBControl();
}

FileSystemAccessEntryFactory*
StoragePartitionImpl::GetFileSystemAccessEntryFactory() {
  DCHECK(initialized_);
  return file_system_access_manager_.get();
}

QuotaContext* StoragePartitionImpl::GetQuotaContext() {
  DCHECK(initialized_);
  return quota_context_.get();
}

storage::mojom::CacheStorageControl*
StoragePartitionImpl::GetCacheStorageControl() {
  DCHECK(initialized_);
  return cache_storage_control_wrapper_.get();
}

ServiceWorkerContextWrapper* StoragePartitionImpl::GetServiceWorkerContext() {
  DCHECK(initialized_);
  return service_worker_context_.get();
}

DedicatedWorkerServiceImpl* StoragePartitionImpl::GetDedicatedWorkerService() {
  DCHECK(initialized_);
  return dedicated_worker_service_.get();
}

SharedWorkerService* StoragePartitionImpl::GetSharedWorkerService() {
  DCHECK(initialized_);
  return shared_worker_service_.get();
}

HostZoomMap* StoragePartitionImpl::GetHostZoomMap() {
  DCHECK(initialized_);
  DCHECK(host_zoom_level_context_.get());
  return host_zoom_level_context_->GetHostZoomMap();
}

HostZoomLevelContext* StoragePartitionImpl::GetHostZoomLevelContext() {
  DCHECK(initialized_);
  return host_zoom_level_context_.get();
}

ZoomLevelDelegate* StoragePartitionImpl::GetZoomLevelDelegate() {
  DCHECK(initialized_);
  DCHECK(host_zoom_level_context_.get());
  return host_zoom_level_context_->GetZoomLevelDelegate();
}

PlatformNotificationContextImpl*
StoragePartitionImpl::GetPlatformNotificationContext() {
  DCHECK(initialized_);
  return platform_notification_context_.get();
}

BackgroundFetchContext* StoragePartitionImpl::GetBackgroundFetchContext() {
  DCHECK(initialized_);
  return background_fetch_context_.get();
}

PaymentAppContextImpl* StoragePartitionImpl::GetPaymentAppContext() {
  DCHECK(initialized_);
  return payment_app_context_.get();
}

BroadcastChannelService* StoragePartitionImpl::GetBroadcastChannelService() {
  DCHECK(initialized_);
  return broadcast_channel_service_.get();
}

BluetoothAllowedDevicesMap*
StoragePartitionImpl::GetBluetoothAllowedDevicesMap() {
  DCHECK(initialized_);
  return bluetooth_allowed_devices_map_.get();
}

BlobRegistryWrapper* StoragePartitionImpl::GetBlobRegistry() {
  DCHECK(initialized_);
  return blob_registry_.get();
}

storage::BlobUrlRegistry* StoragePartitionImpl::GetBlobUrlRegistry() {
  DCHECK(initialized_);
  return blob_url_registry_.get();
}

SubresourceProxyingURLLoaderService*
StoragePartitionImpl::GetSubresourceProxyingURLLoaderService() {
  DCHECK(initialized_);
  return subresource_proxying_url_loader_service_.get();
}

KeepAliveURLLoaderService*
StoragePartitionImpl::GetKeepAliveURLLoaderService() {
  DCHECK(initialized_);
  return keep_alive_url_loader_service_.get();
}

CookieStoreManager* StoragePartitionImpl::GetCookieStoreManager() {
  DCHECK(initialized_);
  return cookie_store_manager_.get();
}

BucketManager* StoragePartitionImpl::GetBucketManager() {
  DCHECK(initialized_);
  return bucket_manager_.get();
}

GeneratedCodeCacheContext*
StoragePartitionImpl::GetGeneratedCodeCacheContext() {
  DCHECK(initialized_);
  return generated_code_cache_context_.get();
}

DevToolsBackgroundServicesContext*
StoragePartitionImpl::GetDevToolsBackgroundServicesContext() {
  DCHECK(initialized_);
  return devtools_background_services_context_.get();
}

FileSystemAccessManagerImpl*
StoragePartitionImpl::GetFileSystemAccessManager() {
  DCHECK(initialized_);
  return file_system_access_manager_.get();
}

FontAccessManager* StoragePartitionImpl::GetFontAccessManager() {
  DCHECK(initialized_);
  return font_access_manager_.get();
}

void StoragePartitionImpl::SetFontAccessManagerForTesting(
    std::unique_ptr<FontAccessManager> font_access_manager) {
  DCHECK(initialized_);
  DCHECK(font_access_manager);
  font_access_manager_ = std::move(font_access_manager);
}

#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
CdmStorageDataModel* StoragePartitionImpl::GetCdmStorageDataModel() {
  DCHECK(initialized_);
  return cdm_storage_manager_.get();
}
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)

network::mojom::DeviceBoundSessionManager*
StoragePartitionImpl::GetDeviceBoundSessionManager() {
  DCHECK(initialized_);
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
  if (!device_bound_session_manager_ ||
      !device_bound_session_manager_.is_connected()) {
    // Reset `device_bound_session_manager_` before binding it again.
    device_bound_session_manager_.reset();
    GetNetworkContext()->GetDeviceBoundSessionManager(
        device_bound_session_manager_.BindNewPipeAndPassReceiver());
  }

  return device_bound_session_manager_.get();
#else
  return nullptr;
#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
}

ContentIndexContextImpl* StoragePartitionImpl::GetContentIndexContext() {
  DCHECK(initialized_);
  return content_index_context_.get();
}

leveldb_proto::ProtoDatabaseProvider*
StoragePartitionImpl::GetProtoDatabaseProvider() {
  if (!proto_database_provider_) {
    proto_database_provider_ =
        std::make_unique<leveldb_proto::ProtoDatabaseProvider>(partition_path_,
                                                               is_in_memory());
  }
  return proto_database_provider_.get();
}

void StoragePartitionImpl::SetProtoDatabaseProvider(
    std::unique_ptr<leveldb_proto::ProtoDatabaseProvider> proto_db_provider) {
  DCHECK(!proto_database_provider_);
  proto_database_provider_ = std::move(proto_db_provider);
}

leveldb_proto::ProtoDatabaseProvider*
StoragePartitionImpl::GetProtoDatabaseProviderForTesting() {
  return proto_database_provider_.get();
}


void StoragePartitionImpl::DeleteStaleSessionData() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (base::FeatureList::IsEnabled(
          features::kDeferSessionStorageScavengingOnStartup)) {
    // Defer session storage scavenging to avoid LevelDB initialization
    // blocking the critical path of startup. We reuse the cookie delay
    // to batch these cleanup tasks.
    GetUIThreadTaskRunner({})->PostDelayedTask(
        FROM_HERE,
        base::BindOnce(&StoragePartitionImpl::DeleteStaleSessionDataAfterDelay,
                       weak_factory_.GetWeakPtr()),
        stale_session_cleanup_delay_);
  } else {
    GetDOMStorageContext()->StartScavengingUnusedSessionStorage();
    // We need to delay deleting stale session cookies until after the cookie db
    // has initialized, otherwise we will bypass lazy loading and block.
    // See crbug.com/40285083 for more info.
    GetUIThreadTaskRunner({})->PostDelayedTask(
        FROM_HERE,
        base::BindOnce(
            &StoragePartitionImpl::DeleteStaleSessionOnlyCookiesAfterDelay,
            weak_factory_.GetWeakPtr()),
        stale_session_cleanup_delay_);
  }
}

void StoragePartitionImpl::DeleteStaleSessionOnlyCookiesAfterDelay() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  GetCookieManagerForBrowserProcess()->DeleteStaleSessionOnlyCookies(
      base::BindOnce([](const uint32_t num_deleted) {
        base::UmaHistogramCounts10M(
            "Cookie.StaleSessionCookiesDeletedOnStartup", num_deleted);
      }));
}

void StoragePartitionImpl::DeleteStaleSessionDataAfterDelay() {
  GetDOMStorageContext()->StartScavengingUnusedSessionStorage();
  DeleteStaleSessionOnlyCookiesAfterDelay();
}

void StoragePartitionImpl::OpenLocalStorage(
    const blink::StorageKey& storage_key,
    const blink::LocalFrameToken& local_frame_token,
    mojo::PendingReceiver<blink::mojom::StorageArea> receiver) {
  DCHECK(initialized_);
  ChildProcessSecurityPolicyImpl::Handle security_policy_handle =
      dom_storage_receivers_.current_context()->Duplicate();
  dom_storage_context_->OpenLocalStorage(
      storage_key, local_frame_token, std::move(receiver),
      std::move(security_policy_handle),
      dom_storage_receivers_.GetBadMessageCallback());
}

void StoragePartitionImpl::BindSessionStorageNamespace(
    const std::string& namespace_id,
    mojo::PendingReceiver<blink::mojom::SessionStorageNamespace> receiver) {
  DCHECK(initialized_);
  dom_storage_context_->BindNamespace(
      namespace_id, dom_storage_receivers_.GetBadMessageCallback(),
      std::move(receiver));
}

void StoragePartitionImpl::BindSessionStorageArea(
    const blink::StorageKey& storage_key,
    const blink::LocalFrameToken& local_frame_token,
    const std::string& namespace_id,
    mojo::PendingReceiver<blink::mojom::StorageArea> receiver) {
  DCHECK(initialized_);
  ChildProcessSecurityPolicyImpl::Handle security_policy_handle =
      dom_storage_receivers_.current_context()->Duplicate();
  dom_storage_context_->BindStorageArea(
      storage_key, local_frame_token, namespace_id, std::move(receiver),
      std::move(security_policy_handle),
      dom_storage_receivers_.GetBadMessageCallback());
}

void StoragePartitionImpl::OnAuthRequired(
    const std::optional<base::UnguessableToken>& window_id,
    int32_t request_id,
    const GURL& url,
    bool first_auth_attempt,
    const net::AuthChallengeInfo& auth_info,
    const scoped_refptr<net::HttpResponseHeaders>& head_headers,
    mojo::PendingRemote<network::mojom::AuthChallengeResponder>
        auth_challenge_responder) {
  URLLoaderNetworkContext context =
      url_loader_network_observers_.current_context();
  URLLoaderNetworkContext original_context = context;
  std::optional<bool> is_primary_main_frame_navigation;
  std::optional<bool> is_navigation_request;

  if (window_id) {
    // Use `window_id` if it is provided, because this request was sent by a
    // service worker; service workers use `window_id` to identify the frame
    // that sends the request since a worker is shared among multiple frames.
    // TODO(crbug.com/40194275): Add a DCHECK here that process_id and
    // routing_id are invalid. It can't be added yet because somehow routing_id
    // is valid here.
    if (service_worker_context_->context()) {
      auto* service_worker_client =
          service_worker_context_->context()
              ->service_worker_client_owner()
              .GetServiceWorkerClientByWindowId(*window_id);
      if (service_worker_client) {
        if (service_worker_client->GetRenderFrameHostId()) {
          // Use ServiceWorkerClient's GlobalRenderFrameHostId when the
          // navigation commit has already started.
          GlobalRenderFrameHostId render_frame_host_id =
              service_worker_client->GetRenderFrameHostId();
          context = URLLoaderNetworkContext::CreateForRenderFrameHost(
              render_frame_host_id);

          // Only the request for a sub resource intercepted by a service worker
          // reaches here.
          is_primary_main_frame_navigation = false;
          is_navigation_request = false;
        } else if (service_worker_client->is_initiated_by_prefetch()) {
          // Do not process auth requests associated with prefetching clients
          // because there are no corresponding Window yet.
          // `is_initiated_by_prefetch()` can be true here (and similarly at
          // `OnCertificateRequested()`) e.g. when a speculation rules prefetch
          // request is intercepted by a ServiceWorker fetch handler and then
          // is fetched as a ServiceWorker subresource via
          // `event.respondWith(fetch(event.request))`.
          static_cast<mojo::Remote<network::mojom::AuthChallengeResponder>>(
              std::move(auth_challenge_responder))
              ->OnAuthCredentials(std::nullopt);
          return;
        } else if (NavigationRequest* ongoing_navigation =
                       service_worker_client
                           ->GetOngoingNavigationRequestBeforeCommit(
                               base::PassKey<StoragePartitionImpl>())) {
          // This auth request is for an ongoing navigation controlled
          // by service worker. The navigation request can be nullptr if user
          // has closed the WebContents.
          // Overwrite the context; set `type` to kNavigationRequestContext.
          // TODO(crbug.com/40784852): Optimize locating logic.
          context =
              URLLoaderNetworkContext::CreateForNavigation(*ongoing_navigation);
        }
      }
    }
  }

  // If the request is for a prerendering page, prerendering should be cancelled
  // because the embedder may show UI for auth requests, and it's unsuitable for
  // a hidden page.
  if (CancelIfPrerendering(context.navigation_or_document(),
                           PrerenderFinalStatus::kLoginAuthRequested)) {
    return;
  }

  // If the request was initiated by a service worker, we should not treat it as
  // a navigation request for the purpose of authentication. This is because
  // even if the request is associated with an ongoing navigation for UI
  // purposes, the network request itself is issued by the service worker.
  // `WebRequestAPI::MaybeProxyAuthRequest` overrides the process ID to -1
  // for navigation requests, but the `WebRequestProxyingURLLoaderFactory`
  // for service worker subresources is registered with the service worker's
  // actual process ID. Treating this as a non-navigation ensures the
  // `GlobalRequestID` matches and the proxy can be found.
  if (original_context.type() == ContextType::kSharedOrServiceWorkerContext) {
    is_navigation_request = false;
  }

  if (!is_primary_main_frame_navigation.has_value()) {
    is_primary_main_frame_navigation = context.IsPrimaryMainFrameRequest();
  }
  if (!is_navigation_request.has_value()) {
    is_navigation_request = context.IsNavigationRequestContext();
  }
  network::OriginatingProcessId process_id =
      network::OriginatingProcessId::browser();
  if (const auto* worker_context =
          std::get_if<URLLoaderNetworkContext::SharedOrServiceWorkerContext>(
              &original_context.context())) {
    // If the request was initiated by a service worker, use the service
    // worker's process ID. This ensures the `GlobalRequestID` used to look up
    // the proxy (e.g. in `WebRequestAPI`) matches the one used when the factory
    // was created, which for service worker subresources is the worker's
    // process ID.
    process_id = worker_context->process_id;
  } else if (context.type() == ContextType::kRenderFrameHostContext) {
    // Set `process_id` to `kInvalidProcessId` considering `render_frame_host`
    // can be null when it's destroyed already. `process_id` is updated only if
    // `render_frame_host` is not null. If `render_frame_host` is null,
    // later logic will call OnAuthCredentials() with a nullopt that triggers
    // CancelAuth().
    process_id = network::OriginatingProcessId();

    // `navigation_or_document_` can be null when `context` is created with
    // an invalid RenderFrameHost after a page is destroyed.
    // It is currently possible for the ServiceWorker case above to use
    // kRenderFrameHostContext for the auth request, after the RenderFrameHost
    // has been deleted. Treating this as an invalid process ID will cancel the
    // auth, which is the same outcome as if the ServiceWorker's process were
    // used.
    // TODO(crbug.com/40224422): Update the ServiceWorker code to
    // recognize when the RenderFrameHost goes away and not use
    // CreateForRenderFrameHost above.
    if (context.navigation_or_document()) {
      auto* render_frame_host = context.navigation_or_document()->GetDocument();
      if (render_frame_host) {
        process_id =
            ToOriginatingProcessId(render_frame_host->GetGlobalId().child_id);
      }
    }
  }

  FrameTreeNodeId frame_tree_node_id;
  if (auto* navigation_or_document = context.navigation_or_document()) {
    if (auto* frame_tree_node = navigation_or_document->GetFrameTreeNode()) {
      frame_tree_node_id = frame_tree_node->frame_tree_node_id();
    }
  }

  WebContents* current_web_contents = context.GetWebContents();
  if (current_web_contents) {
    // Evict all the BFCache entries that
    // 1): are stored in the same BrowserContext
    // 2): were loaded with the "Cache-control: no-store" header
    // 3): match the challenger information of the page that requires HTTP
    // authentication.
    for (WebContentsImpl* web_contents : WebContentsImpl::GetAllWebContents()) {
      if (web_contents->GetBrowserContext()->UniqueId() ==
          current_web_contents->GetBrowserContext()->UniqueId()) {
        for (const std::unique_ptr<BackForwardCacheImpl::Entry>& entry :
             web_contents->GetController().GetBackForwardCache().GetEntries()) {
          RenderFrameHostImpl* rfh = entry->render_frame_host();
          const GURL& last_committed_url = rfh->GetLastCommittedURL();
          if (rfh->LoadedWithCacheControlNoStoreHeader() &&
              auth_info.challenger ==
                  url::SchemeHostPort(last_committed_url.GetScheme(),
                                      last_committed_url.GetHost(),
                                      last_committed_url.IntPort())) {
            BackForwardCacheCanStoreDocumentResult flattened_reasons;
            flattened_reasons.No(BackForwardCacheMetrics::NotRestoredReason::
                                     kCacheControlNoStore);
            flattened_reasons.No(
                BackForwardCacheMetrics::NotRestoredReason::kHTTPAuthRequired);
            rfh->EvictFromBackForwardCacheWithFlattenedReasons(
                flattened_reasons);
          }
        }
      }
    }
  }

  new LoginHandlerDelegate(
      std::move(auth_challenge_responder), current_web_contents,
      browser_context_, auth_info, *is_primary_main_frame_navigation,
      *is_navigation_request, process_id, request_id, url, head_headers,
      first_auth_attempt, frame_tree_node_id);  // deletes self
}

void StoragePartitionImpl::OnLocalNetworkAccessPermissionRequired(
    network::mojom::TransportType transport_type,
    network::mojom::IPAddressSpace ip_address_space,
    OnLocalNetworkAccessPermissionRequiredCallback callback) {
  if (!base::FeatureList::IsEnabled(
          network::features::kLocalNetworkAccessChecks) &&
      !network::features::kLocalNetworkAccessChecksWarn.Get()) {
    // If LNA checks are not enabled, just allow the request by default.
    std::move(callback).Run(network::mojom::LocalNetworkAccessResult::kGranted);
    return;
  }

  if (url_loader_network_observers_.empty()) {
    std::move(callback).Run(network::mojom::LocalNetworkAccessResult::kDenied);
    return;
  }
  const URLLoaderNetworkContext& context =
      url_loader_network_observers_.current_context();

  // Compute the permission that we will check, if we end up checking for one.
  blink::PermissionType permission_type;
  switch (ip_address_space) {
    case network::mojom::IPAddressSpace::kLocal:
      permission_type = blink::PermissionType::LOCAL_NETWORK;
      break;
    case network::mojom::IPAddressSpace::kLoopback:
      permission_type = blink::PermissionType::LOOPBACK_NETWORK;
      break;
    case network::mojom::IPAddressSpace::kPublic:
    case network::mojom::IPAddressSpace::kUnknown:
      NOTREACHED();
  }

  LocalNetworkAccessResolution resolution = ResolveLocalNetworkAccess(context);
  switch (resolution.decision) {
    case LocalNetworkAccessDecision::kBlock:
      std::move(callback).Run(
          network::mojom::LocalNetworkAccessResult::kDenied);
      return;
    case LocalNetworkAccessDecision::kAllow:
      std::move(callback).Run(
          network::mojom::LocalNetworkAccessResult::kGranted);
      return;
    case LocalNetworkAccessDecision::kProceed:
      break;
  }

  if (context.navigation_or_document()) {
    RenderFrameHost* rfh = resolution.rfh;
    if (!rfh) {
      std::move(callback).Run(
          network::mojom::LocalNetworkAccessResult::kDenied);
      return;
    }

    PermissionController& permission_controller =
        CHECK_DEREF(browser_context_->GetPermissionController());

    auto status = permission_controller.GetPermissionStatusForCurrentDocument(
        content::PermissionDescriptorUtil::
            CreatePermissionDescriptorForPermissionType(permission_type),
        rfh);

    // If the request was loaded from cache, prefer retrying over the network
    // over prompting the user or blocking.
    // TODO(crbug.com/457969523): Consider only triggering a retry over the
    // network once per (Document, Subresource) combination, as network changes
    // during the lifetime of a Document may be sufficiently uncommon and we
    // could rely on the cached subresource for any further requests during the
    // document lifetime.
    if ((transport_type == network::mojom::TransportType::kCached ||
         transport_type == network::mojom::TransportType::kCachedFromProxy) &&
        (status == blink::mojom::PermissionStatus::ASK ||
         status == blink::mojom::PermissionStatus::DENIED)) {
      std::move(callback).Run(
          network::mojom::LocalNetworkAccessResult::kRetryDueToCache);
      return;
    }

    if (status == blink::mojom::PermissionStatus::GRANTED) {
      std::move(callback).Run(
          network::mojom::LocalNetworkAccessResult::kGranted);
      return;
    } else if (status == blink::mojom::PermissionStatus::DENIED) {
      std::move(callback).Run(
          network::mojom::LocalNetworkAccessResult::kDenied);
      return;
    } else {
      // PermissionStatus is ASK, so request the permission. Converts the result
      // to pass back to `callback`, capturing whether the permission is granted
      // or not.
      permission_controller.RequestPermissionFromCurrentDocument(
          rfh,
          PermissionRequestDescription(
              content::PermissionDescriptorUtil::
                  CreatePermissionDescriptorForPermissionType(permission_type)),
          base::BindOnce(
              [](OnLocalNetworkAccessPermissionRequiredCallback cb,
                 PermissionResult permission_result) {
                std::move(cb).Run(
                    permission_result.status ==
                            blink::mojom::PermissionStatus::GRANTED
                        ? network::mojom::LocalNetworkAccessResult::kGranted
                        : network::mojom::LocalNetworkAccessResult::kDenied);
              },
              std::move(callback)));
      return;
    }
  } else if (const auto* worker_context = std::get_if<
                 URLLoaderNetworkContext::SharedOrServiceWorkerContext>(
                 &context.context())) {
    // TODO(crbug.com/404887282): Plumb the `frame_window_id` of the worker,
    // if it exists, to allow this to identify the hosting frame of the worker,
    // when available. This would allow us to additionally _request_ the
    // permission when a fetch that happens to go through a service worker would
    // trigger this check. This is beneficial for developers as they can use
    // caching service workers "as expected" without needing to re-engineer
    // their code to do a special "non-cached request" just to trigger the
    // permission prompt. The `frame_window_id` approach is used by other
    // methods in this file, such as OnCertificateRequested(), which require UI
    // dialogs to handle the event. This may also be useful for handling
    // other types of Workers that always have an associated frame (such as
    // Dedicated Worker).

    // If there is no associated worker origin, there is nothing that can be
    // granted a permission, so just deny the permission request. This can
    // potentially happen for things like workers loaded from data: URLs, which
    // are opaque (but workers loaded from blob: URLs should have the origin
    // which created the blob: URL).
    // TODO(crbug.com/404887282): Revisit if opaque origins support is needed.
    CHECK(worker_context->worker_origin);
    if (worker_context->worker_origin->opaque()) {
      std::move(callback).Run(
          network::mojom::LocalNetworkAccessResult::kDenied);
      return;
    }

    PermissionController& permission_controller =
        CHECK_DEREF(browser_context_->GetPermissionController());
    CHECK(!worker_context->process_id.is_browser());
    auto status = permission_controller.GetPermissionStatusForWorker(
        content::PermissionDescriptorUtil::
            CreatePermissionDescriptorForPermissionType(permission_type),
        content::RenderProcessHost::FromID(
            ToChildProcessId(worker_context->process_id.renderer_process_id())),
        worker_context->worker_origin.value());

    // If the request was loaded from cache, prefer retrying over the network
    // over prompting the user or blocking.
    if ((transport_type == network::mojom::TransportType::kCached ||
         transport_type == network::mojom::TransportType::kCachedFromProxy) &&
        (status == blink::mojom::PermissionStatus::ASK ||
         status == blink::mojom::PermissionStatus::DENIED)) {
      std::move(callback).Run(
          network::mojom::LocalNetworkAccessResult::kRetryDueToCache);
      return;
    }

    std::move(callback).Run(
        status == blink::mojom::PermissionStatus::GRANTED
            ? network::mojom::LocalNetworkAccessResult::kGranted
            : network::mojom::LocalNetworkAccessResult::kDenied);
    return;
  }

  // Otherwise default to denying local network access.
  std::move(callback).Run(network::mojom::LocalNetworkAccessResult::kDenied);
  return;
}

// Called when an OS-level local network permission is required (e.g. Android
// ACCESS_LOCAL_NETWORK permission).
//
// Relationship and differences with OnLocalNetworkAccessPermissionRequired():
// 1. Web LNA (OnLocalNetworkAccessPermissionRequired):
//    Implements W3C LNA spec checks for document requests.
//
// 2. Platform LNA (OnPlatformLocalNetworkPermissionRequired):
//    Handles OS-level runtime permission checks when socket connection fails
//    with ERR_LOCAL_NETWORK_PERMISSION_MISSING.
//
// Why parameters received by OnLocalNetworkAccessPermissionRequired()
// (ip_address_space, transport_type) do not need to be plumbed to
// OnPlatformLocalNetworkPermissionRequired():
//    - `ip_address_space`: The target IP address space check has already been
//      performed at the OS-level net stack when the socket connection was
//      attempted. This method is triggered only after the OS net stack checked
//      and rejected the connection due to missing OS permission, so re-checking
//      `ip_address_space` here is unnecessary.
//    - `transport_type`: This method is triggered only when an actual OS socket
//      connection is rejected. Requests served from the HTTP cache do not
//      attempt OS socket connections, so checking HTTP cache status is
//      unnecessary.
void StoragePartitionImpl::OnPlatformLocalNetworkPermissionRequired(
    OnPlatformLocalNetworkPermissionRequiredCallback callback) {
  CHECK_CURRENTLY_ON(BrowserThread::UI);
  URLLoaderNetworkContext& context =
      url_loader_network_observers_.current_context();

  LocalNetworkAccessResolution resolution = ResolveLocalNetworkAccess(context);
  if (resolution.decision == LocalNetworkAccessDecision::kBlock) {
    std::move(callback).Run(/*granted=*/false);
    return;
  }

  WebContents* web_contents = context.GetWebContents();
  // `web_contents` may be null in the following cases:
  // 1. Requests originating from background worker contexts (ServiceWorkers /
  //    SharedWorkers) which do not have an associated tab/frame UI.
  //
  //    TODO(crbug.com/404887282): Consider plumbing `frame_window_id` if
  //    available to associate background worker requests with their hosting
  //    frame's WebContents for permission reprompts.
  // 2. The WebContents was destroyed asynchronously while the network request
  //    was pending. Returning false is expected here as no UI remains to prompt
  //    the user.
  if (!web_contents) {
    std::move(callback).Run(/*granted=*/false);
    return;
  }

  GetContentClient()->browser()->RequestPlatformLocalNetworkPermission(
      *web_contents, std::move(callback));
}

void StoragePartitionImpl::OnCertificateRequested(
    const std::optional<base::UnguessableToken>& window_id,
    const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
    mojo::PendingRemote<network::mojom::ClientCertificateResponder>
        cert_responder) {
  URLLoaderNetworkContext context =
      url_loader_network_observers_.current_context();

  if (window_id) {
    // Use `window_id` if it is provided, because this request was sent by a
    // service worker; service workers use `window_id` to identify the frame
    // that sends the request since a worker is shared among multiple frames.
    // TODO(crbug.com/40194275): Add a DCHECK here that process_id and
    // routing_id are invalid. It can't be added yet because somehow routing_id
    // is valid here.
    if (service_worker_context_->context()) {
      auto* service_worker_client =
          service_worker_context_->context()
              ->service_worker_client_owner()
              .GetServiceWorkerClientByWindowId(*window_id);
      if (service_worker_client) {
        if (service_worker_client->GetRenderFrameHostId()) {
          // Use ServiceWorkerClient's GlobalRenderFrameHostId when the
          // navigation commit has already started.
          GlobalRenderFrameHostId render_frame_host_id =
              service_worker_client->GetRenderFrameHostId();
          context = URLLoaderNetworkContext::CreateForRenderFrameHost(
              render_frame_host_id);
        } else if (service_worker_client->is_initiated_by_prefetch()) {
          // Do not process certification requests associated with prefetching
          // clients because there are no corresponding Window yet. See also the
          // comment at `OnAuthRequired()` for when `is_initiated_by_prefetch()`
          // can be true here.
          CallCancelRequest(std::move(cert_responder));
          return;
        } else if (NavigationRequest* ongoing_navigation =
                       service_worker_client
                           ->GetOngoingNavigationRequestBeforeCommit(
                               base::PassKey<StoragePartitionImpl>())) {
          // This certification request is for an ongoing navigation.
          // Overwrite the context; set `type` to kNavigationRequestContext.
          // TODO(crbug.com/40784852): Optimize locating logic.
          context =
              URLLoaderNetworkContext::CreateForNavigation(*ongoing_navigation);
        } else {
          // The navigation request was canceled since the WebContents was
          // discarded, so it is meaningless to continue the certification
          // request.
          CallCancelRequest(std::move(cert_responder));
          return;
        }
      }
    }
  }

  // If the request is for a prerendering page, prerendering should be cancelled
  // because the embedder may show a dialog and ask users to select client
  // certificates, and it's unsuitable for a hidden page.
  if (CancelIfPrerendering(context.navigation_or_document(),
                           PrerenderFinalStatus::kClientCertRequested)) {
    CallCancelRequest(std::move(cert_responder));
    return;
  }

  base::WeakPtr<WebContents> web_contents_weak;
  int process_id = network::mojom::kInvalidProcessId;
  if (context.type() == ContextType::kSharedOrServiceWorkerContext ||
      context.type() == ContextType::kDeviceBoundSessionContext) {
    if (const auto* worker_context =
            std::get_if<URLLoaderNetworkContext::SharedOrServiceWorkerContext>(
                &context.context())) {
      // TODO(crbug.com/379869738) Remove GetUnsafeValue.
      // TODO(crbug.com/479742988) This can be the browser process and
      // shouldn't.
      process_id = worker_context->process_id.GetUnsafeValue();
    }
  } else {
    WebContents* web_contents = context.GetWebContents();
    // The WebContents is already invalid. Bail.
    if (!web_contents) {
      CallCancelRequest(std::move(cert_responder));
      return;
    }
    CHECK_EQ(web_contents->GetBrowserContext(), browser_context_.get());
    web_contents_weak = web_contents->GetWeakPtr();

    if (context.navigation_or_document()) {
      auto* render_frame_host = context.navigation_or_document()->GetDocument();
      if (render_frame_host) {
        process_id = render_frame_host->GetProcess()->GetDeprecatedID();
      }
    }
  }

  // SSLClientAuthDelegate handles its own lifetime.
  new SSLClientAuthDelegate(std::move(cert_responder), browser_context(),
                            process_id, web_contents_weak, cert_info);
}

void StoragePartitionImpl::OnSSLCertificateError(
    const GURL& url,
    int net_error,
    const net::SSLInfo& ssl_info,
    bool fatal,
    OnSSLCertificateErrorCallback response) {
  URLLoaderNetworkContext context =
      url_loader_network_observers_.current_context();

  // Cancel this request and the prerendering if the request is for a
  // prerendering page, because prerendering pages are invisible and browser
  // cannot show errors on invisible pages.
  if (CancelIfPrerendering(context.navigation_or_document(),
                           PrerenderFinalStatus::kSslCertificateError)) {
    std::move(response).Run(net_error);
    return;
  }

  SSLErrorDelegate* delegate =
      new SSLErrorDelegate(std::move(response));  // deletes self
  bool is_primary_main_frame_request = context.IsPrimaryMainFrameRequest();
  SSLManager::OnSSLCertificateError(
      delegate->GetWeakPtr(), is_primary_main_frame_request, url,
      context.navigation_or_document(), static_cast<net::Error>(net_error),
      ssl_info, fatal);
}

void StoragePartitionImpl::OnLoadingStateUpdate(
    network::mojom::LoadInfoPtr info,
    OnLoadingStateUpdateCallback callback) {
  auto* web_contents =
      url_loader_network_observers_.current_context().GetWebContents();
  if (web_contents) {
    static_cast<WebContentsImpl*>(web_contents)
        ->LoadStateChanged(std::move(info));
  }
  std::move(callback).Run();
}

void StoragePartitionImpl::OnDataUseUpdate(
    int32_t network_traffic_annotation_id_hash,
    base::ByteSize recv_bytes,
    base::ByteSize sent_bytes) {
  GlobalRenderFrameHostId render_frame_host_id =
      GetRenderFrameHostIdFromNetworkContext();
  GetContentClient()->browser()->OnNetworkServiceDataUseUpdate(
      render_frame_host_id, network_traffic_annotation_id_hash, recv_bytes,
      sent_bytes);
}


void StoragePartitionImpl::Clone(
    mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
        observer) {
  url_loader_network_observers_.Add(
      this, std::move(observer),
      url_loader_network_observers_.current_context());
}

void StoragePartitionImpl::OnWebSocketConnectedToLocalNetwork(
    const GURL& request_url,
    network::mojom::IPAddressSpace ip_address_space) {
  RenderFrameHostImpl* render_frame_host_impl =
      RenderFrameHostImpl::FromID(GetRenderFrameHostIdFromNetworkContext());

  if (render_frame_host_impl &&
      network::IsLessPublicAddressSpace(
          ip_address_space, render_frame_host_impl->BuildClientSecurityState()
                                ->ip_address_space)) {
    GetContentClient()->browser()->LogWebFeatureForCurrentPage(
        render_frame_host_impl,
        blink::mojom::WebFeature::kPrivateNetworkAccessWebSocketConnected);

    // Log a UseCounter for potential LNA breakage, where we cannot auto-detect
    // a mixed content bypass situation. This is similar to the check below in
    // StoragePartitionImpl::OnUrlLoaderConnectedToLocalNetwork.
    if (!network::IsUrlPotentiallyTrustworthy(request_url) &&
        !request_url.HostIsIPAddress() && !request_url.DomainIs("local")) {
      GetContentClient()->browser()->LogWebFeatureForCurrentPage(
          render_frame_host_impl,
          blink::mojom::WebFeature::
              kLocalNetworkAccessWebSocketResourceNotKnownPrivate);
    }
  }
}

void StoragePartitionImpl::OnUrlLoaderConnectedToLocalNetwork(
    const GURL& request_url,
    network::mojom::IPAddressSpace response_address_space,
    network::mojom::IPAddressSpace client_address_space,
    network::mojom::IPAddressSpace target_address_space) {
  RenderFrameHostImpl* render_frame_host_impl =
      RenderFrameHostImpl::FromID(GetRenderFrameHostIdFromNetworkContext());
  if (!render_frame_host_impl) {
    return;
  }
  // Log a UseCounter for potential PNA 2.0 breakage. We are interested in the
  // case where post-PNA 2.0 this request would fail due to mixed content
  // blocking:
  // 1. The request was to a private or local address space,
  // 2. The request was from non-secure context,
  // 3. The request was to a URL that is not potentially trustworthy,
  // 4. The request is to a less public address space than the client making the
  //   request, and
  // 5. The request was _not_ a priori private. We a priori know a request is
  //   private (and thus can trigger permission prompts _before_ mixed content
  //   checks) if the target address space is specified, if the request is to a
  //   `.local` domain, or if the request is to a private IP address literal.
  //
  // (1) is checked by the caller in the network service. (2) is implied because
  // if it was from a secure context then the request to a not potentially
  // trustworthy URL would have been blocked as mixed content. (3-5) are checked
  // below.
  //
  // This may be called multiple times over the lifetime of resource load (due
  // to redirect hops), as we want to consider every redirect hop for this
  // metric as breakage may occur at the initial request or at any hop in the
  // redirect chain. This is logged as a UseCounter for the current page, which
  // "deduplicates" these repeated calls.
  if (!network::IsUrlPotentiallyTrustworthy(request_url) &&
      network::IsLessPublicAddressSpace(response_address_space,
                                        client_address_space) &&
      // Not a priori known to be private.
      target_address_space == network::mojom::IPAddressSpace::kUnknown &&
      !request_url.HostIsIPAddress() && !request_url.DomainIs("local")) {
    GetContentClient()->browser()->LogWebFeatureForCurrentPage(
        render_frame_host_impl,
        blink::mojom::WebFeature::
            kPrivateNetworkAccessInsecureResourceNotKnownPrivate);
  }
}

mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
StoragePartitionImpl::CreateURLLoaderNetworkObserverForFrame(
    const content::GlobalRenderFrameHostId& frame_id) {
  mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> remote;
  url_loader_network_observers_.Add(
      this, remote.InitWithNewPipeAndPassReceiver(),
      URLLoaderNetworkContext::CreateForRenderFrameHost(frame_id));
  return remote;
}

mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
StoragePartitionImpl::CreateURLLoaderNetworkObserverForNavigationRequest(
    NavigationRequest& navigation_request) {
  mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> remote;
  url_loader_network_observers_.Add(
      this, remote.InitWithNewPipeAndPassReceiver(),
      URLLoaderNetworkContext::CreateForNavigation(navigation_request));
  return remote;
}

mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
StoragePartitionImpl::CreateURLLoaderNetworkObserverForServiceOrSharedWorker(
    const network::OriginatingProcessId& process_id,
    const url::Origin& worker_origin,
    const std::optional<blink::StorageKey>& storage_key) {
  mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> remote;
  url_loader_network_observers_.Add(
      this, remote.InitWithNewPipeAndPassReceiver(),
      URLLoaderNetworkContext::CreateForServiceOrSharedWorker(
          process_id, worker_origin, storage_key));
  return remote;
}

mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
StoragePartitionImpl::CreateURLLoaderNetworkObserverForDeviceBoundSessions() {
  mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> remote;
  url_loader_network_observers_.Add(
      this, remote.InitWithNewPipeAndPassReceiver(),
      URLLoaderNetworkContext::CreateForDeviceBoundSessions());
  return remote;
}

void StoragePartitionImpl::OnFileUploadRequested(
    const network::OriginatingProcessId& process_id,
    bool async,
    const std::vector<base::FilePath>& file_paths,
    const GURL& destination_url,
    OnFileUploadRequestedCallback callback) {
  NetworkContextOnFileUploadRequested(process_id, async, file_paths,
                                      destination_url, std::move(callback));
}

void StoragePartitionImpl::OnCanSendReportingReports(
    const std::vector<url::Origin>& origins,
    OnCanSendReportingReportsCallback callback) {
  DCHECK(initialized_);
  PermissionController& permission_controller =
      CHECK_DEREF(browser_context_->GetPermissionController());

  std::vector<url::Origin> origins_out;
  for (auto& origin : origins) {
    bool allowed = permission_controller
                       .GetPermissionResultForOriginWithoutContext(
                           content::PermissionDescriptorUtil::
                               CreatePermissionDescriptorForPermissionType(
                                   blink::PermissionType::BACKGROUND_SYNC),
                           origin)
                       .status == blink::mojom::PermissionStatus::GRANTED;
    if (allowed) {
      origins_out.push_back(origin);
    }
  }

  std::move(callback).Run(origins_out);
}

void StoragePartitionImpl::OnCanSendDomainReliabilityUpload(
    const url::Origin& origin,
    OnCanSendDomainReliabilityUploadCallback callback) {
  DCHECK(initialized_);
  PermissionController& permission_controller =
      CHECK_DEREF(browser_context_->GetPermissionController());
  std::move(callback).Run(
      permission_controller
          .GetPermissionResultForOriginWithoutContext(
              content::PermissionDescriptorUtil::
                  CreatePermissionDescriptorForPermissionType(
                      blink::PermissionType::BACKGROUND_SYNC),
              origin)
          .status == blink::mojom::PermissionStatus::GRANTED);
}

void StoragePartitionImpl::OnClearSiteData(
    const GURL& url,
    const std::string& header_value,
    int load_flags,
    const std::optional<net::CookiePartitionKey>& cookie_partition_key,
    bool partitioned_state_allowed_only,
    OnClearSiteDataCallback callback) {
  DCHECK(initialized_);

  base::WeakPtr<WebContents> weak_web_contents;
  WebContents* web_contents =
      url_loader_network_observers_.current_context().GetWebContents();
  if (web_contents) {
    weak_web_contents = web_contents->GetWeakPtr();
    GetContentClient()->browser()->LogWebFeatureForCurrentPage(
        web_contents->GetPrimaryMainFrame(),
        blink::mojom::WebFeature::kClearSiteData);
  }

  std::optional<blink::StorageKey> storage_key = CalculateStorageKey(
      url::Origin::Create(url),
      cookie_partition_key.has_value()
          ? base::OptionalToPtr(cookie_partition_key.value().nonce())
          : nullptr);

  ClearSiteDataHandler::HandleHeader(
      browser_context()->GetWeakPtr(), weak_web_contents, GetConfig(), url,
      header_value, load_flags, cookie_partition_key, storage_key,
      partitioned_state_allowed_only, std::move(callback));
}

#if BUILDFLAG(IS_ANDROID)
void StoragePartitionImpl::OnGenerateHttpNegotiateAuthToken(
    const std::string& server_auth_token,
    bool can_delegate,
    const std::string& auth_negotiate_android_account_type,
    const std::string& spn,
    OnGenerateHttpNegotiateAuthTokenCallback callback) {
  // The callback takes ownership of these unique_ptrs and destroys them when
  // run.
  auto prefs = std::make_unique<net::HttpAuthPreferences>();
  prefs->set_auth_android_negotiate_account_type(
      auth_negotiate_android_account_type);

  auto auth_negotiate =
      std::make_unique<net::android::HttpAuthNegotiateAndroid>(prefs.get());
  net::android::HttpAuthNegotiateAndroid* auth_negotiate_raw =
      auth_negotiate.get();
  auth_negotiate->set_server_auth_token(server_auth_token);
  auth_negotiate->set_can_delegate(can_delegate);

  auto auth_token = std::make_unique<std::string>();
  auth_negotiate_raw->GenerateAuthTokenAndroid(
      nullptr, spn, std::string(), auth_token.get(),
      base::BindOnce(&FinishGenerateNegotiateAuthToken,
                     std::move(auth_negotiate), std::move(auth_token),
                     std::move(prefs), std::move(callback)));
}
#endif

#if BUILDFLAG(IS_CT_SUPPORTED)
void StoragePartitionImpl::OnCanSendSCTAuditingReport(
    OnCanSendSCTAuditingReportCallback callback) {
  bool allowed =
      GetContentClient()->browser()->CanSendSCTAuditingReport(browser_context_);
  std::move(callback).Run(allowed);
}

void StoragePartitionImpl::OnNewSCTAuditingReportSent() {
  GetContentClient()->browser()->OnNewSCTAuditingReportSent(browser_context_);
}
#endif

void StoragePartitionImpl::ClearDataImpl(
    uint32_t remove_mask,
    const blink::StorageKey& storage_key,
    BrowsingDataFilterBuilder* filter_builder,
    StorageKeyPolicyMatcherFunction storage_key_policy_matcher,
    CookieDeletionFilterPtr cookie_deletion_filter,
    bool perform_storage_cleanup,
    const base::Time begin,
    const base::Time end,
    base::OnceClosure callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  bool storage_key_origin_empty = storage_key.origin().opaque();
  DCHECK(storage_key_origin_empty || !filter_builder);
  DCHECK(storage_key_origin_empty || storage_key_policy_matcher.is_null());

  StorageKeyMatcherFunction storage_key_matcher =
      filter_builder ? filter_builder->BuildStorageKeyFilter()
                     : StorageKeyMatcherFunction();

  for (auto& observer : data_removal_observers_) {
    auto filter = CreateGenericStorageKeyMatcher(
        storage_key, storage_key_matcher, storage_key_policy_matcher,
        special_storage_policy_);
    observer.OnStorageKeyDataCleared(remove_mask, std::move(filter), begin,
                                     end);
  }

  DataDeletionHelper* helper = new DataDeletionHelper(
      remove_mask,
      base::BindOnce(&StoragePartitionImpl::DeletionHelperDone,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
  // `helper` deletes itself when done in
  // DataDeletionHelper::DecrementTaskCount().
  deletion_helpers_running_++;
  helper->ClearData(
      storage_key, filter_builder, std::move(storage_key_policy_matcher),
      std::move(cookie_deletion_filter), GetPath(), dom_storage_context_.get(),
      quota_manager_.get(), special_storage_policy_.get(),
      filesystem_context_.get(), GetCookieManagerForBrowserProcess(),
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
      cdm_storage_manager_.get(),
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
      GetDeviceBoundSessionManager(), GetKeepAliveURLLoaderService(),
      declarative_performance_observer_store_.get(), perform_storage_cleanup,
      begin, end);
}

void StoragePartitionImpl::DeletionHelperDone(base::OnceClosure callback) {
  std::move(callback).Run();
  deletion_helpers_running_--;
  if (on_deletion_helpers_done_callback_ && deletion_helpers_running_ == 0) {
    // Notify tests that storage partition is done with all deletion tasks.
    std::move(on_deletion_helpers_done_callback_).Run();
  }
}

// A destructor guard that ensures Disarm() is called. Bind into closures that
// must be called rather than silently dropped.
class TaskCompletionGuard {
 public:
  TaskCompletionGuard() = default;
  ~TaskCompletionGuard() {
    // TODO(https://crbug.com/487338371): ChromeBrowsingDataRemoverDelegate
    // fires fire-and-forget RemoveWithFilter calls for IWA storage partitions
    // that can be abandoned during browser shutdown. Fix that and upgrade to a
    // CHECK.
    LOG_IF(WARNING, !disarmed_)
        << "Task completion closure was destroyed without being called";
  }

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

  void Disarm() { disarmed_ = true; }

 private:
  bool disarmed_ = false;
};

base::OnceClosure
StoragePartitionImpl::DataDeletionHelper::CreateTaskCompletionClosure(
    TracingDataType data_type) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  auto result = pending_tasks_.insert(data_type);
  DCHECK(result.second) << "Task already started: "
                        << static_cast<int>(data_type);

  static int tracing_id = 0;
  TRACE_EVENT_BEGIN("browsing_data", "StoragePartitionImpl",
                    perfetto::NamedTrack("StoragePartitionImpl", ++tracing_id),
                    "data_type", static_cast<int>(data_type));
  return base::BindOnce(&DataDeletionHelper::OnTaskComplete,
                        base::Unretained(this), data_type, tracing_id)
      .Then(base::BindOnce(&TaskCompletionGuard::Disarm,
                           std::make_unique<TaskCompletionGuard>()));
}

void StoragePartitionImpl::DataDeletionHelper::OnTaskComplete(
    TracingDataType data_type,
    int tracing_id) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    GetUIThreadTaskRunner({})->PostTask(
        FROM_HERE,
        base::BindOnce(&DataDeletionHelper::OnTaskComplete,
                       base::Unretained(this), data_type, tracing_id));
    return;
  }
  size_t num_erased = pending_tasks_.erase(data_type);
  DCHECK_EQ(num_erased, 1U) << static_cast<int>(data_type);
  // StoragePartitionImpl
  TRACE_EVENT_END("browsing_data",
                  perfetto::NamedTrack("StoragePartitionImpl", tracing_id));

  if (pending_tasks_.empty()) {
    std::move(callback_).Run();
    delete this;
  }
}

void StoragePartitionImpl::DataDeletionHelper::RecordUnfinishedSubTasks() {
  DCHECK(!pending_tasks_.empty());
  for (TracingDataType task : pending_tasks_) {
    base::UmaHistogramEnumeration(
        "History.ClearBrowsingData.Duration.SlowTasks180sStoragePartition",
        task);
  }
}

void StoragePartitionImpl::DataDeletionHelper::ClearData(
    const blink::StorageKey& storage_key,
    BrowsingDataFilterBuilder* filter_builder,
    StorageKeyPolicyMatcherFunction storage_key_policy_matcher,
    CookieDeletionFilterPtr cookie_deletion_filter,
    const base::FilePath& path,
    DOMStorageContextWrapper* dom_storage_context,
    storage::QuotaManager* quota_manager,
    storage::SpecialStoragePolicy* special_storage_policy,
    storage::FileSystemContext* filesystem_context,
    network::mojom::CookieManager* cookie_manager,
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
    CdmStorageManager* cdm_storage_manager,
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
    network::mojom::DeviceBoundSessionManager* device_bound_session_manager,
    KeepAliveURLLoaderService* keep_alive_url_loader_service,
    DeclarativePerformanceObserverStore* declarative_performance_observer_store,
    bool perform_storage_cleanup,
    const base::Time begin,
    const base::Time end) {
  DCHECK_NE(remove_mask_, 0u);
  DCHECK(callback_);

  // Only one of `storage_key`'s origin and
  // `filter_builder`/`storage_key_policy_matcher` can be set.
  const bool storage_key_origin_empty = storage_key.origin().opaque();
  DCHECK(storage_key_origin_empty || !filter_builder);
  DCHECK(storage_key_origin_empty || storage_key_policy_matcher.is_null());

  GetUIThreadTaskRunner({})->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(
          &StoragePartitionImpl::DataDeletionHelper::RecordUnfinishedSubTasks,
          weak_factory_.GetWeakPtr()),
      kSlowTaskTimeout);

  base::ScopedClosureRunner synchronous_clear_operations(
      CreateTaskCompletionClosure(TracingDataType::kSynchronous));

  scoped_refptr<storage::SpecialStoragePolicy> storage_policy_ref =
      base::WrapRefCounted(special_storage_policy);

  StorageKeyMatcherFunction storage_key_matcher =
      filter_builder ? filter_builder->BuildStorageKeyFilter()
                     : StorageKeyMatcherFunction();

  // This is preferred for new storage APIs to reduce the complexity.
  auto generic_filter = CreateGenericStorageKeyMatcher(
      storage_key, storage_key_matcher, storage_key_policy_matcher,
      storage_policy_ref);

  auto combined_storage_key_matcher = CombineStorageKeyMatcherFunctions(
      storage_key_matcher, storage_key_policy_matcher);

  if (remove_mask_ & REMOVE_DATA_MASK_DECLARATIVE_PERFORMANCE_OBSERVER) {
    if (declarative_performance_observer_store) {
      auto completion_callback = CreateTaskCompletionClosure(
          TracingDataType::kDeclarativePerformanceObserver);
      if (!storage_key_origin_empty) {
        declarative_performance_observer_store->ClearDataForOrigin(
            storage_key.origin(), std::move(completion_callback));
      } else if (generic_filter.is_null()) {
        declarative_performance_observer_store->ClearAllData(
            std::move(completion_callback));
      } else {
        auto origin_matcher = base::BindRepeating(
            [](const StorageKeyMatcherFunction& filter,
               const url::Origin& origin) {
              return filter.Run(blink::StorageKey::CreateFirstParty(origin));
            },
            generic_filter);
        declarative_performance_observer_store->ClearDataWithFilter(
            std::move(origin_matcher), std::move(completion_callback));
      }
    }
  }

  if (remove_mask_ & REMOVE_DATA_MASK_COOKIES) {
    // The CookieDeletionFilter has a redundant time interval to `begin` and
    // `end`. Ensure that the filter has no time interval specified to help
    // callers detect when they are using the wrong interval values.
    DCHECK(!cookie_deletion_filter->created_after_time.has_value());
    DCHECK(!cookie_deletion_filter->created_before_time.has_value());

    if (!begin.is_null()) {
      cookie_deletion_filter->created_after_time = begin;
    }
    if (!end.is_null()) {
      cookie_deletion_filter->created_before_time = end;
    }
    if (!storage_key_origin_empty) {
      cookie_deletion_filter->cookie_partition_key_collection =
          net::CookiePartitionKeyCollection(storage_key.ToCookiePartitionKey());
    }

    cookie_manager->DeleteCookies(
        std::move(cookie_deletion_filter),
        base::BindOnce(
            &OnClearedCookies,
            // Handle the cookie store being destroyed and the callback thus not
            // being called.
            mojo::WrapCallbackWithDefaultInvokeIfNotRun(
                CreateTaskCompletionClosure(TracingDataType::kCookies))));
  }

  // It is not expected to only delete internal interest group data, or to
  // request interest group removal to be extra-thorough w/o asking for
  // interest group removal.
  DCHECK(!(remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS_INTERNAL) ||
         remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS);
  DCHECK(!(remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS_USER_CLEAR) ||
         remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS);


#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
  if ((remove_mask_ & REMOVE_DATA_MASK_MEDIA_LICENSES)) {
    auto cdm_deletion_callback = base::BindOnce(
        base::IgnoreArgs<bool>(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
            CreateTaskCompletionClosure(TracingDataType::kCdmStorage))));

    cdm_storage_manager->DeleteData(generic_filter, storage_key, begin, end,
                                    std::move(cdm_deletion_callback));
  }
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)

  if (remove_mask_ & REMOVE_DATA_MASK_INDEXEDDB ||
      remove_mask_ & REMOVE_DATA_MASK_FILE_SYSTEMS ||
      remove_mask_ & REMOVE_DATA_MASK_SERVICE_WORKERS ||
      remove_mask_ & REMOVE_DATA_MASK_CACHE_STORAGE) {
    ClearQuotaManagedData(
        quota_manager, begin, end,
        StoragePartitionImpl::GenerateQuotaClientTypes(remove_mask_),
        storage_key, storage_policy_ref, combined_storage_key_matcher,
        perform_storage_cleanup,
        CreateTaskCompletionClosure(TracingDataType::kQuota));
  }

  if (remove_mask_ & REMOVE_DATA_MASK_LOCAL_STORAGE) {
    ClearLocalStorage(
        base::WrapRefCounted(dom_storage_context), storage_policy_ref,
        combined_storage_key_matcher, storage_key, perform_storage_cleanup,
        begin, end,
        mojo::WrapCallbackWithDefaultInvokeIfNotRun(
            CreateTaskCompletionClosure(TracingDataType::kLocalStorage)));

    // TODO(crbug.com/41457196): Sometimes SessionStorage fails to call its
    // callback. Figure out why.
    ClearSessionStorage(
        base::WrapRefCounted(dom_storage_context), generic_filter,
        perform_storage_cleanup,
        mojo::WrapCallbackWithDefaultInvokeIfNotRun(
            CreateTaskCompletionClosure(TracingDataType::kSessionStorage)));
  }

  if ((remove_mask_ & REMOVE_DATA_MASK_SHADER_CACHE) &&
      // Old behavior: Always execute the code block below.
      // New behavior: If kDisablePartialStorageCleanupForGPUDiskCache == true
      // then consider the behavior of perform_storage_cleanup
      // in executing the code. Note that the feature flag is only relevant
      // when perform_storage_cleanup is false.
      (!base::FeatureList::IsEnabled(
           features::kDisablePartialStorageCleanupForGPUDiskCache) ||
       perform_storage_cleanup)) {
    gpu::GpuDiskCacheFactory* gpu_cache_factory =
        GetGpuDiskCacheFactorySingleton();
    // May be null in tests where it is difficult to plumb through a test
    // storage partition.
    if (!path.empty() && gpu_cache_factory) {
      // Clear the path for all the different GPU cache sub-types.
      base::RepeatingClosure barrier = base::BarrierClosure(
          gpu::kGpuDiskCacheTypes.size(),
          CreateTaskCompletionClosure(TracingDataType::kGpuCache));
      for (gpu::GpuDiskCacheType type : gpu::kGpuDiskCacheTypes) {
        gpu_cache_factory->ClearByPath(
            path.Append(gpu::GetGpuDiskCacheSubdir(type)), begin, end,
            base::BindOnce(&ClearedGpuCache, barrier));
      }
    }
  }

  if (remove_mask_ & REMOVE_DATA_MASK_DEVICE_BOUND_SESSIONS &&
      device_bound_session_manager) {
    device_bound_session_manager->DeleteAllSessions(
        net::device_bound_sessions::DeletionReason::kStoragePartitionCleared,
        begin, end,
        filter_builder ? filter_builder->BuildNetworkServiceFilter() : nullptr,
        mojo::WrapCallbackWithDefaultInvokeIfNotRun(CreateTaskCompletionClosure(
            TracingDataType::kDeviceBoundSessions)));
  }

  if (remove_mask_ & REMOVE_KEEPALIVE_LOADS_ATTEMPTING_RETRY &&
      keep_alive_url_loader_service) {
    keep_alive_url_loader_service->ClearKeepAliveURLLoadersAttemptingRetry();
  }
}

void StoragePartitionImpl::ClearDataForOrigin(uint32_t remove_mask,
                                              const GURL& storage_origin,
                                              base::OnceClosure callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK(initialized_);
  CookieDeletionFilterPtr deletion_filter = CookieDeletionFilter::New();
  if (!storage_origin.GetHost().empty()) {
    deletion_filter->host_name = storage_origin.GetHost();
  }
  // Construct a |BrowsingDataFilterBuilder| instead of just passing a storage
  // key based on the origin directly. This is needed to be able to delete the
  // associated 3P data embedded on the origin.
  auto filter_builder = BrowsingDataFilterBuilder::Create(
      content::BrowsingDataFilterBuilder::Mode::kDelete);
  filter_builder->AddOrigin(url::Origin::Create(storage_origin));
  ClearDataImpl(remove_mask, blink::StorageKey(), filter_builder.get(),
                StorageKeyPolicyMatcherFunction(), std::move(deletion_filter),
                false, base::Time(), base::Time::Max(), std::move(callback));
}

void StoragePartitionImpl::ClearDataForBuckets(
    const blink::StorageKey& storage_key,
    const std::set<std::string>& storage_buckets,
    base::OnceClosure callback) {
  DCHECK(initialized_);

  const auto remove_buckets_done =
      base::BarrierCallback<blink::mojom::QuotaStatusCode>(
          storage_buckets.size(),
          BindPostTaskToCurrentDefault(
              base::BindOnce(&StoragePartitionImpl::ClearDataForBucketsDone,
                             base::Unretained(this), storage_key,
                             storage_buckets, std::move(callback))));

  storage::QuotaManagerProxy* quota_manager_proxy = GetQuotaManagerProxy();

  for (const auto& bucket : storage_buckets) {
    quota_manager_proxy->DeleteBucket(
        storage_key, bucket, base::SequencedTaskRunner::GetCurrentDefault(),
        remove_buckets_done);
  }
}

void StoragePartitionImpl::ClearDataForBucketsDone(
    const blink::StorageKey& storage_key,
    const std::set<std::string>& storage_buckets,
    base::OnceClosure callback,
    const std::vector<blink::mojom::QuotaStatusCode>& status_codes) {
  auto bucket_iterator = storage_buckets.begin();

  for (const auto status_code : status_codes) {
    if (bucket_iterator == storage_buckets.end()) {
      break;
    }

    const std::string bucket_name = *bucket_iterator;

    if (status_code != blink::mojom::QuotaStatusCode::kOk) {
      DLOG(ERROR) << "Couldn't remove bucket with name" << bucket_name
                  << " with storage key " << storage_key.GetDebugString()
                  << ". Status: " << static_cast<int>(status_code);
    }

    ++bucket_iterator;
  }

  std::move(callback).Run();
}

void StoragePartitionImpl::ClearData(uint32_t remove_mask,
                                     const blink::StorageKey& storage_key,
                                     const base::Time begin,
                                     const base::Time end,
                                     base::OnceClosure callback) {
  DCHECK(initialized_);
  CookieDeletionFilterPtr deletion_filter = CookieDeletionFilter::New();
  if (!storage_key.origin().host().empty()) {
    deletion_filter->host_name = storage_key.origin().host();
  }
  bool perform_storage_cleanup =
      begin.is_null() && end.is_max() && storage_key.origin().opaque();
  ClearDataImpl(remove_mask, storage_key,
                /*filter_builder=*/nullptr, StorageKeyPolicyMatcherFunction(),
                std::move(deletion_filter), perform_storage_cleanup, begin, end,
                std::move(callback));
}

void StoragePartitionImpl::ClearData(
    uint32_t remove_mask,
    BrowsingDataFilterBuilder* filter_builder,
    StorageKeyPolicyMatcherFunction storage_key_policy_matcher,
    network::mojom::CookieDeletionFilterPtr cookie_deletion_filter,
    bool perform_storage_cleanup,
    const base::Time begin,
    const base::Time end,
    base::OnceClosure callback) {
  DCHECK(initialized_);
  ClearDataImpl(remove_mask, blink::StorageKey(), filter_builder,
                std::move(storage_key_policy_matcher),
                std::move(cookie_deletion_filter), perform_storage_cleanup,
                begin, end, std::move(callback));
}

void StoragePartitionImpl::ClearCodeCaches(
    const base::Time begin,
    const base::Time end,
    const base::RepeatingCallback<bool(const GURL&)>& url_matcher,
    base::OnceClosure callback) {
  DCHECK(initialized_);
  // StoragePartitionCodeCacheDataRemover deletes itself when it is done.
  StoragePartitionCodeCacheDataRemover::Create(this, url_matcher, begin, end)
      ->Remove(std::move(callback));
}

void StoragePartitionImpl::Flush() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK(initialized_);
  if (GetDOMStorageContext()) {
    GetDOMStorageContext()->Flush();
  }
}

void StoragePartitionImpl::ResetURLLoaderFactories() {
  CHECK(initialized_);
  GetNetworkContext()->ResetURLLoaderFactories();
  shared_url_loader_factory_for_browser_process_->factory()->Reset();
  shared_url_loader_factory_for_browser_process_->factory_for_io_thread()
      ->Reset();
}

void StoragePartitionImpl::ClearBluetoothAllowedDevicesMap() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  CHECK(initialized_);
  bluetooth_allowed_devices_map_->Clear();
}

void StoragePartitionImpl::AddObserver(DataRemovalObserver* observer) {
  data_removal_observers_.AddObserver(observer);
}

void StoragePartitionImpl::RemoveObserver(DataRemovalObserver* observer) {
  data_removal_observers_.RemoveObserver(observer);
}

void StoragePartitionImpl::FlushNetworkInterfaceForTesting() {
  CHECK(initialized_);
  DCHECK(network_context_owner_->network_context);
  network_context_owner_->network_context.FlushForTesting();  // IN-TEST
  shared_url_loader_factory_for_browser_process_->factory()
      ->FlushForTesting();  // IN-TEST
  if (cookie_manager_for_browser_process_) {
    cookie_manager_for_browser_process_.FlushForTesting();  // IN-TEST
  }
}

void StoragePartitionImpl::FlushNetworkInterfaceOnIOThreadForTesting() {
  CHECK(initialized_);
  CHECK(shared_url_loader_factory_for_browser_process_);
  shared_url_loader_factory_for_browser_process_->factory_for_io_thread()
      ->FlushForTesting();  // IN-TEST
}

void StoragePartitionImpl::FlushCertVerifierInterfaceForTesting() {
  DCHECK(initialized_);
  DCHECK(cert_verifier_service_updater_);
  cert_verifier_service_updater_.FlushForTesting();  // IN-TEST
}

void StoragePartitionImpl::WaitForDeletionTasksForTesting() {
  DCHECK(initialized_);
  if (deletion_helpers_running_) {
    base::RunLoop loop;
    on_deletion_helpers_done_callback_ = loop.QuitClosure();
    loop.Run();
  }
}

void StoragePartitionImpl::SetNetworkContextForTesting(
    mojo::PendingRemote<network::mojom::NetworkContext>
        network_context_remote) {
  network_context_owner_->network_context.reset();
  network_context_owner_->network_context.Bind(
      std::move(network_context_remote));
}

void StoragePartitionImpl::OverrideDeleteStaleSessionCleanupDelayForTesting(
    const base::TimeDelta& delay) {
  stale_session_cleanup_delay_ = delay;
}

void StoragePartitionImpl::
    SetClearNetworkRestrictionsParamsForTesting(  // IN-TEST
        const base::TimeDelta& delay,
        base::RepeatingClosure callback) {
  clear_network_restrictions_delay_ = delay;
  clear_network_restrictions_callback_for_testing_ = callback;
}

base::WeakPtr<StoragePartitionImpl> StoragePartitionImpl::GetWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

BrowserContext* StoragePartitionImpl::browser_context() const {
  return browser_context_;
}

std::optional<base::FilePath> StoragePartitionImpl::GetStoragePartitionPath()
    const {
  if (is_in_memory()) {
    return std::nullopt;
  }
  return browser_context_->GetPath().Append(relative_partition_path_);
}

// static
mojo::Remote<storage::mojom::StorageService>&
StoragePartitionImpl::GetStorageService() {
  return GetStorageServiceRemote();
}

void StoragePartitionImpl::BindIndexedDB(
    const storage::BucketLocator& bucket_locator,
    const storage::BucketClientInfo& client_info,
    mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
        client_state_checker_remote,
    mojo::PendingReceiver<blink::mojom::IDBFactory> receiver) {
  indexed_db_control_wrapper_->BindIndexedDB(
      bucket_locator, client_info, std::move(client_state_checker_remote),
      std::move(receiver));
}

void StoragePartitionImpl::BindLockManager(
    const blink::StorageKey& storage_key,
    const base::UnguessableToken& token,
    mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  GetQuotaManagerProxy()->UpdateOrCreateBucket(
      storage::BucketInitParams::ForDefaultBucket(storage_key),
      GetUIThreadTaskRunner({}),
      base::BindOnce(&StoragePartitionImpl::CreateLockManagerWithBucketInfo,
                     weak_factory_.GetWeakPtr(), std::move(receiver), token));
}

void StoragePartitionImpl::CreateLockManagerWithBucketInfo(
    mojo::PendingReceiver<blink::mojom::LockManager> receiver,
    const base::UnguessableToken& token,
    storage::QuotaErrorOr<storage::BucketInfo> bucket) {
  GetLockManager()->BindReceiver(
      bucket.has_value() ? bucket->id : storage::BucketId(), token,
      std::move(receiver));
}

mojo::ReceiverId StoragePartitionImpl::BindDomStorage(
    int process_id,
    mojo::PendingReceiver<blink::mojom::DomStorage> receiver,
    mojo::PendingRemote<blink::mojom::DomStorageClient> client) {
  DCHECK(initialized_);
  auto handle =
      ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(process_id);
  mojo::ReceiverId id = dom_storage_receivers_.Add(
      this, std::move(receiver),
      std::make_unique<SecurityPolicyHandle>(std::move(handle)));
  dom_storage_clients_[id].Bind(std::move(client));
  return id;
}

void StoragePartitionImpl::UnbindDomStorage(mojo::ReceiverId receiver_id) {
  DCHECK(initialized_);
  dom_storage_receivers_.Remove(receiver_id);
  dom_storage_clients_.erase(receiver_id);
}

void StoragePartitionImpl::OverrideQuotaManagerForTesting(
    storage::QuotaManager* quota_manager) {
  DCHECK(initialized_);
  quota_manager_ = quota_manager;
}

void StoragePartitionImpl::OverrideSpecialStoragePolicyForTesting(
    storage::SpecialStoragePolicy* special_storage_policy) {
  DCHECK(initialized_);
  special_storage_policy_ = special_storage_policy;
}

void StoragePartitionImpl::ShutdownBackgroundSyncContextForTesting() {
  DCHECK(initialized_);
  if (GetBackgroundSyncContext()) {
    GetBackgroundSyncContext()->Shutdown();
  }
}

void StoragePartitionImpl::OverrideBackgroundSyncContextForTesting(
    BackgroundSyncContextImpl* background_sync_context) {
  DCHECK(initialized_);
  DCHECK(!GetBackgroundSyncContext() ||
         !GetBackgroundSyncContext()->background_sync_manager());
  background_sync_context_ = background_sync_context;
}

void StoragePartitionImpl::OverrideSharedWorkerServiceForTesting(
    std::unique_ptr<SharedWorkerServiceImpl> shared_worker_service) {
  DCHECK(initialized_);
  shared_worker_service_ = std::move(shared_worker_service);
}

void StoragePartitionImpl::OverrideDeviceBoundSessionManagerForTesting(
    std::unique_ptr<network::mojom::DeviceBoundSessionManager>
        device_bound_session_manager) {
  DCHECK(initialized_);
  device_bound_session_manager_.reset();

  if (device_bound_session_manager) {
    mojo::MakeSelfOwnedReceiver(
        std::move(device_bound_session_manager),
        device_bound_session_manager_.BindNewPipeAndPassReceiver());
  }
}

void StoragePartitionImpl::GetQuotaSettings(
    storage::OptionalQuotaSettingsCallback callback) {
  if (g_test_quota_settings) {
    // For debugging tests harness can inject settings.
    std::move(callback).Run(*g_test_quota_settings);
    return;
  }

  storage::GetNominalDynamicSettings(
      GetPath(), browser_context_->IsOffTheRecord(),
      storage::GetDefaultDeviceInfoHelper(), std::move(callback));
}

void StoragePartitionImpl::InitNetworkContext() {
  network::mojom::NetworkContextParamsPtr context_params =
      network::mojom::NetworkContextParams::New();
  cert_verifier::mojom::CertVerifierCreationParamsPtr
      cert_verifier_creation_params =
          cert_verifier::mojom::CertVerifierCreationParams::New();
  GetContentClient()->browser()->ConfigureNetworkContextParams(
      browser_context_, is_in_memory(), relative_partition_path_,
      context_params.get(), cert_verifier_creation_params.get());
  // Should be initialized with existing per-profile CORS access lists.
  DCHECK(context_params->cors_origin_access_list.empty())
      << "NetworkContextParams::cors_origin_access_list should be populated "
         "via SharedCorsOriginAccessList";
  context_params->cors_origin_access_list =
      browser_context_->GetSharedCorsOriginAccessList()
          ->GetOriginAccessList()
          .CreateCorsOriginAccessPatternsList();
  devtools_instrumentation::ApplyNetworkContextParamsOverrides(
      browser_context_, context_params.get());
  DCHECK(!context_params->cert_verifier_params)
      << "`cert_verifier_params` should not be set in the "
         "NetworkContextParams, as they will be replaced with a new pipe to "
         "the CertVerifierService.";

  cert_verifier_service_updater_.reset();
  context_params->cert_verifier_params = GetCertVerifierParamsWithUpdater(
      std::move(cert_verifier_creation_params),
      cert_verifier_service_updater_.BindNewPipeAndPassReceiver());

  // This mechanisms should be used only for legacy internal headers. You can
  // find a recommended alternative approach on URLRequest::cors_exempt_headers
  // at services/network/public/mojom/url_loader.mojom.
  context_params->cors_exempt_header_list.push_back(
      GetCorsExemptRequestedWithHeaderName());
  context_params->cors_exempt_header_list.push_back("Last-Event-ID");
  variations::UpdateCorsExemptHeaderForVariations(context_params.get());
  cors_exempt_header_list_ = context_params->cors_exempt_header_list;

  if (base::FeatureList::IsEnabled(
          network::features::kCompressionDictionaryTransport) &&
      GetContentClient()->browser()->AllowCompressionDictionaryTransport(
          browser_context_)) {
    context_params->shared_dictionary_enabled = true;
    if (!is_in_memory()) {
      // Some callers may already initialize NetworkContextFilePaths, and we
      // don't want to overwrite them.
      if (!context_params->file_paths) {
        context_params->file_paths =
            network::mojom::NetworkContextFilePaths::New();
      }
      context_params->file_paths->shared_dictionary_directory =
          partition_path_.Append(FILE_PATH_LITERAL("Shared Dictionary"));
    }
    if (!context_params->shared_dictionary_cache_max_size.has_value()) {
      CalculateAndSetSharedDictionaryCacheMaxSize(
          GetWeakPtr(), is_in_memory() ? base::FilePath() : partition_path_);
    }
  }

#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS) && !BUILDFLAG(IS_ANDROID)
  // TODO(https://crbug.com/353770817): Android does not support non-interactive
  // certificate selection. Do not enable this for Android.
  if (base::FeatureList::IsEnabled(
          net::features::kDeviceBoundSessionsClientCertSelection)) {
    context_params->device_bound_sessions_network_observer =
        CreateURLLoaderNetworkObserverForDeviceBoundSessions();
  }
#endif

  network_context_owner_->network_context.reset();
  CreateNetworkContextInNetworkService(
      network_context_owner_->network_context.BindNewPipeAndPassReceiver(),
      std::move(context_params));
  DCHECK(network_context_owner_->network_context);

  // Restore the saved network revocation nonces. This allows network access
  // states to be persisted in case of a `NetworkService` crash.
  std::vector<network::mojom::IdAndAllowlistedPatternsPtr> dest_vector;
  for (const auto& [id, allowlists] : network_restrictions_ids_) {
    dest_vector.push_back(
        network::mojom::IdAndAllowlistedPatterns::New(id, allowlists));
  }

  network_context_owner_->network_context->RestrictNetworkForIds(
      std::move(dest_vector), base::NullCallback());

  network_context_client_receiver_.reset();
  network_context_owner_->network_context->SetClient(
      network_context_client_receiver_.BindNewPipeAndPassRemote());
  network_context_owner_->network_context.set_disconnect_handler(base::BindOnce(
      &StoragePartitionImpl::InitNetworkContext, weak_factory_.GetWeakPtr()));
}

network::mojom::URLLoaderFactoryParamsPtr
StoragePartitionImpl::CreateURLLoaderFactoryParams() {
  network::mojom::URLLoaderFactoryParamsPtr params =
      network::mojom::URLLoaderFactoryParams::New();
  // This method is used for browser-process initiated requests for which there
  // is no corresponding RenderProcessHost.
  params->process_id = network::OriginatingProcessId::browser();
  params->automatically_assign_isolation_info = true;
  params->is_orb_enabled = false;
  params->is_trusted = true;
  // For browser-process initiated requests there is no corresponding service
  // worker origin, so just pass an opaque origin.
  params->url_loader_network_observer =
      CreateURLLoaderNetworkObserverForServiceOrSharedWorker(params->process_id,
                                                             url::Origin());
  params->disable_web_security =
      base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDisableWebSecurity);
  params->network_restrictions_id = network::GetNoOpNetworkRestrictionsId();
  return params;
}

void StoragePartitionImpl::CreateURLLoaderFactoryForBrowserProcessInternal(
    mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  network::URLLoaderFactoryBuilder factory_builder;
  if (url_loader_factory::GetTestingInterceptor()) {
    url_loader_factory::GetTestingInterceptor().Run(
        network::mojom::kBrowserProcessId, factory_builder);
  }
  *out_factory =
      std::move(factory_builder)
          .Finish<mojo::PendingRemote<network::mojom::URLLoaderFactory>>(
              GetNetworkContext(), CreateURLLoaderFactoryParams());
}

void StoragePartition::SetDefaultQuotaSettingsForTesting(
    const storage::QuotaSettings* settings) {
  g_test_quota_settings = settings;
}

mojo::PendingRemote<network::mojom::CookieAccessObserver>
StoragePartitionImpl::CreateCookieAccessObserverForServiceWorker() {
  mojo::PendingRemote<network::mojom::CookieAccessObserver> remote;
  service_worker_cookie_observers_.Add(
      std::make_unique<ServiceWorkerCookieAccessObserver>(this),
      remote.InitWithNewPipeAndPassReceiver());
  return remote;
}

mojo::PendingRemote<network::mojom::TrustTokenAccessObserver>
StoragePartitionImpl::CreateTrustTokenAccessObserverForServiceWorker() {
  mojo::PendingRemote<network::mojom::TrustTokenAccessObserver> remote;
  service_worker_trust_token_observers_.Add(
      std::make_unique<ServiceWorkerTrustTokenAccessObserver>(this),
      remote.InitWithNewPipeAndPassReceiver());
  return remote;
}

mojo::PendingRemote<network::mojom::SharedDictionaryAccessObserver>
StoragePartitionImpl::CreateSharedDictionaryAccessObserverForServiceWorker() {
  mojo::PendingRemote<network::mojom::SharedDictionaryAccessObserver> remote;
  service_worker_shared_dictionary_observers_.Add(
      std::make_unique<ServiceWorkerSharedDictionaryAccessObserver>(this),
      remote.InitWithNewPipeAndPassReceiver());
  return remote;
}

mojo::PendingRemote<network::mojom::DeviceBoundSessionAccessObserver>
StoragePartitionImpl::CreateDeviceBoundSessionObserverForServiceWorker() {
  mojo::PendingRemote<network::mojom::DeviceBoundSessionAccessObserver> remote;
  service_worker_device_bound_session_observers_.Add(
      std::make_unique<ServiceWorkerDeviceBoundSessionAccessObserver>(this),
      remote.InitWithNewPipeAndPassReceiver());
  return remote;
}

void StoragePartitionImpl::OpenLocalStorageForProcess(
    int process_id,
    const blink::StorageKey& storage_key,
    mojo::PendingReceiver<blink::mojom::StorageArea> receiver) {
  DCHECK(initialized_);
  auto handle =
      ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(process_id);
  dom_storage_context_->OpenLocalStorage(storage_key, std::nullopt,
                                         std::move(receiver), std::move(handle),
                                         base::DoNothing());
}

void StoragePartitionImpl::BindSessionStorageAreaForProcess(
    int process_id,
    const blink::StorageKey& storage_key,
    const std::string& namespace_id,
    mojo::PendingReceiver<blink::mojom::StorageArea> receiver) {
  DCHECK(initialized_);
  auto handle =
      ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(process_id);
  dom_storage_context_->BindStorageArea(storage_key, std::nullopt, namespace_id,
                                        std::move(receiver), std::move(handle),
                                        base::DoNothing());
}

std::optional<blink::StorageKey> StoragePartitionImpl::CalculateStorageKey(
    const url::Origin& origin,
    const base::UnguessableToken* nonce) {
  if (!blink::StorageKey::IsThirdPartyStoragePartitioningEnabled()) {
    return std::nullopt;
  }

  const URLLoaderNetworkContext& context =
      url_loader_network_observers_.current_context();
  if (const auto* worker_context =
          std::get_if<URLLoaderNetworkContext::SharedOrServiceWorkerContext>(
              &context.context())) {
    if (worker_context->storage_key.has_value()) {
      if (nonce) {
        return blink::StorageKey::CreateWithNonce(origin, *nonce);
      }
      return worker_context->storage_key->WithOrigin(origin);
    }
    return std::nullopt;
  }

  NavigationOrDocumentHandle* handle = context.navigation_or_document();
  if (!handle) {
    return std::nullopt;
  }
  FrameTreeNode* node = handle->GetFrameTreeNode();
  if (!node) {
    return std::nullopt;
  }
  RenderFrameHostImpl* frame_host = node->current_frame_host();
  if (!frame_host) {
    return std::nullopt;
  }

  return frame_host->CalculateStorageKey(origin, nonce);
}

GlobalRenderFrameHostId
StoragePartitionImpl::GetRenderFrameHostIdFromNetworkContext() {
  URLLoaderNetworkContext context =
      url_loader_network_observers_.current_context();

  // `navigation_or_document()` can be null for `kSharedOrServiceWorkerContext`.
  RenderFrameHost* render_frame_host =
      context.navigation_or_document()
          ? context.navigation_or_document()->GetDocument()
          : nullptr;

  // It can pass empty GlobalRenderFrameHostId() when the context type is
  // not `kRenderFrameHostContext`.
  return render_frame_host ? render_frame_host->GetGlobalId()
                           : GlobalRenderFrameHostId();
}

void StoragePartitionImpl::IncrementActiveDocumentCount(
    const net::NetworkIsolationKey& nik) {
  if (auto it = active_document_per_nik_count_.find(nik);
      it != active_document_per_nik_count_.end()) {
    it->second++;
    CHECK_GT(it->second, 1);
  } else {
    active_document_per_nik_count_.emplace(nik, 1);
    if (keep_alive_url_loader_service_) {
      keep_alive_url_loader_service_->DidObserveNewlyActiveDocumentWithNIK(nik);
    }
  }
}

void StoragePartitionImpl::DecrementActiveDocumentCount(
    const net::NetworkIsolationKey& nik) {
  auto it = active_document_per_nik_count_.find(nik);
  CHECK(it != active_document_per_nik_count_.end());
  it->second--;
  if (it->second == 0) {
    active_document_per_nik_count_.erase(it);
  }
}

int StoragePartitionImpl::GetActiveDocumentCount(
    const net::NetworkIsolationKey& nik) {
  auto it = active_document_per_nik_count_.find(nik);
  if (it == active_document_per_nik_count_.end()) {
    return 0;
  }
  return it->second;
}

base::UnguessableToken StoragePartitionImpl::GetPartitionUUIDPerStorageKey(
    const blink::StorageKey& storage_key) {
  auto uuid = partition_uuid_per_storage_key_.find(storage_key);
  return uuid == partition_uuid_per_storage_key_.end()
             ? partition_uuid_per_storage_key_[storage_key] =
                   base::UnguessableToken::Create()
             : uuid->second;
}

void StoragePartitionImpl::OnScenarioMatchChanged(
    performance_scenarios::ScenarioScope scope,
    bool matches_pattern) {
  // If the scenario matches `performance_scenarios::kDefaultIdleScenarios` and
  // the network context is available, call `NotifyBrowserIdle()`. It's
  // possible to miss an idle notification if the browser becomes idle before
  // the network context is initialized. This is not a problem because the idle
  // state is also checked when data is committed to the cache, so a checkpoint
  // will eventually be triggered.
  if (matches_pattern && network_context_owner_->network_context.get()) {
    network_context_owner_->network_context->NotifyBrowserIdle();
  }
}

StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext(
    GlobalRenderFrameHostId render_frame_host_id)
    : context_(RenderFrameHostContext{}) {
  if (auto* rfh = RenderFrameHostImpl::FromID(render_frame_host_id)) {
    std::get<RenderFrameHostContext>(context_).navigation_or_document =
        rfh->GetNavigationOrDocumentHandle();
  }
}

StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext(
    NavigationRequest& navigation_request)
    : context_(NavigationRequestContext{
          navigation_request.navigation_or_document_handle()}) {}

StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext(
    const network::OriginatingProcessId& process_id,
    const url::Origin& worker_origin,
    const std::optional<blink::StorageKey>& storage_key)
    : context_(SharedOrServiceWorkerContext{process_id, worker_origin,
                                            storage_key}) {}

StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext(
    const URLLoaderNetworkContext& other) = default;

StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext()
    : context_(DeviceBoundSessionContext{}) {}

StoragePartitionImpl::URLLoaderNetworkContext&
StoragePartitionImpl::URLLoaderNetworkContext::operator=(
    const URLLoaderNetworkContext& other) = default;

StoragePartitionImpl::URLLoaderNetworkContext::~URLLoaderNetworkContext() =
    default;

StoragePartitionImpl::URLLoaderNetworkContext
StoragePartitionImpl::URLLoaderNetworkContext::CreateForRenderFrameHost(
    GlobalRenderFrameHostId render_frame_host_id) {
  return StoragePartitionImpl::URLLoaderNetworkContext(render_frame_host_id);
}

StoragePartitionImpl::URLLoaderNetworkContext
StoragePartitionImpl::URLLoaderNetworkContext::CreateForNavigation(
    NavigationRequest& navigation_request) {
  return StoragePartitionImpl::URLLoaderNetworkContext(navigation_request);
}

StoragePartitionImpl::URLLoaderNetworkContext
StoragePartitionImpl::URLLoaderNetworkContext::CreateForServiceOrSharedWorker(
    const network::OriginatingProcessId& process_id,
    const url::Origin& worker_origin,
    const std::optional<blink::StorageKey>& storage_key) {
  return StoragePartitionImpl::URLLoaderNetworkContext(
      process_id, worker_origin, storage_key);
}

StoragePartitionImpl::URLLoaderNetworkContext
StoragePartitionImpl::URLLoaderNetworkContext::CreateForDeviceBoundSessions() {
  return StoragePartitionImpl::URLLoaderNetworkContext();
}

StoragePartitionImpl::ContextType
StoragePartitionImpl::URLLoaderNetworkContext::type() const {
  if (std::holds_alternative<RenderFrameHostContext>(context_)) {
    return ContextType::kRenderFrameHostContext;
  }
  if (std::holds_alternative<NavigationRequestContext>(context_)) {
    return ContextType::kNavigationRequestContext;
  }
  if (std::holds_alternative<SharedOrServiceWorkerContext>(context_)) {
    return ContextType::kSharedOrServiceWorkerContext;
  }
  CHECK(std::holds_alternative<DeviceBoundSessionContext>(context_));
  return ContextType::kDeviceBoundSessionContext;
}

bool StoragePartitionImpl::URLLoaderNetworkContext::IsNavigationRequestContext()
    const {
  return std::holds_alternative<NavigationRequestContext>(context_);
}

NavigationOrDocumentHandle*
StoragePartitionImpl::URLLoaderNetworkContext::navigation_or_document() const {
  if (const auto* rfh_context =
          std::get_if<RenderFrameHostContext>(&context_)) {
    return rfh_context->navigation_or_document.get();
  }
  if (const auto* nav_context =
          std::get_if<NavigationRequestContext>(&context_)) {
    return nav_context->navigation_or_document.get();
  }
  return nullptr;
}

// Returns the WebContents corresponding to `context`.
WebContents* StoragePartitionImpl::URLLoaderNetworkContext::GetWebContents() {
  if (!navigation_or_document()) {
    return nullptr;
  }
  return navigation_or_document()->GetWebContents();
}

// Returns true if the request is the primary main frame navigation.
bool StoragePartitionImpl::URLLoaderNetworkContext::
    IsPrimaryMainFrameRequest() {
  if (!IsNavigationRequestContext()) {
    return false;
  }

  return navigation_or_document()->IsInPrimaryMainFrame();
}

}  // namespace content
