blob: ffc6c6161f040bd2ce9d6e1de98c28cf4b6ab13b [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// 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 <utility>
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/sequence_local_storage_slot.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/performance_manager/embedder/performance_manager_registry.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/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/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/user_agent.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_quota_permission_context.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_switches.h"
#include "media/mojo/buildflags.h"
#include "media/mojo/mojom/media_service.mojom.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/ssl/client_cert_identity.h"
#include "services/device/public/cpp/geolocation/location_system_permission_status.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "third_party/blink/public/common/features.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 defined(OS_ANDROID)
#include "base/android/apk_assets.h"
#include "base/android/path_utils.h"
#include "content/shell/android/shell_descriptors.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "content/public/browser/context_factory.h"
#endif
#if defined(OS_ANDROID)
#include "components/crash/content/browser/crash_handler_host_linux.h"
#endif
#if defined(OS_MAC)
#include "services/device/public/cpp/test/fake_geolocation_manager.h"
#endif
#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_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
namespace content {
namespace {
ShellContentBrowserClient* g_browser_client = nullptr;
bool g_enable_expect_ct_for_testing = false;
#if defined(OS_ANDROID)
int GetCrashSignalFD(const base::CommandLine& command_line) {
return crashpad::CrashHandlerHost::Get()->GetDeathSignalSocket();
}
#elif defined(OS_LINUX) || defined(OS_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(base::nullopt);
}
void ExecuteJavaScript(const std::u16string& script,
ExecuteJavaScriptCallback callback) override {
CHECK(!Shell::windows().empty());
WebContents* contents = Shell::windows()[0]->web_contents();
contents->GetMainFrame()->ExecuteJavaScriptForTests(script,
std::move(callback));
}
void ShutDown() override { Shell::CloseAllWindows(); }
};
} // namespace
std::string GetShellUserAgent() {
std::string product = "Chrome/" CONTENT_SHELL_VERSION;
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (base::FeatureList::IsEnabled(blink::features::kFreezeUserAgent)) {
return content::GetFrozenUserAgent(
command_line->HasSwitch(switches::kUseMobileUserAgent),
CONTENT_SHELL_MAJOR_VERSION);
}
if (command_line->HasSwitch(switches::kUseMobileUserAgent))
product += " Mobile";
return BuildUserAgentFromProduct(product);
}
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.full_version = CONTENT_SHELL_VERSION;
metadata.platform = BuildOSCpuInfo(IncludeAndroidBuildNumber::Exclude,
IncludeAndroidModel::Exclude);
metadata.architecture = BuildCpuInfo();
metadata.model = BuildModelInfo();
return metadata;
}
// static
bool ShellContentBrowserClient::allow_any_cors_exempt_header_for_browser_ =
false;
ShellContentBrowserClient* ShellContentBrowserClient::Get() {
return g_browser_client;
}
ShellContentBrowserClient::ShellContentBrowserClient() {
DCHECK(!g_browser_client);
#if defined(OS_MAC)
location_manager_ = std::make_unique<device::FakeGeolocationManager>();
location_manager_->SetSystemPermission(
device::LocationSystemPermissionStatus::kAllowed);
#endif
g_browser_client = this;
}
ShellContentBrowserClient::~ShellContentBrowserClient() {
g_browser_client = nullptr;
}
std::unique_ptr<BrowserMainParts>
ShellContentBrowserClient::CreateBrowserMainParts(
const MainFunctionParams& parameters) {
auto browser_main_parts = std::make_unique<ShellBrowserMainParts>(parameters);
shell_browser_main_parts_ = browser_main_parts.get();
return browser_main_parts;
}
bool ShellContentBrowserClient::IsHandledURL(const GURL& url) {
if (!url.is_valid())
return false;
static const char* const kProtocolList[] = {
url::kHttpScheme, url::kHttpsScheme, url::kWsScheme,
url::kWssScheme, url::kBlobScheme, url::kFileSystemScheme,
kChromeUIScheme, kChromeUIUntrustedScheme, kChromeDevToolsScheme,
url::kDataScheme, url::kFileScheme,
};
for (const char* supported_protocol : kProtocolList) {
if (url.scheme_piece() == supported_protocol)
return true;
}
return false;
}
void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
base::CommandLine* command_line,
int child_process_id) {
static const char* kForwardSwitches[] = {
#if defined(OS_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,
};
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
kForwardSwitches,
base::size(kForwardSwitches));
#if defined(OS_LINUX) || defined(OS_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 // defined(OS_LINUX) || defined(OS_CHROMEOS)
}
device::GeolocationManager* ShellContentBrowserClient::GetGeolocationManager() {
#if defined(OS_MAC)
return location_manager_.get();
#else
return nullptr;
#endif
}
std::string ShellContentBrowserClient::GetAcceptLangs(BrowserContext* context) {
return GetShellLanguage();
}
std::string ShellContentBrowserClient::GetDefaultDownloadName() {
return "download";
}
WebContentsViewDelegate* ShellContentBrowserClient::GetWebContentsViewDelegate(
WebContents* web_contents) {
performance_manager::PerformanceManagerRegistry::GetInstance()
->MaybeCreatePageNodeForWebContents(web_contents);
return CreateShellWebContentsViewDelegate(web_contents);
}
scoped_refptr<content::QuotaPermissionContext>
ShellContentBrowserClient::CreateQuotaPermissionContext() {
return new ShellQuotaPermissionContext();
}
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(
WebContents* web_contents,
net::SSLCertRequestInfo* cert_request_info,
net::ClientCertIdentityList client_certs,
std::unique_ptr<ClientCertificateDelegate> delegate) {
if (select_client_certificate_callback_)
std::move(select_client_certificate_callback_).Run();
return base::OnceClosure();
}
SpeechRecognitionManagerDelegate*
ShellContentBrowserClient::CreateSpeechRecognitionManagerDelegate() {
return new ShellSpeechRecognitionManagerDelegate();
}
void ShellContentBrowserClient::OverrideWebkitPrefs(
WebContents* web_contents,
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->preferred_contrast = blink::mojom::PreferredContrast::kMore;
} else {
prefs->preferred_contrast = blink::mojom::PreferredContrast::kNoPreference;
}
if (override_web_preferences_callback_)
override_web_preferences_callback_.Run(prefs);
}
base::FilePath ShellContentBrowserClient::GetFontLookupTableCacheDir() {
return browser_context()->GetPath().Append(
FILE_PATH_LITERAL("FontLookupTableCache"));
}
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) {
performance_manager::PerformanceManagerRegistry::GetInstance()
->CreateProcessNodeAndExposeInterfacesToRendererProcess(
registry, render_process_host);
}
mojo::Remote<::media::mojom::MediaService>
ShellContentBrowserClient::RunSecondaryMediaService() {
mojo::Remote<::media::mojom::MediaService> remote;
#if BUILDFLAG(ENABLE_CAST_RENDERER)
static base::NoDestructor<
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) {
performance_manager::PerformanceManagerRegistry::GetInstance()
->ExposeInterfacesToRenderFrame(map);
}
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());
}
std::vector<std::unique_ptr<NavigationThrottle>>
ShellContentBrowserClient::CreateThrottlesForNavigation(
NavigationHandle* navigation_handle) {
std::vector<std::unique_ptr<NavigationThrottle>> empty_throttles;
if (create_throttles_for_navigation_callback_)
return create_throttles_for_navigation_callback_.Run(navigation_handle);
return empty_throttles;
}
std::unique_ptr<LoginDelegate> ShellContentBrowserClient::CreateLoginDelegate(
const net::AuthChallengeInfo& auth_info,
content::WebContents* web_contents,
const content::GlobalRequestID& request_id,
bool is_main_frame,
const GURL& url,
scoped_refptr<net::HttpResponseHeaders> response_headers,
bool first_auth_attempt,
LoginAuthRequiredCallback auth_required_callback) {
if (!login_request_callback_.is_null()) {
std::move(login_request_callback_).Run(is_main_frame);
}
return nullptr;
}
base::DictionaryValue ShellContentBrowserClient::GetNetLogConstants() {
base::DictionaryValue client_constants;
client_constants.SetString("name", "content_shell");
base::CommandLine::StringType command_line =
base::CommandLine::ForCurrentProcess()->GetCommandLineString();
#if defined(OS_WIN)
client_constants.SetString("command_line", base::WideToUTF8(command_line));
#else
client_constants.SetString("command_line", command_line);
#endif
base::DictionaryValue constants;
constants.SetKey("clientInfo", std::move(client_constants));
return constants;
}
base::FilePath
ShellContentBrowserClient::GetSandboxedStorageServiceDataDirectory() {
return browser_context()->GetPath();
}
std::string ShellContentBrowserClient::GetUserAgent() {
return GetShellUserAgent();
}
blink::UserAgentMetadata ShellContentBrowserClient::GetUserAgentMetadata() {
return GetShellUserAgentMetadata();
}
void ShellContentBrowserClient::OverrideURLLoaderFactoryParams(
BrowserContext* browser_context,
const url::Origin& origin,
bool is_for_isolated_world,
network::mojom::URLLoaderFactoryParams* factory_params) {
if (url_loader_factory_params_callback_) {
url_loader_factory_params_callback_.Run(factory_params, origin,
is_for_isolated_world);
}
}
#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
void ShellContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
const base::CommandLine& command_line,
int child_process_id,
content::PosixFileDescriptorInfo* mappings) {
#if defined(OS_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 // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
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()};
}
void ShellContentBrowserClient::BindBrowserControlInterface(
mojo::ScopedMessagePipeHandle pipe) {
if (!pipe.is_valid())
return;
mojo::MakeSelfOwnedReceiver(
std::make_unique<ShellControllerImpl>(),
mojo::PendingReceiver<mojom::ShellController>(std::move(pipe)));
}
ShellBrowserContext* ShellContentBrowserClient::browser_context() {
return shell_browser_main_parts_->browser_context();
}
ShellBrowserContext*
ShellContentBrowserClient::off_the_record_browser_context() {
return shell_browser_main_parts_->off_the_record_browser_context();
}
void ShellContentBrowserClient::set_enable_expect_ct_for_testing(
bool enable_expect_ct_for_testing) {
g_enable_expect_ct_for_testing = enable_expect_ct_for_testing;
}
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);
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);
if (g_enable_expect_ct_for_testing) {
context_params->enforce_chrome_ct_policy = true;
context_params->ct_log_update_time = base::Time::Now();
context_params->enable_expect_ct_reporting = true;
}
}
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_SOURCE_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;
}
} // namespace content