// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/network/network_context.h"

#include <algorithm>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "base/barrier_closure.h"
#include "base/base64.h"
#include "base/build_time.h"
#include "base/byte_size.h"
#include "base/callback_list.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/extend.h"
#include "base/containers/flat_set.h"
#include "base/containers/to_vector.h"
#include "base/containers/unique_ptr_adapters.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/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/sequence_checker.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/current_thread.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/types/optional_util.h"
#include "build/build_config.h"
#include "build/chromecast_buildflags.h"
#include "components/cookie_config/cookie_store_util.h"
#include "components/domain_reliability/monitor.h"
#include "components/enterprise/buildflags/buildflags.h"
#include "components/network_session_configurator/browser/network_session_configurator.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/os_crypt/async/common/encryptor.h"
#include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/pref_service_factory.h"
#include "components/unexportable_keys/mojom/unexportable_key_service_proxied.h"
#include "components/url_matcher/url_matcher.h"
#include "components/url_matcher/url_util.h"
#include "components/url_pattern/simple_url_pattern_matcher.h"
#include "components/variations/net/variations_http_headers.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "net/base/features.h"
#include "net/base/isolation_info.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/network_delegate.h"
#include "net/base/network_isolation_key.h"
#include "net/base/port_util.h"
#include "net/base/reconnect_notifier.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cert/caching_cert_verifier.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/coalescing_cert_verifier.h"
#include "net/cookies/cookie_access_delegate.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_setting_override.h"
#include "net/cookies/cookie_store.h"
#include "net/cookies/site_for_cookies.h"
#include "net/device_bound_sessions/session_service.h"
#include "net/disk_cache/disk_cache.h"
#include "net/dns/context_host_resolver.h"
#include "net/dns/host_cache.h"
#include "net/dns/mapped_host_resolver.h"
#include "net/extras/sqlite/cookie_crypto_delegate.h"
#include "net/extras/sqlite/sqlite_persistent_cookie_store.h"
#include "net/first_party_sets/first_party_set_metadata.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_auth_preferences.h"
#include "net/http/http_auth_scheme.h"
#include "net/http/http_cache.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_server_properties.h"
#include "net/http/http_transaction_factory.h"
#include "net/log/net_log_source_type.h"
#include "net/net_buildflags.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/proxy_resolution/proxy_config.h"
#include "net/shared_dictionary/shared_dictionary_isolation_key.h"
#include "net/ssl/ech_mode_getter.h"
#include "net/ssl/ssl_config_service.h"
#include "net/storage_access_api/status.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "services/network/brokered_client_socket_factory.h"
#include "services/network/connection_change_observer.h"
#include "services/network/cookie_manager.h"
#include "services/network/data_remover_util.h"
#include "services/network/device_bound_session_manager.h"
#include "services/network/disk_cache/mojo_backend_file_operations_factory.h"
#include "services/network/enterprise/encryption/encrypted_backend_file_operations_factory.h"
#include "services/network/enterprise/encryption/os_crypt_cache_encryption_delegate.h"
#include "services/network/host_resolver.h"
#include "services/network/http_auth_cache_proxy_copier.h"
#include "services/network/http_server_properties_pref_delegate.h"
#include "services/network/ignore_errors_cert_verifier.h"
#include "services/network/is_browser_initiated.h"
#include "services/network/known_legacy_scope_domains_delegate.h"
#include "services/network/logical_invalidation_store.h"
#include "services/network/net_log_exporter.h"
#include "services/network/network_service.h"
#include "services/network/network_service_network_delegate.h"
#include "services/network/network_service_proxy_delegate.h"
#include "services/network/oblivious_http_request_handler.h"
#include "services/network/pervasive_resources/shared_resource_checker.h"
#include "services/network/prefetch_cache.h"
#include "services/network/prefetch_matching_url_loader_factory.h"
#include "services/network/prefetch_url_loader_client.h"
#include "services/network/proxy_checking_host_resolver_request.h"
#include "services/network/proxy_config_service_mojo.h"
#include "services/network/proxy_lookup_request.h"
#include "services/network/proxy_resolving_socket_factory_mojo.h"
#include "services/network/public/cpp/cert_verifier/mojo_cert_verifier.h"
#include "services/network/public/cpp/constants.h"
#include "services/network/public/cpp/content_security_policy/content_security_policy.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/network_switches.h"
#include "services/network/public/cpp/parsed_headers.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_host_resolver.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"
#include "services/network/public/mojom/connection_change_observer_client.mojom-forward.h"
#include "services/network/public/mojom/cookie_encryption_provider.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/reporting_service.mojom.h"
#include "services/network/public/mojom/trust_tokens.mojom-forward.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/web_transport.mojom.h"
#include "services/network/resolve_host_request.h"
#include "services/network/resource_scheduler/resource_scheduler_client.h"
#include "services/network/restricted_cookie_manager.h"
#include "services/network/session_cleanup_cookie_store.h"
#include "services/network/shared_dictionary/shared_dictionary_constants.h"
#include "services/network/shared_dictionary/shared_dictionary_manager.h"
#include "services/network/shared_dictionary/shared_dictionary_storage.h"
#include "services/network/ssl_config_service_mojo.h"
#include "services/network/throttling/network_conditions.h"
#include "services/network/throttling/throttling_controller.h"
#include "services/network/throttling/throttling_network_transaction_factory.h"
#include "services/network/trust_tokens/expiry_inspecting_record_expiry_delegate.h"
#include "services/network/trust_tokens/in_memory_trust_token_persister.h"
#include "services/network/trust_tokens/pending_trust_token_store.h"
#include "services/network/trust_tokens/sqlite_trust_token_persister.h"
#include "services/network/trust_tokens/suitable_trust_token_origin.h"
#include "services/network/trust_tokens/trust_token_parameterization.h"
#include "services/network/trust_tokens/trust_token_query_answerer.h"
#include "services/network/trust_tokens/trust_token_store.h"
#include "services/network/url_loader.h"
#include "services/network/url_request_context_builder_mojo.h"
#include "services/network/web_transport.h"
#include "url/gurl.h"

#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
#include "services/network/device_bound_session_service_delegate.h"
#include "services/network/ssl_private_key_proxy.h"
#endif

#if BUILDFLAG(IS_CT_SUPPORTED)
// gn check does not account for BUILDFLAG(). So, for iOS builds, it will
// complain about a missing dependency on the target exposing this header. Add a
// nogncheck to stop it from yelling.
#include "components/certificate_transparency/chrome_require_ct_delegate.h"  // nogncheck
#include "services/network/sct_auditing/sct_auditing_cache.h"
#include "services/network/sct_auditing/sct_auditing_handler.h"
#endif  // BUILDFLAG(IS_CT_SUPPORTED)

#if BUILDFLAG(ENABLE_WEBSOCKETS)
#include "services/network/websocket.h"
#include "services/network/websocket_factory.h"
#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)

#if BUILDFLAG(ENABLE_REPORTING)
#include "net/base/http_user_agent_settings.h"
#include "net/extras/sqlite/sqlite_persistent_reporting_and_nel_store.h"
#include "net/network_error_logging/network_error_logging_service.h"
#include "net/reporting/reporting_browsing_data_remover.h"
#include "net/reporting/reporting_policy.h"
#include "net/reporting/reporting_service.h"
#endif  // BUILDFLAG(ENABLE_REPORTING)

#if BUILDFLAG(ENABLE_MDNS)
#include "services/network/mdns_responder.h"
#endif  // BUILDFLAG(ENABLE_MDNS)

#if BUILDFLAG(IS_P2P_ENABLED)
#include "services/network/p2p/socket_manager.h"
#endif  // BUILDFLAG(IS_P2P_ENABLED)

#if BUILDFLAG(IS_ANDROID)
#include "base/android/application_status_listener.h"
#include "net/ssl/ech_mode_getter_android.h"
#endif  // BUILDFLAG(IS_ANDROID)

namespace network {

namespace {

net::CertVerifier* g_cert_verifier_for_testing = nullptr;

// A CertVerifier that forwards all requests to |g_cert_verifier_for_testing|.
// This is used to allow NetworkContexts to have their own
// std::unique_ptr<net::CertVerifier> while forwarding calls to the shared
// verifier.
class WrappedTestingCertVerifier : public net::CertVerifier {
 public:
  ~WrappedTestingCertVerifier() override = default;

  // CertVerifier implementation
  int Verify(const RequestParams& params,
             net::CertVerifyResult* verify_result,
             net::CompletionOnceCallback callback,
             std::unique_ptr<Request>* out_req,
             const net::NetLogWithSource& net_log) override {
    verify_result->Reset();
    if (!g_cert_verifier_for_testing) {
      return net::ERR_FAILED;
    }
    return g_cert_verifier_for_testing->Verify(
        params, verify_result, std::move(callback), out_req, net_log);
  }
  void Verify2QwacBinding(
      const std::string& binding,
      const std::string& hostname,
      const scoped_refptr<net::X509Certificate>& tls_cert,
      base::OnceCallback<void(const scoped_refptr<net::X509Certificate>&)>
          callback,
      const net::NetLogWithSource& net_log) override {
    if (!g_cert_verifier_for_testing) {
      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
          FROM_HERE, base::BindOnce(std::move(callback), nullptr));
      return;
    }
    g_cert_verifier_for_testing->Verify2QwacBinding(
        binding, hostname, tls_cert, std::move(callback), net_log);
  }
  void SetConfig(const Config& config) override {
    if (!g_cert_verifier_for_testing) {
      return;
    }
    g_cert_verifier_for_testing->SetConfig(config);
  }
  void AddObserver(Observer* observer) override {
    if (!g_cert_verifier_for_testing) {
      return;
    }
    g_cert_verifier_for_testing->AddObserver(observer);
  }
  void RemoveObserver(Observer* observer) override {
    if (!g_cert_verifier_for_testing) {
      return;
    }
    g_cert_verifier_for_testing->RemoveObserver(observer);
  }
};

// This implementation initializes an OSCryptAsync Encryptor instance and uses
// that.
class CookieOSCryptAsyncDelegate : public net::CookieCryptoDelegate {
 public:
  explicit CookieOSCryptAsyncDelegate(
      mojo::PendingRemote<network::mojom::CookieEncryptionProvider> provider);

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

  void Init(base::OnceClosure callback) override;
  bool EncryptString(const std::string& plaintext,
                     std::string* ciphertext) override;
  bool DecryptString(const std::string& ciphertext,
                     std::string* plaintext) override;

 private:
  void InitCallback(
      mojo::Remote<network::mojom::CookieEncryptionProvider> lifetime,
      scoped_refptr<os_crypt_async::Encryptor> encryptor);

  scoped_refptr<os_crypt_async::Encryptor> instance_;
  mojo::PendingRemote<network::mojom::CookieEncryptionProvider> provider_
      GUARDED_BY_CONTEXT(sequence_checker_);
  base::OnceClosureList callbacks_ GUARDED_BY_CONTEXT(sequence_checker_);
  bool is_initializing_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
  bool is_initialized_ GUARDED_BY_CONTEXT(sequence_checker_) = false;

