blob: 9fd8b1fd6c4a6c6b5b2fe5249a5386581bd5785b [file] [log] [blame]
// Copyright 2013 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/shell/browser/shell_content_browser_client.h"
#include <stddef.h>
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/containers/fixed_flat_set.h"
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/sequence_local_storage_slot.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "components/custom_handlers/protocol_handler_registry.h"
#include "components/custom_handlers/protocol_handler_throttle.h"
#include "components/custom_handlers/simple_protocol_handler_registry_factory.h"
#include "components/embedder_support/switches.h"
#include "components/embedder_support/user_agent_utils.h"
#include "components/metrics/client_info.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/metrics_state_manager.h"
#include "components/metrics/test/test_enabled_state_provider.h"
#include "components/network_hints/browser/simple_network_hints_handler_impl.h"
#include "components/performance_manager/embedder/binders.h"
#include "components/performance_manager/embedder/performance_manager_registry.h"
#include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service_factory.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/variations/platform_field_trials.h"
#include "components/variations/pref_names.h"
#include "components/variations/service/safe_seed_manager.h"
#include "components/variations/service/variations_field_trial_creator.h"
#include "components/variations/service/variations_service.h"
#include "components/variations/service/variations_service_client.h"
#include "components/variations/variations_safe_seed_store_local_state.h"
#include "components/variations/variations_switches.h"
#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/browser/login_delegate.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/navigation_throttle_registry.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view_delegate.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switch_dependent_feature_overrides.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/common/url_constants.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_browser_context.h"
#include "content/shell/browser/shell_browser_main_parts.h"
#include "content/shell/browser/shell_devtools_manager_delegate.h"
#include "content/shell/browser/shell_web_contents_view_delegate_creator.h"
#include "content/shell/common/shell_controller.test-mojom.h"
#include "content/shell/common/shell_paths.h"
#include "content/shell/common/shell_switches.h"
#include "media/mojo/buildflags.h"
#include "media/mojo/mojom/media_service.mojom.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/features.h"
#include "net/dns/public/dns_over_https_config.h"
#include "net/dns/public/secure_dns_mode.h"
#include "net/ssl/client_cert_identity.h"
#include "services/device/public/cpp/geolocation/location_system_permission_status.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/network_service_buildflags.h"
#include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/switches.h"
#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/ui_base_switches.h"
#include "url/gurl.h"
#include "url/origin.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/apk_assets.h"
#include "base/android/path_utils.h"
#include "components/variations/android/variations_seed_bridge.h"
#include "content/shell/android/shell_descriptors.h"
#endif
#if BUILDFLAG(IS_ANDROID)
#include "components/crash/content/browser/crash_handler_host_linux.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "services/device/public/cpp/test/fake_geolocation_system_permission_manager.h"
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
#include "components/crash/core/app/crash_switches.h"
#include "components/crash/core/app/crashpad.h"
#include "content/public/common/content_descriptors.h"
#endif
#if BUILDFLAG(ENABLE_CAST_RENDERER)
#include "media/mojo/services/media_service_factory.h" // nogncheck
#endif
#if BUILDFLAG(IS_CT_SUPPORTED)
#include "services/network/public/mojom/ct_log_info.mojom.h"
#endif
#if BUILDFLAG(IS_IOS)
#include "components/permissions/bluetooth_delegate_impl.h"
#include "content/shell/browser/bluetooth/shell_bluetooth_delegate_impl_client.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "media/mojo/mojom/media_foundation_preferences.mojom.h"
#include "media/mojo/services/media_foundation_preferences.h"
#endif // BUILDFLAG(IS_WIN)
namespace content {
namespace {
#if BUILDFLAG(IS_IOS)
inline constexpr char kJITEnabled[] = "settings.javascript.jit";
#endif
using PerformanceManagerRegistry =
performance_manager::PerformanceManagerRegistry;
// Tests may install their own ShellContentBrowserClient, track the list here.
// The list is ordered with oldest first and newer ones added after it.
std::vector<ShellContentBrowserClient*>&
GetShellContentBrowserClientInstancesImpl() {
static base::NoDestructor<std::vector<ShellContentBrowserClient*>> instances;
return *instances;
}
#if BUILDFLAG(IS_ANDROID)
int GetCrashSignalFD(const base::CommandLine& command_line) {
return crashpad::CrashHandlerHost::Get()->GetDeathSignalSocket();
}
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
int GetCrashSignalFD(const base::CommandLine& command_line) {
int fd;
pid_t pid;
return crash_reporter::GetHandlerSocket(&fd, &pid) ? fd : -1;
}
#endif
class ShellControllerImpl : public mojom::ShellController {
public:
ShellControllerImpl() = default;
~ShellControllerImpl() override = default;
// mojom::ShellController:
void GetSwitchValue(const std::string& name,
GetSwitchValueCallback callback) override {
const auto& command_line = *base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(name)) {
std::move(callback).Run(command_line.GetSwitchValueASCII(name));
} else {
std::move(callback).Run(std::nullopt);
}
}
void ExecuteJavaScript(const std::u16string& script,
ExecuteJavaScriptCallback callback) override {
CHECK(!Shell::windows().empty());
WebContents* contents = Shell::windows()[0]->web_contents();
contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
script, std::move(callback), ISOLATED_WORLD_ID_GLOBAL);
}
void ShutDown() override { Shell::Shutdown(); }
};
// TODO(crbug.com/40772375): Consider not needing VariationsServiceClient just
// to use VariationsFieldTrialCreator.
class ShellVariationsServiceClient
: public variations::VariationsServiceClient {
public:
ShellVariationsServiceClient() = default;
~ShellVariationsServiceClient() override = default;
// variations::VariationsServiceClient:
base::Version GetVersionForSimulation() override { return base::Version(); }
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
override {
return nullptr;
}
network_time::NetworkTimeTracker* GetNetworkTimeTracker() override {
return nullptr;
}
version_info::Channel GetChannel() override {
return version_info::Channel::UNKNOWN;
}
bool OverridesRestrictParameter(std::string* parameter) override {
return false;
}
base::FilePath GetVariationsSeedFileDir() override {
base::FilePath seed_file_dir;
base::PathService::Get(SHELL_DIR_USER_DATA, &seed_file_dir);
return seed_file_dir;
}
bool IsEnterprise() override { return false; }
// Profiles aren't supported, so nothing to do here.
void RemoveGoogleGroupsFromPrefsForDeletedProfiles(
PrefService* local_state) override {}
};
void BindNetworkHintsHandler(
content::RenderFrameHost* frame_host,
mojo::PendingReceiver<network_hints::mojom::NetworkHintsHandler> receiver) {
DCHECK(frame_host);
network_hints::SimpleNetworkHintsHandlerImpl::Create(frame_host,
std::move(receiver));
}
#if BUILDFLAG(IS_WIN)
void BindMediaFoundationPreferences(
content::RenderFrameHost* frame_host,
mojo::PendingReceiver<media::mojom::MediaFoundationPreferences> receiver) {
// Passing in a NullCallback since we don't have MediaFoundationServiceMonitor
// in content.
MediaFoundationPreferencesImpl::Create(
frame_host->GetSiteInstance()->GetSiteURL(), base::NullCallback(),
std::move(receiver));
}
#endif // BUILDFLAG(IS_WIN)
base::flat_set<url::Origin> GetIsolatedContextOriginSetFromFlag() {
std::string cmdline_origins(
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kIsolatedContextOrigins));
std::vector<std::string_view> origin_strings = base::SplitStringPiece(
cmdline_origins, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
base::flat_set<url::Origin> origin_set;
for (std::string_view origin_string : origin_strings) {
url::Origin allowed_origin = url::Origin::Create(GURL(origin_string));
if (!allowed_origin.opaque()) {
origin_set.insert(allowed_origin);
} else {
LOG(ERROR) << "Error parsing IsolatedContext origin: " << origin_string;
}
}
return origin_set;
}
// In content browser tests we allow more than one ShellContentBrowserClient
// to be created (actually, ContentBrowserTestContentBrowserClient). Any state
// needed should be added here so that it's shared between the instances.
struct SharedState {
SharedState() {
#if BUILDFLAG(IS_MAC)
location_manager =
std::make_unique<device::FakeGeolocationSystemPermissionManager>();
location_manager->SetSystemPermission(
device::LocationSystemPermissionStatus::kAllowed);
#endif
}
#if BUILDFLAG(IS_MAC)
std::unique_ptr<device::FakeGeolocationSystemPermissionManager>
location_manager;
#endif
// Owned by content::BrowserMainLoop.
raw_ptr<ShellBrowserMainParts, DanglingUntriaged> shell_browser_main_parts =
nullptr;
std::unique_ptr<PrefService> local_state;
};
SharedState& GetSharedState() {
static SharedState* g_shared_state = nullptr;
if (!g_shared_state) {
g_shared_state = new SharedState();
}
return *g_shared_state;
}
std::unique_ptr<PrefService> CreateLocalState() {
auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
metrics::MetricsService::RegisterPrefs(pref_registry.get());
variations::VariationsService::RegisterPrefs(pref_registry.get());
#if BUILDFLAG(IS_IOS)
pref_registry->RegisterBooleanPref(kJITEnabled, true);
#endif
base::FilePath path;
CHECK(base::PathService::Get(SHELL_DIR_USER_DATA, &path));
path = path.AppendASCII("Local State");
PrefServiceFactory pref_service_factory;
pref_service_factory.set_user_prefs(
base::MakeRefCounted<JsonPrefStore>(path));
return pref_service_factory.Create(pref_registry);
}
bool AreIsolatedWebAppsEnabled() {
return base::FeatureList::IsEnabled(features::kIsolatedWebApps);
}
} // namespace
std::string GetShellLanguage() {
return "en-us,en";
}
blink::UserAgentMetadata GetShellUserAgentMetadata() {
blink::UserAgentMetadata metadata;
metadata.brand_version_list.emplace_back("content_shell",
CONTENT_SHELL_MAJOR_VERSION);
metadata.brand_full_version_list.emplace_back("content_shell",
CONTENT_SHELL_VERSION);
metadata.full_version = CONTENT_SHELL_VERSION;
metadata.platform = "Unknown";
metadata.architecture = embedder_support::GetCpuArchitecture();
metadata.model = embedder_support::BuildModelInfo();
metadata.bitness = embedder_support::GetCpuBitness();
metadata.wow64 = embedder_support::IsWoW64();
metadata.form_factors = {"Desktop"};
return metadata;
}
// static
bool ShellContentBrowserClient::allow_any_cors_exempt_header_for_browser_ =
false;
ShellContentBrowserClient* ShellContentBrowserClient::Get() {
auto& instances = GetShellContentBrowserClientInstancesImpl();
return instances.empty() ? nullptr : instances.back();
}
ShellContentBrowserClient::ShellContentBrowserClient() {
GetShellContentBrowserClientInstancesImpl().push_back(this);
}
ShellContentBrowserClient::~ShellContentBrowserClient() {
std::erase(GetShellContentBrowserClientInstancesImpl(), this);
}
std::unique_ptr<BrowserMainParts>
ShellContentBrowserClient::CreateBrowserMainParts(
bool /* is_integration_test */) {
auto browser_main_parts = std::make_unique<ShellBrowserMainParts>();
DCHECK(!GetSharedState().shell_browser_main_parts);
GetSharedState().shell_browser_main_parts = browser_main_parts.get();
return browser_main_parts;
}
bool ShellContentBrowserClient::HasCustomSchemeHandler(
content::BrowserContext* browser_context,
const std::string& scheme) {
if (custom_handlers::ProtocolHandlerRegistry* protocol_handler_registry =
custom_handlers::SimpleProtocolHandlerRegistryFactory::
GetForBrowserContext(browser_context)) {
return protocol_handler_registry->IsHandledProtocol(scheme);
}
return false;
}
std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
ShellContentBrowserClient::CreateURLLoaderThrottles(
const network::ResourceRequest& request,
BrowserContext* browser_context,
const base::RepeatingCallback<WebContents*()>& wc_getter,
NavigationUIData* navigation_ui_data,
FrameTreeNodeId frame_tree_node_id,
std::optional<int64_t> navigation_id) {
std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
auto* factory = custom_handlers::SimpleProtocolHandlerRegistryFactory::
GetForBrowserContext(browser_context);
// null in unit tests.
if (factory) {
result.push_back(
std::make_unique<custom_handlers::ProtocolHandlerThrottle>(*factory));
}
return result;
}
bool ShellContentBrowserClient::IsHandledURL(const GURL& url) {
if (!url.is_valid()) {
return false;
}
constexpr auto kProtocolList = base::MakeFixedFlatSet<std::string_view>({
url::kHttpScheme,
url::kHttpsScheme,
url::kWsScheme,
url::kWssScheme,
url::kBlobScheme,
url::kFileSystemScheme,
kChromeUIScheme,
kChromeUIUntrustedScheme,
kChromeDevToolsScheme,
url::kDataScheme,
url::kFileScheme,
});
return kProtocolList.contains(url.scheme_piece());
}
bool ShellContentBrowserClient::AreIsolatedWebAppsEnabled(
BrowserContext* browser_context) {
return ::content::AreIsolatedWebAppsEnabled();
}
void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
base::CommandLine* command_line,
int child_process_id) {
static const char* const kForwardSwitches[] = {
#if BUILDFLAG(IS_MAC)
// Needed since on Mac, content_browsertests doesn't use
// content_test_launcher.cc and instead uses shell_main.cc. So give a
// signal
// to shell_main.cc that it's a browser test.
switches::kBrowserTest,
#endif
switches::kCrashDumpsDir,
switches::kEnableCrashReporter,
switches::kExposeInternalsForTesting,
switches::kRunWebTests,
switches::kTestRegisterStandardScheme,
};
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
kForwardSwitches);
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableCrashReporter)) {
int fd;
pid_t pid;
if (crash_reporter::GetHandlerSocket(&fd, &pid)) {
command_line->AppendSwitchASCII(
crash_reporter::switches::kCrashpadHandlerPid,
base::NumberToString(pid));
}
}
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
if (command_line->GetSwitchValueASCII(switches::kProcessType) ==
switches::kRendererProcess &&
::content::AreIsolatedWebAppsEnabled()) {
command_line->AppendSwitch(switches::kEnableIsolatedWebAppsInRenderer);
}
#if BUILDFLAG(IS_IOS)
if (command_line->GetSwitchValueASCII(switches::kProcessType) ==
switches::kRendererProcess &&
!IsJITEnabled()) {
command_line->AppendSwitchASCII(blink::switches::kJavaScriptFlags,
"--jitless");
}
#endif
}
#if BUILDFLAG(IS_IOS)
bool ShellContentBrowserClient::IsJITEnabled() {
return GetSharedState().local_state->GetBoolean(kJITEnabled);
}
void ShellContentBrowserClient::SetJITEnabled(bool value) {
GetSharedState().local_state->SetBoolean(kJITEnabled, value);
GetSharedState().local_state->CommitPendingWrite();
}
#endif
device::GeolocationSystemPermissionManager*
ShellContentBrowserClient::GetGeolocationSystemPermissionManager() {
#if BUILDFLAG(IS_MAC)
return GetSharedState().location_manager.get();
#elif BUILDFLAG(IS_IOS)
// TODO(crbug.com/1431447, 1411704): Unify this to
// FakeGeolocationSystemPermissionManager once exploring browser features in
// ContentShell on iOS is done.
return GetSharedState()
.shell_browser_main_parts->GetGeolocationSystemPermissionManager();
#else
return nullptr;
#endif
}
std::string ShellContentBrowserClient::GetAcceptLangs(BrowserContext* context) {
return GetShellLanguage();
}
std::string ShellContentBrowserClient::GetDefaultDownloadName() {
return "download";
}
std::unique_ptr<WebContentsViewDelegate>
ShellContentBrowserClient::GetWebContentsViewDelegate(
WebContents* web_contents) {
return CreateShellWebContentsViewDelegate(web_contents);
}
bool ShellContentBrowserClient::IsIsolatedContextAllowedForUrl(
BrowserContext* browser_context,
const GURL& lock_url) {
static base::flat_set<url::Origin> isolated_context_origins =
GetIsolatedContextOriginSetFromFlag();
return isolated_context_origins.contains(url::Origin::Create(lock_url));
}
bool ShellContentBrowserClient::IsSharedStorageAllowed(
content::BrowserContext* browser_context,
content::RenderFrameHost* rfh,
const url::Origin& top_frame_origin,
const url::Origin& accessing_origin,
std::string* out_debug_message,
bool* out_block_is_site_setting_specific) {
return true;
}
bool ShellContentBrowserClient::IsSharedStorageSelectURLAllowed(
content::BrowserContext* browser_context,
const url::Origin& top_frame_origin,
const url::Origin& accessing_origin,
std::string* out_debug_message,
bool* out_block_is_site_setting_specific) {
return true;
}
bool ShellContentBrowserClient::IsFencedStorageReadAllowed(
content::BrowserContext* browser_context,
content::RenderFrameHost* rfh,
const url::Origin& top_frame_origin,
const url::Origin& accessing_origin) {
return true;
}
bool ShellContentBrowserClient::IsCookieDeprecationLabelAllowed(
content::BrowserContext* browser_context) {
return true;
}
bool ShellContentBrowserClient::IsCookieDeprecationLabelAllowedForContext(
content::BrowserContext* browser_context,
const url::Origin& top_frame_origin,
const url::Origin& context_origin) {
return true;
}
GeneratedCodeCacheSettings
ShellContentBrowserClient::GetGeneratedCodeCacheSettings(
content::BrowserContext* context) {
// If we pass 0 for size, disk_cache will pick a default size using the
// heuristics based on available disk size. These are implemented in
// disk_cache::PreferredCacheSize in net/disk_cache/cache_util.cc.
return GeneratedCodeCacheSettings(true, 0, context->GetPath());
}
base::OnceClosure ShellContentBrowserClient::SelectClientCertificate(
BrowserContext* browser_context,
int process_id,
WebContents* web_contents,
net::SSLCertRequestInfo* cert_request_info,
net::ClientCertIdentityList client_certs,
std::unique_ptr<ClientCertificateDelegate> delegate) {
if (select_client_certificate_callback_ && web_contents) {
return std::move(select_client_certificate_callback_)
.Run(web_contents, cert_request_info, std::move(client_certs),
std::move(delegate));
}
return base::OnceClosure();
}
SpeechRecognitionManagerDelegate*
ShellContentBrowserClient::CreateSpeechRecognitionManagerDelegate() {
return new ShellSpeechRecognitionManagerDelegate();
}
void ShellContentBrowserClient::OverrideWebPreferences(
WebContents* web_contents,
SiteInstance& main_frame_site,
blink::web_pref::WebPreferences* prefs) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kForceDarkMode)) {
prefs->preferred_color_scheme = blink::mojom::PreferredColorScheme::kDark;
} else {
prefs->preferred_color_scheme = blink::mojom::PreferredColorScheme::kLight;
}
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kForceHighContrast)) {
prefs->in_forced_colors = true;
prefs->preferred_contrast = blink::mojom::PreferredContrast::kMore;
} else {
prefs->in_forced_colors = false;
prefs->preferred_contrast = blink::mojom::PreferredContrast::kNoPreference;
}
}
std::unique_ptr<content::DevToolsManagerDelegate>
ShellContentBrowserClient::CreateDevToolsManagerDelegate() {
return std::make_unique<ShellDevToolsManagerDelegate>(browser_context());
}
void ShellContentBrowserClient::ExposeInterfacesToRenderer(
service_manager::BinderRegistry* registry,
blink::AssociatedInterfaceRegistry* associated_registry,
RenderProcessHost* render_process_host) {
PerformanceManagerRegistry::GetInstance()->CreateProcessNode(
render_process_host);
PerformanceManagerRegistry::GetInstance()
->GetBinders()
.ExposeInterfacesToRendererProcess(registry, render_process_host);
}
void ShellContentBrowserClient::ExposeInterfacesToChild(
mojo::BinderMapWithContext<content::BrowserChildProcessHost*>* map) {
PerformanceManagerRegistry::GetInstance()
->GetBinders()
.ExposeInterfacesToBrowserChildProcess(map);
}
mojo::Remote<::media::mojom::MediaService>
ShellContentBrowserClient::RunSecondaryMediaService() {
mojo::Remote<::media::mojom::MediaService> remote;
#if BUILDFLAG(ENABLE_CAST_RENDERER)
static base::SequenceLocalStorageSlot<std::unique_ptr<::media::MediaService>>
service;
service.emplace(::media::CreateMediaServiceForTesting(
remote.BindNewPipeAndPassReceiver()));
#endif
return remote;
}
void ShellContentBrowserClient::RegisterBrowserInterfaceBindersForFrame(
RenderFrameHost* render_frame_host,
mojo::BinderMapWithContext<RenderFrameHost*>* map) {
PerformanceManagerRegistry::GetInstance()
->GetBinders()
.ExposeInterfacesToRenderFrame(map);
map->Add<network_hints::mojom::NetworkHintsHandler>(&BindNetworkHintsHandler);
#if BUILDFLAG(IS_WIN)
map->Add<media::mojom::MediaFoundationPreferences>(
&BindMediaFoundationPreferences);
#endif // BUILDFLAG(IS_WIN)
}
void ShellContentBrowserClient::OpenURL(
SiteInstance* site_instance,
const OpenURLParams& params,
base::OnceCallback<void(WebContents*)> callback) {
std::move(callback).Run(
Shell::CreateNewWindow(site_instance->GetBrowserContext(), params.url,
nullptr, gfx::Size())
->web_contents());
}
void ShellContentBrowserClient::CreateThrottlesForNavigation(
NavigationThrottleRegistry& registry) {
if (create_throttles_for_navigation_callback_) {
create_throttles_for_navigation_callback_.Run(registry);
}
}
std::unique_ptr<LoginDelegate> ShellContentBrowserClient::CreateLoginDelegate(
const net::AuthChallengeInfo& auth_info,
WebContents* web_contents,
BrowserContext* browser_context,
const GlobalRequestID& request_id,
bool is_request_for_primary_main_frame_navigation,
bool is_request_for_navigation,
const GURL& url,
scoped_refptr<net::HttpResponseHeaders> response_headers,
bool first_auth_attempt,
GuestPageHolder* guest,
LoginDelegate::LoginAuthRequiredCallback auth_required_callback) {
if (!login_request_callback_.is_null()) {
std::move(login_request_callback_)
.Run(is_request_for_primary_main_frame_navigation,
is_request_for_navigation);
}
return nullptr;
}
base::Value::Dict ShellContentBrowserClient::GetNetLogConstants() {
base::Value::Dict client_constants;
client_constants.Set("name", "content_shell");
base::CommandLine::StringType command_line =
base::CommandLine::ForCurrentProcess()->GetCommandLineString();
#if BUILDFLAG(IS_WIN)
client_constants.Set("command_line", base::WideToUTF8(command_line));
#else
client_constants.Set("command_line", command_line);
#endif
base::Value::Dict constants;
constants.Set("clientInfo", std::move(client_constants));
return constants;
}
base::FilePath
ShellContentBrowserClient::GetSandboxedStorageServiceDataDirectory() {
return browser_context()->GetPath();
}
base::FilePath ShellContentBrowserClient::GetFirstPartySetsDirectory() {
return browser_context()->GetPath();
}
std::optional<base::FilePath>
ShellContentBrowserClient::GetLocalTracesDirectory() {
return browser_context()->GetPath();
}
std::string ShellContentBrowserClient::GetUserAgent() {
const auto custom_ua = embedder_support::GetUserAgentFromCommandLine();
if (custom_ua.has_value()) {
return custom_ua.value();
}
std::string product =
base::StringPrintf("Chrome/%s.0.0.0", CONTENT_SHELL_MAJOR_VERSION);
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
embedder_support::kUseMobileUserAgent)) {
product += " Mobile";
}
#endif
return embedder_support::BuildUnifiedPlatformUserAgentFromProduct(product);
}
blink::UserAgentMetadata ShellContentBrowserClient::GetUserAgentMetadata() {
return GetShellUserAgentMetadata();
}
void ShellContentBrowserClient::OverrideURLLoaderFactoryParams(
BrowserContext* browser_context,
const url::Origin& origin,
bool is_for_isolated_world,
bool is_for_service_worker,
network::mojom::URLLoaderFactoryParams* factory_params) {
if (url_loader_factory_params_callback_) {
url_loader_factory_params_callback_.Run(
factory_params, origin, is_for_isolated_world, is_for_service_worker);
}
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
void ShellContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
const base::CommandLine& command_line,
int child_process_id,
content::PosixFileDescriptorInfo* mappings) {
#if BUILDFLAG(IS_ANDROID)
mappings->ShareWithRegion(
kShellPakDescriptor,
base::GlobalDescriptors::GetInstance()->Get(kShellPakDescriptor),
base::GlobalDescriptors::GetInstance()->GetRegion(kShellPakDescriptor));
#endif
int crash_signal_fd = GetCrashSignalFD(command_line);
if (crash_signal_fd >= 0) {
mappings->Share(kCrashDumpSignal, crash_signal_fd);
}
}
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
// BUILDFLAG(IS_ANDROID)
// Note that ShellContentBrowserClient overrides this method to work around
// test flakiness that happens when NetworkService::SetTestDohConfigForTesting()
// is used.
// TODO(crbug.com/41494161): Remove that override once the flakiness is fixed.
void ShellContentBrowserClient::OnNetworkServiceCreated(
network::mojom::NetworkService* network_service) {
// TODO(bashi): Consider enabling this for Android. Excluded because the
// built-in resolver may not work on older SDK versions.
#if !BUILDFLAG(IS_ANDROID)
if (base::FeatureList::IsEnabled(net::features::kAsyncDns)) {
network_service->ConfigureStubHostResolver(
/*insecure_dns_client_enabled=*/true,
base::FeatureList::IsEnabled(net::features::kHappyEyeballsV3),
/*secure_dns_mode=*/net::SecureDnsMode::kAutomatic,
net::DnsOverHttpsConfig(),
/*additional_dns_types_enabled=*/true);
}
#endif
}
void ShellContentBrowserClient::ConfigureNetworkContextParams(
BrowserContext* context,
bool in_memory,
const base::FilePath& relative_partition_path,
network::mojom::NetworkContextParams* network_context_params,
cert_verifier::mojom::CertVerifierCreationParams*
cert_verifier_creation_params) {
ConfigureNetworkContextParamsForShell(context, network_context_params,
cert_verifier_creation_params);
}
std::vector<base::FilePath>
ShellContentBrowserClient::GetNetworkContextsParentDirectory() {
return {browser_context()->GetPath()};
}
#if BUILDFLAG(IS_IOS)
BluetoothDelegate* ShellContentBrowserClient::GetBluetoothDelegate() {
if (!bluetooth_delegate_) {
bluetooth_delegate_ = std::make_unique<permissions::BluetoothDelegateImpl>(
std::make_unique<ShellBluetoothDelegateImplClient>());
}
return bluetooth_delegate_.get();
}
#endif
void ShellContentBrowserClient::BindBrowserControlInterface(
mojo::ScopedMessagePipeHandle pipe) {
if (!pipe.is_valid()) {
return;
}
mojo::MakeSelfOwnedReceiver(
std::make_unique<ShellControllerImpl>(),
mojo::PendingReceiver<mojom::ShellController>(std::move(pipe)));
}
void ShellContentBrowserClient::set_browser_main_parts(
ShellBrowserMainParts* parts) {
GetSharedState().shell_browser_main_parts = parts;
}
ShellBrowserContext* ShellContentBrowserClient::browser_context() {
return GetSharedState().shell_browser_main_parts->browser_context();
}
ShellBrowserContext*
ShellContentBrowserClient::off_the_record_browser_context() {
return GetSharedState()
.shell_browser_main_parts->off_the_record_browser_context();
}
ShellBrowserMainParts* ShellContentBrowserClient::shell_browser_main_parts() {
return GetSharedState().shell_browser_main_parts;
}
void ShellContentBrowserClient::ConfigureNetworkContextParamsForShell(
BrowserContext* context,
network::mojom::NetworkContextParams* context_params,
cert_verifier::mojom::CertVerifierCreationParams*
cert_verifier_creation_params) {
context_params->allow_any_cors_exempt_header_for_browser =
allow_any_cors_exempt_header_for_browser_;
context_params->user_agent = GetUserAgent();
context_params->accept_language = GetAcceptLangs(context);
context_params->enable_zstd = true;
auto exempt_header =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
"cors_exempt_header_list");
if (!exempt_header.empty()) {
context_params->cors_exempt_header_list.push_back(exempt_header);
}
context_params->device_bound_sessions_enabled =
base::FeatureList::IsEnabled(net::features::kDeviceBoundSessions);
}
void ShellContentBrowserClient::GetHyphenationDictionary(
base::OnceCallback<void(const base::FilePath&)> callback) {
// If we have the source tree, return the dictionary files in the tree.
base::FilePath dir;
if (base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &dir)) {
dir = dir.AppendASCII("third_party")
.AppendASCII("hyphenation-patterns")
.AppendASCII("hyb");
std::move(callback).Run(dir);
}
// No need to callback if there were no dictionaries.
}
bool ShellContentBrowserClient::HasErrorPage(int http_status_code) {
return http_status_code >= 400 && http_status_code < 600;
}
void ShellContentBrowserClient::OnWebContentsCreated(
WebContents* web_contents) {
PerformanceManagerRegistry::GetInstance()->MaybeCreatePageNodeForWebContents(
web_contents);
}
void ShellContentBrowserClient::CreateFeatureListAndFieldTrials() {
GetSharedState().local_state = CreateLocalState();
SetUpFieldTrials();
// Schedule a Local State write since the above function resulted in some
// prefs being updated.
GetSharedState().local_state->CommitPendingWrite();
}
void ShellContentBrowserClient::SetUpFieldTrials() {
metrics::TestEnabledStateProvider enabled_state_provider(/*consent=*/false,
/*enabled=*/false);
base::FilePath user_data_dir;
base::PathService::Get(SHELL_DIR_USER_DATA, &user_data_dir);
std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager =
metrics::MetricsStateManager::Create(
GetSharedState().local_state.get(), &enabled_state_provider,
std::wstring(), user_data_dir, metrics::StartupVisibility::kUnknown,
{
.force_benchmarking_mode =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableGpuBenchmarking),
});
metrics_state_manager->InstantiateFieldTrialList();
std::vector<std::string> variation_ids;
auto feature_list = std::make_unique<base::FeatureList>();
std::unique_ptr<variations::SeedResponse> initial_seed;
#if BUILDFLAG(IS_ANDROID)
if (!GetSharedState().local_state->HasPrefPath(
variations::prefs::kVariationsSeedSignature)) {
DVLOG(1) << "Importing first run seed from Java preferences.";
initial_seed = variations::android::GetVariationsFirstRunSeed();
}
#endif
ShellVariationsServiceClient variations_service_client;
variations::VariationsFieldTrialCreator field_trial_creator(
&variations_service_client,
std::make_unique<variations::VariationsSeedStore>(
GetSharedState().local_state.get(), std::move(initial_seed),
/*signature_verification_enabled=*/true,
std::make_unique<variations::VariationsSafeSeedStoreLocalState>(
GetSharedState().local_state.get(),
variations_service_client.GetVariationsSeedFileDir(),
variations_service_client.GetChannelForVariations(),
/*entropy_providers=*/nullptr),
variations_service_client.GetChannelForVariations(),
variations_service_client.GetVariationsSeedFileDir()),
variations::UIStringOverrider());
variations::SafeSeedManager safe_seed_manager(
GetSharedState().local_state.get());
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
// Overrides for content/common and lower layers' switches.
std::vector<base::FeatureList::FeatureOverrideInfo> feature_overrides =
content::GetSwitchDependentFeatureOverrides(command_line);
// Overrides for content/shell switches.
// Overrides for --run-web-tests.
if (switches::IsRunWebTestsSwitchPresent()) {
// Disable artificial timeouts for PNA-only preflights in warning-only mode
// for web tests. We do not exercise this behavior with web tests as it is
// intended to be a temporary rollout stage, and the short timeout causes
// flakiness when the test server takes just a tad too long to respond.
feature_overrides.emplace_back(
std::cref(
network::features::kPrivateNetworkAccessPreflightShortTimeout),
base::FeatureList::OVERRIDE_DISABLE_FEATURE);
}
// Since this is a test-only code path, some arguments to SetUpFieldTrials are
// null.
// TODO(crbug.com/40790318): Consider passing a low entropy source.
variations::PlatformFieldTrials platform_field_trials;
field_trial_creator.SetUpFieldTrials(
variation_ids,
command_line.GetSwitchValueASCII(
variations::switches::kForceVariationIds),
feature_overrides, std::move(feature_list), metrics_state_manager.get(),
&platform_field_trials, &safe_seed_manager,
/*add_entropy_source_to_variations_ids=*/false,
*metrics_state_manager->CreateEntropyProviders(
/*enable_limited_entropy_mode=*/false));
}
std::optional<network::ParsedPermissionsPolicy>
ShellContentBrowserClient::GetPermissionsPolicyForIsolatedWebApp(
WebContents* web_contents,
const url::Origin& app_origin) {
network::ParsedPermissionsPolicyDeclaration coi_decl(
network::mojom::PermissionsPolicyFeature::kCrossOriginIsolated,
/*allowed_origins=*/{},
/*self_if_matches=*/std::nullopt,
/*matches_all_origins=*/true, /*matches_opaque_src=*/false);
network::ParsedPermissionsPolicyDeclaration socket_decl(
network::mojom::PermissionsPolicyFeature::kDirectSockets,
/*allowed_origins=*/{}, app_origin,
/*matches_all_origins=*/false, /*matches_opaque_src=*/false);
return {{coi_decl, socket_decl}};
}
// Tests may install their own ShellContentBrowserClient, track the list here.
// The list is ordered with oldest first and newer ones added after it.
// static
const std::vector<ShellContentBrowserClient*>&
ShellContentBrowserClient::GetShellContentBrowserClientInstances() {
return GetShellContentBrowserClientInstancesImpl();
}
} // namespace content