[go: nahoru, domu]

blob: 2663278131065f849b6a1c9d3f37ba2d1627a3df [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "headless/lib/browser/headless_browser_impl.h"
#include <string>
#include <utility>
#include <vector>
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "components/embedder_support/user_agent_utils.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/user_agent.h"
#include "headless/lib/browser/headless_browser_context_impl.h"
#include "headless/lib/browser/headless_web_contents_impl.h"
#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
#if BUILDFLAG(IS_WIN)
#include "base/command_line.h"
#include "headless/public/switches.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "services/device/public/cpp/geolocation/geolocation_manager.h"
#endif
#if defined(HEADLESS_USE_PREFS)
#include "components/os_crypt/sync/os_crypt.h" // nogncheck
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/in_memory_pref_store.h"
#include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_service_factory.h"
#endif
#if defined(HEADLESS_USE_POLICY)
#include "components/headless/policy/headless_mode_policy.h" // nogncheck
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "headless/lib/browser/policy/headless_policies.h"
#endif
namespace headless {
namespace {
// Product name for building the default user agent string.
const char kHeadlessProductName[] = "HeadlessChrome";
constexpr gfx::Size kDefaultWindowSize(800, 600);
constexpr gfx::FontRenderParams::Hinting kDefaultFontRenderHinting =
gfx::FontRenderParams::Hinting::HINTING_FULL;
#if defined(HEADLESS_USE_PREFS)
const base::FilePath::CharType kLocalStateFilename[] =
FILE_PATH_LITERAL("Local State");
#endif
} // namespace
using Options = HeadlessBrowser::Options;
using Builder = HeadlessBrowser::Options::Builder;
Options::Options()
: user_agent(content::BuildUserAgentFromProduct(
HeadlessBrowser::GetProductNameAndVersion())),
window_size(kDefaultWindowSize),
font_render_hinting(kDefaultFontRenderHinting) {}
Options::Options(Options&& options) = default;
Options::~Options() = default;
Options& Options::operator=(Options&& options) = default;
bool Options::DevtoolsServerEnabled() {
return (devtools_pipe_enabled || !devtools_endpoint.IsEmpty());
}
Builder::Builder() = default;
Builder::~Builder() = default;
Builder& Builder::SetUserAgent(const std::string& agent) {
options_.user_agent = agent;
return *this;
}
Builder& Builder::SetEnableLazyLoading(bool enable) {
options_.lazy_load_enabled = enable;
return *this;
}
Builder& Builder::SetAcceptLanguage(const std::string& language) {
options_.accept_language = language;
return *this;
}
Builder& Builder::SetEnableBeginFrameControl(bool enable) {
options_.enable_begin_frame_control = enable;
return *this;
}
Builder& Builder::EnableDevToolsServer(const net::HostPortPair& endpoint) {
options_.devtools_endpoint = endpoint;
return *this;
}
Builder& Builder::EnableDevToolsPipe() {
options_.devtools_pipe_enabled = true;
return *this;
}
Builder& Builder::SetProxyConfig(std::unique_ptr<net::ProxyConfig> config) {
options_.proxy_config = std::move(config);
return *this;
}
Builder& Builder::SetUserDataDir(const base::FilePath& dir) {
options_.user_data_dir = dir;
return *this;
}
Builder& Builder::SetDiskCacheDir(const base::FilePath& dir) {
options_.disk_cache_dir = dir;
return *this;
}
Builder& Builder::SetWindowSize(const gfx::Size& size) {
options_.window_size = size;
return *this;
}
Builder& Builder::SetIncognitoMode(bool incognito) {
options_.incognito_mode = incognito;
return *this;
}
Builder& Builder::SetBlockNewWebContents(bool block) {
options_.block_new_web_contents = block;
return *this;
}
Builder& Builder::SetFontRenderHinting(gfx::FontRenderParams::Hinting hinting) {
options_.font_render_hinting = hinting;
return *this;
}
Options Builder::Build() {
return std::move(options_);
}
/// static
std::string HeadlessBrowser::GetProductNameAndVersion() {
return std::string(kHeadlessProductName) + "/" + PRODUCT_VERSION;
}
/// static
blink::UserAgentMetadata HeadlessBrowser::GetUserAgentMetadata() {
auto metadata = embedder_support::GetUserAgentMetadata(nullptr);
// Skip override brand version information if components' API returns a blank
// UserAgentMetadata.
if (metadata == blink::UserAgentMetadata()) {
return metadata;
}
std::string significant_version = version_info::GetMajorVersionNumber();
constexpr bool kEnableUpdatedGreaseByPolicy = true;
// Use the major version number as a greasing seed
int seed = 1;
bool got_seed = base::StringToInt(significant_version, &seed);
DCHECK(got_seed);
// Rengenerate the brand version lists with kHeadlessProductName.
metadata.brand_version_list = embedder_support::GenerateBrandVersionList(
seed, kHeadlessProductName, significant_version, absl::nullopt,
absl::nullopt, kEnableUpdatedGreaseByPolicy,
blink::UserAgentBrandVersionType::kMajorVersion);
metadata.brand_full_version_list = embedder_support::GenerateBrandVersionList(
seed, kHeadlessProductName, metadata.full_version, absl::nullopt,
absl::nullopt, kEnableUpdatedGreaseByPolicy,
blink::UserAgentBrandVersionType::kFullVersion);
return metadata;
}
HeadlessBrowserImpl::HeadlessBrowserImpl(
base::OnceCallback<void(HeadlessBrowser*)> on_start_callback)
: on_start_callback_(std::move(on_start_callback)) {}
HeadlessBrowserImpl::~HeadlessBrowserImpl() = default;
void HeadlessBrowserImpl::SetOptions(HeadlessBrowser::Options options) {
options_ = std::move(options);
}
HeadlessBrowserContext::Builder
HeadlessBrowserImpl::CreateBrowserContextBuilder() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return HeadlessBrowserContext::Builder(this);
}
scoped_refptr<base::SingleThreadTaskRunner>
HeadlessBrowserImpl::BrowserMainThread() const {
return content::GetUIThreadTaskRunner({});
}
void HeadlessBrowserImpl::Shutdown() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
weak_ptr_factory_.InvalidateWeakPtrs();
// Make sure GetAllBrowserContexts is sane if called after this point.
auto tmp = std::move(browser_contexts_);
tmp.clear();
if (system_request_context_manager_) {
content::GetIOThreadTaskRunner({})->DeleteSoon(
FROM_HERE, system_request_context_manager_.release());
}
// We might have posted task during shutdown, let these run
// before quitting the message loop. See ~HeadlessWebContentsImpl
// for additional context.
if (quit_main_message_loop_) {
BrowserMainThread()->PostTask(FROM_HERE,
std::move(quit_main_message_loop_));
}
}
void HeadlessBrowserImpl::ShutdownWithExitCode(int exit_code) {
exit_code_ = exit_code;
Shutdown();
}
std::vector<HeadlessBrowserContext*>
HeadlessBrowserImpl::GetAllBrowserContexts() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::vector<HeadlessBrowserContext*> result;
result.reserve(browser_contexts_.size());
for (const auto& browser_context_pair : browser_contexts_) {
result.push_back(browser_context_pair.second.get());
}
return result;
}
HeadlessBrowserContext* HeadlessBrowserImpl::CreateBrowserContext(
HeadlessBrowserContext::Builder* builder) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto browser_context = HeadlessBrowserContextImpl::Create(builder);
HeadlessBrowserContext* result = browser_context.get();
browser_contexts_[browser_context->Id()] = std::move(browser_context);
return result;
}
void HeadlessBrowserImpl::DestroyBrowserContext(
HeadlessBrowserContextImpl* browser_context) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
int erased = browser_contexts_.erase(browser_context->Id());
DCHECK(erased);
if (default_browser_context_ == browser_context)
SetDefaultBrowserContext(nullptr);
}
void HeadlessBrowserImpl::SetDefaultBrowserContext(
HeadlessBrowserContext* browser_context) {
DCHECK(!browser_context ||
this == HeadlessBrowserContextImpl::From(browser_context)->browser());
default_browser_context_ = browser_context;
if (default_browser_context_ && !system_request_context_manager_) {
system_request_context_manager_ =
HeadlessRequestContextManager::CreateSystemContext(
HeadlessBrowserContextImpl::From(browser_context)->options());
}
}
HeadlessBrowserContext* HeadlessBrowserImpl::GetDefaultBrowserContext() {
return default_browser_context_;
}
base::WeakPtr<HeadlessBrowserImpl> HeadlessBrowserImpl::GetWeakPtr() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return weak_ptr_factory_.GetWeakPtr();
}
HeadlessWebContents* HeadlessBrowserImpl::GetWebContentsForDevToolsAgentHostId(
const std::string& devtools_agent_host_id) {
for (HeadlessBrowserContext* context : GetAllBrowserContexts()) {
HeadlessWebContents* web_contents =
context->GetWebContentsForDevToolsAgentHostId(devtools_agent_host_id);
if (web_contents)
return web_contents;
}
return nullptr;
}
HeadlessWebContentsImpl* HeadlessBrowserImpl::GetWebContentsForWindowId(
const int window_id) {
for (HeadlessBrowserContext* context : GetAllBrowserContexts()) {
for (HeadlessWebContents* web_contents : context->GetAllWebContents()) {
auto* contents = HeadlessWebContentsImpl::From(web_contents);
if (contents->window_id() == window_id) {
return contents;
}
}
}
return nullptr;
}
HeadlessBrowserContext* HeadlessBrowserImpl::GetBrowserContextForId(
const std::string& id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto find_it = browser_contexts_.find(id);
if (find_it == browser_contexts_.end())
return nullptr;
return find_it->second.get();
}
bool HeadlessBrowserImpl::ShouldStartDevToolsServer() {
if (!options()->DevtoolsServerEnabled()) {
return false;
}
#if defined(HEADLESS_USE_POLICY)
CHECK(local_state_);
if (!IsRemoteDebuggingAllowed(local_state_.get())) {
// Follow content/browser/devtools/devtools_http_handler.cc that reports its
// remote debugging port on stderr for symmetry.
fputs("\nDevTools remote debugging is disallowed by the system admin.\n",
stderr);
fflush(stderr);
return false;
}
#endif
return true;
}
void HeadlessBrowserImpl::PreMainMessageLoopRun() {
PlatformInitialize();
#if defined(HEADLESS_USE_PREFS)
CreatePrefService();
#endif
// We don't support the tethering domain on this agent host.
agent_host_ = content::DevToolsAgentHost::CreateForBrowser(
nullptr, content::DevToolsAgentHost::CreateServerSocketCallback());
PlatformStart();
std::move(on_start_callback_).Run(this);
}
void HeadlessBrowserImpl::WillRunMainMessageLoop(base::RunLoop& run_loop) {
quit_main_message_loop_ = run_loop.QuitClosure();
}
void HeadlessBrowserImpl::PostMainMessageLoopRun() {
#if defined(HEADLESS_USE_PREFS)
if (local_state_) {
local_state_->CommitPendingWrite();
local_state_.reset(nullptr);
}
#endif
#if defined(HEADLESS_USE_POLICY)
if (policy_connector_) {
policy_connector_->Shutdown();
policy_connector_.reset(nullptr);
}
#endif
}
#if defined(HEADLESS_USE_PREFS)
PrefService* HeadlessBrowserImpl::GetPrefs() {
return local_state_.get();
}
#endif
#if defined(HEADLESS_USE_POLICY)
policy::PolicyService* HeadlessBrowserImpl::GetPolicyService() {
return policy_connector_ ? policy_connector_->GetPolicyService() : nullptr;
}
#endif
#if defined(HEADLESS_USE_PREFS)
void HeadlessBrowserImpl::CreatePrefService() {
scoped_refptr<PersistentPrefStore> pref_store;
if (options()->user_data_dir.empty()) {
pref_store = base::MakeRefCounted<InMemoryPrefStore>();
} else {
base::FilePath local_state_file =
options()->user_data_dir.Append(kLocalStateFilename);
pref_store = base::MakeRefCounted<JsonPrefStore>(
local_state_file,
/*pref_filter=*/nullptr,
/*file_task_runner=*/
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
/*read_only=*/true);
auto result = pref_store->ReadPrefs();
if (result != JsonPrefStore::PREF_READ_ERROR_NONE &&
result != JsonPrefStore::PREF_READ_ERROR_NO_FILE) {
LOG(ERROR) << "Failed to read prefs in '" << local_state_file
<< "', error: " << result;
BrowserMainThread()->PostTask(
FROM_HERE,
base::BindOnce(&HeadlessBrowserImpl::ShutdownWithExitCode,
weak_ptr_factory_.GetWeakPtr(), EXIT_FAILURE));
return;
}
}
auto pref_registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
#if BUILDFLAG(IS_WIN)
OSCrypt::RegisterLocalPrefs(pref_registry.get());
#endif
PrefServiceFactory factory;
#if defined(HEADLESS_USE_POLICY)
RegisterHeadlessPrefs(pref_registry.get());
policy_connector_ =
std::make_unique<policy::HeadlessBrowserPolicyConnector>();
factory.set_managed_prefs(
policy_connector_->CreatePrefStore(policy::POLICY_LEVEL_MANDATORY));
BrowserContextDependencyManager::GetInstance()
->RegisterProfilePrefsForServices(pref_registry.get());
#endif // defined(HEADLESS_USE_POLICY)
factory.set_user_prefs(pref_store);
local_state_ = factory.Create(std::move(pref_registry));
#if BUILDFLAG(IS_WIN)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (!command_line->HasSwitch(switches::kDisableCookieEncryption) &&
OSCrypt::InitWithExistingKey(local_state_.get()) != OSCrypt::kSuccess) {
command_line->AppendSwitch(switches::kDisableCookieEncryption);
}
#endif // BUILDFLAG(IS_WIN)
}
#endif // defined(HEADLESS_USE_PREFS)
} // namespace headless