  SEQUENCE_CHECKER(sequence_checker_);

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

CookieOSCryptAsyncDelegate::CookieOSCryptAsyncDelegate(
    mojo::PendingRemote<network::mojom::CookieEncryptionProvider> provider)
    : provider_(std::move(provider)) {
  DETACH_FROM_SEQUENCE(sequence_checker_);
}

bool CookieOSCryptAsyncDelegate::EncryptString(const std::string& plaintext,
                                               std::string* ciphertext) {
  return instance_->EncryptString(plaintext, ciphertext);
}

bool CookieOSCryptAsyncDelegate::DecryptString(const std::string& ciphertext,
                                               std::string* plaintext) {
  return instance_->DecryptString(ciphertext, plaintext);
}

void CookieOSCryptAsyncDelegate::InitCallback(
    mojo::Remote<network::mojom::CookieEncryptionProvider> lifetime,
    scoped_refptr<os_crypt_async::Encryptor> encryptor) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  // The Encryptor is moved between sequences here. Verify that it's only moved
  // once.
  CHECK(!instance_);
  instance_ = std::move(encryptor);
  is_initialized_ = true;
  callbacks_.Notify();
}

void CookieOSCryptAsyncDelegate::Init(base::OnceClosure callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (is_initialized_) {
    std::move(callback).Run();
    return;
  }

  // AddUnsafe is safe here because it's always called with a callback that is
  // owned by a refcounted object. See SQLitePersistentCookieStore::Backend.
  callbacks_.AddUnsafe(std::move(callback));

  if (is_initializing_) {
    return;
  }

  is_initializing_ = true;
  mojo::Remote<network::mojom::CookieEncryptionProvider> remote(
      std::move(provider_));
  auto* raw_remote = remote.get();
  raw_remote->GetEncryptor(
      base::BindOnce(&CookieOSCryptAsyncDelegate::InitCallback,
                     weak_ptr_factory_.GetWeakPtr(), std::move(remote)));
}

// Predicate function to determine if the given |domain| matches the
// |filter_type| and |filter_domains| from a |mojom::ClearDataFilter|.
bool MatchesDomainFilter(mojom::ClearDataFilter_Type filter_type,
                         std::set<std::string> filter_domains,
                         const std::string& domain) {
  bool found_domain = filter_domains.find(domain) != filter_domains.end();
  return (filter_type == mojom::ClearDataFilter_Type::DELETE_MATCHES) ==
         found_domain;
}

// Returns a callback that checks if a domain matches the |filter|. |filter|
// must contain no origins. A null filter matches everything.
base::RepeatingCallback<bool(const std::string& host_name)> MakeDomainFilter(
    mojom::ClearDataFilter* filter) {
  if (!filter) {
    return base::BindRepeating([](const std::string&) { return true; });
  }

  DCHECK(filter->origins.empty())
      << "Origin filtering not allowed in a domain-only filter";

  std::set<std::string> filter_domains;
  filter_domains.insert(filter->domains.begin(), filter->domains.end());
  return base::BindRepeating(&MatchesDomainFilter, filter->type,
                             std::move(filter_domains));
}

// Predicate function to determine if the given |origin| matches the
// |filter_type|, |filter_domains| and |filter_origins| from a
// |mojom::ClearDataFilter|.
bool MatchesOriginFilter(mojom::ClearDataFilter_Type filter_type,
                         std::set<std::string> filter_domains,
                         std::set<url::Origin> filter_origins,
                         const url::Origin& origin) {
  std::string url_registrable_domain =
      net::registry_controlled_domains::GetDomainAndRegistry(
          origin, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
  bool found_domain =
      (filter_domains.find(url_registrable_domain != ""
                               ? url_registrable_domain
                               : origin.host()) != filter_domains.end());

  bool found_origin = (filter_origins.find(origin) != filter_origins.end());

  return (filter_type == mojom::ClearDataFilter_Type::DELETE_MATCHES) ==
         (found_domain || found_origin);
}

// Builds a generic Origin-matching predicate function based on |filter|. If
// |filter| is null, creates an always-true predicate.
base::RepeatingCallback<bool(const url::Origin&)> BuildOriginFilter(
    mojom::ClearDataFilterPtr filter) {
  if (!filter) {
    return base::BindRepeating([](const url::Origin&) { return true; });
  }

  std::set<std::string> filter_domains;
  filter_domains.insert(filter->domains.begin(), filter->domains.end());

  std::set<url::Origin> filter_origins;
  filter_origins.insert(filter->origins.begin(), filter->origins.end());

  return base::BindRepeating(&MatchesOriginFilter, filter->type,
                             std::move(filter_domains),
                             std::move(filter_origins));
}

// Builds a generic URL-matching predicate function based on |filter|.
// If |filter| is null, creates an always-true predicate.
base::RepeatingCallback<bool(const GURL&)> BuildUrlFilter(
    mojom::ClearDataFilterPtr filter) {
  return filter ? BindDoesUrlMatchFilter(filter->type, filter->origins,
                                         filter->domains)
                : base::NullCallback();
}

#if BUILDFLAG(IS_ANDROID)
class NetworkContextApplicationStatusListener
    : public base::android::ApplicationStatusListener {
 public:
  // Sets `get_callback` to be a callback that returns nullptr if the created
  // NetworkContextApplicationStatusListener has been destroyed, and the
  // listener itself, otherwise. It's a constructor argument to avoid needing
  // a cast, moving this into the header, or other complexities around
  // `app_status_listeners_` being a vector of the parent class.
  explicit NetworkContextApplicationStatusListener(
      disk_cache::ApplicationStatusListenerGetter& get_callback) {
    get_callback =
        base::BindRepeating(&NetworkContextApplicationStatusListener::
                                ReturnAppStatusListenerIfAlive,
                            weak_ptr_factory_.GetWeakPtr());
  }

  // base::android::ApplicationStatusListener implementation:
  void SetCallback(const ApplicationStateChangeCallback& callback) override {
    DCHECK(!callback_);
    DCHECK(callback);
    callback_ = callback;
  }

  void Notify(base::android::ApplicationState state) override {
    if (callback_) {
      callback_.Run(state);
    }
  }

 private:
  static base::android::ApplicationStatusListener*
  ReturnAppStatusListenerIfAlive(
      base::WeakPtr<base::android::ApplicationStatusListener> listener) {
    return listener.get();
  }

  ApplicationStateChangeCallback callback_;
  base::WeakPtrFactory<base::android::ApplicationStatusListener>
      weak_ptr_factory_{this};
};

#endif  // BUILDFLAG(IS_ANDROID)

struct TestVerifyCertState {
  net::CertVerifyResult result;
  std::unique_ptr<net::CertVerifier::Request> request;
};

void TestVerifyCertCallback(
    std::unique_ptr<TestVerifyCertState> request,
    NetworkContext::VerifyCertificateForTestingCallback callback,
    int result) {
  std::move(callback).Run(result);
}

std::string HashesToBase64String(
    const absl::flat_hash_set<net::SHA256HashValue>& hashes) {
  std::vector<std::string> strings;
  strings.reserve(hashes.size());
  for (const auto& hash : hashes) {
    strings.push_back(
        net::HashValue(net::HashValueTag::HASH_VALUE_SHA256, hash).ToString());
  }
  return base::JoinString(strings, ",");
}

#if BUILDFLAG(IS_CT_SUPPORTED)
// SCTAuditingDelegate is an implementation of the delegate interface that is
// aware of per-NetworkContext details (to allow the cache to notify the
// associated NetworkContextClient of new reports, and to apply
// per-NetworkContext enabled/disabled status for the auditing feature).
class SCTAuditingDelegate : public net::SCTAuditingDelegate {
 public:
  explicit SCTAuditingDelegate(const base::WeakPtr<NetworkContext>& context);
  ~SCTAuditingDelegate() override;

  // net::SCTAuditingDelegate:
  void MaybeEnqueueReport(
      const net::HostPortPair& host_port_pair,
      const net::X509Certificate* validated_certificate_chain,
      const net::SignedCertificateTimestampAndStatusList&
          signed_certificate_timestamps) override;

 private:
  base::WeakPtr<NetworkContext> context_;
};

SCTAuditingDelegate::SCTAuditingDelegate(
    const base::WeakPtr<NetworkContext>& context)
    : context_(context) {}

SCTAuditingDelegate::~SCTAuditingDelegate() = default;

void SCTAuditingDelegate::MaybeEnqueueReport(
    const net::HostPortPair& host_port_pair,
    const net::X509Certificate* validated_certificate_chain,
    const net::SignedCertificateTimestampAndStatusList&
        signed_certificate_timestamps) {
  if (!context_) {
    return;
  }
  context_->MaybeEnqueueSCTReport(host_port_pair, validated_certificate_chain,
                                  signed_certificate_timestamps);
}
#endif  // BUILDFLAG(IS_CT_SUPPORTED)

// Obtains a full data file path from a NetworkContextFilePaths, a class member
// pointer to the data file. If valid, then returns true and places the full
// path into `full_path` otherwise returns false.
bool GetFullDataFilePath(
    const mojom::NetworkContextFilePathsPtr& file_paths,
    std::optional<base::FilePath> network::mojom::NetworkContextFilePaths::*
        field_name,
    base::FilePath& full_path) {
  if (!file_paths) {
    return false;
  }

  std::optional<base::FilePath> relative_file_path =
      file_paths.get()->*field_name;
  if (!relative_file_path.has_value()) {
    return false;
  }

  // Path to a data file should always be a plain filename.
  DCHECK_EQ(relative_file_path->BaseName(), *relative_file_path);

  full_path =
      file_paths->data_directory.path().Append(relative_file_path->value());
  return true;
}

// Produces URLLoaderFactoryParams suitable for a prefetch. Generally this
// should match the params that are used for subresource fetches to render
// processes.
mojom::URLLoaderFactoryParamsPtr CreateURLLoaderFactoryParamsForPrefetch() {
  auto params = mojom::URLLoaderFactoryParams::New();
  params->process_id = OriginatingProcessId::browser();
  // We want to be able to use TrustedParams to set the IsolationInfo for each
  // prefetch separately, so make it trusted.
  // TODO(crbug.com/342445996): Maybe stop using TrustedParams and lock this
  // down?
  params->is_trusted = true;

  // This can be set to true by the content::switches::kDisableWebSecurity
  // switch, but that's not available in the network service. The consequences
  // of this being disabled should just be that prefetches fail where this flag
  // would have taken effect.
  // TODO(crbug.com/342445996): Pass this through from the caller.
  //
  // Android WebView also disables web security when the
  // `allow_universal_access_from_file_urls` flag is set, however, WebView
  // doesn't currently support prefetch anyway.
  params->disable_web_security = false;

  // TODO(crbug.com/342445996): Find out if we need to set anything here.
  params->client_security_state = mojom::ClientSecurityState::New();

  // TODO(crbug.com/342445996): params->coep_reporter

  // --disable-web-security also disables Opaque Response Blocking (ORB).
  params->is_orb_enabled = !params->disable_web_security;

  // TODO(crbug.com/342445996): trust_token_issuance_policy,
  // trust_token_redemption_policy

  // TODO(crbug.com/342445996): Add observers as needed for DevTools support,
  // etc.

  // TODO(crbug.com/342445996): params->cookie_setting_overrides

  params->debug_tag = "CreateURLLoaderFactoryParamsForPrefetch";

  // TODO(crbug.com/342445996): params->require_cross_site_requests_for_cookies

  return params;
}

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// LINT.IfChange(HSTSRedirectUpgradeReason)
enum class HSTSRedirectUpgradeReason {
  kNotUpgradedNotHTTP = 0,
  kNotUpgradedHSTSUnavailable = 1,
  kNotUpgradedNoHSTSPin = 2,
  kUpgraded = 3,
  kMaxValue = kUpgraded,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/net/enums.xml:HSTSRedirectUpgradeReason)

void RecordHSTSPreconnectUpgradeReason(HSTSRedirectUpgradeReason reason) {
  if (!base::FeatureList::IsEnabled(
          net::features::kHstsTopLevelNavigationsOnly)) {
    base::UmaHistogramEnumeration("Net.PreconnectHSTSUpgradesUrl", reason);
  }
}

bool IsIncognitoFromParams(const mojom::NetworkContextParams& params) {
  // Checking both to see if there are any file paths at all, and if there is
  // specifically an http_cache_directory filepath in order to avoid a
  // potential nullptr dereference if we just checked that
  // `params_->file_paths->http_cache_directory' existed.
  return !params.file_paths || !params.file_paths->http_cache_directory;
}

void RemoveInvalidationFilterCallback(
    base::WeakPtr<NetworkContext> context,
    net::HttpCache::InvalidationFilter filter) {
  if (!context) {
    return;
  }
  net::HttpCache* cache =
      context->url_request_context()->http_transaction_factory()->GetCache();
  if (cache) {
    cache->RemoveInvalidationFilter(filter);
  }
  context->OnLogicalFilterRemoved(filter);
}

}  // namespace

constexpr uint32_t NetworkContext::kMaxOutstandingRequestsPerProcess;

NetworkContext::NetworkContextHttpAuthPreferences::
    NetworkContextHttpAuthPreferences(NetworkService* network_service)
    : network_service_(network_service) {}

NetworkContext::NetworkContextHttpAuthPreferences::
    ~NetworkContextHttpAuthPreferences() = default;

#if BUILDFLAG(IS_LINUX)
bool NetworkContext::NetworkContextHttpAuthPreferences::AllowGssapiLibraryLoad()
    const {
  if (network_service_) {
    network_service_->OnBeforeGssapiLibraryLoad();
  }
  return net::HttpAuthPreferences::AllowGssapiLibraryLoad();
}
#endif  // BUILDFLAG(IS_LINUX)

NetworkContext::NetworkRestriction::NetworkRestriction() = default;
NetworkContext::NetworkRestriction::NetworkRestriction(
    NetworkRestriction&& other) = default;
NetworkContext::NetworkRestriction&
NetworkContext::NetworkRestriction::operator=(NetworkRestriction&& other) =
    default;
NetworkContext::NetworkRestriction::~NetworkRestriction() = default;

NetworkContext::PendingCertVerify::PendingCertVerify() = default;
NetworkContext::PendingCertVerify::~PendingCertVerify() = default;

NetworkContext::NetworkContext(
    NetworkService* network_service,
    mojo::PendingReceiver<mojom::NetworkContext> receiver,
    mojom::NetworkContextParamsPtr params,
    OnConnectionCloseCallback on_connection_close_callback)
    : NetworkContext(base::PassKey<NetworkContext>(),
                     network_service,
                     std::move(receiver),
                     std::move(params),
                     std::move(on_connection_close_callback),
                     OnURLRequestContextBuilderConfiguredCallback()) {}

// net::NetworkDelegate that wraps
NetworkContext::NetworkContext(
    base::PassKey<NetworkContext> pass_key,
    NetworkService* network_service,
    mojo::PendingReceiver<mojom::NetworkContext> receiver,
    mojom::NetworkContextParamsPtr params,
    OnConnectionCloseCallback on_connection_close_callback,
    OnURLRequestContextBuilderConfiguredCallback
        on_url_request_context_builder_configured)
    : network_service_(network_service),
      url_request_context_(nullptr),
#if BUILDFLAG(ENABLE_REPORTING)
      is_observing_reporting_service_(false),
#endif  // BUILDFLAG(ENABLE_REPORTING)
      params_(std::move(params)),
      on_connection_close_callback_(std::move(on_connection_close_callback)),
      receiver_(std::in_place_type<Receiver>, this),
      first_party_sets_access_delegate_(
          std::move(params_->first_party_sets_access_delegate_receiver),
          std::move(params_->first_party_sets_access_delegate_params),
          network_service_->first_party_sets_manager()),
      cors_preflight_controller_(network_service),
      http_auth_merged_preferences_(network_service),
      ohttp_handler_(this),
      prefetch_enabled_(
          base::FeatureList::IsEnabled(features::kNetworkContextPrefetch) &&
          (params_->bound_network == net::handles::kInvalidNetworkHandle)),
      cors_non_wildcard_request_headers_support_(base::FeatureList::IsEnabled(
          features::kCorsNonWildcardRequestHeadersSupport)),
      prefetch_cache_(prefetch_enabled_ ? std::make_unique<PrefetchCache>()
                                        : nullptr),
      variations_headers_(std::move(params_->initial_variations_headers)) {
  TRACE_EVENT0("loading", "NetworkContext::NetworkContext");

  if (features::ShouldBindNetworkContextDirectReceiver()) {
    receiver_.emplace<DirectReceiver>(mojo::DirectReceiverKey{}, this);
  }
  std::visit(
      [&](auto& receiver_to_bind) {
        receiver_to_bind.Bind(std::move(receiver));
      },
      receiver_);
#if BUILDFLAG(IS_WIN) && DCHECK_IS_ON()
  if (params_->file_paths) {
    DCHECK(params_->win_permissions_set)
        << "Permissions not set on files. Network context should be created "
           "using CreateNetworkContextInNetworkService rather than directly on "
           "the network service.";
  }
#endif  // BUILDFLAG(IS_WIN) && DCHECK_IS_ON()

#if BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED)
  if (params_->file_paths) {
    if (params_->file_paths->http_cache_directory) {
      EnsureMounted(&*params_->file_paths->http_cache_directory);
    }
    if (params_->file_paths->shared_dictionary_directory) {
      EnsureMounted(&*params_->file_paths->shared_dictionary_directory);
    }
    EnsureMounted(&params_->file_paths->data_directory);
  }
#endif  // BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED)

  if (params_->shared_dictionary_enabled) {
    if (params_->file_paths &&
        params_->file_paths->shared_dictionary_directory &&
        !params_->file_paths->shared_dictionary_directory->path().empty()) {
#if BUILDFLAG(IS_ANDROID)
      disk_cache::ApplicationStatusListenerGetter get_callback;
      app_status_listeners_.push_back(
          std::make_unique<NetworkContextApplicationStatusListener>(
              get_callback));
#endif  // BUILDFLAG(IS_ANDROID)
      // TODO(crbug.com/40255884): Set `file_operations_factory` to support
      // sandboxed network service on Android.
      shared_dictionary_manager_ = SharedDictionaryManager::CreateOnDisk(
          params_->file_paths->shared_dictionary_directory->path().Append(
              FILE_PATH_LITERAL("db")),
          params_->file_paths->shared_dictionary_directory->path().Append(
              FILE_PATH_LITERAL("cache")),
          params_->shared_dictionary_cache_max_size,
          shared_dictionary::kDictionaryMaxCountPerNetworkContext,
#if BUILDFLAG(IS_ANDROID)
          std::move(get_callback),
#endif  // BUILDFLAG(IS_ANDROID)
          /*file_operations_factory=*/nullptr);
    } else {
      shared_dictionary_manager_ = SharedDictionaryManager::CreateInMemory(
          params_->shared_dictionary_cache_max_size,
          shared_dictionary::kDictionaryMaxCountPerNetworkContext);
    }
  }

  mojo::PendingRemote<mojom::URLLoaderFactory>
      url_loader_factory_for_cert_net_fetcher;
  mojo::PendingReceiver<mojom::URLLoaderFactory>
      url_loader_factory_for_cert_net_fetcher_receiver =
          url_loader_factory_for_cert_net_fetcher
              .InitWithNewPipeAndPassReceiver();

  scoped_refptr<SessionCleanupCookieStore> session_cleanup_cookie_store =
      MakeSessionCleanupCookieStore();

  url_request_context_owner_ = MakeURLRequestContext(
      std::move(url_loader_factory_for_cert_net_fetcher),
      session_cleanup_cookie_store,
      std::move(on_url_request_context_builder_configured),
      params_->bound_network);
  url_request_context_ = url_request_context_owner_.url_request_context.get();

  cookie_manager_ = std::make_unique<CookieManager>(
      url_request_context_, &first_party_sets_access_delegate_,
      std::move(session_cleanup_cookie_store),
      std::move(params_->cookie_manager_params));

  cookie_manager_->AddSettingsWillChangeCallback(
      base::BindRepeating(&NetworkContext::OnCookieManagerSettingsChanged,
                          weak_factory_.GetWeakPtr()));

  shared_resource_checker_ = std::make_unique<SharedResourceChecker>(
      cookie_manager_->cookie_settings());

  network_service_->RegisterNetworkContext(this);

  // Only register for destruction if |this| will be wholly lifetime-managed
  // by the NetworkService. In the other constructors, lifetime is shared with
  // other consumers, and thus self-deletion is not safe and can result in
  // double-frees.
  std::visit(
      [&](auto& receiver) {
        receiver.set_disconnect_handler(base::BindOnce(
            &NetworkContext::OnConnectionError, base::Unretained(this)));
      },
      receiver_);

  socket_factory_ = std::make_unique<SocketFactory>(
      url_request_context_->net_log(), url_request_context_);
#if BUILDFLAG(IS_WIN)
  if (params_->socket_brokers) {
    socket_factory_->BindSocketBroker(
        std::move(params_->socket_brokers->server));
  }
#endif
  resource_scheduler_ = std::make_unique<ResourceScheduler>();

  if (params_->http_auth_static_network_context_params) {
    http_auth_merged_preferences_.SetAllowDefaultCredentials(
        params_->http_auth_static_network_context_params
            ->allow_default_credentials);
  }

  InitializeCorsParams();

#if BUILDFLAG(IS_CT_SUPPORTED)
  if (params_->ct_policy) {
    SetCTPolicy(std::move(params_->ct_policy));
  }

  base::FilePath sct_auditing_path;
  GetFullDataFilePath(params_->file_paths,
                      &network::mojom::NetworkContextFilePaths::
                          sct_auditing_pending_reports_file_name,
                      sct_auditing_path);
  sct_auditing_handler_ =
      std::make_unique<SCTAuditingHandler>(this, sct_auditing_path);
  sct_auditing_handler()->SetMode(params_->sct_auditing_mode);
#endif  // BUILDFLAG(IS_CT_SUPPORTED)

#if BUILDFLAG(IS_ANDROID)
  if (params_->cookie_manager) {
    GetCookieManager(std::move(params_->cookie_manager));
  }
#endif  // BUILDFLAG(IS_ANDROID)

  CreateURLLoaderFactoryForCertNetFetcher(
      std::move(url_loader_factory_for_cert_net_fetcher_receiver));

  SetBlockTrustTokens(params_->block_trust_tokens);

  if (params_ && params_->http_cache_file_operations_factory) {
    http_cache_file_operations_factory_ =
        base::MakeRefCounted<MojoBackendFileOperationsFactory>(
            std::move(params_->http_cache_file_operations_factory));
  }

  if (prefetch_enabled_) {
    InitializePrefetchURLLoaderFactory();
  }

  device_bound_session_manager_ = DeviceBoundSessionManager::Create(
      url_request_context_->device_bound_session_service(),
      cookie_manager_.get());

  if (params_->file_paths &&
      params_->file_paths->logical_invalidation_directory) {
    base::TaskPriority priority =
        base::FeatureList::IsEnabled(net::features::kLogicalClearHttpCache) &&
                net::features::kLogicalClearHttpCacheUserVisiblePriority.Get()
            ? base::TaskPriority::USER_VISIBLE
            : base::TaskPriority::BEST_EFFORT;
    scoped_refptr<base::SequencedTaskRunner> file_task_runner =
        base::ThreadPool::CreateSequencedTaskRunner(
            {base::MayBlock(), priority,
             base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
    logical_invalidation_store_ = std::make_unique<LogicalInvalidationStore>(
        params_->file_paths->logical_invalidation_directory->path(),
        file_task_runner);
    // Start loading persisted invalidation filters asynchronously on startup.
    logical_invalidation_store_->Load(
        base::BindOnce(&NetworkContext::OnInvalidationFiltersLoaded,
                       weak_factory_.GetWeakPtr()));
  } else {
    initial_filters_loaded_ = true;
  }
}

NetworkContext::NetworkContext(
    NetworkService* network_service,
    mojo::PendingReceiver<mojom::NetworkContext> receiver,
    net::URLRequestContext* url_request_context,
    const std::vector<std::string>& cors_exempt_header_list)
    : network_service_(network_service),
      url_request_context_(url_request_context),
#if BUILDFLAG(ENABLE_REPORTING)
      is_observing_reporting_service_(false),
#endif  // BUILDFLAG(ENABLE_REPORTING)
      receiver_(std::in_place_type<Receiver>, this),
      first_party_sets_access_delegate_(
          /*receiver=*/mojo::NullReceiver(),
          /*params=*/nullptr,
          /*manager=*/nullptr),
      cookie_manager_(std::make_unique<CookieManager>(
          url_request_context,
          nullptr,
          /*first_party_sets_access_delegate=*/nullptr,
          /*params=*/nullptr)),
      socket_factory_(
          std::make_unique<SocketFactory>(url_request_context_->net_log(),
                                          url_request_context)),
      cors_preflight_controller_(network_service),
      http_auth_merged_preferences_(network_service),
      ohttp_handler_(this),
      prefetch_enabled_(
          base::FeatureList::IsEnabled(features::kNetworkContextPrefetch) &&
          (url_request_context_->bound_network() ==
           net::handles::kInvalidNetworkHandle)),
      prefetch_cache_(prefetch_enabled_ ? std::make_unique<PrefetchCache>()
                                        : nullptr) {
  TRACE_EVENT0("loading", "NetworkContext::NetworkContext");

  if (features::ShouldBindNetworkContextDirectReceiver()) {
    receiver_.emplace<DirectReceiver>(mojo::DirectReceiverKey{}, this);
  }
  std::visit(
      [&](auto& receiver_to_bind) {
        receiver_to_bind.Bind(std::move(receiver));
      },
      receiver_);

  shared_resource_checker_ = std::make_unique<SharedResourceChecker>(
      cookie_manager_->cookie_settings());

  // May be nullptr in tests.
  if (network_service_) {
    network_service_->RegisterNetworkContext(this);
  }
  resource_scheduler_ = std::make_unique<ResourceScheduler>();

  for (const auto& key : cors_exempt_header_list) {
    cors_exempt_header_list_.insert(key);
  }

  if (prefetch_enabled_) {
    InitializePrefetchURLLoaderFactory();
  }
}

#if BUILDFLAG(ENABLE_WEBSOCKETS)
void NetworkContext::CreateNetLogEntriesForActiveWebSockets(
    net::NetLog::ThreadSafeObserver* observer) const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (websocket_factory_) {
    websocket_factory_->CreateNetLogEntriesForActiveConnections(observer);
  }
}
#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)

NetworkContext::~NetworkContext() {
  is_destructing_ = true;

  // Clear pending HttpCacheDataRemovers and counters explicitly to cancel
  // any in-flight background operations and avoid callback races.
  http_cache_data_removers_.clear();
  http_cache_data_counters_.clear();

  // May be nullptr in tests.
  if (network_service_) {
    network_service_->DeregisterNetworkContext(this);
  }

  if (domain_reliability_monitor_) {
    domain_reliability_monitor_->Shutdown();
  }
  // Because of the order of declaration in the class,
  // domain_reliability_monitor_ will be destroyed before
  // |url_loader_factories_| which could own URLLoader's whose destructor call
  // back into this class and might use domain_reliability_monitor_. So we reset
  // |domain_reliability_monitor_| here explicitly, instead of changing the
  // order, because any work calling into |domain_reliability_monitor_| at
  // shutdown would be unnecessary as the reports would be thrown out.
  domain_reliability_monitor_.reset();

  if (url_request_context_ &&
      url_request_context_->transport_security_state()) {
#if BUILDFLAG(IS_CT_SUPPORTED)
    if (require_ct_delegate_) {
      url_request_context_->transport_security_state()->SetRequireCTDelegate(
          nullptr);
    }
#endif  // BUILDFLAG(IS_CT_SUPPORTED)
  }

#if BUILDFLAG(ENABLE_REPORTING)
  if (is_observing_reporting_service_) {
    DCHECK(url_request_context());
    // May be nullptr in tests.
    if (url_request_context()->reporting_service()) {
      url_request_context()->reporting_service()->RemoveReportingCacheObserver(
          this);
    }
  }
#endif  // BUILDFLAG(ENABLE_REPORTING)

#if BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED)
  if (!dismount_closures_.empty()) {
    // Dismount all mounted directories after a generous delay, so that
    // pending asynchronous IO tasks have a chance to complete before the
    // directory is unmounted.
    constexpr base::TimeDelta kDismountDelay = base::Minutes(5);

    for (auto& dismount_closure : dismount_closures_) {
      std::ignore = base::ThreadPool::PostDelayedTask(
          FROM_HERE, std::move(dismount_closure), kDismountDelay);
    }
  }
#endif  // BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED)

  // Clear `url_loader_factories_` before deleting the contents, as it can
  // result in re-entrant calls to DestroyURLLoaderFactory().
  std::set<std::unique_ptr<PrefetchMatchingURLLoaderFactory>,
           base::UniquePtrComparator>
      url_loader_factories = std::move(url_loader_factories_);
}

void NetworkContext::OnCookieManagerSettingsChanged() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  for (const std::unique_ptr<network::RestrictedCookieManager>& rcm :
       restricted_cookie_managers_) {
    rcm->OnCookieSettingsChanged();
  }
}

// static
std::unique_ptr<NetworkContext> NetworkContext::CreateForTesting(
    NetworkService* network_service,
    mojo::PendingReceiver<mojom::NetworkContext> receiver,
    mojom::NetworkContextParamsPtr params,
    OnURLRequestContextBuilderConfiguredCallback
        on_url_request_context_builder_configured) {
  return std::make_unique<NetworkContext>(
      base::PassKey<NetworkContext>(), network_service, std::move(receiver),
      std::move(params), OnConnectionCloseCallback(),
      std::move(on_url_request_context_builder_configured));
}

// static
void NetworkContext::SetCertVerifierForTesting(
    net::CertVerifier* cert_verifier) {
  g_cert_verifier_for_testing = cert_verifier;
}

void NetworkContext::CreateURLLoaderFactory(
    mojo::PendingReceiver<mojom::URLLoaderFactory> receiver,
    mojom::URLLoaderFactoryParamsPtr params,
    scoped_refptr<ResourceSchedulerClient> resource_scheduler_client) {
  url_loader_factories_.emplace(
      std::make_unique<PrefetchMatchingURLLoaderFactory>(
          this, std::move(params), std::move(resource_scheduler_client),
          std::move(receiver), &cors_origin_access_list_,
          prefetch_cache_.get()));
}

void NetworkContext::CreateURLLoaderFactoryForCertNetFetcher(
    mojo::PendingReceiver<mojom::URLLoaderFactory> factory_receiver) {
  // TODO(crbug.com/40695068): investigate changing these params.
  auto url_loader_factory_params = mojom::URLLoaderFactoryParams::New();
  url_loader_factory_params->is_trusted = true;
  url_loader_factory_params->process_id = OriginatingProcessId::browser();
  url_loader_factory_params->automatically_assign_isolation_info = true;
  url_loader_factory_params->is_orb_enabled = false;
  if (url_request_context()->bound_network() !=
      net::handles::kInvalidNetworkHandle) {
    // This is done to maintain consistency with network-bound
    // URLLoaderFactories used by multi-network CCT. See
    // CorsURLLoaderFactory::IsCorsPreflighLoadOptionAllowed
    // http://crrev.com/c/5899528.
    url_loader_factory_params->disable_web_security = true;
  }
  CreateURLLoaderFactory(std::move(factory_receiver),
                         std::move(url_loader_factory_params));
}

void NetworkContext::ActivateDohProbes() {
  DCHECK(url_request_context_->host_resolver());

  doh_probes_request_.reset();
  doh_probes_request_ =
      url_request_context_->host_resolver()->CreateDohProbeRequest();
  doh_probes_request_->Start();
}

void NetworkContext::SetClient(
    mojo::PendingRemote<mojom::NetworkContextClient> client) {
  client_.reset();
  client_.Bind(std::move(client));
}

void NetworkContext::CreateSocketFactory(
    mojo::PendingReceiver<mojom::SocketFactory> receiver) {
  socket_factory_receivers_.Add(socket_factory_.get(), std::move(receiver));
}

void NetworkContext::CreateURLLoaderFactory(
    mojo::PendingReceiver<mojom::URLLoaderFactory> receiver,
    mojom::URLLoaderFactoryParamsPtr params) {
  scoped_refptr<ResourceSchedulerClient> resource_scheduler_client =
      base::MakeRefCounted<ResourceSchedulerClient>(
          ResourceScheduler::ClientId::Create(),
          IsBrowserInitiated(params->process_id.is_browser()),
          resource_scheduler_.get(),
          url_request_context_->network_quality_estimator());
  CreateURLLoaderFactory(std::move(receiver), std::move(params),
                         std::move(resource_scheduler_client));
}

void NetworkContext::ResetURLLoaderFactories() {
  // Move all factories to a temporary vector so ClearBindings() does not
  // invalidate the iterator if the factory gets deleted.
  std::vector<PrefetchMatchingURLLoaderFactory*> factories;
  factories.reserve(url_loader_factories_.size());
  for (const auto& factory : url_loader_factories_) {
    if (!factory->ShouldIgnoreFactoryReset()) {
      factories.push_back(factory.get());
    }
  }
  for (auto* factory : factories) {
    factory->ClearBindings();
  }
}

void NetworkContext::GetViaObliviousHttp(
    mojom::ObliviousHttpRequestPtr request,
    mojo::PendingRemote<mojom::ObliviousHttpClient> client) {
  ohttp_handler_.StartRequest(std::move(request), std::move(client));
}

void NetworkContext::GetCookieManager(
    mojo::PendingReceiver<mojom::CookieManager> receiver) {
  cookie_manager_->AddReceiver(std::move(receiver));
}

void NetworkContext::OnRCMDisconnect(
    const network::RestrictedCookieManager* rcm) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  auto it = restricted_cookie_managers_.find(rcm);
  CHECK(it != restricted_cookie_managers_.end());
  restricted_cookie_managers_.erase(it);
}

