910e62b5创建于 1月15日历史提交
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/loader/url_loader_factory_utils.h"

#include <variant>

#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/browser_thread.h"
#include "net/cookies/cookie_setting_override.h"

namespace content {
namespace url_loader_factory {

namespace {

Interceptor& GetMutableInterceptor() {
  static base::NoDestructor<Interceptor> s_callback;
  return *s_callback;
}

bool s_has_interceptor = false;

}  // namespace

const Interceptor& GetTestingInterceptor() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  return GetMutableInterceptor();
}

void SetInterceptorForTesting(const Interceptor& interceptor) {
  CHECK(!BrowserThread::IsThreadInitialized(BrowserThread::UI) ||
        BrowserThread::CurrentlyOn(BrowserThread::UI));
  CHECK(interceptor.is_null() || GetMutableInterceptor().is_null())
      << "It is not expected that this is called with non-null callback when "
      << "another overriding callback is already set.";
  GetMutableInterceptor() = interceptor;
}

bool HasInterceptorOnIOThreadForTesting() {
  CHECK(!BrowserThread::IsThreadInitialized(BrowserThread::IO) ||
        BrowserThread::CurrentlyOn(BrowserThread::IO));
  return s_has_interceptor;
}

void SetHasInterceptorOnIOThreadForTesting(bool has_interceptor) {
  CHECK(!BrowserThread::IsThreadInitialized(BrowserThread::IO) ||
        BrowserThread::CurrentlyOn(BrowserThread::IO));
  s_has_interceptor = has_interceptor;
}

TerminalParams::TerminalParams(
    network::mojom::NetworkContext* network_context,
    network::mojom::URLLoaderFactoryParamsPtr factory_params,
    HeaderClientOption header_client_option,
    FactoryOverrideOption factory_override_option,
    DisableSecureDnsOption disable_secure_dns_option,
    StoragePartitionImpl* storage_partition,
    std::optional<URLLoaderFactoryTypes> url_loader_factory,
    int process_id)
    : network_context_(network_context),
      factory_params_(std::move(factory_params)),
      header_client_option_(header_client_option),
      factory_override_option_(factory_override_option),
      disable_secure_dns_option_(disable_secure_dns_option),
      storage_partition_(storage_partition),
      url_loader_factory_(std::move(url_loader_factory)),
      process_id_(process_id) {}

TerminalParams::~TerminalParams() = default;
TerminalParams::TerminalParams(TerminalParams&&) = default;
TerminalParams& TerminalParams::operator=(TerminalParams&&) = default;

// static
TerminalParams TerminalParams::ForNetworkContext(
    network::mojom::NetworkContext* network_context,
    network::mojom::URLLoaderFactoryParamsPtr factory_params,
    HeaderClientOption header_client_option,
    FactoryOverrideOption factory_override_option,
    DisableSecureDnsOption disable_secure_dns_option) {
  CHECK(network_context);
  CHECK(factory_params);
  int process_id = factory_params->process_id;
  return TerminalParams(network_context, std::move(factory_params),
                        header_client_option, factory_override_option,
                        disable_secure_dns_option,
                        /*storage_partition=*/nullptr,
                        /*url_loader_factory=*/std::nullopt, process_id);
}

// static
TerminalParams TerminalParams::ForBrowserProcess(
    StoragePartitionImpl* storage_partition,
    HeaderClientOption header_client_option) {
  CHECK(storage_partition);
  return TerminalParams(storage_partition->GetNetworkContext(),
                        storage_partition->CreateURLLoaderFactoryParams(),
                        header_client_option, FactoryOverrideOption::kDisallow,
                        DisableSecureDnsOption::kDisallow, storage_partition,
                        /*url_loader_factory=*/std::nullopt,
                        network::mojom::kBrowserProcessId);
}

// static
TerminalParams TerminalParams::ForNonNetwork(
    URLLoaderFactoryTypes url_loader_factory,
    int process_id) {
  return TerminalParams(
      /*network_context=*/nullptr, /*factory_params=*/nullptr,
      HeaderClientOption::kDisallow, FactoryOverrideOption::kDisallow,
      DisableSecureDnsOption::kDisallow,
      /*storage_partition=*/nullptr, std::move(url_loader_factory), process_id);
}

network::mojom::NetworkContext* TerminalParams::network_context() const {
  return network_context_.get();
}
HeaderClientOption TerminalParams::header_client_option() const {
  return header_client_option_;
}
FactoryOverrideOption TerminalParams::factory_override_option() const {
  return factory_override_option_;
}
DisableSecureDnsOption TerminalParams::disable_secure_dns_option() const {
  return disable_secure_dns_option_;
}
StoragePartitionImpl* TerminalParams::storage_partition() const {
  return storage_partition_.get();
}
int TerminalParams::process_id() const {
  return process_id_;
}
network::mojom::URLLoaderFactoryParamsPtr TerminalParams::TakeFactoryParams() {
  return std::move(factory_params_);
}
std::optional<TerminalParams::URLLoaderFactoryTypes>
TerminalParams::TakeURLLoaderFactory() {
  return std::move(url_loader_factory_);
}

ContentClientParams::ContentClientParams(
    BrowserContext* browser_context,
    RenderFrameHost* frame,
    int render_process_id,
    const url::Origin& request_initiator,
    const net::IsolationInfo& isolation_info,
    ukm::SourceIdObj ukm_source_id,
    bool* bypass_redirect_checks,
    std::optional<int64_t> navigation_id,
    scoped_refptr<base::SequencedTaskRunner> navigation_response_task_runner)
    : browser_context_(browser_context),
      frame_(frame),
      render_process_id_(render_process_id),
      request_initiator_(request_initiator),
      isolation_info_(isolation_info),
      ukm_source_id_(ukm_source_id),
      bypass_redirect_checks_(bypass_redirect_checks),
      navigation_id_(navigation_id),
      navigation_response_task_runner_(
          std::move(navigation_response_task_runner)) {}

ContentClientParams::ContentClientParams(ContentClientParams&&) = default;
ContentClientParams& ContentClientParams::operator=(ContentClientParams&&) =
    default;
ContentClientParams::~ContentClientParams() = default;

void ContentClientParams::Run(
    network::URLLoaderFactoryBuilder& factory_builder,
    ContentBrowserClient::URLLoaderFactoryType type,
    mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
        header_client,
    bool* disable_secure_dns,
    network::mojom::URLLoaderFactoryOverridePtr* factory_override) {
  GetContentClient()->browser()->WillCreateURLLoaderFactory(
      browser_context_.get(), frame_.get(), render_process_id_, type,
      request_initiator_.get(), isolation_info_.get(),
      std::move(navigation_id_), std::move(ukm_source_id_), factory_builder,
      header_client, bypass_redirect_checks_.get(), disable_secure_dns,
      factory_override, std::move(navigation_response_task_runner_));
}

namespace {

// Returns (is_navigation, is_download).
std::tuple<bool, bool> GetIsNavigationAndDownload(
    ContentBrowserClient::URLLoaderFactoryType type) {
  switch (type) {
    case ContentBrowserClient::URLLoaderFactoryType::kNavigation:
      return std::make_tuple(/*is_navigation=*/true, /*is_download=*/false);
    case ContentBrowserClient::URLLoaderFactoryType::kDownload:
      return std::make_tuple(/*is_navigation=*/true, /*is_download=*/true);

    case ContentBrowserClient::URLLoaderFactoryType::kDocumentSubResource:
    case ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript:
    case ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerSubResource:
    case ContentBrowserClient::URLLoaderFactoryType::kWorkerSubResource:
    case ContentBrowserClient::URLLoaderFactoryType::kWorkerMainResource:
      return std::make_tuple(/*is_navigation=*/false, /*is_download=*/false);

    // The following cases are not reached just because `devtools_params` is
    // always `std::nullopt` for the current `Create*` callers.
    // TODO(crbug.com/40947547): Return proper values once non-nullopt
    // `devtools_params` is given.
    case ContentBrowserClient::URLLoaderFactoryType::kPrefetch:
    case ContentBrowserClient::URLLoaderFactoryType::kDevTools:
    case ContentBrowserClient::URLLoaderFactoryType::kEarlyHints:
      NOTREACHED();
  }
}

// `FinishArgs...` is `mojo::PendingReceiver<network::mojom::URLLoaderFactory>`
// when called from `CreateAndConnectToReceiver`, and empty otherwise.
template <typename OutType, typename... FinishArgs>
[[nodiscard]] OutType CreateInternal(
    ContentBrowserClient::URLLoaderFactoryType type,
    TerminalParams terminal_params,
    std::optional<ContentClientParams> content_client_params,
    std::optional<devtools_instrumentation::WillCreateURLLoaderFactoryParams>
        devtools_params,
    FinishArgs... finish_args) {
  mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>
      header_client;
  network::mojom::URLLoaderFactoryOverridePtr factory_override;
  auto* factory_override_ptr =
      terminal_params.factory_override_option() == FactoryOverrideOption::kAllow
          ? &factory_override
          : nullptr;
  bool disable_secure_dns = false;

  network::URLLoaderFactoryBuilder factory_builder;

  if (content_client_params) {
    content_client_params->Run(
        factory_builder, type,
        terminal_params.header_client_option() == HeaderClientOption::kAllow
            ? &header_client
            : nullptr,
        terminal_params.disable_secure_dns_option() ==
                DisableSecureDnsOption::kAllow
            ? &disable_secure_dns
            : nullptr,
        factory_override_ptr);
  }

  if (devtools_params) {
    auto [is_navigation, is_download] = GetIsNavigationAndDownload(type);
    devtools_params->Run(is_navigation, is_download, factory_builder,
                         factory_override_ptr);
  }

  if (auto terminal_url_loader_factory =
          terminal_params.TakeURLLoaderFactory()) {
    if (GetTestingInterceptor()) {
      GetTestingInterceptor().Run(terminal_params.process_id(),
                                  factory_builder);
    }

    return std::visit(
        [&factory_builder, &finish_args...](auto&& terminal) {
          return std::move(factory_builder)
              .template Finish<OutType>(
                  std::forward<FinishArgs>(finish_args)...,
                  std::move(terminal));
        },
        std::move(*terminal_url_loader_factory));
  }

  if (!header_client && terminal_params.storage_partition()) {
    CHECK(!factory_override);
    CHECK(!disable_secure_dns);
    // `GetTestingInterceptor()` isn't used here because it is anyway used
    // inside `GetURLLoaderFactoryForBrowserProcess()`.
    return std::move(factory_builder)
        .template Finish<OutType>(std::forward<FinishArgs>(finish_args)...,
                                  terminal_params.storage_partition()
                                      ->GetURLLoaderFactoryForBrowserProcess());
  }

  CHECK(terminal_params.network_context());
  auto factory_params = terminal_params.TakeFactoryParams();
  CHECK(factory_params);
  factory_params->header_client = std::move(header_client);
  factory_params->factory_override = std::move(factory_override);
  factory_params->disable_secure_dns = disable_secure_dns;
  if (devtools_params) {
    devtools_instrumentation::ApplyNetworkCookieControlsOverrides(
        devtools_params->agent_host(),
        factory_params->devtools_cookie_setting_overrides);
  }

  if (GetTestingInterceptor()) {
    GetTestingInterceptor().Run(terminal_params.process_id(), factory_builder);
  }

  return std::move(factory_builder)
      .template Finish<OutType>(std::forward<FinishArgs>(finish_args)...,
                                terminal_params.network_context(),
                                std::move(factory_params));
}

}  // namespace

scoped_refptr<network::SharedURLLoaderFactory> Create(
    ContentBrowserClient::URLLoaderFactoryType type,
    TerminalParams terminal_params,
    std::optional<ContentClientParams> content_client_params,
    std::optional<devtools_instrumentation::WillCreateURLLoaderFactoryParams>
        devtools_params) {
  return CreateInternal<scoped_refptr<network::SharedURLLoaderFactory>>(
      type, std::move(terminal_params), std::move(content_client_params),
      std::move(devtools_params));
}

mojo::PendingRemote<network::mojom::URLLoaderFactory> CreatePendingRemote(
    ContentBrowserClient::URLLoaderFactoryType type,
    TerminalParams terminal_params,
    std::optional<ContentClientParams> content_client_params,
    std::optional<devtools_instrumentation::WillCreateURLLoaderFactoryParams>
        devtools_params) {
  return CreateInternal<mojo::PendingRemote<network::mojom::URLLoaderFactory>>(
      type, std::move(terminal_params), std::move(content_client_params),
      std::move(devtools_params));
}

void CreateAndConnectToPendingReceiver(
    mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver_to_connect,
    ContentBrowserClient::URLLoaderFactoryType type,
    TerminalParams terminal_params,
    std::optional<ContentClientParams> content_client_params,
    std::optional<devtools_instrumentation::WillCreateURLLoaderFactoryParams>
        devtools_params) {
  CreateInternal<void>(
      type, std::move(terminal_params), std::move(content_client_params),
      std::move(devtools_params), std::move(receiver_to_connect));
}

}  // namespace url_loader_factory
}  // namespace content