summaryrefslogtreecommitdiff
path: root/libcef/browser/context.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libcef/browser/context.cc')
-rw-r--r--libcef/browser/context.cc624
1 files changed, 624 insertions, 0 deletions
diff --git a/libcef/browser/context.cc b/libcef/browser/context.cc
new file mode 100644
index 00000000..e4539f31
--- /dev/null
+++ b/libcef/browser/context.cc
@@ -0,0 +1,624 @@
+// Copyright (c) 2012 The Chromium Embedded Framework 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 "libcef/browser/context.h"
+
+#include "libcef/browser/browser_info_manager.h"
+#include "libcef/browser/request_context_impl.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/trace_subscriber.h"
+#include "libcef/common/cef_switches.h"
+
+#include "base/files/file_util.h"
+#include "base/functional/bind.h"
+#include "base/run_loop.h"
+#include "base/task/current_thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "components/network_session_configurator/common/network_switches.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "ui/base/ui_base_switches.h"
+
+#if BUILDFLAG(IS_WIN)
+#include "base/debug/alias.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/win_util.h"
+#include "chrome/chrome_elf/chrome_elf_main.h"
+#include "chrome/install_static/initialize_from_primary_module.h"
+#include "include/internal/cef_win.h"
+#endif
+
+namespace {
+
+CefContext* g_context = nullptr;
+
+#if DCHECK_IS_ON()
+// When the process terminates check if CefShutdown() has been called.
+class CefShutdownChecker {
+ public:
+ ~CefShutdownChecker() { DCHECK(!g_context) << "CefShutdown was not called"; }
+} g_shutdown_checker;
+#endif // DCHECK_IS_ON()
+
+#if BUILDFLAG(IS_WIN)
+
+// Transfer state from chrome_elf.dll to the libcef.dll. Accessed when
+// loading chrome://system.
+void InitInstallDetails() {
+ static bool initialized = false;
+ if (initialized) {
+ return;
+ }
+ initialized = true;
+ install_static::InitializeFromPrimaryModule();
+}
+
+// Signal chrome_elf to initialize crash reporting, rather than doing it in
+// DllMain. See https://crbug.com/656800 for details.
+void InitCrashReporter() {
+ static bool initialized = false;
+ if (initialized) {
+ return;
+ }
+ initialized = true;
+ SignalInitializeCrashReporting();
+}
+
+#endif // BUILDFLAG(IS_WIN)
+
+bool GetColor(const cef_color_t cef_in, bool is_windowless, SkColor* sk_out) {
+ // Windowed browser colors must be fully opaque.
+ if (!is_windowless && CefColorGetA(cef_in) != SK_AlphaOPAQUE) {
+ return false;
+ }
+
+ // Windowless browser colors may be fully transparent.
+ if (is_windowless && CefColorGetA(cef_in) == SK_AlphaTRANSPARENT) {
+ *sk_out = SK_ColorTRANSPARENT;
+ return true;
+ }
+
+ // Ignore the alpha component.
+ *sk_out = SkColorSetRGB(CefColorGetR(cef_in), CefColorGetG(cef_in),
+ CefColorGetB(cef_in));
+ return true;
+}
+
+// Convert |path_str| to a normalized FilePath.
+base::FilePath NormalizePath(const cef_string_t& path_str,
+ const char* name,
+ bool* has_error = nullptr) {
+ if (has_error) {
+ *has_error = false;
+ }
+
+ base::FilePath path = base::FilePath(CefString(&path_str));
+ if (path.EndsWithSeparator()) {
+ // Remove the trailing separator because it will interfere with future
+ // equality checks.
+ path = path.StripTrailingSeparators();
+ }
+
+ if (!path.empty() && !path.IsAbsolute()) {
+ LOG(ERROR) << "The " << name << " directory (" << path.value()
+ << ") is not an absolute path. Defaulting to empty.";
+ if (has_error) {
+ *has_error = true;
+ }
+ path = base::FilePath();
+ }
+
+ return path;
+}
+
+void SetPath(cef_string_t& path_str, const base::FilePath& path) {
+#if BUILDFLAG(IS_WIN)
+ CefString(&path_str).FromWString(path.value());
+#else
+ CefString(&path_str).FromString(path.value());
+#endif
+}
+
+// Convert |path_str| to a normalized FilePath and update the |path_str| value.
+base::FilePath NormalizePathAndSet(cef_string_t& path_str, const char* name) {
+ const base::FilePath& path = NormalizePath(path_str, name);
+ SetPath(path_str, path);
+ return path;
+}
+
+// Verify that |cache_path| is valid and create it if necessary.
+bool ValidateCachePath(const base::FilePath& cache_path,
+ const base::FilePath& root_cache_path) {
+ if (cache_path.empty()) {
+ return true;
+ }
+
+ if (!root_cache_path.empty() && root_cache_path != cache_path &&
+ !root_cache_path.IsParent(cache_path)) {
+ LOG(ERROR) << "The cache_path directory (" << cache_path.value()
+ << ") is not a child of the root_cache_path directory ("
+ << root_cache_path.value() << ")";
+ return false;
+ }
+
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ if (!base::DirectoryExists(cache_path) &&
+ !base::CreateDirectory(cache_path)) {
+ LOG(ERROR) << "The cache_path directory (" << cache_path.value()
+ << ") could not be created.";
+ return false;
+ }
+
+ return true;
+}
+
+// Like NormalizePathAndSet but with additional checks specific to the
+// cache_path value.
+base::FilePath NormalizeCachePathAndSet(cef_string_t& path_str,
+ const base::FilePath& root_cache_path) {
+ bool has_error = false;
+ base::FilePath path = NormalizePath(path_str, "cache_path", &has_error);
+ if (has_error || !ValidateCachePath(path, root_cache_path)) {
+ LOG(ERROR) << "The cache_path is invalid. Defaulting to in-memory storage.";
+ path = base::FilePath();
+ }
+ SetPath(path_str, path);
+ return path;
+}
+
+// Based on chrome/app/chrome_exe_main_win.cc.
+// In 32-bit builds, the main thread starts with the default (small) stack size.
+// The ARCH_CPU_32_BITS blocks here and below are in support of moving the main
+// thread to a fiber with a larger stack size.
+#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_32_BITS)
+// The information needed to transfer control to the large-stack fiber and later
+// pass the main routine's exit code back to the small-stack fiber prior to
+// termination.
+struct FiberState {
+ FiberState(wWinMainPtr wWinMain,
+ HINSTANCE hInstance,
+ LPWSTR lpCmdLine,
+ int nCmdShow) {
+ this->wWinMain = wWinMain;
+ this->hInstance = hInstance;
+ this->lpCmdLine = lpCmdLine;
+ this->nCmdShow = nCmdShow;
+ }
+
+ FiberState(mainPtr main, int argc, char** argv) {
+ this->main = main;
+ this->argc = argc;
+ this->argv = argv;
+ }
+
+ wWinMainPtr wWinMain = nullptr;
+ HINSTANCE hInstance;
+ LPWSTR lpCmdLine;
+ int nCmdShow;
+
+ mainPtr main = nullptr;
+ int argc;
+ char** argv;
+
+ LPVOID original_fiber;
+ int fiber_result;
+};
+
+// A PFIBER_START_ROUTINE function run on a large-stack fiber that calls the
+// main routine, stores its return value, and returns control to the small-stack
+// fiber. |params| must be a pointer to a FiberState struct.
+void WINAPI FiberBinder(void* params) {
+ auto* fiber_state = static_cast<FiberState*>(params);
+ // Call the main routine from the fiber. Reusing the entry point minimizes
+ // confusion when examining call stacks in crash reports - seeing wWinMain on
+ // the stack is a handy hint that this is the main thread of the process.
+ if (fiber_state->main) {
+ fiber_state->fiber_result =
+ fiber_state->main(fiber_state->argc, fiber_state->argv);
+ } else {
+ fiber_state->fiber_result =
+ fiber_state->wWinMain(fiber_state->hInstance, nullptr,
+ fiber_state->lpCmdLine, fiber_state->nCmdShow);
+ }
+
+ // Switch back to the main thread to exit.
+ ::SwitchToFiber(fiber_state->original_fiber);
+}
+
+int RunMainWithPreferredStackSize(FiberState& fiber_state) {
+ enum class FiberStatus { kConvertFailed, kCreateFiberFailed, kSuccess };
+ FiberStatus fiber_status = FiberStatus::kSuccess;
+ // GetLastError result if fiber conversion failed.
+ DWORD fiber_error = ERROR_SUCCESS;
+ if (!::IsThreadAFiber()) {
+ // Make the main thread's stack size 4 MiB so that it has roughly the same
+ // effective size as the 64-bit build's 8 MiB stack.
+ constexpr size_t kStackSize = 4 * 1024 * 1024; // 4 MiB
+ // Leak the fiber on exit.
+ LPVOID original_fiber =
+ ::ConvertThreadToFiberEx(nullptr, FIBER_FLAG_FLOAT_SWITCH);
+ if (original_fiber) {
+ fiber_state.original_fiber = original_fiber;
+ // Create a fiber with a bigger stack and switch to it. Leak the fiber on
+ // exit.
+ LPVOID big_stack_fiber = ::CreateFiberEx(
+ 0, kStackSize, FIBER_FLAG_FLOAT_SWITCH, FiberBinder, &fiber_state);
+ if (big_stack_fiber) {
+ ::SwitchToFiber(big_stack_fiber);
+ // The fibers must be cleaned up to avoid obscure TLS-related shutdown
+ // crashes.
+ ::DeleteFiber(big_stack_fiber);
+ ::ConvertFiberToThread();
+ // Control returns here after CEF has finished running on FiberMain.
+ return fiber_state.fiber_result;
+ }
+ fiber_status = FiberStatus::kCreateFiberFailed;
+ } else {
+ fiber_status = FiberStatus::kConvertFailed;
+ }
+ // If we reach here then creating and switching to a fiber has failed. This
+ // probably means we are low on memory and will soon crash. Try to report
+ // this error once crash reporting is initialized.
+ fiber_error = ::GetLastError();
+ base::debug::Alias(&fiber_error);
+ }
+
+ // If we are already a fiber then continue normal execution.
+ // Intentionally crash if converting to a fiber failed.
+ CHECK_EQ(fiber_status, FiberStatus::kSuccess);
+ return -1;
+}
+#endif // BUILDFLAG(IS_WIN) && defined(ARCH_CPU_32_BITS)
+
+} // namespace
+
+int CefExecuteProcess(const CefMainArgs& args,
+ CefRefPtr<CefApp> application,
+ void* windows_sandbox_info) {
+#if BUILDFLAG(IS_WIN)
+ InitInstallDetails();
+ InitCrashReporter();
+#endif
+
+ return CefMainRunner::RunAsHelperProcess(args, application,
+ windows_sandbox_info);
+}
+
+bool CefInitialize(const CefMainArgs& args,
+ const CefSettings& settings,
+ CefRefPtr<CefApp> application,
+ void* windows_sandbox_info) {
+#if BUILDFLAG(IS_WIN)
+ InitInstallDetails();
+ InitCrashReporter();
+#endif
+
+ // Return true if the global context already exists.
+ if (g_context) {
+ return true;
+ }
+
+ if (settings.size != sizeof(cef_settings_t)) {
+ NOTREACHED() << "invalid CefSettings structure size";
+ return false;
+ }
+
+ // Create the new global context object.
+ g_context = new CefContext();
+
+ // Initialize the global context.
+ return g_context->Initialize(args, settings, application,
+ windows_sandbox_info);
+}
+
+void CefShutdown() {
+ // Verify that the context is in a valid state.
+ if (!CONTEXT_STATE_VALID()) {
+ NOTREACHED() << "context not valid";
+ return;
+ }
+
+ // Must always be called on the same thread as Initialize.
+ if (!g_context->OnInitThread()) {
+ NOTREACHED() << "called on invalid thread";
+ return;
+ }
+
+ // Shut down the global context. This will block until shutdown is complete.
+ g_context->Shutdown();
+
+ // Delete the global context object.
+ delete g_context;
+ g_context = nullptr;
+}
+
+void CefDoMessageLoopWork() {
+ // Verify that the context is in a valid state.
+ if (!CONTEXT_STATE_VALID()) {
+ NOTREACHED() << "context not valid";
+ return;
+ }
+
+ // Must always be called on the same thread as Initialize.
+ if (!g_context->OnInitThread()) {
+ NOTREACHED() << "called on invalid thread";
+ return;
+ }
+
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+}
+
+void CefRunMessageLoop() {
+ // Verify that the context is in a valid state.
+ if (!CONTEXT_STATE_VALID()) {
+ NOTREACHED() << "context not valid";
+ return;
+ }
+
+ // Must always be called on the same thread as Initialize.
+ if (!g_context->OnInitThread()) {
+ NOTREACHED() << "called on invalid thread";
+ return;
+ }
+
+ g_context->RunMessageLoop();
+}
+
+void CefQuitMessageLoop() {
+ // Verify that the context is in a valid state.
+ if (!CONTEXT_STATE_VALID()) {
+ NOTREACHED() << "context not valid";
+ return;
+ }
+
+ // Must always be called on the same thread as Initialize.
+ if (!g_context->OnInitThread()) {
+ NOTREACHED() << "called on invalid thread";
+ return;
+ }
+
+ g_context->QuitMessageLoop();
+}
+
+#if BUILDFLAG(IS_WIN)
+
+#if defined(ARCH_CPU_32_BITS)
+int CefRunWinMainWithPreferredStackSize(wWinMainPtr wWinMain,
+ HINSTANCE hInstance,
+ LPWSTR lpCmdLine,
+ int nCmdShow) {
+ CHECK(wWinMain && hInstance);
+ FiberState fiber_state(wWinMain, hInstance, lpCmdLine, nCmdShow);
+ return RunMainWithPreferredStackSize(fiber_state);
+}
+
+int CefRunMainWithPreferredStackSize(mainPtr main, int argc, char* argv[]) {
+ CHECK(main);
+ FiberState fiber_state(main, argc, argv);
+ return RunMainWithPreferredStackSize(fiber_state);
+}
+#endif // defined(ARCH_CPU_32_BITS)
+
+void CefEnableHighDPISupport() {
+ base::win::EnableHighDPISupport();
+}
+
+void CefSetOSModalLoop(bool osModalLoop) {
+ // Verify that the context is in a valid state.
+ if (!CONTEXT_STATE_VALID()) {
+ NOTREACHED() << "context not valid";
+ return;
+ }
+
+ if (!CEF_CURRENTLY_ON_UIT()) {
+ CEF_POST_TASK(CEF_UIT, base::BindOnce(CefSetOSModalLoop, osModalLoop));
+ return;
+ }
+
+ base::CurrentThread::Get()->set_os_modal_loop(osModalLoop);
+}
+
+#endif // BUILDFLAG(IS_WIN)
+
+// CefContext
+
+CefContext::CefContext()
+ : initialized_(false), shutting_down_(false), init_thread_id_(0) {}
+
+CefContext::~CefContext() {}
+
+// static
+CefContext* CefContext::Get() {
+ return g_context;
+}
+
+bool CefContext::Initialize(const CefMainArgs& args,
+ const CefSettings& settings,
+ CefRefPtr<CefApp> application,
+ void* windows_sandbox_info) {
+ init_thread_id_ = base::PlatformThread::CurrentId();
+ settings_ = settings;
+ application_ = application;
+
+#if !(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX))
+ if (settings.multi_threaded_message_loop) {
+ NOTIMPLEMENTED() << "multi_threaded_message_loop is not supported.";
+ return false;
+ }
+#endif
+
+#if BUILDFLAG(IS_WIN)
+ // Signal Chrome Elf that Chrome has begun to start.
+ SignalChromeElf();
+#endif
+
+ const base::FilePath& root_cache_path =
+ NormalizePathAndSet(settings_.root_cache_path, "root_cache_path");
+ const base::FilePath& cache_path =
+ NormalizeCachePathAndSet(settings_.cache_path, root_cache_path);
+ if (root_cache_path.empty() && !cache_path.empty()) {
+ CefString(&settings_.root_cache_path) = cache_path.value();
+ }
+
+ // All other paths that need to be normalized.
+ NormalizePathAndSet(settings_.browser_subprocess_path,
+ "browser_subprocess_path");
+ NormalizePathAndSet(settings_.framework_dir_path, "framework_dir_path");
+ NormalizePathAndSet(settings_.main_bundle_path, "main_bundle_path");
+ NormalizePathAndSet(settings_.user_data_path, "user_data_path");
+ NormalizePathAndSet(settings_.resources_dir_path, "resources_dir_path");
+ NormalizePathAndSet(settings_.locales_dir_path, "locales_dir_path");
+
+ browser_info_manager_.reset(new CefBrowserInfoManager);
+
+ main_runner_.reset(new CefMainRunner(settings_.multi_threaded_message_loop,
+ settings_.external_message_pump));
+ return main_runner_->Initialize(
+ &settings_, application, args, windows_sandbox_info, &initialized_,
+ base::BindOnce(&CefContext::OnContextInitialized,
+ base::Unretained(this)));
+}
+
+void CefContext::RunMessageLoop() {
+ // Must always be called on the same thread as Initialize.
+ DCHECK(OnInitThread());
+
+ // Blocks until QuitMessageLoop() is called.
+ main_runner_->RunMessageLoop();
+}
+
+void CefContext::QuitMessageLoop() {
+ // Must always be called on the same thread as Initialize.
+ DCHECK(OnInitThread());
+
+ main_runner_->QuitMessageLoop();
+}
+
+void CefContext::Shutdown() {
+ // Must always be called on the same thread as Initialize.
+ DCHECK(OnInitThread());
+
+ shutting_down_ = true;
+
+ main_runner_->Shutdown(
+ base::BindOnce(&CefContext::ShutdownOnUIThread, base::Unretained(this)),
+ base::BindOnce(&CefContext::FinalizeShutdown, base::Unretained(this)));
+}
+
+bool CefContext::OnInitThread() {
+ return (base::PlatformThread::CurrentId() == init_thread_id_);
+}
+
+SkColor CefContext::GetBackgroundColor(
+ const CefBrowserSettings* browser_settings,
+ cef_state_t windowless_state) const {
+ bool is_windowless = windowless_state == STATE_ENABLED
+ ? true
+ : (windowless_state == STATE_DISABLED
+ ? false
+ : !!settings_.windowless_rendering_enabled);
+
+ // Default to opaque white if no acceptable color values are found.
+ SkColor sk_color = SK_ColorWHITE;
+
+ if (!browser_settings ||
+ !GetColor(browser_settings->background_color, is_windowless, &sk_color)) {
+ GetColor(settings_.background_color, is_windowless, &sk_color);
+ }
+ return sk_color;
+}
+
+CefTraceSubscriber* CefContext::GetTraceSubscriber() {
+ CEF_REQUIRE_UIT();
+ if (shutting_down_) {
+ return nullptr;
+ }
+ if (!trace_subscriber_.get()) {
+ trace_subscriber_.reset(new CefTraceSubscriber());
+ }
+ return trace_subscriber_.get();
+}
+
+void CefContext::PopulateGlobalRequestContextSettings(
+ CefRequestContextSettings* settings) {
+ CefRefPtr<CefCommandLine> command_line =
+ CefCommandLine::GetGlobalCommandLine();
+
+ // This value was already normalized in Initialize.
+ CefString(&settings->cache_path) = CefString(&settings_.cache_path);
+
+ settings->persist_session_cookies =
+ settings_.persist_session_cookies ||
+ command_line->HasSwitch(switches::kPersistSessionCookies);
+ settings->persist_user_preferences =
+ settings_.persist_user_preferences ||
+ command_line->HasSwitch(switches::kPersistUserPreferences);
+
+ CefString(&settings->cookieable_schemes_list) =
+ CefString(&settings_.cookieable_schemes_list);
+ settings->cookieable_schemes_exclude_defaults =
+ settings_.cookieable_schemes_exclude_defaults;
+}
+
+void CefContext::NormalizeRequestContextSettings(
+ CefRequestContextSettings* settings) {
+ // The |root_cache_path| value was already normalized in Initialize.
+ const base::FilePath& root_cache_path = CefString(&settings_.root_cache_path);
+ NormalizeCachePathAndSet(settings->cache_path, root_cache_path);
+}
+
+void CefContext::AddObserver(Observer* observer) {
+ CEF_REQUIRE_UIT();
+ observers_.AddObserver(observer);
+}
+
+void CefContext::RemoveObserver(Observer* observer) {
+ CEF_REQUIRE_UIT();
+ observers_.RemoveObserver(observer);
+}
+
+bool CefContext::HasObserver(Observer* observer) const {
+ CEF_REQUIRE_UIT();
+ return observers_.HasObserver(observer);
+}
+
+void CefContext::OnContextInitialized() {
+ CEF_REQUIRE_UIT();
+
+ if (application_) {
+ // Notify the handler after the global browser context has initialized.
+ CefRefPtr<CefRequestContext> request_context =
+ CefRequestContext::GetGlobalContext();
+ auto impl = static_cast<CefRequestContextImpl*>(request_context.get());
+ impl->ExecuteWhenBrowserContextInitialized(base::BindOnce(
+ [](CefRefPtr<CefApp> app) {
+ CefRefPtr<CefBrowserProcessHandler> handler =
+ app->GetBrowserProcessHandler();
+ if (handler) {
+ handler->OnContextInitialized();
+ }
+ },
+ application_));
+ }
+}
+
+void CefContext::ShutdownOnUIThread() {
+ CEF_REQUIRE_UIT();
+
+ browser_info_manager_->DestroyAllBrowsers();
+
+ for (auto& observer : observers_) {
+ observer.OnContextDestroyed();
+ }
+
+ if (trace_subscriber_.get()) {
+ trace_subscriber_.reset(nullptr);
+ }
+}
+
+void CefContext::FinalizeShutdown() {
+ browser_info_manager_.reset(nullptr);
+ application_ = nullptr;
+}