void NetworkContext::RemoveConnectionChangeObserver(
    const net::ConnectionChangeNotifier::Observer* observer) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  auto it = connection_change_observers_.find(observer);
  if (it != connection_change_observers_.end()) {
    connection_change_observers_.erase(it);
  }
}

void NetworkContext::GetRestrictedCookieManager(
    mojo::PendingReceiver<mojom::RestrictedCookieManager> receiver,
    mojom::RestrictedCookieManagerRole role,
    const url::Origin& origin,
    const net::IsolationInfo& isolation_info,
    const net::CookieSettingOverrides& cookie_setting_overrides,
    const net::CookieSettingOverrides& devtools_cookie_setting_overrides,
    bool prefer_bound_cookie_context,
    mojo::PendingRemote<mojom::CookieAccessObserver> cookie_observer) {
  net::FirstPartySetMetadata first_party_set_metadata =
      RestrictedCookieManager::ComputeFirstPartySetMetadata(
          origin, url_request_context_->cookie_store(), isolation_info);

  std::unique_ptr<RestrictedCookieManager> ptr =
      std::make_unique<RestrictedCookieManager>(
          role, url_request_context_->cookie_store(),
          cookie_manager_->cookie_settings(), origin, isolation_info,
          cookie_setting_overrides, devtools_cookie_setting_overrides,
          prefer_bound_cookie_context, std::move(cookie_observer),
          std::move(first_party_set_metadata),
          network_service_->GetMetricsUpdater());

  auto callback = base::BindOnce(&NetworkContext::OnRCMDisconnect,
                                 base::Unretained(this), ptr.get());
  ptr->InstallReceiver(std::move(receiver), std::move(callback));
  restricted_cookie_managers_.insert(std::move(ptr));
}

void NetworkContext::GetTrustTokenQueryAnswerer(
    mojo::PendingReceiver<mojom::TrustTokenQueryAnswerer> receiver,
    const url::Origin& top_frame_origin) {
  // Only called when Trust Tokens is enabled, i.e. trust_token_store_ is
  // non-null.
  DCHECK(trust_token_store_);
  DCHECK(network_service_);

  std::optional<SuitableTrustTokenOrigin> suitable_top_frame_origin =
      SuitableTrustTokenOrigin::Create(top_frame_origin);

  const SynchronousTrustTokenKeyCommitmentGetter* const key_commitment_getter =
      network_service_->trust_token_key_commitments();

  // It's safe to dereference |suitable_top_frame_origin| here as, during the
  // process of vending the TrustTokenQueryAnswerer, the browser ensures that
  // the requesting context's top frame origin is suitable for Trust Tokens.
  auto answerer = std::make_unique<TrustTokenQueryAnswerer>(
      std::move(*suitable_top_frame_origin), trust_token_store_.get(),
      key_commitment_getter);

  trust_token_query_answerers_.Add(std::move(answerer), std::move(receiver));
}

void NetworkContext::GetStoredTrustTokenCounts(
    GetStoredTrustTokenCountsCallback callback) {
  if (trust_token_store_) {
    auto get_trust_token_counts_from_store =
        [](NetworkContext::GetStoredTrustTokenCountsCallback callback,
           TrustTokenStore* trust_token_store) {
          std::vector<mojom::StoredTrustTokensForIssuerPtr> result;
          for (auto& issuer_count_pair :
               trust_token_store->GetStoredTrustTokenCounts()) {
            result.push_back(mojom::StoredTrustTokensForIssuer::New(
                std::move(issuer_count_pair.first), issuer_count_pair.second));
          }
          std::move(callback).Run(std::move(result));
        };
    trust_token_store_->ExecuteOrEnqueue(
        base::BindOnce(get_trust_token_counts_from_store, std::move(callback)));
  } else {
    // The Trust Tokens feature is disabled, return immediately with an empty
    // vector.
    std::move(callback).Run({});
  }
}

void NetworkContext::GetPrivateStateTokenRedemptionRecords(
    GetPrivateStateTokenRedemptionRecordsCallback callback) {
  // The Trust Tokens feature is disabled, return immediately with an empty
  // map.
  if (!trust_token_store_) {
    base::flat_map<url::Origin, std::vector<mojom::ToplevelRedemptionRecordPtr>>
        empty_result;
    std::move(callback).Run(std::move(empty_result));
    return;
  }
  auto get_redemption_records_from_store =
      [](NetworkContext::GetPrivateStateTokenRedemptionRecordsCallback callback,
         TrustTokenStore* trust_token_store) {
        std::move(callback).Run(trust_token_store->GetRedemptionRecords());
      };
  trust_token_store_->ExecuteOrEnqueue(
      base::BindOnce(get_redemption_records_from_store, std::move(callback)));
}

void NetworkContext::DeleteStoredTrustTokens(
    const url::Origin& issuer,
    DeleteStoredTrustTokensCallback callback) {
  if (!trust_token_store_) {
    std::move(callback).Run(
        mojom::DeleteStoredTrustTokensStatus::kFailureFeatureDisabled);
    return;
  }

  std::optional<SuitableTrustTokenOrigin> suitable_issuer_origin =
      SuitableTrustTokenOrigin::Create(issuer);
  if (!suitable_issuer_origin) {
    std::move(callback).Run(
        mojom::DeleteStoredTrustTokensStatus::kFailureInvalidOrigin);
    return;
  }

  trust_token_store_->ExecuteOrEnqueue(base::BindOnce(
      [](SuitableTrustTokenOrigin issuer,
         DeleteStoredTrustTokensCallback callback, TrustTokenStore* store) {
        const bool did_delete_tokens = store->DeleteStoredTrustTokens(issuer);
        const auto status =
            did_delete_tokens
                ? mojom::DeleteStoredTrustTokensStatus::kSuccessTokensDeleted
                : mojom::DeleteStoredTrustTokensStatus::kSuccessNoTokensDeleted;
        std::move(callback).Run(status);
      },
      std::move(*suitable_issuer_origin), std::move(callback)));
}

void NetworkContext::SetBlockTrustTokens(bool block) {
  block_trust_tokens_ = block;
}

void NetworkContext::OnProxyLookupComplete(
    ProxyLookupRequest* proxy_lookup_request) {
  auto it = proxy_lookup_requests_.find(proxy_lookup_request);
  CHECK(it != proxy_lookup_requests_.end());
  proxy_lookup_requests_.erase(it);
}

void NetworkContext::OnProxyCheckingHostResolverRequestComplete(
    ProxyCheckingHostResolverRequest* request) {
  auto it = proxy_checking_host_resolver_requests_.find(request);
  CHECK(it != proxy_checking_host_resolver_requests_.end());
  proxy_checking_host_resolver_requests_.erase(it);
}

void NetworkContext::SetTLS13EarlyDataEnabled(bool enabled) {
  url_request_context_->http_transaction_factory()
      ->GetSession()
      ->SetTLS13EarlyDataEnabled(enabled);
}

void NetworkContext::DisableQuic() {
  url_request_context_->http_transaction_factory()->GetSession()->DisableQuic();
}

void NetworkContext::DestroyURLLoaderFactory(
    PrefetchMatchingURLLoaderFactory* url_loader_factory) {
  if (is_destructing_) {
    return;
  }
  auto it = url_loader_factories_.find(url_loader_factory);
  CHECK(it != url_loader_factories_.end());
  url_loader_factories_.erase(it);
}

void NetworkContext::Remove(WebTransport* transport) {
  auto it = web_transports_.find(transport);
  if (it != web_transports_.end()) {
    web_transports_.erase(it);
  }
}

void NetworkContext::LoaderCreated(const OriginatingProcessId& process_id) {
  loader_count_per_process_[process_id] += 1;
}

void NetworkContext::LoaderDestroyed(const OriginatingProcessId& process_id) {
  auto it = loader_count_per_process_.find(process_id);
  CHECK(it != loader_count_per_process_.end());
  it->second -= 1;
  if (it->second == 0) {
    loader_count_per_process_.erase(it);
  }
}

bool NetworkContext::CanCreateLoader(const OriginatingProcessId& process_id) {
  auto it = loader_count_per_process_.find(process_id);
  uint32_t count = (it == loader_count_per_process_.end() ? 0 : it->second);
  return count < max_loaders_per_process_;
}

size_t NetworkContext::GetNumOutstandingResolveHostRequestsForTesting() const {
  size_t sum = proxy_checking_host_resolver_requests_.size();
  if (internal_host_resolver_) {
    sum += internal_host_resolver_->GetNumOutstandingRequestsForTesting();
  }
  for (const auto& host_resolver : host_resolvers_) {
    sum += host_resolver->GetNumOutstandingRequestsForTesting();  // IN-TEST
  }
  return sum;
}

bool NetworkContext::SkipReportingPermissionCheck() const {
#if BUILDFLAG(ENABLE_REPORTING)
  return params_ && params_->skip_reporting_send_permission_check;
#else
  return false;
#endif  // BUILDFLAG(ENABLE_REPORTING)
}

void NetworkContext::ClearTrustTokenData(mojom::ClearDataFilterPtr filter,
                                         base::OnceClosure done) {
  if (!trust_token_store_) {
    std::move(done).Run();
    return;
  }
  trust_token_store_->ExecuteOrEnqueue(base::BindOnce(
      [](mojom::ClearDataFilterPtr filter, base::OnceClosure done,
         TrustTokenStore* store) {
        std::ignore = store->ClearDataForFilter(std::move(filter));
        std::move(done).Run();
      },
      std::move(filter), std::move(done)));
}

void NetworkContext::ClearTrustTokenSessionOnlyData(
    ClearTrustTokenSessionOnlyDataCallback callback) {
  // Only called when Private State Tokens is enabled, i.e.,
  // `trust_token_store_` is non-null.
  DCHECK(trust_token_store_);
  DCHECK(cookie_manager_);

  DeleteCookiePredicate cookie_predicate =
      cookie_manager_->cookie_settings().CreateDeleteCookieOnExitPredicate();

  auto store_predicate = base::BindRepeating(
      [](DeleteCookiePredicate predicate, const std::string& origin) {
        return predicate.Run(origin, net::CookieSourceScheme::kSecure);
      },
      std::move(cookie_predicate));
  trust_token_store_->ExecuteOrEnqueue(base::BindOnce(
      [](base::RepeatingCallback<bool(const std::string&)> pred,
         ClearTrustTokenSessionOnlyDataCallback cb, TrustTokenStore* store) {
        bool any_data_deleted = store->ClearDataForPredicate(std::move(pred));
        std::move(cb).Run(any_data_deleted);
      },
      std::move(store_predicate), std::move(callback)));
}

void NetworkContext::ClearNetworkingHistoryBetween(
    base::Time start_time,
    base::Time end_time,
    base::OnceClosure completion_callback) {
#if BUILDFLAG(IS_CT_SUPPORTED)
  auto barrier = base::BarrierClosure(3, std::move(completion_callback));
  sct_auditing_handler()->ClearPendingReports(barrier);
#else
  auto barrier = base::BarrierClosure(2, std::move(completion_callback));
#endif  // BUIDLFLAG(IS_CT_SUPPORTED)

  url_request_context_->transport_security_state()->DeleteAllDynamicDataBetween(
      start_time, end_time, barrier);

  // TODO(mmenke): Neither of these methods waits until the changes have been
  // commited to disk. They probably should, as most similar methods net/
  // exposes do.
  // May not be set in all tests.
  if (network_qualities_pref_delegate_) {
    network_qualities_pref_delegate_->ClearPrefs();
  }

  url_request_context_->http_server_properties()->Clear(barrier);
}

void NetworkContext::ClearHttpCache(base::Time start_time,
                                    base::Time end_time,
                                    mojom::ClearDataFilterPtr filter,
                                    ClearHttpCacheCallback callback) {
  ClearHttpCacheInternal(start_time, end_time, std::move(filter),
                         ClearHttpCacheMode::kPhysical, std::move(callback));
}

void NetworkContext::ClearHttpCacheLogically(
    base::Time start_time,
    base::Time end_time,
    mojom::ClearDataFilterPtr filter,
    ClearHttpCacheLogicallyCallback callback) {
  ClearHttpCacheInternal(start_time, end_time, std::move(filter),
                         ClearHttpCacheMode::kLogical, std::move(callback));
}

void NetworkContext::ClearHttpCacheInternal(base::Time start_time,
                                            base::Time end_time,
                                            mojom::ClearDataFilterPtr filter,
                                            ClearHttpCacheMode mode,
                                            base::OnceClosure callback) {
  base::UmaHistogramEnumeration("NetworkService.ClearHttpCacheMode", mode);

  if (mode == ClearHttpCacheMode::kLogical &&
      base::FeatureList::IsEnabled(net::features::kLogicalClearHttpCache)) {
    net::HttpCache* cache =
        url_request_context_->http_transaction_factory()->GetCache();
    net::HttpCache::InvalidationFilter filter_for_callback;
    if (cache) {
      // Step 1: Add a logical filter to the HttpCache. This is near-instant
      // and ensures that subsequent requests won't see invalidated data.
      net::HttpCache::InvalidationFilter invalidation_filter;
      invalidation_filter.begin_time = start_time;
      // Cap the end_time to Now() so we don't accidentally invalidate future
      // cache entries if the caller passes Time::Max().
      invalidation_filter.end_time = std::min(end_time, base::Time::Now());
      if (filter) {
        invalidation_filter.filter_type =
            ConvertClearDataFilterType(filter->type);
        invalidation_filter.origins = base::flat_set<url::Origin>(
            filter->origins.begin(), filter->origins.end());
        invalidation_filter.domains = base::flat_set<std::string>(
            filter->domains.begin(), filter->domains.end());
      } else {
        invalidation_filter.filter_type = net::UrlFilterType::kFalseIfMatches;
      }
      filter_for_callback = invalidation_filter;

      cache->AddInvalidationFilter(std::move(invalidation_filter));
      // Queue the filter for disk persistence and invoke `callback` once
      // LogicalInvalidationStore::Save() finishes saving to disk.
      OnLogicalFilterAdded(filter_for_callback, std::move(callback));
    } else {
      if (callback) {
        std::move(callback).Run();
      }
    }

    // Step 2: Trigger the slow physical cleanup in the background.
    // When it completes, the logical filter is removed since the data is
    // physically gone.
    auto logical_cleanup_done_callback = base::BindOnce(
        &RemoveInvalidationFilterCallback, weak_factory_.GetWeakPtr(),
        std::move(filter_for_callback));

    StartHttpCacheDataRemover(std::move(filter), start_time, end_time,
                              std::move(logical_cleanup_done_callback));

    // Step 3: Responding to the caller immediately is omitted to wait for
    // persistence.
    return;
  }

  // It's safe to use Unretained below as the HttpCacheDataRemover is owned by
  // |this| and guarantees it won't call its callback if deleted.
  StartHttpCacheDataRemover(std::move(filter), start_time, end_time,
                            std::move(callback));
}

void NetworkContext::ComputeHttpCacheSize(
    base::Time start_time,
    base::Time end_time,
    ComputeHttpCacheSizeCallback callback) {
  // It's safe to use Unretained below as the HttpCacheDataCounter is owned by
  // |this| and guarantees it won't call its callback if deleted.
  http_cache_data_counters_.push_back(HttpCacheDataCounter::CreateAndStart(
      url_request_context_, start_time, end_time,
      base::BindOnce(&NetworkContext::OnHttpCacheSizeComputed,
                     base::Unretained(this), std::move(callback))));
}

void NetworkContext::NotifyBrowserIdle() {
  net::HttpCache* htp_cache =
      url_request_context_->http_transaction_factory()->GetCache();
  if (!htp_cache) {
    return;
  }
  disk_cache::Backend* backend = htp_cache->GetCurrentBackend();
  if (!backend) {
    return;
  }
  backend->OnBrowserIdle();
}

void NetworkContext::ClearCorsPreflightCache(
    mojom::ClearDataFilterPtr filter,
    ClearCorsPreflightCacheCallback callback) {
  cors_preflight_controller_.ClearCorsPreflightCache(std::move(filter));
  std::move(callback).Run();
}

void NetworkContext::ClearHostCache(mojom::ClearDataFilterPtr filter,
                                    ClearHostCacheCallback callback) {
  net::HostCache* host_cache =
      url_request_context_->host_resolver()->GetHostCache();
  DCHECK(host_cache);
  host_cache->ClearForHosts(MakeDomainFilter(filter.get()));
  std::move(callback).Run();
}

