blob: 21e2774f53bf6df6cd00ac02e1a603d1d9b6ba66 [file] [log] [blame]
// 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