void NetworkContext::ClearHttpAuthCache(base::Time start_time,
                                        base::Time end_time,
                                        mojom::ClearDataFilterPtr filter,
                                        ClearHttpAuthCacheCallback callback) {
  net::HttpNetworkSession* http_session =
      url_request_context_->http_transaction_factory()->GetSession();
  DCHECK(http_session);

  if (http_session->http_auth_cache()->ClearEntriesAddedBetween(
          start_time, end_time, BuildUrlFilter(std::move(filter)))) {
    // TODO(mmenke): Use another error code for this, as ERR_ABORTED has
    // somewhat magical handling with respect to navigations.
    http_session->CloseAllConnections(net::ERR_ABORTED, "Clearing auth cache");
  }

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

void NetworkContext::ClearReportingCacheReports(
    mojom::ClearDataFilterPtr filter,
    ClearReportingCacheReportsCallback callback) {
#if BUILDFLAG(ENABLE_REPORTING)
  net::ReportingService* reporting_service =
      url_request_context_->reporting_service();
  if (reporting_service) {
    if (filter) {
      reporting_service->RemoveBrowsingData(
          net::ReportingBrowsingDataRemover::DATA_TYPE_REPORTS,
          BuildOriginFilter(std::move(filter)));
    } else {
      reporting_service->RemoveAllBrowsingData(
          net::ReportingBrowsingDataRemover::DATA_TYPE_REPORTS);
    }
  }
#endif  // BUILDFLAG(ENABLE_REPORTING)

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

void NetworkContext::ClearReportingCacheClients(
    mojom::ClearDataFilterPtr filter,
    ClearReportingCacheClientsCallback callback) {
#if BUILDFLAG(ENABLE_REPORTING)
  net::ReportingService* reporting_service =
      url_request_context_->reporting_service();
  if (reporting_service) {
    if (filter) {
      reporting_service->RemoveBrowsingData(
          net::ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS,
          BuildOriginFilter(std::move(filter)));
    } else {
      reporting_service->RemoveAllBrowsingData(
          net::ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS);
    }
  }
#endif  // BUILDFLAG(ENABLE_REPORTING)

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

void NetworkContext::ClearNetworkErrorLogging(
    mojom::ClearDataFilterPtr filter,
    ClearNetworkErrorLoggingCallback callback) {
#if BUILDFLAG(ENABLE_REPORTING)
  net::NetworkErrorLoggingService* logging_service =
      url_request_context_->network_error_logging_service();
  if (logging_service) {
    if (filter) {
      logging_service->RemoveBrowsingData(BuildOriginFilter(std::move(filter)));
    } else {
      logging_service->RemoveAllBrowsingData();
    }
  }
#endif  // BUILDFLAG(ENABLE_REPORTING)

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

void NetworkContext::SetDocumentReportingEndpoints(
    const base::UnguessableToken& reporting_source,
    const url::Origin& origin,
    const net::IsolationInfo& isolation_info,
    const base::flat_map<std::string, std::string>& endpoints) {
#if BUILDFLAG(ENABLE_REPORTING)
  DCHECK(!reporting_source.is_empty());
  DCHECK_EQ(net::IsolationInfo::RequestType::kOther,
            isolation_info.request_type());
  net::ReportingService* reporting_service =
      url_request_context()->reporting_service();
  if (reporting_service) {
    reporting_service->SetDocumentReportingEndpoints(reporting_source, origin,
                                                     isolation_info, endpoints);
  }
#endif  // BUILDFLAG(ENABLE_REPORTING)
}

void NetworkContext::SendReportsAndRemoveSource(
    const base::UnguessableToken& reporting_source) {
#if BUILDFLAG(ENABLE_REPORTING)
  DCHECK(!reporting_source.is_empty());
  net::ReportingService* reporting_service =
      url_request_context()->reporting_service();
  if (reporting_service) {
    reporting_service->SendReportsAndRemoveSource(reporting_source);
  }
#endif  // BUILDFLAG(ENABLE_REPORTING)
}

void NetworkContext::SendReportsForSource(
    const base::UnguessableToken& reporting_source) {
#if BUILDFLAG(ENABLE_REPORTING)
  CHECK(!reporting_source.is_empty());
  net::ReportingService* reporting_service =
      url_request_context()->reporting_service();
  if (reporting_service) {
    reporting_service->SendReportsForSource(reporting_source);
  }
#endif  // BUILDFLAG(ENABLE_REPORTING)
}

void NetworkContext::QueueReport(
    const std::string& type,
    const std::string& group,
    const GURL& url,
    const std::optional<base::UnguessableToken>& reporting_source,
    const net::NetworkAnonymizationKey& network_anonymization_key,
    base::DictValue body) {
  QueueReportInternal(type, group, url, reporting_source,
                      network_anonymization_key, std::move(body),
                      net::ReportingTargetType::kDeveloper);
}

void NetworkContext::QueueEnterpriseReport(const std::string& type,
                                           const std::string& group,
                                           const GURL& url,
                                           base::DictValue body) {
  // Enterprise reports don't use a |reporting_source| or
  // |network_anonymization_key|. Enterprise endpoints are profile-bound and not
  // document-bound like web developer endpoints.
  QueueReportInternal(type, group, url, /*reporting_source=*/std::nullopt,
                      net::NetworkAnonymizationKey(), std::move(body),
                      net::ReportingTargetType::kEnterprise);
}

void NetworkContext::QueueReportInternal(
    const std::string& type,
    const std::string& group,
    const GURL& url,
    const std::optional<base::UnguessableToken>& reporting_source,
    const net::NetworkAnonymizationKey& network_anonymization_key,
    base::DictValue body,
    net::ReportingTargetType target_type) {
#if BUILDFLAG(ENABLE_REPORTING)
  // If |reporting_source| is provided, it must not be empty.
  DCHECK(!(reporting_source.has_value() && reporting_source->is_empty()));
  // Enterprise reports have an empty |network_anonymization_key|.
  if (target_type == net::ReportingTargetType::kDeveloper &&
      require_network_anonymization_key_) {
    DCHECK(!network_anonymization_key.IsEmpty());
  }

  // Get the ReportingService.
  net::URLRequestContext* request_context = url_request_context();
  net::ReportingService* reporting_service =
      request_context->reporting_service();
  // TODO(paulmeyer): Remove this once the network service ships everywhere.
  if (!reporting_service) {
    return;
  }

  std::string reported_user_agent = "";
  if (request_context->http_user_agent_settings() != nullptr) {
    reported_user_agent =
        request_context->http_user_agent_settings()->GetUserAgent();
  }

  reporting_service->QueueReport(
      url, reporting_source, network_anonymization_key, reported_user_agent,
      group, type, std::move(body), 0 /* depth */, target_type);
#endif  // BUILDFLAG(ENABLE_REPORTING)
}

void NetworkContext::QueueConnectionAllowlistReport(
    const GURL& context,
    const GURL& resource,
    const net::NetworkAnonymizationKey& key,
    const std::optional<base::UnguessableToken>& reporting_source,
    const std::string& group,
    bool enforced) {
  base::DictValue body;
  // Strip username, password, and ref fragments from the blocked resource URL.
  body.Set("connection", resource.GetAsReferrer().spec());
  body.Set("disposition", enforced ? "enforce" : "report");

  QueueReport("connection-allowlist", group, context, reporting_source, key,
              std::move(body));
}

void NetworkContext::QueueSignedExchangeReport(
    mojom::SignedExchangeReportPtr report,
    const net::NetworkAnonymizationKey& network_anonymization_key) {
#if BUILDFLAG(ENABLE_REPORTING)
  if (require_network_anonymization_key_) {
    DCHECK(!network_anonymization_key.IsEmpty());
  }

  net::NetworkErrorLoggingService* logging_service =
      url_request_context_->network_error_logging_service();
  if (!logging_service) {
    return;
  }

  std::string user_agent;
  if (url_request_context_->http_user_agent_settings() != nullptr) {
    user_agent =
        url_request_context_->http_user_agent_settings()->GetUserAgent();
  }
  net::NetworkErrorLoggingService::SignedExchangeReportDetails details;
  details.network_anonymization_key = network_anonymization_key;
  details.success = report->success;
  details.type = std::move(report->type);
  details.outer_url = std::move(report->outer_url);
  details.inner_url = std::move(report->inner_url);
  details.cert_url = std::move(report->cert_url);
  details.referrer = std::move(report->referrer);
  details.server_ip_address = std::move(report->server_ip_address);
  details.protocol = std::move(report->protocol);
  details.method = std::move(report->method);
  details.status_code = report->status_code;
  details.elapsed_time = report->elapsed_time;
  details.user_agent = std::move(user_agent);
  logging_service->QueueSignedExchangeReport(std::move(details));
#endif  // BUILDFLAG(ENABLE_REPORTING)
}

#if BUILDFLAG(ENABLE_REPORTING)
void NetworkContext::AddReportingApiObserver(
    mojo::PendingRemote<network::mojom::ReportingApiObserver> observer) {
  if (url_request_context() && url_request_context()->reporting_service()) {
    if (!is_observing_reporting_service_) {
      is_observing_reporting_service_ = true;
      url_request_context()->reporting_service()->AddReportingCacheObserver(
          this);
      reporting_api_observers_.set_disconnect_handler(
          base::BindRepeating(&NetworkContext::OnReportingObserverDisconnect,
                              weak_factory_.GetWeakPtr()));
    }
    auto id = reporting_api_observers_.Add(std::move(observer));

    auto service_reports =
        url_request_context()->reporting_service()->GetReports();
    for (const net::ReportingReport* service_report : service_reports) {
      reporting_api_observers_.Get(id)->OnReportAdded(*service_report);
    }

    base::flat_map<url::Origin, std::vector<net::ReportingEndpoint>>
        endpoints_by_origin = url_request_context()
                                  ->reporting_service()
                                  ->GetV1ReportingEndpointsByOrigin();
    for (auto const& origin_and_endpoints : endpoints_by_origin) {
      OnEndpointsUpdatedForOrigin(origin_and_endpoints.second);
    }
  }
}

void NetworkContext::OnReportAdded(const net::ReportingReport* service_report) {
  for (const auto& observer : reporting_api_observers_) {
    observer->OnReportAdded(*service_report);
  }
}

void NetworkContext::OnEndpointsUpdatedForOrigin(
    const std::vector<net::ReportingEndpoint>& endpoints) {
  for (const auto& observer : reporting_api_observers_) {
    observer->OnEndpointsUpdatedForOrigin(endpoints);
  }
}

void NetworkContext::OnReportUpdated(
    const net::ReportingReport* service_report) {
  for (const auto& observer : reporting_api_observers_) {
    observer->OnReportUpdated(*service_report);
  }
}

void NetworkContext::OnReportingObserverDisconnect(
    mojo::RemoteSetElementId /*mojo_id*/) {
  if (!reporting_api_observers_.size()) {
    DCHECK(url_request_context());
    DCHECK(url_request_context()->reporting_service());
    url_request_context()->reporting_service()->RemoveReportingCacheObserver(
        this);
    is_observing_reporting_service_ = false;
  }
}

void NetworkContext::AddVariationsHeadersToReportingRequest(
    net::URLRequest* request) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  variations::AppendVariationsHeaderWithCustomValue(
      request->url(),
      IsIncognitoFromParams(*params_) ? variations::InIncognito::kYes
                                      : variations::InIncognito::kNo,
      variations_headers_.get(), request);
}
#endif  // BUILDFLAG(ENABLE_REPORTING)

void NetworkContext::ClearDomainReliability(
    mojom::ClearDataFilterPtr filter,
    DomainReliabilityClearMode mode,
    ClearDomainReliabilityCallback callback) {
  if (domain_reliability_monitor_) {
    domain_reliability::DomainReliabilityClearMode dr_mode;
    if (mode ==
        mojom::NetworkContext::DomainReliabilityClearMode::CLEAR_CONTEXTS) {
      dr_mode = domain_reliability::CLEAR_CONTEXTS;
    } else {
      dr_mode = domain_reliability::CLEAR_BEACONS;
    }

    domain_reliability_monitor_->ClearBrowsingData(
        dr_mode, BuildOriginFilter(std::move(filter)));
  }
  std::move(callback).Run();
}

void NetworkContext::CloseAllConnections(CloseAllConnectionsCallback callback) {
  net::HttpNetworkSession* http_session =
      url_request_context_->http_transaction_factory()->GetSession();
  DCHECK(http_session);

  // TODO(mmenke): Use another error code for this, as ERR_ABORTED has somewhat
  // magical handling with respect to navigations.
  http_session->CloseAllConnections(net::ERR_ABORTED,
                                    "Embedder closing all connections");

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

void NetworkContext::CloseIdleConnections(
    CloseIdleConnectionsCallback callback) {
  net::HttpNetworkSession* http_session =
      url_request_context_->http_transaction_factory()->GetSession();
  DCHECK(http_session);

  http_session->CloseIdleConnections("Embedder closing idle connections");

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

void NetworkContext::SetNetworkConditions(
    const base::UnguessableToken& throttling_profile_id,
    const base::UnguessableToken& throttling_client_id,
    std::vector<mojom::MatchedNetworkConditionsPtr> conditions) {
  std::vector<MatchedNetworkConditions> network_conditions;
  for (auto& condition : conditions) {
    network_conditions.emplace_back(
        std::move(condition->pattern),
        NetworkConditions{condition->conditions->offline,
                          condition->conditions->latency.InMillisecondsF(),
                          condition->conditions->download_throughput,
                          condition->conditions->upload_throughput,
                          condition->conditions->packet_loss,
                          condition->conditions->packet_queue_length,
                          condition->conditions->packet_reordering,
                          condition->conditions->rule_id});
  }
  ThrottlingController::SetConditions(throttling_profile_id,
                                      throttling_client_id,
                                      std::move(network_conditions));
}

void NetworkContext::SetAcceptLanguage(const std::string& new_accept_language) {
  // This may only be called on NetworkContexts created with the constructor
  // that calls MakeURLRequestContext().
  DCHECK(user_agent_settings_);
  user_agent_settings_->set_accept_language(new_accept_language);
}

void NetworkContext::SetEnableReferrers(bool enable_referrers) {
  // This may only be called on NetworkContexts created with the constructor
  // that calls MakeURLRequestContext().
  DCHECK(network_delegate_);
  network_delegate_->set_enable_referrers(enable_referrers);
}

#if BUILDFLAG(IS_CT_SUPPORTED)
void NetworkContext::SetCTPolicy(mojom::CTPolicyPtr ct_policy) {
  if (!require_ct_delegate_) {
    return;
  }

  require_ct_delegate_->UpdateCTPolicies(ct_policy->excluded_hosts,
                                         ct_policy->excluded_spkis);
}

void NetworkContext::MaybeEnqueueSCTReport(
    const net::HostPortPair& host_port_pair,
    const net::X509Certificate* validated_certificate_chain,
    const net::SignedCertificateTimestampAndStatusList&
        signed_certificate_timestamps) {
  sct_auditing_handler()->MaybeEnqueueReport(host_port_pair,
                                             validated_certificate_chain,
                                             signed_certificate_timestamps);
}

void NetworkContext::SetSCTAuditingMode(mojom::SCTAuditingMode mode) {
  sct_auditing_handler()->SetMode(mode);
}

void NetworkContext::CanSendSCTAuditingReport(
    base::OnceCallback<void(bool)> callback) {
  // If the NetworkContextClient hasn't been set yet or has disconnected for
  // some reason, just return `false`. (One case where this could occur is when
  // restarting SCTAuditingReporter instances loaded form disk at startup -- see
  // crbug.com/1347180 for more details on that case.)
  if (!client_) {
    std::move(callback).Run(false);
    return;
  }
  client_->OnCanSendSCTAuditingReport(std::move(callback));
}

void NetworkContext::OnNewSCTAuditingReportSent() {
  client_->OnNewSCTAuditingReportSent();
}
#endif  // BUILDFLAG(IS_CT_SUPPORTED)

void NetworkContext::CreateUDPSocket(
    mojo::PendingReceiver<mojom::UDPSocket> receiver,
    mojo::PendingRemote<mojom::UDPSocketListener> listener) {
  socket_factory_->CreateUDPSocket(std::move(receiver), std::move(listener));
}

void NetworkContext::CreateRestrictedUDPSocket(
    const net::IPEndPoint& addr,
    mojom::RestrictedUDPSocketMode mode,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
    mojom::RestrictedUDPSocketParamsPtr params,
    mojo::PendingReceiver<mojom::RestrictedUDPSocket> receiver,
    mojo::PendingRemote<mojom::UDPSocketListener> listener,
    bool allow_multicast,
    bool allow_source_specific_multicast,
    CreateRestrictedUDPSocketCallback callback) {
  // SimpleHostResolver is transitively owned by |this|.
  socket_factory_->CreateRestrictedUDPSocket(
      addr, mode, traffic_annotation, std::move(params), std::move(receiver),
      std::move(listener), SimpleHostResolver::Create(this), allow_multicast,
      allow_source_specific_multicast, std::move(callback));
}

void NetworkContext::CreateTCPServerSocket(
    const net::IPEndPoint& local_addr,
    mojom::TCPServerSocketOptionsPtr options,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
    mojo::PendingReceiver<mojom::TCPServerSocket> receiver,
    CreateTCPServerSocketCallback callback) {
  socket_factory_->CreateTCPServerSocket(
      local_addr, std::move(options),
      static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
      std::move(receiver), std::move(callback));
}

void NetworkContext::CreateTCPConnectedSocket(
    const std::optional<net::IPEndPoint>& local_addr,
    const net::AddressList& remote_addr_list,
    mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
    mojo::PendingReceiver<mojom::TCPConnectedSocket> receiver,
    mojo::PendingRemote<mojom::SocketObserver> observer,
    CreateTCPConnectedSocketCallback callback) {
  socket_factory_->CreateTCPConnectedSocket(
      local_addr, remote_addr_list, std::move(tcp_connected_socket_options),
      traffic_annotation, std::move(receiver), std::move(observer),
      std::move(callback));
}

void NetworkContext::CreateTCPBoundSocket(
    const net::IPEndPoint& local_addr,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
    mojo::PendingReceiver<mojom::TCPBoundSocket> receiver,
    CreateTCPBoundSocketCallback callback) {
  socket_factory_->CreateTCPBoundSocket(
      local_addr,
      static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
      std::move(receiver), std::move(callback));
}

void NetworkContext::CreateProxyResolvingSocketFactory(
    mojo::PendingReceiver<mojom::ProxyResolvingSocketFactory> receiver) {
  proxy_resolving_socket_factories_.Add(
      std::make_unique<ProxyResolvingSocketFactoryMojo>(url_request_context()),
      std::move(receiver));
}

void NetworkContext::LookUpProxyForURL(
    const GURL& url,
    const net::NetworkAnonymizationKey& network_anonyization_key,
    mojo::PendingRemote<mojom::ProxyLookupClient> proxy_lookup_client) {
  DCHECK(proxy_lookup_client);
  std::unique_ptr<ProxyLookupRequest> proxy_lookup_request(
      std::make_unique<ProxyLookupRequest>(std::move(proxy_lookup_client), this,
                                           network_anonyization_key));
  ProxyLookupRequest* proxy_lookup_request_ptr = proxy_lookup_request.get();
  proxy_lookup_requests_.insert(std::move(proxy_lookup_request));
  proxy_lookup_request_ptr->Start(url);
}

void NetworkContext::ForceReloadProxyConfig(
    ForceReloadProxyConfigCallback callback) {
  net::ConfiguredProxyResolutionService* configured_proxy_resolution_service =
      nullptr;
  if (url_request_context()
          ->proxy_resolution_service()
          ->CastToConfiguredProxyResolutionService(
              &configured_proxy_resolution_service)) {
    configured_proxy_resolution_service->ForceReloadProxyConfig();
  } else {
    LOG(WARNING)
        << "NetworkContext::ForceReloadProxyConfig() had no effect, as the "
           "underlying ProxyResolutionService does not support that concept.";
  }
  std::move(callback).Run();
}

void NetworkContext::ClearBadProxiesCache(
    ClearBadProxiesCacheCallback callback) {
  url_request_context()->proxy_resolution_service()->ClearBadProxiesCache();
  std::move(callback).Run();
}

void NetworkContext::CreateWebSocket(
    const GURL& url,
    const std::vector<std::string>& requested_protocols,
    net::StorageAccessApiStatus storage_access_api_status,
    const net::IsolationInfo& isolation_info,
    std::vector<mojom::HttpHeaderPtr> additional_headers,
    const network::OriginatingProcessId& process_id,
    const url::Origin& origin,
    network::mojom::ClientSecurityStatePtr client_security_state,
    uint32_t options,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
    mojo::PendingRemote<mojom::WebSocketHandshakeClient> handshake_client,
    mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver>
        url_loader_network_observer,
    mojo::PendingRemote<mojom::WebSocketAuthenticationHandler> auth_handler,
    mojo::PendingRemote<mojom::TrustedHeaderClient> header_client,
    const std::optional<base::UnguessableToken>& throttling_profile_id,
    const base::UnguessableToken& network_restrictions_id,
    mojom::IPAddressSpace target_address_space) {
#if BUILDFLAG(ENABLE_WEBSOCKETS)
  if (!websocket_factory_) {
    websocket_factory_ = std::make_unique<WebSocketFactory>(this);
  }

  DCHECK(process_id);

  websocket_factory_->CreateWebSocket(
      url, requested_protocols, storage_access_api_status, isolation_info,
      std::move(additional_headers), process_id, origin,
      std::move(client_security_state), options,
      static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
      std::move(handshake_client), std::move(url_loader_network_observer),
      std::move(auth_handler), std::move(header_client), throttling_profile_id,
      network_restrictions_id, target_address_space);
#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
}

void NetworkContext::CreateWebTransport(
    const GURL& url,
    const url::Origin& origin,
    const net::NetworkAnonymizationKey& key,
    std::vector<mojom::WebTransportCertificateFingerprintPtr> fingerprints,
    const std::vector<std::string>& application_protocols,
    mojom::WebTransportCongestionControl congestion_control,
    std::optional<uint16_t>
        anticipated_concurrent_incoming_unidirectional_streams,
    std::optional<uint16_t>
        anticipated_concurrent_incoming_bidirectional_streams,
    std::vector<net::HttpRequestHeaders::HeaderKeyValuePair> additional_headers,
    mojo::PendingRemote<mojom::WebTransportHandshakeClient>
        pending_handshake_client,
    mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver>
        url_loader_network_observer,
    mojom::ClientSecurityStatePtr client_security_state,
    const base::UnguessableToken& network_restrictions_id) {
  if (!IsNetworkForNetworkRestrictionsIdAndUrlAllowed(network_restrictions_id,
                                                      url, key)) {
    mojo::Remote<mojom::WebTransportHandshakeClient> remote_handshake_client(
        std::move(pending_handshake_client));
    remote_handshake_client->OnHandshakeFailed(
        net::WebTransportError(net::ERR_NETWORK_ACCESS_REVOKED));
    return;
  }
  web_transports_.insert(std::make_unique<WebTransport>(
      url, origin, key, fingerprints, application_protocols, congestion_control,
      anticipated_concurrent_incoming_unidirectional_streams,
      anticipated_concurrent_incoming_bidirectional_streams,
      std::move(additional_headers), this, std::move(pending_handshake_client),
      std::move(url_loader_network_observer),
      std::move(client_security_state)));
}

void NetworkContext::CreateNetLogExporter(
    mojo::PendingReceiver<mojom::NetLogExporter> receiver) {
  net_log_exporter_receivers_.Add(std::make_unique<NetLogExporter>(this),
                                  std::move(receiver));
}

void NetworkContext::ResolveHost(
    mojom::HostResolverHostPtr host,
    const net::NetworkAnonymizationKey& network_anonymization_key,
    mojom::ResolveHostParametersPtr optional_parameters,
    mojo::PendingRemote<mojom::ResolveHostClient> response_client) {
  // Dns request is disallowed if network access is disabled for the partition
  // nonce or the network_restrictions_id.
  // TODO(crbug.com/447954811): For Connection Allowlist DNS lookups, define a
  // less-strict mechanism that matches against the host portion of the URL.
  GURL url = host->is_host_port_pair()
                 ? url::SchemeHostPort(url::kHttpsScheme,
                                       host->get_host_port_pair().host(),
                                       host->get_host_port_pair().port())
                       .GetURL()
                 : host->get_scheme_host_port().GetURL();
  bool is_network_disallowed_for_restrictions_id =
      (optional_parameters &&
       optional_parameters->network_restrictions_id.has_value() &&
       !IsHostResolutionForNetworkRestrictionsIdAndHostAllowed(
           optional_parameters->network_restrictions_id.value(), *host,
           network_anonymization_key));
  if (is_network_disallowed_for_restrictions_id) {
    mojo::Remote<mojom::ResolveHostClient> remote_response_client(
        std::move(response_client));
    remote_response_client->OnComplete(
        net::ERR_NETWORK_ACCESS_REVOKED,
        net::ResolveErrorInfo(net::ERR_NETWORK_ACCESS_REVOKED),
        /*resolved_addresses=*/{},
        /*alternative_endpoints=*/{});
    return;
  }

  if (optional_parameters && optional_parameters->direct_only) {
    auto request = std::make_unique<ProxyCheckingHostResolverRequest>(
        this, std::move(host), network_anonymization_key,
        std::move(optional_parameters), std::move(response_client));
    auto* request_ptr = request.get();
    proxy_checking_host_resolver_requests_.insert(std::move(request));
    request_ptr->Start(url);
    return;
  }

  if (!internal_host_resolver_) {
    internal_host_resolver_ = std::make_unique<HostResolver>(
        url_request_context_->host_resolver(), url_request_context_->net_log());
  }
  internal_host_resolver_->ResolveHost(
      std::move(host), network_anonymization_key,
      std::move(optional_parameters), std::move(response_client));
}

void NetworkContext::CreateHostResolver(
    const std::optional<net::DnsConfigOverrides>& config_overrides,
    mojo::PendingReceiver<mojom::HostResolver> receiver) {
  net::HostResolver* internal_resolver = url_request_context_->host_resolver();
  std::unique_ptr<net::HostResolver> private_internal_resolver;

  if (config_overrides &&
      config_overrides.value() != net::DnsConfigOverrides()) {
    // If custom configuration is needed, create a separate internal resolver
    // with the specified configuration overrides. Because we are using a non-
    // standard resolver, disable the cache.
    //
    // TODO(crbug.com/40577881): Consider allowing per-resolve overrides, so the
    // same net::HostResolver with the same scheduler and cache can be used with
    // different overrides.  But since this is only used for special cases for
    // now, much easier to create entirely separate net::HostResolver instances.
    net::HostResolver::ManagerOptions options;
    options.insecure_dns_mode = net::InsecureDnsMode::kEnabledBuiltIn;
    // Assume additional types are unnecessary for these special cases.
    options.additional_types_via_insecure_dns_enabled = false;
    options.dns_config_overrides = config_overrides.value();
    private_internal_resolver =
        network_service_->host_resolver_factory()->CreateStandaloneResolver(
            url_request_context_->net_log(), std::move(options),
            /* host_mapping_rules */ "", /* enable_caching */ false,
            /* enable_stale */ false);
    private_internal_resolver->SetRequestContext(url_request_context_);
    internal_resolver = private_internal_resolver.get();
  }

  host_resolvers_.emplace(std::make_unique<HostResolver>(
      std::move(receiver),
      base::BindOnce(&NetworkContext::OnHostResolverShutdown,
                     base::Unretained(this)),
      internal_resolver, std::move(private_internal_resolver),
      url_request_context_->net_log()));
}

void NetworkContext::VerifyCertInternal(
    const scoped_refptr<net::X509Certificate>& certificate,
    const net::HostPortPair& host_port,
    const std::string& ocsp_response,
    const std::string& sct_list,
    CTVerificationMode ct_verification_mode,
    VerifyCertCallback callback) {
  uint64_t cert_verify_id = ++next_cert_verify_id_;
  CHECK_NE(0u, next_cert_verify_id_);  // The request ID should not wrap around.
  auto pending_cert_verify = std::make_unique<PendingCertVerify>();
  pending_cert_verify->callback = std::move(callback);
  pending_cert_verify->result = std::make_unique<net::CertVerifyResult>();
  pending_cert_verify->certificate = certificate;
  pending_cert_verify->host_port = host_port;
  net::CertVerifier* cert_verifier =
      g_cert_verifier_for_testing ? g_cert_verifier_for_testing
                                  : url_request_context_->cert_verifier();
  int flags = 0;
  switch (ct_verification_mode) {
    case CTVerificationMode::kTlsCertificate:
      break;
    case CTVerificationMode::kSignedExchange:
      flags = net::CertVerifier::VERIFY_SXG_CT_REQUIREMENTS;
      break;
  }
  int result = cert_verifier->Verify(
      net::CertVerifier::RequestParams(certificate, host_port.host(), flags,
                                       ocsp_response, sct_list),
      pending_cert_verify->result.get(),
      base::BindOnce(&NetworkContext::OnVerifyCertComplete,
                     base::Unretained(this), cert_verify_id),
      &pending_cert_verify->request,
      net::NetLogWithSource::Make(url_request_context_->net_log(),
                                  net::NetLogSourceType::CERT_VERIFIER_JOB));
  cert_verifier_requests_[cert_verify_id] = std::move(pending_cert_verify);

  if (result != net::ERR_IO_PENDING) {
    OnVerifyCertComplete(cert_verify_id, result);
  }
}

void NetworkContext::VerifyCert(
    const scoped_refptr<net::X509Certificate>& certificate,
    const net::HostPortPair& host_port,
    const std::string& ocsp_response,
    const std::string& sct_list,
    VerifyCertCallback callback) {
  VerifyCertInternal(certificate, host_port, ocsp_response, sct_list,
                     CTVerificationMode::kTlsCertificate, std::move(callback));
}

void NetworkContext::VerifyCertForSignedExchange(
    const scoped_refptr<net::X509Certificate>& certificate,
    const net::HostPortPair& host_port,
    const std::string& ocsp_response,
    const std::string& sct_list,
    VerifyCertCallback callback) {
  VerifyCertInternal(certificate, host_port, ocsp_response, sct_list,
                     CTVerificationMode::kSignedExchange, std::move(callback));
}

void NetworkContext::Verify2QwacCertBinding(
    const std::string& binding,
    const std::string& hostname,
    const scoped_refptr<net::X509Certificate>& tls_certificate,
    Verify2QwacCertBindingCallback callback) {
  net::CertVerifier* cert_verifier =
      g_cert_verifier_for_testing ? g_cert_verifier_for_testing
                                  : url_request_context_->cert_verifier();
  cert_verifier->Verify2QwacBinding(
      binding, hostname, tls_certificate, std::move(callback),
      net::NetLogWithSource::Make(
          url_request_context_->net_log(),
          net::NetLogSourceType::CERT_VERIFIER_2QWAC_JOB));
}

void NetworkContext::NotifyExternalCacheHit(const GURL& url,
                                            const std::string& http_method,
                                            const net::NetworkIsolationKey& key,
                                            bool include_credentials) {
  net::HttpCache* cache =
      url_request_context_->http_transaction_factory()->GetCache();
  if (!cache) {
    return;
  }
  cache->OnExternalCacheHit(url, http_method, key, include_credentials);
}

#if BUILDFLAG(IS_ANDROID)
void NetworkContext::SetHttpCacheMaxSize(base::ByteSize http_cache_max_size,
                                         bool force_initialization) {
  // Note: we do not update params_->http_cache_max_size.
  net::HttpCache* cache =
      url_request_context_->http_transaction_factory()->GetCache();
  if (!cache) {
    return;
  }
  cache->SetMaxBytes(http_cache_max_size, force_initialization);
}
#endif  // BUILDFLAG(IS_ANDROID)

void NetworkContext::SetCorsOriginAccessListsForOrigin(
    const url::Origin& source_origin,
    std::vector<mojom::CorsOriginPatternPtr> allow_patterns,
    std::vector<mojom::CorsOriginPatternPtr> block_patterns,
    SetCorsOriginAccessListsForOriginCallback callback) {
  cors_origin_access_list_.SetAllowListForOrigin(source_origin, allow_patterns);
  cors_origin_access_list_.SetBlockListForOrigin(source_origin, block_patterns);
  std::move(callback).Run();
}

void NetworkContext::AddHSTS(const std::string& host,
                             base::Time expiry,
                             bool include_subdomains,
                             AddHSTSCallback callback) {
  net::TransportSecurityState* state =
      url_request_context_->transport_security_state();
  state->AddHSTS(host, expiry, include_subdomains);
  std::move(callback).Run();
}

void NetworkContext::IsHSTSActiveForHost(const std::string& host,
                                         bool is_top_level_nav,
                                         IsHSTSActiveForHostCallback callback) {
  net::TransportSecurityState* security_state =
      url_request_context_->transport_security_state();

  if (!security_state) {
    std::move(callback).Run(false);
    return;
  }

  std::move(callback).Run(
      security_state->ShouldUpgradeToSSL(host, is_top_level_nav));
}

void NetworkContext::GetHSTSState(const std::string& domain,
                                  GetHSTSStateCallback callback) {
  base::DictValue result;

  if (base::IsStringASCII(domain)) {
    net::TransportSecurityState* transport_security_state =
        url_request_context()->transport_security_state();
    if (transport_security_state) {
      net::TransportSecurityState::STSState static_sts_state;
      net::TransportSecurityState::PKPState static_pkp_state;
      bool found_sts_static = transport_security_state->GetStaticSTSState(
          domain, &static_sts_state);
      bool found_pkp_static = transport_security_state->GetStaticPKPState(
          domain, &static_pkp_state);
      if (found_sts_static || found_pkp_static) {
        result.Set("static_upgrade_mode",
                   static_cast<int>(static_sts_state.upgrade_mode));
        result.Set("static_sts_include_subdomains",
                   static_sts_state.include_subdomains);
        result.Set("static_sts_observed",
                   static_sts_state.last_observed.InSecondsFSinceUnixEpoch());
        result.Set("static_sts_expiry",
                   static_sts_state.expiry.InSecondsFSinceUnixEpoch());
        result.Set("static_pkp_include_subdomains",
                   static_pkp_state.include_subdomains);
        result.Set("static_pkp_observed",
                   static_pkp_state.last_observed.InSecondsFSinceUnixEpoch());
        result.Set("static_pkp_expiry",
                   static_pkp_state.expiry.InSecondsFSinceUnixEpoch());
        result.Set("static_spki_hashes",
                   HashesToBase64String(static_pkp_state.spki_hashes));
        result.Set("static_sts_domain", static_sts_state.domain);
        result.Set("static_pkp_domain", static_pkp_state.domain);
      }

      net::TransportSecurityState::STSState dynamic_sts_state;
      net::TransportSecurityState::PKPState dynamic_pkp_state;
      bool found_sts_dynamic = transport_security_state->GetDynamicSTSState(
          domain, &dynamic_sts_state);

      bool found_pkp_dynamic = transport_security_state->GetDynamicPKPState(
          domain, &dynamic_pkp_state);
      if (found_sts_dynamic) {
        result.Set("dynamic_upgrade_mode",
                   static_cast<int>(dynamic_sts_state.upgrade_mode));
        result.Set("dynamic_sts_include_subdomains",
                   dynamic_sts_state.include_subdomains);
        result.Set("dynamic_sts_observed",
                   dynamic_sts_state.last_observed.InSecondsFSinceUnixEpoch());
        result.Set("dynamic_sts_expiry",
                   dynamic_sts_state.expiry.InSecondsFSinceUnixEpoch());
        result.Set("dynamic_sts_domain", dynamic_sts_state.domain);
      }

      if (found_pkp_dynamic) {
        result.Set("dynamic_pkp_include_subdomains",
                   dynamic_pkp_state.include_subdomains);
        result.Set("dynamic_pkp_observed",
                   dynamic_pkp_state.last_observed.InSecondsFSinceUnixEpoch());
        result.Set("dynamic_pkp_expiry",
                   dynamic_pkp_state.expiry.InSecondsFSinceUnixEpoch());
        result.Set("dynamic_spki_hashes",
                   HashesToBase64String(dynamic_pkp_state.spki_hashes));
        result.Set("dynamic_pkp_domain", dynamic_pkp_state.domain);
      }

      result.Set("result", found_sts_static || found_pkp_static ||
                               found_sts_dynamic || found_pkp_dynamic);
    } else {
      result.Set("error", "no TransportSecurityState active");
    }
  } else {
    result.Set("error", "non-ASCII domain name");
  }

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

void NetworkContext::DeleteDynamicDataForHost(
    const std::string& host,
    DeleteDynamicDataForHostCallback callback) {
  net::TransportSecurityState* transport_security_state =
      url_request_context()->transport_security_state();
  if (!transport_security_state) {
    std::move(callback).Run(false);
    return;
  }

  std::move(callback).Run(
      transport_security_state->DeleteDynamicDataForHost(host));
}

void NetworkContext::EnableStaticKeyPinningForTesting(
    EnableStaticKeyPinningForTestingCallback callback) {
  net::TransportSecurityState* state =
      url_request_context_->transport_security_state();
  state->EnableStaticPinsForTesting();
  state->SetPinningListAlwaysTimelyForTesting(true);
  std::move(callback).Run();
}

void NetworkContext::VerifyCertificateForTesting(
    const scoped_refptr<net::X509Certificate>& certificate,
    const std::string& hostname,
    const std::string& ocsp_response,
    const std::string& sct_list,
    VerifyCertificateForTestingCallback callback) {
  net::CertVerifier* cert_verifier = url_request_context_->cert_verifier();

  auto state = std::make_unique<TestVerifyCertState>();
  auto* request = &state->request;
  auto* result = &state->result;

  cert_verifier->Verify(
      net::CertVerifier::RequestParams(certificate.get(), hostname, 0,
                                       ocsp_response, sct_list),
      result,
      base::BindOnce(TestVerifyCertCallback, std::move(state),
                     std::move(callback)),
      request,
      net::NetLogWithSource::Make(net::NetLog::Get(),
                                  net::NetLogSourceType::NONE));
}

void NetworkContext::GetTrustAnchorIDsForTesting(
    GetTrustAnchorIDsForTestingCallback callback) {
  const net::SSLContextConfig& ssl_context_config =
      url_request_context_->ssl_config_service()->GetSSLContextConfig();
  std::vector<std::vector<uint8_t>> all_trust_anchor_ids =
      ssl_context_config.mtc_trust_anchor_ids;
  base::Extend(all_trust_anchor_ids,
               base::ToVector(ssl_context_config.trust_anchor_ids));
  std::move(callback).Run(all_trust_anchor_ids);
}

void NetworkContext::PreconnectSockets(
    uint32_t num_streams,
    const GURL& original_url,
    mojom::CredentialsMode credentials_mode,
    const net::NetworkAnonymizationKey& network_anonymization_key,
    const base::UnguessableToken& network_restrictions_id,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
    const std::optional<net::ConnectionKeepAliveConfig>& keepalive_config,
    mojo::PendingRemote<mojom::ConnectionChangeObserverClient>
        connection_change_observer_client) {
  DCHECK(!require_network_anonymization_key_ ||
         !network_anonymization_key.IsEmpty());

  if (base::FeatureList::IsEnabled(
          net::features::kEarlyCookieLoadOnPreconnect)) {
    net::CookieStore* cookie_store = url_request_context_->cookie_store();
    if (cookie_store) {
      cookie_store->OnPreconnect(original_url);
    }
  }

  GURL url = GetHSTSRedirectForPreconnect(original_url);

  // |PreconnectSockets| may receive arguments from the renderer, which is not
  // guaranteed to validate them.
  if (num_streams == 0) {
    return;
  }

  // Preconnect is disallowed if network access is disabled for the
  // network_restrictions_id.
  if (!IsNetworkForNetworkRestrictionsIdAndUrlAllowed(
          network_restrictions_id, url, network_anonymization_key)) {
    return;
  }

  std::string user_agent;
  if (url_request_context_->http_user_agent_settings()) {
    user_agent =
        url_request_context_->http_user_agent_settings()->GetUserAgent();
  }
  net::HttpRequestInfo request_info;
  request_info.url = url;
  request_info.method = net::HttpRequestHeaders::kGetMethod;
  request_info.extra_headers.SetHeader(net::HttpRequestHeaders::kUserAgent,
                                       user_agent);
  request_info.traffic_annotation = traffic_annotation;

  if (keepalive_config.has_value() ||
      connection_change_observer_client.is_valid()) {
    request_info.connection_management_config =
        net::ConnectionManagementConfig();
    request_info.connection_management_config->keep_alive_config =
        keepalive_config;
    if (connection_change_observer_client.is_valid()) {
      auto change_observer = std::make_unique<ConnectionChangeObserver>(
          std::move(connection_change_observer_client), this);

      request_info.connection_management_config->connection_change_observer =
          change_observer->GetWeakPtr();
      connection_change_observers_.insert(std::move(change_observer));
    }
  }

  switch (credentials_mode) {
    case mojom::CredentialsMode::kOmit:
      request_info.load_flags = net::LOAD_DO_NOT_SAVE_COOKIES;
      request_info.privacy_mode = net::PRIVACY_MODE_ENABLED;
      break;

    case mojom::CredentialsMode::kSameOrigin:
      // Not yet implemented. If you need this credentials mode please update
      // this branch to set the correct request_info fields.
      NOTREACHED() << "kSameOrigin not yet implemented";

    case mojom::CredentialsMode::kInclude:
      request_info.load_flags = net::LOAD_NORMAL;
      request_info.privacy_mode = net::PRIVACY_MODE_DISABLED;
      break;

    case mojom::CredentialsMode::kOmitBug_775438_Workaround:
      request_info.load_flags = net::LOAD_DO_NOT_SAVE_COOKIES;
      request_info.privacy_mode =
          net::PRIVACY_MODE_ENABLED_WITHOUT_CLIENT_CERTS;
      break;
  }

  request_info.network_anonymization_key = network_anonymization_key;

  net::HttpTransactionFactory* factory =
      url_request_context_->http_transaction_factory();
  net::HttpNetworkSession* session = factory->GetSession();
  net::HttpStreamFactory* http_stream_factory = session->http_stream_factory();
  http_stream_factory->PreconnectStreams(
      base::saturated_cast<int32_t>(num_streams), request_info,
      base::OnceClosure());
}

#if BUILDFLAG(IS_P2P_ENABLED)
void NetworkContext::CreateP2PSocketManager(
    const net::NetworkAnonymizationKey& network_anonymization_key,
    mojo::PendingRemote<mojom::P2PTrustedSocketManagerClient> client,
    mojo::PendingReceiver<mojom::P2PTrustedSocketManager>
        trusted_socket_manager,
    mojo::PendingReceiver<mojom::P2PSocketManager> socket_manager_receiver) {
  std::unique_ptr<P2PSocketManager> socket_manager =
      std::make_unique<P2PSocketManager>(
          network_anonymization_key, std::move(client),
          std::move(trusted_socket_manager), std::move(socket_manager_receiver),
          base::BindRepeating(&NetworkContext::DestroySocketManager,
                              base::Unretained(this)),
          url_request_context_);
  socket_managers_[socket_manager.get()] = std::move(socket_manager);
}
#endif  // BUILDFLAG(IS_P2P_ENABLED)

void NetworkContext::CreateMdnsResponder(
    mojo::PendingReceiver<mojom::MdnsResponder> responder_receiver) {
#if BUILDFLAG(ENABLE_MDNS)
  if (!mdns_responder_manager_) {
    mdns_responder_manager_ = std::make_unique<MdnsResponderManager>();
  }

  mdns_responder_manager_->CreateMdnsResponder(std::move(responder_receiver));
#else
  NOTREACHED();
#endif  // BUILDFLAG(ENABLE_MDNS)
}

void NetworkContext::AddDomainReliabilityContextForTesting(
    const url::Origin& origin,
    const GURL& upload_url,
    AddDomainReliabilityContextForTestingCallback callback) {
  auto config = std::make_unique<domain_reliability::DomainReliabilityConfig>();
  config->origin = origin;
  config->include_subdomains = false;
  config->collectors.push_back(std::make_unique<GURL>(upload_url));
  config->success_sample_rate = 1.0;
  config->failure_sample_rate = 1.0;
  domain_reliability_monitor_->AddContextForTesting(std::move(config));
  std::move(callback).Run();
}

void NetworkContext::ForceDomainReliabilityUploadsForTesting(
    ForceDomainReliabilityUploadsForTestingCallback callback) {
  domain_reliability_monitor_->ForceUploadsForTesting();
  std::move(callback).Run();
}

void NetworkContext::SetSplitAuthCacheByNetworkAnonymizationKey(
    bool split_auth_cache_by_network_anonymization_key) {
  url_request_context_->http_transaction_factory()
      ->GetSession()
      ->http_auth_cache()
      ->SetKeyServerEntriesByNetworkAnonymizationKey(
          split_auth_cache_by_network_anonymization_key);
}

void NetworkContext::SaveHttpAuthCacheProxyEntries(
    SaveHttpAuthCacheProxyEntriesCallback callback) {
  net::HttpAuthCache* http_auth_cache =
      url_request_context_->http_transaction_factory()
          ->GetSession()
          ->http_auth_cache();
  base::UnguessableToken cache_key =
      network_service_->http_auth_cache_proxy_copier()->SaveHttpAuthCache(
          *http_auth_cache);
  std::move(callback).Run(cache_key);
}

void NetworkContext::LoadHttpAuthCacheProxyEntries(
    const base::UnguessableToken& cache_key,
    LoadHttpAuthCacheProxyEntriesCallback callback) {
  net::HttpAuthCache* http_auth_cache =
      url_request_context_->http_transaction_factory()
          ->GetSession()
          ->http_auth_cache();
  network_service_->http_auth_cache_proxy_copier()->LoadHttpAuthCache(
      cache_key, http_auth_cache);
  std::move(callback).Run();
}

void NetworkContext::AddAuthCacheEntry(
    const net::AuthChallengeInfo& challenge,
    const net::NetworkAnonymizationKey& network_anonymization_key,
    const net::AuthCredentials& credentials,
    AddAuthCacheEntryCallback callback) {
  net::HttpAuthCache* http_auth_cache =
      url_request_context_->http_transaction_factory()
          ->GetSession()
          ->http_auth_cache();
  http_auth_cache->Add(challenge.challenger,
                       challenge.is_proxy ? net::HttpAuth::AUTH_PROXY
                                          : net::HttpAuth::AUTH_SERVER,
                       challenge.realm,
                       net::HttpAuth::StringToScheme(challenge.scheme),
                       network_anonymization_key, challenge.challenge,
                       credentials, challenge.path);
  std::move(callback).Run();
}

void NetworkContext::SetCorsNonWildcardRequestHeadersSupport(bool value) {
  if (!base::FeatureList::IsEnabled(
          features::kCorsNonWildcardRequestHeadersSupport)) {
    return;
  }
  cors_non_wildcard_request_headers_support_ =
      cors::NonWildcardRequestHeadersSupport(value);
}

#if BUILDFLAG(IS_CHROMEOS)
void NetworkContext::LookupProxyAuthCredentials(
    const net::ProxyServer& proxy_server,
    const std::string& auth_scheme,
    const std::string& realm,
    LookupProxyAuthCredentialsCallback callback) {
  net::HttpAuth::Scheme net_scheme =
      net::HttpAuth::StringToScheme(base::ToLowerASCII(auth_scheme));
  if (net_scheme == net::HttpAuth::Scheme::AUTH_SCHEME_MAX) {
    std::move(callback).Run(std::nullopt);
    return;
  }
  net::HttpAuthCache* http_auth_cache =
      url_request_context_->http_transaction_factory()
          ->GetSession()
          ->http_auth_cache();
  // TODO(crbug.com/40704785): Mapping proxy addresses to URLs is a
  // lossy conversion, shouldn't do this.
  const char* scheme =
      proxy_server.is_secure_http_like() ? "https://" : "http://";
  url::SchemeHostPort scheme_host_port(
      GURL(scheme + proxy_server.host_port_pair().ToString()));
  if (!scheme_host_port.IsValid()) {
    std::move(callback).Run(std::nullopt);
    return;
  }

  //  Unlike server credentials, proxy credentials are not keyed on
  //  NetworkAnonymizationKey.
  net::HttpAuthCache::Entry* entry = http_auth_cache->Lookup(
      scheme_host_port, net::HttpAuth::AUTH_PROXY, realm, net_scheme,
      net::NetworkAnonymizationKey());
  if (entry) {
    std::move(callback).Run(entry->credentials());
  } else {
    std::move(callback).Run(std::nullopt);
  }
}
#endif  // BUILDFLAG(IS_CHROMEOS)

const net::HttpAuthPreferences* NetworkContext::GetHttpAuthPreferences() const {
  return &http_auth_merged_preferences_;
}

size_t NetworkContext::NumOpenWebTransports() const {
  return std::ranges::count(web_transports_, false, &WebTransport::torn_down);
}

WebTransport* NetworkContext::GetWebTransportForTesting() {
  CHECK_EQ(web_transports_.size(), 1u);
  return web_transports_.begin()->get();
}

size_t NetworkContext::CountURLLoaderFactoriesBoundToNetworkForTesting(
    net::handles::NetworkHandle target_network) const {
  return std::ranges::count_if(
      url_loader_factories_, [target_network](const auto& factory) {
        return factory->GetBoundNetworkForTesting() == target_network;
      });
}

void NetworkContext::OnHttpAuthDynamicParamsChanged(
    const mojom::HttpAuthDynamicParams*
        http_auth_dynamic_network_service_params) {
  http_auth_merged_preferences_.SetServerAllowlist(
      http_auth_dynamic_network_service_params->server_allowlist);
  http_auth_merged_preferences_.SetDelegateAllowlist(
      http_auth_dynamic_network_service_params->delegate_allowlist);
  http_auth_merged_preferences_.set_delegate_by_kdc_policy(
      http_auth_dynamic_network_service_params->delegate_by_kdc_policy);
  http_auth_merged_preferences_.set_negotiate_disable_cname_lookup(
      http_auth_dynamic_network_service_params->negotiate_disable_cname_lookup);
  http_auth_merged_preferences_.set_negotiate_enable_port(
      http_auth_dynamic_network_service_params->enable_negotiate_port);
  http_auth_merged_preferences_.set_basic_over_http_enabled(
      http_auth_dynamic_network_service_params->basic_over_http_enabled);
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
  http_auth_merged_preferences_.set_ntlm_v2_enabled(
      http_auth_dynamic_network_service_params->ntlm_v2_enabled);
#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)

#if BUILDFLAG(IS_ANDROID)
  http_auth_merged_preferences_.set_auth_android_negotiate_account_type(
      http_auth_dynamic_network_service_params->android_negotiate_account_type);
#endif  // BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
  http_auth_merged_preferences_.set_allow_gssapi_library_load(
      http_auth_dynamic_network_service_params->allow_gssapi_library_load);
#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
  if (http_auth_dynamic_network_service_params->allowed_schemes.has_value()) {
    http_auth_merged_preferences_.SetAllowedSchemes(base::flat_set<std::string>(
        http_auth_dynamic_network_service_params->allowed_schemes->begin(),
        http_auth_dynamic_network_service_params->allowed_schemes->end()));
  } else {
    http_auth_merged_preferences_.SetAllowedSchemes(std::nullopt);
  }

  url_matcher_ = std::make_unique<url_matcher::URLMatcher>();
  url_matcher::util::AddAllowFiltersWithLimit(
      url_matcher_.get(), http_auth_dynamic_network_service_params
                              ->patterns_allowed_to_use_all_schemes);
  http_auth_merged_preferences_.set_http_auth_scheme_filter(
      base::BindRepeating(&NetworkContext::IsAllowedToUseAllHttpAuthSchemes,
                          base::Unretained(this)));
}

URLRequestContextOwner NetworkContext::MakeURLRequestContext(
    mojo::PendingRemote<mojom::URLLoaderFactory>
        url_loader_factory_for_cert_net_fetcher,
    scoped_refptr<SessionCleanupCookieStore> session_cleanup_cookie_store,
    OnURLRequestContextBuilderConfiguredCallback
        on_url_request_context_builder_configured,
    net::handles::NetworkHandle bound_network) {
  URLRequestContextBuilderMojo builder;
  const base::CommandLine* command_line =
      base::CommandLine::ForCurrentProcess();

  bool is_network_bound = bound_network != net::handles::kInvalidNetworkHandle;
  if (is_network_bound) {
    builder.BindToNetwork(bound_network);
  }

  std::unique_ptr<net::CertVerifier> cert_verifier;
  if (g_cert_verifier_for_testing) {
    cert_verifier = std::make_unique<WrappedTestingCertVerifier>();
  } else {
    DCHECK(params_->cert_verifier_params);
    // base::Unretained() is safe below because |this| will own
    // |cert_verifier|.
    // TODO(crbug.com/40693524): this cert verifier should deal with
    // disconnections if the CertVerifierService is run outside of the browser
    // process.
    cert_verifier = std::make_unique<cert_verifier::MojoCertVerifier>(
        std::move(params_->cert_verifier_params->cert_verifier_service),
        std::move(params_->cert_verifier_params
                      ->cert_verifier_service_client_receiver),
        std::move(url_loader_factory_for_cert_net_fetcher),
        base::BindRepeating(
            &NetworkContext::CreateURLLoaderFactoryForCertNetFetcher,
            base::Unretained(this)));

    // Whether the cert verifier is remote or in-process, we should wrap it in
    // caching and coalescing layers to avoid extra verifications and IPCs.
    cert_verifier = std::make_unique<net::CachingCertVerifier>(
        std::make_unique<net::CoalescingCertVerifier>(
            std::move(cert_verifier)));
  }

  builder.SetCertVerifier(IgnoreErrorsCertVerifier::MaybeWrapCertVerifier(
      *command_line, nullptr, std::move(cert_verifier)));

#if BUILDFLAG(IS_CT_SUPPORTED)
  builder.set_sct_auditing_delegate(
      std::make_unique<SCTAuditingDelegate>(weak_factory_.GetWeakPtr()));
#endif  // BUILDFLAG(IS_CT_SUPPORTED)

  std::unique_ptr<NetworkServiceNetworkDelegate> network_delegate =
      std::make_unique<NetworkServiceNetworkDelegate>(
          params_->enable_referrers,
          params_->validate_referrer_policy_on_initial_request,
          std::move(params_->proxy_error_client), this);
  network_delegate_ = network_delegate.get();
  builder.set_network_delegate(std::move(network_delegate));

  if (params_->initial_custom_proxy_config ||
      params_->custom_proxy_config_client_receiver) {
    builder.set_proxy_delegate(std::make_unique<NetworkServiceProxyDelegate>(
        std::move(params_->initial_custom_proxy_config),
        std::move(params_->custom_proxy_config_client_receiver),
        std::move(params_->custom_proxy_connection_observer_remote)));
  }

  net::NetLog* net_log = nullptr;
  if (network_service_) {
    net_log = network_service_->net_log();
    builder.set_net_log(net_log);
    if (!is_network_bound) {
      // Network bound URLRequestContexts build and configure their own special
      // HostResolverManager and HostResolver. So, don't inject the
      // NetworkService one even if NetworkService is enabled.
      builder.set_host_resolver_manager(
          network_service_->host_resolver_manager());
      builder.set_host_resolver_factory(
          network_service_->host_resolver_factory());
    }
    builder.SetHttpAuthHandlerFactory(
        network_service_->CreateHttpAuthHandlerFactory(this));
    builder.set_network_quality_estimator(
        network_service_->network_quality_estimator());
  }
  trust_token_store_ = std::make_unique<PendingTrustTokenStore>();

  base::FilePath trust_token_path;
  if (GetFullDataFilePath(
          params_->file_paths,
          &network::mojom::NetworkContextFilePaths::trust_token_database_name,
          trust_token_path)) {
    SQLiteTrustTokenPersister::CreateForFilePath(
        base::ThreadPool::CreateSequencedTaskRunner(
            {base::MayBlock(), kTrustTokenDatabaseTaskPriority,
             base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
        trust_token_path, kTrustTokenWriteBufferingWindow,
        base::BindOnce(&NetworkContext::FinishConstructingTrustTokenStore,
                       weak_factory_.GetWeakPtr()));
  } else {
    trust_token_store_->OnStoreReady(std::make_unique<TrustTokenStore>(
        std::make_unique<InMemoryTrustTokenPersister>(),
        std::make_unique<ExpiryInspectingRecordExpiryDelegate>(
            network_service()->trust_token_key_commitments())));
  }

  std::unique_ptr<net::StaticHttpUserAgentSettings> user_agent_settings =
      std::make_unique<net::StaticHttpUserAgentSettings>(
          params_->accept_language, params_->user_agent);
  // Borrow an alias for future use before giving the builder ownership.
  user_agent_settings_ = user_agent_settings.get();
  builder.set_http_user_agent_settings(std::move(user_agent_settings));

  builder.set_enable_brotli(params_->enable_brotli);
  builder.set_enable_zstd(params_->enable_zstd);

  if (params_->proxy_resolver_factory) {
    builder.SetMojoProxyResolverFactory(
        std::move(params_->proxy_resolver_factory));
  }

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
  if (params_->system_proxy_resolver) {
    builder.SetMojoSystemProxyResolver(
        std::move(params_->system_proxy_resolver));
  }
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)

#if BUILDFLAG(IS_CHROMEOS)
  if (params_->dhcp_wpad_url_client) {
    builder.SetDhcpWpadUrlClient(std::move(params_->dhcp_wpad_url_client));
  }
#endif  // BUILDFLAG(IS_CHROMEOS)

  if (!params_->http_cache_enabled) {
    builder.DisableHttpCache();
  } else {
    net::URLRequestContextBuilder::HttpCacheParams cache_params;
    cache_params.max_size = params_->http_cache_max_size;
    if (IsIncognitoFromParams(*params_)) {
      cache_params.type =
          net::URLRequestContextBuilder::HttpCacheParams::IN_MEMORY;
    } else {
      // Network-bound NetworkContexts should not persist state on disk.
      CHECK(!is_network_bound);

      cache_params.path = params_->file_paths->http_cache_directory->path();
      if (params_->file_paths->no_vary_search_directory) {
        cache_params.no_vary_search_path =
            params_->file_paths->no_vary_search_directory->path();
      }
      if (params_->file_paths->logical_invalidation_directory) {
        cache_params.logical_invalidation_path =
            params_->file_paths->logical_invalidation_directory->path();
      }
      cache_params.type = network_session_configurator::ChooseCacheType();

      if (params_->http_cache_file_operations_factory) {
        cache_params.file_operations_factory =
            base::MakeRefCounted<MojoBackendFileOperationsFactory>(
                std::move(params_->http_cache_file_operations_factory));
      }

      if (params_->enable_encrypted_http_cache) {
#if BUILDFLAG(ENTERPRISE_CACHE_ENCRYPTION)
        // For enterprise users, we always use simple backend when encryption is
        // enabled.
        cache_params.type =
            net::URLRequestContextBuilder::HttpCacheParams::DISK_SIMPLE;

        // For enterprise users, we wrap `BackendFileOperations` to intercept
        // file I/O with encryption ops, and create and pass in a
        // `CacheEncryptionDelegate` to encrypt/decrypt cache entries on disk.
        if (params_->encryption_provider) {
          std::unique_ptr<net::CacheEncryptionDelegate>
              cache_encryption_delegate = std::make_unique<
                  enterprise_encryption::OSCryptCacheEncryptionDelegate>(
                  std::move(params_->encryption_provider));
          if (!cache_params.file_operations_factory) {
            // Since it's the fallback later anyways, explicitly created here to
            // wrap. EncryptedBackendFileOperationsFactory will wrap this later
            // when os_crypt_cache_encryption_delegate is created and
            // initialized.
            cache_params.file_operations_factory = base::MakeRefCounted<
                disk_cache::TrivialFileOperationsFactory>();
          }
          builder.set_cache_encryption_delegate(
              std::move(cache_encryption_delegate));
        }
#else
        NOTREACHED();
#endif  // BUILDFLAG(ENTERPRISE_CACHE_ENCRYPTION)
      }
    }
    cache_params.reset_cache = params_->reset_http_cache_backend;

#if BUILDFLAG(IS_ANDROID)
    app_status_listeners_.push_back(
        std::make_unique<NetworkContextApplicationStatusListener>(
            cache_params.app_status_listener_getter));
#endif  // BUILDFLAG(IS_ANDROID)
    builder.EnableHttpCache(cache_params);
  }

  std::unique_ptr<net::EchModeGetter> ech_mode_getter;
  if (params_->use_platform_ech_policy) {
#if BUILDFLAG(IS_ANDROID)
    ech_mode_getter = std::make_unique<net::EchModeGetterAndroid>();
#endif
  }
  std::unique_ptr<SSLConfigServiceMojo> ssl_config_service =
      std::make_unique<SSLConfigServiceMojo>(
          std::move(params_->initial_ssl_config),
          std::move(params_->ssl_config_client_receiver),
          std::move(ech_mode_getter));
  SSLConfigServiceMojo* ssl_config_service_raw = ssl_config_service.get();
  builder.set_ssl_config_service(std::move(ssl_config_service));

  if (!params_->initial_proxy_config &&
      !params_->proxy_config_client_receiver.is_valid()) {
    params_->initial_proxy_config =
        net::ProxyConfigWithAnnotation::CreateDirect();
  }
  builder.set_proxy_config_service(std::make_unique<ProxyConfigServiceMojo>(
      std::move(params_->proxy_config_client_receiver),
      std::move(params_->initial_proxy_config),
      std::move(params_->proxy_config_poller_client)));
  builder.set_pac_quick_check_enabled(params_->pac_quick_check_enabled);

  std::unique_ptr<PrefService> pref_service;

  base::FilePath http_server_properties_file_name;
  if (GetFullDataFilePath(params_->file_paths,
                          &network::mojom::NetworkContextFilePaths::
                              http_server_properties_file_name,
                          http_server_properties_file_name)) {
    // Network-bound NetworkContexts should not persist state on disk.
    CHECK(!is_network_bound);
    scoped_refptr<JsonPrefStore> json_pref_store(new JsonPrefStore(
        http_server_properties_file_name, nullptr,
        base::ThreadPool::CreateSequencedTaskRunner(
            {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN,
             base::TaskPriority::BEST_EFFORT})));
    PrefServiceFactory pref_service_factory;
    pref_service_factory.set_user_prefs(json_pref_store);
    pref_service_factory.set_async(true);
    scoped_refptr<PrefRegistrySimple> pref_registry(new PrefRegistrySimple());
    HttpServerPropertiesPrefDelegate::RegisterPrefs(pref_registry.get());
    NetworkQualitiesPrefDelegate::RegisterPrefs(pref_registry.get());
    KnownLegacyScopeDomainsPrefDelegate::RegisterPrefs(pref_registry.get());
    pref_service = pref_service_factory.Create(pref_registry.get());

    builder.SetHttpServerProperties(std::make_unique<net::HttpServerProperties>(
        std::make_unique<HttpServerPropertiesPrefDelegate>(pref_service.get()),
        net_log));

    network_qualities_pref_delegate_ =
        std::make_unique<NetworkQualitiesPrefDelegate>(
            pref_service.get(), network_service_->network_quality_estimator());
  }

  if (session_cleanup_cookie_store) {
    // If the pref service was registered and initialized use it.
    // If not, use nullptr to indicate prefs aren't available.
    std::unique_ptr<net::CookieMonster> cookie_store =
        std::make_unique<net::CookieMonster>(
            session_cleanup_cookie_store.get(), net_log,
            pref_service
                ? std::make_unique<KnownLegacyScopeDomainsPrefDelegate>(
                      pref_service.get())
                : nullptr);
    if (params_->persist_session_cookies) {
      cookie_store->SetPersistSessionCookies(true);
    }

    builder.SetCookieStore(std::move(cookie_store));
  }

  base::FilePath transport_security_persister_file_name;
  if (GetFullDataFilePath(params_->file_paths,
                          &network::mojom::NetworkContextFilePaths::
                              transport_security_persister_file_name,
                          transport_security_persister_file_name)) {
    // Network-bound NetworkContexts should not persist state on disk.
    CHECK(!is_network_bound);
    builder.set_transport_security_persister_file_path(
        transport_security_persister_file_name);
  }
  builder.set_hsts_policy_bypass_list(params_->hsts_policy_bypass_list);

#if BUILDFLAG(ENABLE_REPORTING)
  bool reporting_enabled = base::FeatureList::IsEnabled(features::kReporting);
  if (reporting_enabled) {
    auto reporting_policy = net::ReportingPolicy::Create();
    if (params_->reporting_delivery_interval) {
      reporting_policy->delivery_interval =
          *params_->reporting_delivery_interval;
      reporting_policy->endpoint_backoff_policy.initial_delay_ms =
          params_->reporting_delivery_interval->InMilliseconds();
    }
    builder.set_reporting_policy(std::move(reporting_policy));
  } else {
    builder.set_reporting_policy(nullptr);
  }

  bool nel_enabled =
      base::FeatureList::IsEnabled(features::kNetworkErrorLogging);
  builder.set_network_error_logging_enabled(nel_enabled);

  base::FilePath reporting_and_nel_store_database_name;
  if (GetFullDataFilePath(params_->file_paths,
                          &network::mojom::NetworkContextFilePaths::
                              reporting_and_nel_store_database_name,
                          reporting_and_nel_store_database_name) &&
      (reporting_enabled || nel_enabled)) {
    // Network-bound NetworkContexts should not persist state on disk.
    CHECK(!is_network_bound);
    scoped_refptr<base::SequencedTaskRunner> client_task_runner =
        base::SingleThreadTaskRunner::GetCurrentDefault();
    scoped_refptr<base::SequencedTaskRunner> background_task_runner =
        base::ThreadPool::CreateSequencedTaskRunner(
            {base::MayBlock(),
             net::GetReportingAndNelStoreBackgroundSequencePriority(),
             base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
    std::unique_ptr<net::SQLitePersistentReportingAndNelStore> sqlite_store(
        new net::SQLitePersistentReportingAndNelStore(
            reporting_and_nel_store_database_name, client_task_runner,
            background_task_runner));
    builder.set_persistent_reporting_and_nel_store(std::move(sqlite_store));
  } else {
    builder.set_persistent_reporting_and_nel_store(nullptr);
  }
#endif  // BUILDFLAG(ENABLE_REPORTING)

  net::HttpNetworkSessionParams session_params;
  bool is_quic_force_disabled = false;
  if (network_service_ && network_service_->quic_disabled()) {
    is_quic_force_disabled = true;
  }

  auto quic_context = std::make_unique<net::QuicContext>();
  if (params_->quic_idle_connection_timeout_seconds &&
      params_->quic_idle_connection_timeout_seconds.value() >= 0) {
    quic_context->params()->idle_connection_timeout =
        base::Seconds(params_->quic_idle_connection_timeout_seconds.value());
  }
  network_session_configurator::ParseCommandLineAndFieldTrials(
      *base::CommandLine::ForCurrentProcess(), is_quic_force_disabled,
      &session_params, quic_context->params());

  session_params.key_auth_cache_server_entries_by_network_anonymization_key =
      params_->split_auth_cache_by_network_anonymization_key;

  builder.set_http_network_session_params(session_params);
  builder.set_quic_context(std::move(quic_context));

  if (params_->shared_dictionary_enabled) {
    builder.set_enable_shared_dictionary(true);
    builder.set_enable_shared_zstd(true);
  }

#if BUILDFLAG(ENABLE_REPORTING)
  if (base::FeatureList::IsEnabled(
          features::kReportingApiEnableVariationsHeaders)) {
    builder.set_prepare_upload_request_callback(base::BindRepeating(
        &NetworkContext::AddVariationsHeadersToReportingRequest,
        // `this` outlives the URLRequestContext that owns the ReportingService,
        // which owns the ReportingUploader that calls this callback.
        base::Unretained(this)));
  }
#endif

  builder.SetWrapHttpNetworkLayerCallback(
      base::BindOnce([](std::unique_ptr<net::HttpNetworkLayer> network_layer)
                         -> std::unique_ptr<net::HttpTransactionFactory> {
        return std::make_unique<ThrottlingNetworkTransactionFactory>(
            std::move(network_layer));
      }));

  if (command_line->HasSwitch(switches::kHostResolverRules)) {
    builder.set_host_mapping_rules(
        command_line->GetSwitchValueASCII(switches::kHostResolverRules));
  } else if (command_line->HasSwitch(switches::kHostRules)) {
    LOG(WARNING)
        << "The --" << switches::kHostRules
        << " command line switch is deprecated, and now has the same behavior "
           "as --"
        << switches::kHostResolverRules << ". Use --"
        << switches::kHostResolverRules
        << " directly to avoid this LOG message. If you get certificate error "
           "where --"
        << switches::kHostRules
        << " previously did not give one, you need to ensure the server has a "
           "valid certificate for the mapped hostname, not the destination "
           "hostname.";
    builder.set_host_mapping_rules(
        command_line->GetSwitchValueASCII(switches::kHostRules));
  }

#if BUILDFLAG(IS_WIN)
  if (params_->socket_brokers) {
    builder.set_client_socket_factory(
        std::make_unique<BrokeredClientSocketFactory>(
            std::move(params_->socket_brokers->client)));
  }
#endif

  require_network_anonymization_key_ =
      params_->require_network_anonymization_key;

  // If `require_network_anonymization_key_` is true, but the features that can
  // trigger another URLRequest are not set to respect NetworkAnonymizationKeys,
  // the URLRequests that they create might not have a NAK, so only set the
  // corresponding value in the URLRequestContext to true at the URLRequest
  // layer if all those features are set to respect NAK. The Domain Reliability
  // feature, which is partitioned by NIK instead of NAK, triggers creation of
  // URLRequests as well, so also check `net::HttpCache::IsSplitCacheEnabled()`.
  if (require_network_anonymization_key_ &&
      net::NetworkAnonymizationKey::IsPartitioningEnabled() &&
      net::HttpCache::IsSplitCacheEnabled()) {
    builder.set_require_network_anonymization_key(true);
  }

#if BUILDFLAG(IS_ANDROID)
  builder.set_check_cleartext_permitted(params_->check_clear_text_permitted);
#endif  // BUILDFLAG(IS_ANDROID)

  if (params_->device_bound_sessions_enabled) {
    builder.set_has_device_bound_session_service(true);
    builder.set_device_bound_sessions_restricted_sites(
        params_->device_bound_sessions_restricted_sites);

#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
    if (base::FeatureList::IsEnabled(
            net::features::kDeviceBoundSessionsForSingleSignOn)) {
      builder.set_device_bound_sessions_cookie_access_callback(
          base::BindRepeating(
              &NetworkContext::HasCookieAccessForDeviceBoundSession,
              // `this` outlives the `URLRequestContext` that owns the
              // device_bound_sessions::SessionService which calls this
              // callback.
              base::Unretained(this)));
    }
    device_bound_session_service_delegate_ =
        std::make_unique<DeviceBoundSessionServiceDelegate>(
            std::move(params_->device_bound_sessions_network_observer));
    builder.set_device_bound_sessions_client_cert_handler(base::BindRepeating(
        &DeviceBoundSessionServiceDelegate::SelectClientCertificate,
        base::Unretained(device_bound_session_service_delegate_.get())));
    if (params_->bound_sessions_unexportable_key_service.is_valid()) {
      builder.set_unexportable_key_service(
          std::make_unique<unexportable_keys::UnexportableKeyServiceProxied>(
              std::move(params_->bound_sessions_unexportable_key_service)));
    }
#endif

    base::FilePath device_bound_sessions_file_path;
    if (GetFullDataFilePath(params_->file_paths,
                            &network::mojom::NetworkContextFilePaths::
                                device_bound_sessions_database_name,
                            device_bound_sessions_file_path)) {
      // Network-bound NetworkContexts should not persist state on disk.
      CHECK(!is_network_bound);
      builder.set_device_bound_sessions_file_path(
          device_bound_sessions_file_path);
    }
  }

#if BUILDFLAG(IS_ANDROID)
  builder.enable_stale_dns_resolver(params_->stale_dns_enabled);
#endif  // BUILDFLAG(IS_ANDROID)

  if (on_url_request_context_builder_configured) {
    std::move(on_url_request_context_builder_configured).Run(&builder);
  }
  auto result =
      URLRequestContextOwner(std::move(pref_service), builder.Build());

  // Subscribe the CertVerifier to configuration changes that are exposed via
  // the mojom::SSLConfig, but which are not part of the
  // net::SSLConfig[Service] interfaces.
  ssl_config_service_raw->SetCertVerifierForConfiguring(
      result.url_request_context->cert_verifier());

  // Attach some things to the URLRequestContextBuilder's
  // TransportSecurityState.  Since no requests have been made yet, safe to do
  // this even after the call to Build().

  if (network_service_->pins_list_updated()) {
    result.url_request_context->transport_security_state()->UpdatePinList(
        network_service_->pinsets(), network_service_->host_pins(),
        network_service_->pins_list_update_time());
  }

#if BUILDFLAG(IS_CT_SUPPORTED)
  if (params_->enforce_chrome_ct_policy) {
    require_ct_delegate_ = base::MakeRefCounted<
        certificate_transparency::ChromeRequireCTDelegate>();
    result.url_request_context->transport_security_state()
        ->SetRequireCTDelegate(require_ct_delegate_);
  }
#endif  // BUILDFLAG(IS_CT_SUPPORTED)

  if (params_->enable_domain_reliability) {
    domain_reliability_monitor_ =
        std::make_unique<domain_reliability::DomainReliabilityMonitor>(
            result.url_request_context.get(),
            params_->domain_reliability_upload_reporter,
            base::BindRepeating(&NetworkContext::CanUploadDomainReliability,
                                base::Unretained(this)));
    domain_reliability_monitor_->AddBakedInConfigs();
    domain_reliability_monitor_->SetDiscardUploads(
        params_->discard_domain_reliablity_uploads);
  }

  return result;
}

scoped_refptr<SessionCleanupCookieStore>
NetworkContext::MakeSessionCleanupCookieStore() const {
  base::FilePath cookie_path;
  if (!GetFullDataFilePath(
          params_->file_paths,
          &network::mojom::NetworkContextFilePaths::cookie_database_name,
          cookie_path)) {
    DCHECK(!params_->restore_old_session_cookies);
    DCHECK(!params_->persist_session_cookies);
    return nullptr;
  }
  scoped_refptr<base::SequencedTaskRunner> client_task_runner =
      base::SingleThreadTaskRunner::GetCurrentDefault();
  scoped_refptr<base::SequencedTaskRunner> background_task_runner =
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), net::GetCookieStoreBackgroundSequencePriority(),
           base::TaskShutdownBehavior::BLOCK_SHUTDOWN});

  std::unique_ptr<net::CookieCryptoDelegate> crypto_delegate = nullptr;

  if (params_->enable_encrypted_cookies) {
    if (params_->cookie_encryption_provider) {
      crypto_delegate = std::make_unique<CookieOSCryptAsyncDelegate>(
          std::move(params_->cookie_encryption_provider));
    } else {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
      // A cookie crypto delegate should not be created on Android or iOS
      // to match the behavior of cookie_config::GetCookieCryptoDelegate().
      // See https://crbug.com/449652881
      NOTREACHED();
#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
    }
  }

#if BUILDFLAG(IS_WIN)
  const bool enable_exclusive_access =
      network_service()->exclusive_cookie_database_locking();
#else
  const bool enable_exclusive_access = false;
#endif  // BUILDFLAG(IS_WIN)

  scoped_refptr<net::SQLitePersistentCookieStore> sqlite_store(
      new net::SQLitePersistentCookieStore(
          cookie_path, client_task_runner, background_task_runner,
          params_->restore_old_session_cookies, std::move(crypto_delegate),
          enable_exclusive_access));

  return base::MakeRefCounted<SessionCleanupCookieStore>(sqlite_store);
}

void NetworkContext::OnHttpCacheCleared(base::OnceClosure callback,
                                        HttpCacheDataRemover* remover) {
  bool removed = false;
  for (auto iter = http_cache_data_removers_.begin();
       iter != http_cache_data_removers_.end(); ++iter) {
    if (iter->get() == remover) {
      removed = true;
      http_cache_data_removers_.erase(iter);
      break;
    }
  }
  DCHECK(removed);
  std::move(callback).Run();
}

void NetworkContext::OnHostResolverShutdown(HostResolver* resolver) {
  auto found_resolver = host_resolvers_.find(resolver);
  CHECK(found_resolver != host_resolvers_.end());
  host_resolvers_.erase(found_resolver);
}

void NetworkContext::OnHttpCacheSizeComputed(
    ComputeHttpCacheSizeCallback callback,
    HttpCacheDataCounter* counter,
    bool is_upper_limit,
    int64_t result_or_error) {
  std::erase_if(http_cache_data_counters_, base::MatchesUniquePtr(counter));
  std::move(callback).Run(is_upper_limit, result_or_error);
}

void NetworkContext::OnConnectionError() {
  // If owned by the network service, this call will delete |this|.
  if (on_connection_close_callback_) {
    std::move(on_connection_close_callback_).Run(this);
  }
}

GURL NetworkContext::GetHSTSRedirectForPreconnect(const GURL& original_url) {
  // TODO(lilyhoughton) This needs to be gotten rid of once explicit
  // construction with a URLRequestContext is no longer supported.

  if (!url_request_context_->transport_security_state()) {
    RecordHSTSPreconnectUpgradeReason(
        HSTSRedirectUpgradeReason::kNotUpgradedHSTSUnavailable);
    return original_url;
  }

  if (!original_url.SchemeIs(url::kHttpScheme)) {
    RecordHSTSPreconnectUpgradeReason(
        HSTSRedirectUpgradeReason::kNotUpgradedNotHTTP);
    return original_url;
  }

  // TODO(crbug.com/40725781); This currently doesn't allow for preconnect
  // upgrades when kHstsTopLevelNavigationsOnly is enabled. Revisit this
  // decision after analyzing Net.PreconnectHSTSUpgradesUrl metric.
  //
  // When kHstsTopLevelNavigationsOnly is enabled only top-level navigations
  // will be eligable for HSTS upgrades (not sub-resource requests).
  // Unfortunately we don't know at this point if this request is for a
  // top-level navigation so we need to disallow HSTS upgrades for every
  // preconnect.
  if (!url_request_context_->transport_security_state()->ShouldUpgradeToSSL(
          original_url.GetHost(), /*is_top_level_nav=*/false)) {
    RecordHSTSPreconnectUpgradeReason(
        HSTSRedirectUpgradeReason::kNotUpgradedNoHSTSPin);
    return original_url;
  }

  RecordHSTSPreconnectUpgradeReason(HSTSRedirectUpgradeReason::kUpgraded);

  GURL::Replacements replacements;
  replacements.SetSchemeStr("https");
  return original_url.ReplaceComponents(replacements);
}

#if BUILDFLAG(IS_P2P_ENABLED)
void NetworkContext::DestroySocketManager(P2PSocketManager* socket_manager) {
  auto iter = socket_managers_.find(socket_manager);
  CHECK(iter != socket_managers_.end());
  socket_managers_.erase(iter);
}
#endif  // BUILDFLAG(IS_P2P_ENABLED)

void NetworkContext::CanUploadDomainReliability(
    const url::Origin& origin,
    base::OnceCallback<void(bool)> callback) {
  // If the NetworkContextClient hasn't been set yet or has disconnected for
  // some reason, just return `false`. This could occur in the case of CCT for
  // captive portal -- see crbug.com/446496025 for more details, do the similar
  // check to CanSendSCTAuditingReport().
  if (!client_) {
    std::move(callback).Run(false);
    return;
  }
  client_->OnCanSendDomainReliabilityUpload(
      origin,
      base::BindOnce([](base::OnceCallback<void(bool)> callback,
                        bool allowed) { std::move(callback).Run(allowed); },
                     std::move(callback)));
}

void NetworkContext::OnVerifyCertComplete(uint64_t cert_verify_id, int result) {
  auto iter = cert_verifier_requests_.find(cert_verify_id);
  CHECK(iter != cert_verifier_requests_.end());

  auto pending_cert_verify = std::move(iter->second);
  cert_verifier_requests_.erase(iter);

  bool pkp_bypassed = false;
  if (result == net::OK ||
      result == net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED) {
#if BUILDFLAG(IS_CT_SUPPORTED)
    if (url_request_context_->sct_auditing_delegate()) {
      url_request_context_->sct_auditing_delegate()->MaybeEnqueueReport(
          pending_cert_verify->host_port,
          pending_cert_verify->result->verified_cert.get(),
          pending_cert_verify->result->scts);
    }
#endif  // BUILDFLAG(IS_CT_SUPPORTED)
    net::TransportSecurityState::PKPStatus pin_validity =
        url_request_context_->transport_security_state()->CheckPublicKeyPins(
            pending_cert_verify->host_port.host(),
            pending_cert_verify->result->is_issued_by_known_root,
            pending_cert_verify->result->public_key_hashes);
    switch (pin_validity) {
      case net::TransportSecurityState::PKPStatus::VIOLATED:
        pending_cert_verify->result->cert_status |=
            net::CERT_STATUS_PINNED_KEY_MISSING;
        result = net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
        break;
      case net::TransportSecurityState::PKPStatus::BYPASSED:
        pkp_bypassed = true;
        [[fallthrough]];
      case net::TransportSecurityState::PKPStatus::OK:
        // Do nothing.
        break;
    }
  }

  std::move(pending_cert_verify->callback)
      .Run(result, *pending_cert_verify->result.get(), pkp_bypassed);
}

#if BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED)
void NetworkContext::EnsureMounted(network::TransferableDirectory* directory) {
  if (directory->NeedsMount()) {
    dismount_closures_.push_back(directory->Mount());
  }
}
#endif  // BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED)

void NetworkContext::InitializeCorsParams() {
  TRACE_EVENT0("loading", "NetworkContext::InitializeCorsParams");
  for (const auto& pattern : params_->cors_origin_access_list) {
    cors_origin_access_list_.SetAllowListForOrigin(pattern->source_origin,
                                                   pattern->allow_patterns);
    cors_origin_access_list_.SetBlockListForOrigin(pattern->source_origin,
                                                   pattern->block_patterns);
  }
  for (const auto& key : params_->cors_exempt_header_list) {
    cors_exempt_header_list_.insert(key);
  }

  acam_preflight_spec_conformant_ = params_->acam_preflight_spec_conformant;
}

void NetworkContext::FinishConstructingTrustTokenStore(
    std::unique_ptr<SQLiteTrustTokenPersister> persister) {
  trust_token_store_->OnStoreReady(std::make_unique<TrustTokenStore>(
      std::move(persister),
      std::make_unique<ExpiryInspectingRecordExpiryDelegate>(
          network_service()->trust_token_key_commitments())));
}

bool NetworkContext::IsAllowedToUseAllHttpAuthSchemes(
    const url::SchemeHostPort& scheme_host_port) {
  DCHECK(url_matcher_);
  return !url_matcher_->MatchURL(scheme_host_port.GetURL()).empty();
}

void NetworkContext::CreateTrustedUrlLoaderFactoryForNetworkService(
    mojo::PendingReceiver<mojom::URLLoaderFactory>
        url_loader_factory_pending_receiver) {
  auto url_loader_factory_params = mojom::URLLoaderFactoryParams::New();
  url_loader_factory_params->is_trusted = true;
  url_loader_factory_params->process_id = OriginatingProcessId::browser();
  CreateURLLoaderFactory(std::move(url_loader_factory_pending_receiver),
                         std::move(url_loader_factory_params));
}

void NetworkContext::SetSharedDictionaryCacheMaxSize(
    std::optional<base::ByteSize> cache_max_size) {
  if (!shared_dictionary_manager_) {
    return;
  }
  shared_dictionary_manager_->SetCacheMaxSize(cache_max_size);
}

void NetworkContext::ClearSharedDictionaryCache(
    base::Time start_time,
    base::Time end_time,
    mojom::ClearDataFilterPtr filter,
    ClearSharedDictionaryCacheCallback callback) {
  if (!shared_dictionary_manager_) {
    std::move(callback).Run();
    return;
  }
  shared_dictionary_manager_->ClearData(start_time, end_time,
                                        BuildUrlFilter(std::move(filter)),
                                        std::move(callback));
}

void NetworkContext::ClearSharedDictionaryCacheForIsolationKey(
    const net::SharedDictionaryIsolationKey& isolation_key,
    ClearSharedDictionaryCacheForIsolationKeyCallback callback) {
  if (!shared_dictionary_manager_) {
    std::move(callback).Run();
    return;
  }
  shared_dictionary_manager_->ClearDataForIsolationKey(isolation_key,
                                                       std::move(callback));
}

void NetworkContext::GetSharedDictionaryUsageInfo(
    GetSharedDictionaryUsageInfoCallback callback) {
  if (!shared_dictionary_manager_) {
    std::move(callback).Run({});
    return;
  }
  shared_dictionary_manager_->GetUsageInfo(std::move(callback));
}

void NetworkContext::GetSharedDictionaryInfo(
    const net::SharedDictionaryIsolationKey& isolation_key,
    GetSharedDictionaryInfoCallback callback) {
  if (!shared_dictionary_manager_) {
    std::move(callback).Run({});
    return;
  }
  shared_dictionary_manager_->GetSharedDictionaryInfo(isolation_key,
                                                      std::move(callback));
}

void NetworkContext::GetSharedDictionaryOriginsBetween(
    base::Time start_time,
    base::Time end_time,
    GetSharedDictionaryOriginsBetweenCallback callback) {
  if (!shared_dictionary_manager_) {
    std::move(callback).Run({});
    return;
  }
  shared_dictionary_manager_->GetOriginsBetween(start_time, end_time,
                                                std::move(callback));
}

void NetworkContext::PreloadSharedDictionaryInfoForDocument(
    const std::vector<GURL>& urls,
    mojo::PendingReceiver<mojom::PreloadedSharedDictionaryInfoHandle>
        preload_handle) {
  if (shared_dictionary_manager_) {
    shared_dictionary_manager_->PreloadSharedDictionaryInfoForDocument(
        urls, std::move(preload_handle));
  }
}

void NetworkContext::HasPreloadedSharedDictionaryInfoForTesting(
    HasPreloadedSharedDictionaryInfoForTestingCallback callback) {
  std::move(callback).Run(
      shared_dictionary_manager_ &&
      shared_dictionary_manager_->HasPreloadedSharedDictionaryInfo());
}

void NetworkContext::FlushCachedClientCertIfNeeded(
    const net::HostPortPair& host,
    const scoped_refptr<net::X509Certificate>& certificate) {
  net::HttpNetworkSession* http_session =
      url_request_context_->http_transaction_factory()->GetSession();
  DCHECK(http_session);
  if (http_session->ssl_client_context()) {
    http_session->ssl_client_context()->ClearClientCertificateIfNeeded(
        host, certificate);
  }
}

void NetworkContext::FlushMatchingCachedClientCert(
    const scoped_refptr<net::X509Certificate>& certificate) {
  net::HttpNetworkSession* http_session =
      url_request_context_->http_transaction_factory()->GetSession();
  DCHECK(http_session);
  if (http_session->ssl_client_context()) {
    http_session->ssl_client_context()->ClearMatchingClientCertificate(
        certificate);
  }
}

void NetworkContext::FlushClientCertCache() {
  net::HttpNetworkSession* http_session =
      url_request_context_->http_transaction_factory()->GetSession();
  DCHECK(http_session);
  if (http_session->ssl_client_context()) {
    http_session->ssl_client_context()->OnClientCertStoreChanged();
  }
}

void NetworkContext::RestrictNetworkForIds(
    std::vector<mojom::IdAndAllowlistedPatternsPtr> ids_to_patterns,
    RestrictNetworkForIdsCallback callback) {
  auto parse_allowlist =
      [](std::optional<ConnectionAllowlist>& source,
         std::optional<std::string>& dest_endpoint,
         std::optional<
             std::set<std::unique_ptr<url_pattern::SimpleUrlPatternMatcher>>>&
             dest_patterns,
         ConnectionAllowlist::RedirectBehavior& dest_redirect_behavior) {
        if (!source) {
          return;
        }
        dest_endpoint = source->reporting_endpoint;
        dest_redirect_behavior = source->redirect_behavior;
        dest_patterns.emplace();
        for (const std::string& pattern : source->allowlist) {
          // TODO(crbug.com/447954811): We can safely DCHECK here, as we've done
          // pattern validation already while validating the header's syntax.
          // That said, parsing the pattern twice has performance overhead, and
          // it would be ideal to change our infrastructure to allow passing a
          // SimpleUrlPatternMatcher directly rather than creating it anew here.
          auto matcher = url_pattern::SimpleUrlPatternMatcher::Create(
              pattern, /*base_url=*/nullptr);

          DCHECK(matcher.has_value());
          dest_patterns->insert(std::move(matcher.value()));
        }
      };

  for (auto& entry : ids_to_patterns) {
    const base::UnguessableToken& network_restrictions_id =
        entry->network_restrictions_id;

    // Accessing `network_restrictions_ids_[network_restrictions_id]` here
    // ensures that if it's not already present in the set, it's
    // default-constructed. This is important, as an empty
    // `entry->allowlists.enforced->allowlist` represents complete network
    // revocation.
    NetworkRestriction& restriction =
        network_restrictions_ids_[network_restrictions_id];
    restriction.response_url = entry->allowlists.response_url;
    restriction.reporting_source = entry->allowlists.reporting_source;

    parse_allowlist(entry->allowlists.enforced,
                    restriction.enforced_reporting_endpoint,
                    restriction.enforced_allowlisted_patterns,
                    restriction.enforced_redirect_behavior);
    parse_allowlist(entry->allowlists.report_only,
                    restriction.report_only_reporting_endpoint,
                    restriction.report_only_allowlisted_patterns,
                    restriction.report_only_redirect_behavior);
  }

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

void NetworkContext::ClearNetworkRestrictions(
    const std::vector<base::UnguessableToken>& network_restrictions_ids) {
  for (const auto& network_restrictions_id : network_restrictions_ids) {
    network_restrictions_ids_.erase(network_restrictions_id);
  }
}

void NetworkContext::Prefetch(
    int32_t request_id,
    uint32_t options,
    const ResourceRequest& request,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
    const base::UnguessableToken& network_restrictions_id) {
  if (!prefetch_enabled_) {
    return;
  }

  // Retrieve the NAK for Connection Allowlist enforcement.
  const net::NetworkAnonymizationKey& nak =
      request.trusted_params.has_value()
          ? request.trusted_params->isolation_info.network_anonymization_key()
          : net::NetworkAnonymizationKey::CreateTransient();

  // TODO(crbug.com/536913419): Handle the Connection Allowlist redirects
  // directive correctly, rather than cancelling the whole prefetch.
  if (!IsNetworkForNetworkRestrictionsIdAndUrlAllowed(
          network_restrictions_id, request.url, nak, /*is_redirect=*/true)) {
    return;
  }

  // The UrlLoaderFactory used below is different from the one used for
  // subresources, which is normally where enforcement for Connection Allowlist
  // happens. Enforce here instead before delegating the request to the
  // prefetch-specific factory. This request is also an initial prefetch and
  // not a redirect, so perform the allowlist check accordingly.
  if (!IsNetworkForNetworkRestrictionsIdAndUrlAllowed(
          network_restrictions_id, request.url, nak, /*is_redirect=*/false)) {
    return;
  }

  PrefetchURLLoaderClient* client = prefetch_cache_->Emplace(request);
  if (!client) {
    // This is normal if we already have a prefetch in progress for the {NIK,
    // URL} combination.
    return;
  }

  prefetch_url_loader_factory_remote_->CreateLoaderAndStart(
      client->GetURLLoaderPendingReceiver(), request_id, options, request,
      client->BindNewPipeAndPassRemote(), traffic_annotation);
}

void NetworkContext::GetDeviceBoundSessionManager(
    mojo::PendingReceiver<network::mojom::DeviceBoundSessionManager>
        device_bound_session_manager) {
  if (device_bound_session_manager_) {
    device_bound_session_manager_->AddReceiver(
        std::move(device_bound_session_manager));
  }
}

void NetworkContext::AddQuicHints(
    const std::vector<url::SchemeHostPort>& origins,
    const net::NetworkAnonymizationKey& network_anonymization_key) {
  CHECK(url_request_context_);

  for (const auto& origin : origins) {
    url::CanonHostInfo host_info;
    std::string canonical_host(
        net::CanonicalizeHost(origin.host(), &host_info));
    if (!host_info.IsIPAddress() &&
        !net::IsCanonicalizedHostCompliant(canonical_host)) {
      LOG(ERROR) << "Invalid QUIC hint host: " << origin.host();
      continue;
    }

    // The AlternativeService hostname can be used to signal a change of host.
    // By passing in an empty host, the origin's host will be used.
    net::AlternativeService alternative_service(net::NextProto::kProtoQUIC, "",
                                                origin.port());

    url::SchemeHostPort canonical_origin(url::kHttpsScheme, canonical_host,
                                         origin.port());
    url_request_context_->http_server_properties()->SetQuicAlternativeService(
        canonical_origin, network_anonymization_key, alternative_service,
        base::Time::Max(), quic::ParsedQuicVersionVector());
  }
}

bool NetworkContext::IsNetworkForNetworkRestrictionsIdAndUrlAllowed(
    const base::UnguessableToken& network_restrictions_id,
    const GURL& url,
    const net::NetworkAnonymizationKey& network_anonymization_key,
    bool is_redirect) {
  if (!base::FeatureList::IsEnabled(network::features::kConnectionAllowlists)) {
    return true;
  }

  // If network hasn't been revoked for the network restrictions ID, it's
  // allowed. Likewise, local schemes that reach this point should be excluded
  // as they don't generate network requests.
  if (network_restrictions_ids_.empty() || url.SchemeIsLocal()) {
    return true;
  }
  auto it = network_restrictions_ids_.find(network_restrictions_id);
  if (it == network_restrictions_ids_.end()) {
    return true;
  }

  const NetworkRestriction& restriction = it->second;

  // For connection allowlist feature, network_restrictions_ids_ map contains
  // the allowed URL Patterns.
  // If there are no allowlisted URLs then it is assumed that all network URLs
  // are restricted.
  auto restriction_allowed = [&](const NetworkRestriction& r, bool enforced) {
    const auto& patterns = enforced ? r.enforced_allowlisted_patterns
                                    : r.report_only_allowlisted_patterns;
    if (!patterns.has_value()) {
      // A null pattern implies the response does not contain the corresponding
      // header at all. No restriction should be applied.
      return true;
    }

    if (is_redirect) {
      // For redirects, the URL is not matched against the patterns. Connection
      // allowlist's redirect directive specifies the behavior.
      const auto& redirect_behavior = enforced
                                          ? r.enforced_redirect_behavior
                                          : r.report_only_redirect_behavior;
      return redirect_behavior == ConnectionAllowlist::RedirectBehavior::kAllow;
    }

    // Match the URL against the patterns.
    return std::ranges::any_of(
        *patterns,
        [&url](const std::unique_ptr<url_pattern::SimpleUrlPatternMatcher>&
                   matcher) { return matcher->Match(url); });
  };

  // First, check against the report-only allowlist, reporting violations:
  if (restriction.report_only_reporting_endpoint.has_value() &&
      !restriction_allowed(restriction, /*enforced=*/false)) {
    QueueConnectionAllowlistReport(
        restriction.response_url, url, network_anonymization_key,
        restriction.reporting_source,
        *restriction.report_only_reporting_endpoint, /*enforced=*/false);
  }

  // Then, match against the enforced allowlist, and return `false` to cancel
  // the request if a violation is found:
  if (!restriction_allowed(restriction, /*enforced=*/true)) {
    if (restriction.enforced_reporting_endpoint.has_value()) {
      QueueConnectionAllowlistReport(
          restriction.response_url, url, network_anonymization_key,
          restriction.reporting_source,
          *restriction.enforced_reporting_endpoint, /*enforced=*/true);
    }
    return false;
  }

  return true;
}

bool NetworkContext::IsHostResolutionForNetworkRestrictionsIdAndHostAllowed(
    const base::UnguessableToken& network_restrictions_id,
    const mojom::HostResolverHost& host,
    const net::NetworkAnonymizationKey& network_anonymization_key) {
  if (!base::FeatureList::IsEnabled(network::features::kConnectionAllowlists)) {
    return true;
  }

  auto it = network_restrictions_ids_.find(network_restrictions_id);
  if (it == network_restrictions_ids_.end()) {
    return true;
  }

  std::string host_fragment = host.is_host_port_pair()
                                  ? host.get_host_port_pair().host()
                                  : host.get_scheme_host_port().host();
  GURL synthetic_url = GURL(base::StrCat(
      {url::kHttpsScheme, url::kStandardSchemeSeparator, host_fragment}));
  if (!synthetic_url.is_valid()) {
    return false;
  }

  // Per
  // https://wicg.github.io/connection-allowlists/#abstract-opdef-match-a-host-to-a-connection-allowlist,
  // we need to match `synthetic_url` against a host-only variant against each
  // URLPattern corresponding to `nonce`.
  const NetworkRestriction& restriction = it->second;

  auto restriction_allowed = [&](const NetworkRestriction& r, bool enforced) {
    const auto& patterns = enforced ? r.enforced_allowlisted_patterns
                                    : r.report_only_allowlisted_patterns;
    return !patterns.has_value() ||
           std::ranges::any_of(
               *patterns,
               [&synthetic_url](
                   const std::unique_ptr<url_pattern::SimpleUrlPatternMatcher>&
                       matcher) {
                 return matcher->HostOnlyMatch(synthetic_url);
               });
  };

  // First, check against the report-only allowlist, reporting violations:
  if (restriction.report_only_reporting_endpoint.has_value() &&
      !restriction_allowed(restriction, /*enforced=*/false)) {
    QueueConnectionAllowlistReport(restriction.response_url, synthetic_url,
                                   network_anonymization_key,
                                   restriction.reporting_source,
                                   *restriction.report_only_reporting_endpoint,
                                   /*enforced=*/false);
  }

  // Then, match against the enforced allowlist, and return `false` to cancel
  // the request if a violation is found:
  if (!restriction_allowed(restriction, /*enforced=*/true)) {
    if (restriction.enforced_reporting_endpoint.has_value()) {
      QueueConnectionAllowlistReport(restriction.response_url, synthetic_url,
                                     network_anonymization_key,
                                     restriction.reporting_source,
                                     *restriction.enforced_reporting_endpoint,
                                     /*enforced=*/true);
    }
    return false;
  }

  // If we didn't block the request via the enforcement check directly above,
  // it should be allowed:
  return true;
}

void NetworkContext::InitializePrefetchURLLoaderFactory() {
  TRACE_EVENT0("loading", "NetworkContext::InitializePrefetchURLLoaderFactory");
  auto pending_receiver =
      prefetch_url_loader_factory_remote_.BindNewPipeAndPassReceiver();
  CreateURLLoaderFactory(std::move(pending_receiver),
                         CreateURLLoaderFactoryParamsForPrefetch());
}

GURL NetworkContext::GetNetworkRestrictionResponseUrlForTesting(
    const base::UnguessableToken& network_restrictions_id) const {
  auto it = network_restrictions_ids_.find(network_restrictions_id);
  if (it == network_restrictions_ids_.end()) {
    return GURL();
  }
  return it->second.response_url;
}

void NetworkContext::SetVariationsHeaders(
    variations::mojom::VariationsHeadersPtr variations_headers) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  variations_headers_ = std::move(variations_headers);
}


bool NetworkContext::HasCookieAccessForDeviceBoundSession(
    const net::device_bound_sessions::CookieAccessCheckParams& params) {
  if (!cookie_manager_) {
    return false;
  }
  return cookie_manager_->cookie_settings().IsFullCookieAccessAllowed(
      params.provider_origin->GetURL(),
      net::SiteForCookies::FromOrigin(*params.relying_party_origin),
      *params.relying_party_origin,
      {net::CookieSettingOverride::kStorageAccessGrantEligible},
      // TODO(crbug.com/353772143): update once partitioned cookies are
      // supported. DBSC does not support cookie partition yet.
      /*cookie_partition_key=*/std::nullopt);
}

void NetworkContext::OnInvalidationFiltersLoaded(
    LogicalInvalidationStore::LoadResult result,
    std::vector<net::HttpCache::InvalidationFilter> loaded_filters) {
  net::HttpCache* cache =
      url_request_context_->http_transaction_factory()->GetCache();
  if (!cache) {
    return;
  }

  // 1. Determine and save the final list of filters in O(N log N) time.
  if (logical_invalidation_store_) {
    std::vector<net::HttpCache::InvalidationFilter> filters_to_save(
        loaded_filters);
    filters_to_save.append_range(pending_additions_);
    std::erase_if(filters_to_save, [&](const auto& filter) {
      return std::ranges::contains(pending_removals_, filter);
    });
    std::ranges::sort(filters_to_save, std::less<>());
    auto [first, last] =
        std::ranges::unique(filters_to_save, std::equal_to<>());
    filters_to_save.erase(first, last);
    logical_invalidation_store_->Save(std::move(filters_to_save));
  }

  // 2. Determine which loaded filters need to be registered logically in
  // memory.
  std::vector<net::HttpCache::InvalidationFilter> filters_to_register_logically;
  for (auto& filter : loaded_filters) {
    if (!std::ranges::contains(pending_removals_, filter) &&
        !std::ranges::contains(pending_additions_, filter)) {
      filters_to_register_logically.push_back(std::move(filter));
    }
  }

  // 4. Register the logical filters in memory before starting physical purges.
  for (auto& filter : filters_to_register_logically) {
    filter.was_loaded_from_disk = true;
    cache->AddInvalidationFilter(filter);
  }

  // 5. Start background physical purges for newly registered filters.
  for (const auto& filter : filters_to_register_logically) {
    mojom::ClearDataFilterPtr filter_ptr = mojom::ClearDataFilter::New();
    filter_ptr->type =
        (filter.filter_type == net::UrlFilterType::kTrueIfMatches)
            ? mojom::ClearDataFilter_Type::DELETE_MATCHES
            : mojom::ClearDataFilter_Type::KEEP_MATCHES;
    filter_ptr->origins.assign_range(filter.origins);
    filter_ptr->domains.assign_range(filter.domains);

    auto logical_cleanup_done_callback = base::BindOnce(
        &RemoveInvalidationFilterCallback, weak_factory_.GetWeakPtr(), filter);

    StartHttpCacheDataRemover(std::move(filter_ptr), filter.begin_time,
                              filter.end_time,
                              std::move(logical_cleanup_done_callback));
  }

  // 6. Set state to loaded and clear queues.
  initial_filters_loaded_ = true;
  pending_additions_.clear();
  pending_removals_.clear();
}

void NetworkContext::StartHttpCacheDataRemover(
    mojom::ClearDataFilterPtr filter,
    base::Time start_time,
    base::Time end_time,
    base::OnceClosure done_callback) {
  http_cache_data_removers_.push_back(HttpCacheDataRemover::CreateAndStart(
      url_request_context_, std::move(filter), start_time, end_time,
      base::BindOnce(&NetworkContext::OnHttpCacheCleared,
                     base::Unretained(this), std::move(done_callback))));
}

void NetworkContext::UpdatePersistenceQueueOrSave(
    const net::HttpCache::InvalidationFilter& filter,
    std::vector<net::HttpCache::InvalidationFilter>* pending_queue,
    base::OnceClosure callback) {
  if (!initial_filters_loaded_) {
    pending_queue->push_back(filter);
    if (callback) {
      std::move(callback).Run();
    }
    return;
  }
  if (logical_invalidation_store_) {
    net::HttpCache* cache =
        url_request_context_->http_transaction_factory()->GetCache();
    if (cache) {
      logical_invalidation_store_->Save(cache->invalidation_filters(),
                                        std::move(callback));
      return;
    }
  }
  if (callback) {
    std::move(callback).Run();
  }
}

void NetworkContext::OnLogicalFilterAdded(
    const net::HttpCache::InvalidationFilter& filter,
    base::OnceClosure callback) {
  UpdatePersistenceQueueOrSave(filter, &pending_additions_,
                               std::move(callback));
}

void NetworkContext::OnLogicalFilterRemoved(
    const net::HttpCache::InvalidationFilter& filter) {
  UpdatePersistenceQueueOrSave(filter, &pending_removals_);
}

}  // namespace network
