diff --git a/BUILD.gn b/BUILD.gn index 52518b9d1..0ab4331c5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -511,6 +511,8 @@ static_library("libcef_static") { "libcef/browser/javascript_dialog_runner.h", "libcef/browser/javascript_dialog_manager.cc", "libcef/browser/javascript_dialog_manager.h", + "libcef/browser/main_runner.cc", + "libcef/browser/main_runner.h", "libcef/browser/media_capture_devices_dispatcher.cc", "libcef/browser/media_capture_devices_dispatcher.h", "libcef/browser/media_router/media_route_impl.cc", diff --git a/libcef/browser/browser_message_loop.cc b/libcef/browser/browser_message_loop.cc index d81088406..43a968559 100644 --- a/libcef/browser/browser_message_loop.cc +++ b/libcef/browser/browser_message_loop.cc @@ -3,7 +3,6 @@ // be found in the LICENSE file. #include "libcef/browser/browser_message_loop.h" -#include "libcef/browser/context.h" #include "libcef/common/content_client.h" #include "base/memory/ptr_util.h" @@ -116,9 +115,6 @@ std::unique_ptr MessagePumpFactoryForUI() { } // namespace -void InitMessagePumpFactoryForUI() { - const CefSettings& settings = CefContext::Get()->settings(); - if (settings.external_message_pump) { - base::MessagePump::OverrideMessagePumpForUIFactory(MessagePumpFactoryForUI); - } +void InitExternalMessagePumpFactoryForUI() { + base::MessagePump::OverrideMessagePumpForUIFactory(MessagePumpFactoryForUI); } diff --git a/libcef/browser/browser_message_loop.h b/libcef/browser/browser_message_loop.h index 8510c1cf8..38f2b025d 100644 --- a/libcef/browser/browser_message_loop.h +++ b/libcef/browser/browser_message_loop.h @@ -5,6 +5,6 @@ #ifndef CEF_LIBCEF_BROWSER_BROWSER_MESSAGE_LOOP_H_ #define CEF_LIBCEF_BROWSER_BROWSER_MESSAGE_LOOP_H_ -void InitMessagePumpFactoryForUI(); +void InitExternalMessagePumpFactoryForUI(); #endif // CEF_LIBCEF_BROWSER_BROWSER_MESSAGE_LOOP_H_ diff --git a/libcef/browser/chrome_browser_process_stub.cc b/libcef/browser/chrome_browser_process_stub.cc index 428b59dd9..8912530c2 100644 --- a/libcef/browser/chrome_browser_process_stub.cc +++ b/libcef/browser/chrome_browser_process_stub.cc @@ -62,7 +62,7 @@ void ChromeBrowserProcessStub::OnContextInitialized() { context_initialized_ = true; } -void ChromeBrowserProcessStub::Shutdown() { +void ChromeBrowserProcessStub::CleanupOnUIThread() { CEF_REQUIRE_UIT(); DCHECK(initialized_); DCHECK(context_initialized_); diff --git a/libcef/browser/chrome_browser_process_stub.h b/libcef/browser/chrome_browser_process_stub.h index 9f86a41fa..aa5b34998 100644 --- a/libcef/browser/chrome_browser_process_stub.h +++ b/libcef/browser/chrome_browser_process_stub.h @@ -36,7 +36,7 @@ class ChromeBrowserProcessStub : public BrowserProcess { void Initialize(); void OnContextInitialized(); - void Shutdown(); + void CleanupOnUIThread(); // BrowserProcess implementation. void EndSession() override; diff --git a/libcef/browser/context.cc b/libcef/browser/context.cc index 5fd2138b4..ff667b82f 100644 --- a/libcef/browser/context.cc +++ b/libcef/browser/context.cc @@ -3,34 +3,20 @@ // be found in the LICENSE file. #include "libcef/browser/context.h" -#include "libcef/browser/browser_host_impl.h" -#include "libcef/browser/browser_info.h" + #include "libcef/browser/browser_info_manager.h" -#include "libcef/browser/browser_main.h" -#include "libcef/browser/chrome_browser_process_stub.h" #include "libcef/browser/thread_util.h" #include "libcef/browser/trace_subscriber.h" #include "libcef/common/cef_switches.h" -#include "libcef/common/main_delegate.h" -#include "libcef/common/widevine_loader.h" -#include "libcef/renderer/content_renderer_client.h" -#include "base/base_switches.h" #include "base/bind.h" -#include "base/command_line.h" -#include "base/debug/debugger.h" #include "base/files/file_util.h" +#include "base/message_loop/message_loop_current.h" #include "base/run_loop.h" -#include "base/synchronization/waitable_event.h" #include "base/threading/thread_restrictions.h" #include "components/network_session_configurator/common/network_switches.h" -#include "content/app/content_service_manager_main_delegate.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" -#include "content/public/browser/render_process_host.h" -#include "content/public/common/content_switches.h" -#include "services/service_manager/embedder/main.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches.h" #if defined(OS_WIN) @@ -38,13 +24,6 @@ #include "chrome/chrome_elf/chrome_elf_main.h" #include "chrome/install_static/initialize_from_primary_module.h" #include "components/crash/core/app/crashpad.h" -#include "content/public/app/sandbox_helper_win.h" -#include "sandbox/win/src/sandbox_types.h" -#endif - -#if defined(OS_MACOSX) || defined(OS_WIN) -#include "components/crash/core/app/crash_switches.h" -#include "third_party/crashpad/crashpad/handler/handler_main.h" #endif namespace { @@ -95,54 +74,6 @@ void InitCrashReporter() { } #endif // defined(OS_WIN) -#if defined(OS_MACOSX) || defined(OS_WIN) - -// Based on components/crash/core/app/run_as_crashpad_handler_win.cc -// Remove the "--type=crashpad-handler" command-line flag that will otherwise -// confuse the crashpad handler. -// Chrome uses an embedded crashpad handler on Windows only and imports this -// function via the existing "run_as_crashpad_handler" target defined in -// components/crash/core/app/BUILD.gn. CEF uses an embedded handler on both -// Windows and macOS so we define the function here instead of using the -// existing target (because we can't use that target on macOS). -int RunAsCrashpadHandler(const base::CommandLine& command_line) { - base::CommandLine::StringVector argv = command_line.argv(); - const base::CommandLine::StringType process_type = - FILE_PATH_LITERAL("--type="); - argv.erase( - std::remove_if(argv.begin(), argv.end(), - [&process_type](const base::CommandLine::StringType& str) { - return base::StartsWith(str, process_type, - base::CompareCase::SENSITIVE) || - (!str.empty() && str[0] == L'/'); - }), - argv.end()); - -#if defined(OS_MACOSX) - // HandlerMain on macOS uses the system version of getopt_long which expects - // the first argument to be the program name. - argv.insert(argv.begin(), command_line.GetProgram().value()); -#endif - - std::unique_ptr argv_as_utf8(new char*[argv.size() + 1]); - std::vector storage; - storage.reserve(argv.size()); - for (size_t i = 0; i < argv.size(); ++i) { -#if defined(OS_WIN) - storage.push_back(base::UTF16ToUTF8(argv[i])); -#else - storage.push_back(argv[i]); -#endif - argv_as_utf8[i] = &storage[i][0]; - } - argv_as_utf8[argv.size()] = nullptr; - argv.clear(); - return crashpad::HandlerMain(static_cast(storage.size()), - argv_as_utf8.get(), nullptr); -} - -#endif // defined(OS_MACOSX) || defined(OS_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) @@ -252,52 +183,8 @@ int CefExecuteProcess(const CefMainArgs& args, InitCrashReporter(); #endif - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); -#if defined(OS_WIN) - command_line.ParseFromString(::GetCommandLineW()); -#else - command_line.InitFromArgv(args.argc, args.argv); -#endif - - // Wait for the debugger as early in process initialization as possible. - if (command_line.HasSwitch(switches::kWaitForDebugger)) - base::debug::WaitForDebugger(60, true); - - // If no process type is specified then it represents the browser process and - // we do nothing. - std::string process_type = - command_line.GetSwitchValueASCII(switches::kProcessType); - if (process_type.empty()) - return -1; - -#if defined(OS_MACOSX) || defined(OS_WIN) - if (process_type == crash_reporter::switches::kCrashpadHandler) - return RunAsCrashpadHandler(command_line); -#endif - - CefMainDelegate main_delegate(application); - -// Execute the secondary process. -#if defined(OS_WIN) - sandbox::SandboxInterfaceInfo sandbox_info = {0}; - if (windows_sandbox_info == nullptr) { - content::InitializeSandboxInfo(&sandbox_info); - windows_sandbox_info = &sandbox_info; - } - - content::ContentMainParams params(&main_delegate); - params.instance = args.instance; - params.sandbox_info = - static_cast(windows_sandbox_info); - - return content::ContentMain(params); -#else - content::ContentMainParams params(&main_delegate); - params.argc = args.argc; - params.argv = const_cast(args.argv); - - return content::ContentMain(params); -#endif + return CefMainRunner::RunAsHelperProcess(args, application, + windows_sandbox_info); } bool CefInitialize(const CefMainArgs& args, @@ -321,8 +208,6 @@ bool CefInitialize(const CefMainArgs& args, return false; } - g_browser_process = new ChromeBrowserProcessStub(); - // Create the new global context object. g_context = new CefContext(); @@ -466,78 +351,14 @@ bool CefContext::Initialize(const CefMainArgs& args, NormalizePathAndSet(settings_.resources_dir_path, "resources_dir_path"); NormalizePathAndSet(settings_.locales_dir_path, "locales_dir_path"); - main_delegate_.reset(new CefMainDelegate(application)); browser_info_manager_.reset(new CefBrowserInfoManager); - int exit_code; - - // Initialize the content runner. - content::ContentMainParams params(main_delegate_.get()); -#if defined(OS_WIN) - sandbox::SandboxInterfaceInfo sandbox_info = {0}; - if (windows_sandbox_info == nullptr) { - windows_sandbox_info = &sandbox_info; - settings_.no_sandbox = true; - } - - params.instance = args.instance; - params.sandbox_info = - static_cast(windows_sandbox_info); -#else - params.argc = args.argc; - params.argv = const_cast(args.argv); -#endif - - sm_main_delegate_.reset( - new content::ContentServiceManagerMainDelegate(params)); - sm_main_params_.reset( - new service_manager::MainParams(sm_main_delegate_.get())); - -#if defined(OS_POSIX) && !defined(OS_ANDROID) - sm_main_params_->argc = params.argc; - sm_main_params_->argv = params.argv; -#endif - - exit_code = service_manager::MainInitialize(*sm_main_params_); - DCHECK_LT(exit_code, 0); - if (exit_code >= 0) - return false; - - static_cast(g_browser_process)->Initialize(); - - if (settings.multi_threaded_message_loop) { - base::WaitableEvent uithread_startup_event( - base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - if (!main_delegate_->CreateUIThread(base::BindOnce( - [](CefContext* context, base::WaitableEvent* event) { - service_manager::MainRun(*context->sm_main_params_); - event->Signal(); - }, - base::Unretained(this), - base::Unretained(&uithread_startup_event)))) { - return false; - } - - initialized_ = true; - - // We need to wait until service_manager::MainRun has finished. - uithread_startup_event.Wait(); - } else { - initialized_ = true; - service_manager::MainRun(*sm_main_params_); - } - - if (CEF_CURRENTLY_ON_UIT()) { - OnContextInitialized(); - } else { - // Continue initialization on the UI thread. - CEF_POST_TASK(CEF_UIT, base::Bind(&CefContext::OnContextInitialized, - base::Unretained(this))); - } - - return true; + 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::Shutdown() { @@ -546,28 +367,9 @@ void CefContext::Shutdown() { shutting_down_ = true; - if (settings_.multi_threaded_message_loop) { - // Events that will be used to signal when shutdown is complete. Start in - // non-signaled mode so that the event will block. - base::WaitableEvent uithread_shutdown_event( - base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - // Finish shutdown on the UI thread. - CEF_POST_TASK(CEF_UIT, - base::Bind(&CefContext::FinishShutdownOnUIThread, - base::Unretained(this), &uithread_shutdown_event)); - - /// Block until UI thread shutdown is complete. - uithread_shutdown_event.Wait(); - - FinalizeShutdown(); - } else { - // Finish shutdown on the current thread, which should be the UI thread. - FinishShutdownOnUIThread(nullptr); - - FinalizeShutdown(); - } + main_runner_->Shutdown( + base::BindOnce(&CefContext::ShutdownOnUIThread, base::Unretained(this)), + base::BindOnce(&CefContext::FinalizeShutdown, base::Unretained(this))); } bool CefContext::OnInitThread() { @@ -654,13 +456,6 @@ bool CefContext::HasObserver(Observer* observer) const { void CefContext::OnContextInitialized() { CEF_REQUIRE_UIT(); - static_cast(g_browser_process) - ->OnContextInitialized(); - -#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS) - CefWidevineLoader::GetInstance()->OnContextInitialized(); -#endif - // Notify the handler. CefRefPtr app = CefContentClient::Get()->application(); if (app.get()) { @@ -671,8 +466,7 @@ void CefContext::OnContextInitialized() { } } -void CefContext::FinishShutdownOnUIThread( - base::WaitableEvent* uithread_shutdown_event) { +void CefContext::ShutdownOnUIThread() { CEF_REQUIRE_UIT(); browser_info_manager_->DestroyAllBrowsers(); @@ -682,34 +476,8 @@ void CefContext::FinishShutdownOnUIThread( if (trace_subscriber_.get()) trace_subscriber_.reset(nullptr); - - static_cast(g_browser_process)->Shutdown(); - - ui::ResourceBundle::GetSharedInstance().CleanupOnUIThread(); - - sm_main_delegate_->ShutdownOnUIThread(); - - if (uithread_shutdown_event) - uithread_shutdown_event->Signal(); } void CefContext::FinalizeShutdown() { - if (content::RenderProcessHost::run_renderer_in_process()) { - // Blocks until RenderProcess cleanup is complete. - CefContentRendererClient::Get()->RunSingleProcessCleanup(); - } - - // Shut down the browser runner or UI thread. - main_delegate_->ShutdownBrowser(); - - // Shut down the content runner. - service_manager::MainShutdown(*sm_main_params_); - browser_info_manager_.reset(nullptr); - sm_main_params_.reset(nullptr); - sm_main_delegate_.reset(nullptr); - main_delegate_.reset(nullptr); - - delete g_browser_process; - g_browser_process = nullptr; } diff --git a/libcef/browser/context.h b/libcef/browser/context.h index eacc02a52..f95acb09b 100644 --- a/libcef/browser/context.h +++ b/libcef/browser/context.h @@ -11,26 +11,14 @@ #include #include "include/cef_app.h" +#include "libcef/browser/main_runner.h" #include "base/observer_list.h" #include "base/threading/platform_thread.h" #include "third_party/skia/include/core/SkColor.h" -namespace base { -class WaitableEvent; -} - -namespace content { -class ContentServiceManagerMainDelegate; -} - -namespace service_manager { -struct MainParams; -} - class CefBrowserHostImpl; class CefBrowserInfoManager; -class CefMainDelegate; class CefTraceSubscriber; class CefContext { @@ -105,7 +93,7 @@ class CefContext { // Performs shutdown actions that need to occur on the UI thread before any // threads are destroyed. - void FinishShutdownOnUIThread(base::WaitableEvent* uithread_shutdown_event); + void ShutdownOnUIThread(); // Destroys the main runner and related objects. void FinalizeShutdown(); @@ -119,9 +107,7 @@ class CefContext { CefSettings settings_; - std::unique_ptr main_delegate_; - std::unique_ptr sm_main_delegate_; - std::unique_ptr sm_main_params_; + std::unique_ptr main_runner_; std::unique_ptr trace_subscriber_; std::unique_ptr browser_info_manager_; diff --git a/libcef/browser/main_runner.cc b/libcef/browser/main_runner.cc new file mode 100644 index 000000000..ce93847bc --- /dev/null +++ b/libcef/browser/main_runner.cc @@ -0,0 +1,489 @@ +// Copyright 2020 The Chromium Embedded Framework Authors. +// Portions copyright 2014 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 "libcef/browser/main_runner.h" + +#include "libcef/browser/browser_message_loop.h" +#include "libcef/browser/thread_util.h" +#include "libcef/renderer/content_renderer_client.h" + +#include "base/at_exit.h" +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/debug/debugger.h" +#include "base/run_loop.h" +#include "base/sequence_checker.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "content/app/content_service_manager_main_delegate.h" +#include "content/browser/scheduler/browser_task_executor.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/common/content_switches.h" +#include "services/service_manager/embedder/main.h" + +#if defined(OS_WIN) +#include +#include +#include "content/public/app/sandbox_helper_win.h" +#include "sandbox/win/src/sandbox_types.h" +#endif + +#if defined(OS_MACOSX) || defined(OS_WIN) +#include "components/crash/core/app/crash_switches.h" +#include "third_party/crashpad/crashpad/handler/handler_main.h" +#endif + +namespace { + +#if defined(OS_MACOSX) || defined(OS_WIN) + +// Based on components/crash/core/app/run_as_crashpad_handler_win.cc +// Remove the "--type=crashpad-handler" command-line flag that will otherwise +// confuse the crashpad handler. +// Chrome uses an embedded crashpad handler on Windows only and imports this +// function via the existing "run_as_crashpad_handler" target defined in +// components/crash/core/app/BUILD.gn. CEF uses an embedded handler on both +// Windows and macOS so we define the function here instead of using the +// existing target (because we can't use that target on macOS). +int RunAsCrashpadHandler(const base::CommandLine& command_line) { + base::CommandLine::StringVector argv = command_line.argv(); + const base::CommandLine::StringType process_type = + FILE_PATH_LITERAL("--type="); + argv.erase( + std::remove_if(argv.begin(), argv.end(), + [&process_type](const base::CommandLine::StringType& str) { + return base::StartsWith(str, process_type, + base::CompareCase::SENSITIVE) || + (!str.empty() && str[0] == L'/'); + }), + argv.end()); + +#if defined(OS_MACOSX) + // HandlerMain on macOS uses the system version of getopt_long which expects + // the first argument to be the program name. + argv.insert(argv.begin(), command_line.GetProgram().value()); +#endif + + std::unique_ptr argv_as_utf8(new char*[argv.size() + 1]); + std::vector storage; + storage.reserve(argv.size()); + for (size_t i = 0; i < argv.size(); ++i) { +#if defined(OS_WIN) + storage.push_back(base::UTF16ToUTF8(argv[i])); +#else + storage.push_back(argv[i]); +#endif + argv_as_utf8[i] = &storage[i][0]; + } + argv_as_utf8[argv.size()] = nullptr; + argv.clear(); + return crashpad::HandlerMain(static_cast(storage.size()), + argv_as_utf8.get(), nullptr); +} + +#endif // defined(OS_MACOSX) || defined(OS_WIN) + +} // namespace + +// Used to run the UI on a separate thread. +class CefUIThread : public base::PlatformThread::Delegate { + public: + explicit CefUIThread(base::OnceClosure setup_callback) + : setup_callback_(std::move(setup_callback)) {} + ~CefUIThread() override { Stop(); } + + void Start() { + base::AutoLock lock(thread_lock_); + bool success = base::PlatformThread::CreateWithPriority( + 0, this, &thread_, base::ThreadPriority::NORMAL); + if (!success) { + LOG(FATAL) << "failed to UI create thread"; + } + } + + void Stop() { + base::AutoLock lock(thread_lock_); + + if (!stopping_) { + stopping_ = true; + CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefUIThread::ThreadQuitHelper, + base::Unretained(this))); + } + + // Can't join if the |thread_| is either already gone or is non-joinable. + if (thread_.is_null()) + return; + + base::PlatformThread::Join(thread_); + thread_ = base::PlatformThreadHandle(); + + stopping_ = false; + } + + bool WaitUntilThreadStarted() const { + DCHECK(owning_sequence_checker_.CalledOnValidSequence()); + start_event_.Wait(); + return true; + } + + void InitializeBrowserRunner( + const content::MainFunctionParams& main_function_params) { + // Use our own browser process runner. + browser_runner_ = content::BrowserMainRunner::Create(); + + // Initialize browser process state. Uses the current thread's message loop. + int exit_code = browser_runner_->Initialize(main_function_params); + CHECK_EQ(exit_code, -1); + } + + protected: + void ThreadMain() override { + base::PlatformThread::SetName("CefUIThread"); + +#if defined(OS_WIN) + // Initializes the COM library on the current thread. + CoInitialize(nullptr); +#endif + + start_event_.Signal(); + + std::move(setup_callback_).Run(); + + base::RunLoop run_loop; + run_loop_ = &run_loop; + run_loop.Run(); + + browser_runner_->Shutdown(); + browser_runner_.reset(nullptr); + + content::BrowserTaskExecutor::Shutdown(); + + // Run exit callbacks on the UI thread to avoid sequence check failures. + base::AtExitManager::ProcessCallbacksNow(); + +#if defined(OS_WIN) + // Closes the COM library on the current thread. CoInitialize must + // be balanced by a corresponding call to CoUninitialize. + CoUninitialize(); +#endif + + run_loop_ = nullptr; + } + + void ThreadQuitHelper() { + DCHECK(run_loop_); + run_loop_->QuitWhenIdle(); + } + + std::unique_ptr browser_runner_; + base::OnceClosure setup_callback_; + + bool stopping_ = false; + + // The thread's handle. + base::PlatformThreadHandle thread_; + mutable base::Lock thread_lock_; // Protects |thread_|. + + base::RunLoop* run_loop_ = nullptr; + + mutable base::WaitableEvent start_event_; + + // This class is not thread-safe, use this to verify access from the owning + // sequence of the Thread. + base::SequenceChecker owning_sequence_checker_; +}; + +CefMainRunner::CefMainRunner(bool multi_threaded_message_loop, + bool external_message_pump) + : multi_threaded_message_loop_(multi_threaded_message_loop), + external_message_pump_(external_message_pump) {} + +CefMainRunner::~CefMainRunner() = default; + +bool CefMainRunner::Initialize(CefSettings* settings, + CefRefPtr application, + const CefMainArgs& args, + void* windows_sandbox_info, + bool* initialized, + base::OnceClosure context_initialized) { + CefRuntimeInitialize(settings, application); + + const int exit_code = + ContentMainInitialize(args, windows_sandbox_info, &settings->no_sandbox); + if (exit_code >= 0) { + NOTREACHED() << "ContentMainInitialize failed"; + return false; + } + + if (!ContentMainRun(initialized, std::move(context_initialized))) { + NOTREACHED() << "ContentMainRun failed"; + return false; + } + + return true; +} + +void CefMainRunner::Shutdown(base::OnceClosure shutdown_on_ui_thread, + base::OnceClosure finalize_shutdown) { + if (multi_threaded_message_loop_) { + // Events that will be used to signal when shutdown is complete. Start in + // non-signaled mode so that the event will block. + base::WaitableEvent uithread_shutdown_event( + base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + // Finish shutdown on the UI thread. + CEF_POST_TASK( + CEF_UIT, + base::BindOnce(&CefMainRunner::FinishShutdownOnUIThread, + base::Unretained(this), std::move(shutdown_on_ui_thread), + &uithread_shutdown_event)); + + /// Block until UI thread shutdown is complete. + uithread_shutdown_event.Wait(); + + FinalizeShutdown(std::move(finalize_shutdown)); + } else { + // Finish shutdown on the current thread, which should be the UI thread. + FinishShutdownOnUIThread(std::move(shutdown_on_ui_thread), nullptr); + + FinalizeShutdown(std::move(finalize_shutdown)); + } +} + +// static +int CefMainRunner::RunAsHelperProcess(const CefMainArgs& args, + CefRefPtr application, + void* windows_sandbox_info) { + base::CommandLine command_line(base::CommandLine::NO_PROGRAM); +#if defined(OS_WIN) + command_line.ParseFromString(::GetCommandLineW()); +#else + command_line.InitFromArgv(args.argc, args.argv); +#endif + + // Wait for the debugger as early in process initialization as possible. + if (command_line.HasSwitch(switches::kWaitForDebugger)) + base::debug::WaitForDebugger(60, true); + + // If no process type is specified then it represents the browser process and + // we do nothing. + const std::string& process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + if (process_type.empty()) + return -1; + +#if defined(OS_MACOSX) || defined(OS_WIN) + if (process_type == crash_reporter::switches::kCrashpadHandler) + return RunAsCrashpadHandler(command_line); +#endif + + std::unique_ptr main_delegate; + main_delegate.reset(new CefMainDelegate(/*context=*/nullptr, + /*settings=*/nullptr, application)); + +// Execute the secondary process. +#if defined(OS_WIN) + sandbox::SandboxInterfaceInfo sandbox_info = {0}; + if (windows_sandbox_info == nullptr) { + content::InitializeSandboxInfo(&sandbox_info); + windows_sandbox_info = &sandbox_info; + } + + content::ContentMainParams params(main_delegate.get()); + params.instance = args.instance; + params.sandbox_info = + static_cast(windows_sandbox_info); + + return content::ContentMain(params); +#else + content::ContentMainParams params(main_delegate.get()); + params.argc = args.argc; + params.argv = const_cast(args.argv); + + return content::ContentMain(params); +#endif +} + +void CefMainRunner::CefRuntimeInitialize(CefSettings* settings, + CefRefPtr app) { + DCHECK_EQ(RuntimeType::UNINITIALIZED, runtime_type_); + DCHECK(!main_delegate_); + + runtime_type_ = RuntimeType::CEF; + CefMainDelegate::CefInitialize(); + main_delegate_.reset(new CefMainDelegate(this, settings, app)); +} + +int CefMainRunner::ContentMainInitialize(const CefMainArgs& args, + void* windows_sandbox_info, + int* no_sandbox) { + DCHECK(main_delegate_); + + // Initialize the content runner. + content::ContentMainParams params(main_delegate_.get()); +#if defined(OS_WIN) + sandbox::SandboxInterfaceInfo sandbox_info = {0}; + if (windows_sandbox_info == nullptr) { + windows_sandbox_info = &sandbox_info; + *no_sandbox = true; + } + + params.instance = args.instance; + params.sandbox_info = + static_cast(windows_sandbox_info); +#else + params.argc = args.argc; + params.argv = const_cast(args.argv); +#endif + + sm_main_delegate_.reset( + new content::ContentServiceManagerMainDelegate(params)); + sm_main_params_.reset( + new service_manager::MainParams(sm_main_delegate_.get())); + +#if defined(OS_POSIX) && !defined(OS_ANDROID) + sm_main_params_->argc = params.argc; + sm_main_params_->argv = params.argv; +#endif + + return service_manager::MainInitialize(*sm_main_params_); +} + +bool CefMainRunner::ContentMainRun(bool* initialized, + base::OnceClosure context_initialized) { + if (IsCefRuntime()) + CefMainDelegate::MainThreadInitialize(); + + if (multi_threaded_message_loop_) { + base::WaitableEvent uithread_startup_event( + base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + if (!CreateUIThread(base::BindOnce( + [](CefMainRunner* runner, base::WaitableEvent* event) { + service_manager::MainRun(*runner->sm_main_params_); + event->Signal(); + }, + base::Unretained(this), + base::Unretained(&uithread_startup_event)))) { + return false; + } + + *initialized = true; + + // We need to wait until service_manager::MainRun has finished. + uithread_startup_event.Wait(); + } else { + *initialized = true; + service_manager::MainRun(*sm_main_params_); + } + + if (CEF_CURRENTLY_ON_UIT()) { + OnContextInitialized(std::move(context_initialized)); + } else { + // Continue initialization on the UI thread. + CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefMainRunner::OnContextInitialized, + base::Unretained(this), + std::move(context_initialized))); + } + + return true; +} + +void CefMainRunner::PreCreateMainMessageLoop() { + if (external_message_pump_) { + InitExternalMessagePumpFactoryForUI(); + } +} + +int CefMainRunner::RunMainProcess( + const content::MainFunctionParams& main_function_params) { + if (!multi_threaded_message_loop_) { + // Use our own browser process runner. + browser_runner_ = content::BrowserMainRunner::Create(); + + // Initialize browser process state. Results in a call to + // CefBrowserMain::PreMainMessageLoopStart() which creates the UI message + // loop. + int exit_code = browser_runner_->Initialize(main_function_params); + if (exit_code >= 0) + return exit_code; + } else { + // Running on the separate UI thread. + DCHECK(ui_thread_); + ui_thread_->InitializeBrowserRunner(main_function_params); + } + + return 0; +} + +bool CefMainRunner::CreateUIThread(base::OnceClosure setup_callback) { + DCHECK(!ui_thread_); + + ui_thread_.reset(new CefUIThread(std::move(setup_callback))); + ui_thread_->Start(); + ui_thread_->WaitUntilThreadStarted(); + + if (external_message_pump_) { + InitExternalMessagePumpFactoryForUI(); + } + return true; +} + +void CefMainRunner::OnContextInitialized( + base::OnceClosure context_initialized) { + CEF_REQUIRE_UIT(); + if (IsCefRuntime()) + CefMainDelegate::UIThreadInitialize(); + std::move(context_initialized).Run(); +} + +void CefMainRunner::FinishShutdownOnUIThread( + base::OnceClosure shutdown_on_ui_thread, + base::WaitableEvent* uithread_shutdown_event) { + CEF_REQUIRE_UIT(); + + sm_main_delegate_->ShutdownOnUIThread(); + + std::move(shutdown_on_ui_thread).Run(); + if (IsCefRuntime()) + CefMainDelegate::UIThreadShutdown(); + + if (uithread_shutdown_event) + uithread_shutdown_event->Signal(); +} + +void CefMainRunner::FinalizeShutdown(base::OnceClosure finalize_shutdown) { + if (content::RenderProcessHost::run_renderer_in_process() && IsCefRuntime()) { + // Blocks until RenderProcess cleanup is complete. + CefContentRendererClient::Get()->RunSingleProcessCleanup(); + } + + if (browser_runner_.get()) { + browser_runner_->Shutdown(); + browser_runner_.reset(nullptr); + } + + if (ui_thread_.get()) { + // Blocks until the thread has stopped. + ui_thread_->Stop(); + ui_thread_.reset(); + } + + // Shut down the content runner. + service_manager::MainShutdown(*sm_main_params_); + + sm_main_params_.reset(nullptr); + sm_main_delegate_.reset(nullptr); + + std::move(finalize_shutdown).Run(); + if (IsCefRuntime()) + CefMainDelegate::MainThreadShutdown(); + + main_delegate_.reset(nullptr); + runtime_type_ = RuntimeType::UNINITIALIZED; +} diff --git a/libcef/browser/main_runner.h b/libcef/browser/main_runner.h new file mode 100644 index 000000000..7e1c906cd --- /dev/null +++ b/libcef/browser/main_runner.h @@ -0,0 +1,103 @@ +// Copyright 2020 The Chromium Embedded Framework Authors. +// Portions copyright 2014 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. + +#ifndef CEF_LIBCEF_BROWSER_MAIN_RUNNER_H_ +#define CEF_LIBCEF_BROWSER_MAIN_RUNNER_H_ +#pragma once + +#include "libcef/common/main_delegate.h" + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "content/public/browser/browser_main_runner.h" + +namespace base { +class WaitableEvent; +} + +namespace content { +class ContentMainDelegate; +class ContentServiceManagerMainDelegate; +struct MainFunctionParams; +} // namespace content + +namespace service_manager { +struct MainParams; +} + +class CefUIThread; + +// Manages the main process lifespan and related objects. +class CefMainRunner : public CefMainDelegate::Runner { + public: + CefMainRunner(bool multi_threaded_message_loop, bool external_message_pump); + ~CefMainRunner(); + + // Called from CefContext::Initialize. + bool Initialize(CefSettings* settings, + CefRefPtr application, + const CefMainArgs& args, + void* windows_sandbox_info, + bool* initialized, + base::OnceClosure context_initialized); + + // Called from CefContext::Shutdown. + void Shutdown(base::OnceClosure shutdown_on_ui_thread, + base::OnceClosure finalize_shutdown); + + bool IsCefRuntime() const { return runtime_type_ == RuntimeType::CEF; } + + // Called from CefExecuteProcess. + static int RunAsHelperProcess(const CefMainArgs& args, + CefRefPtr application, + void* windows_sandbox_info); + + private: + // Called from Initialize(). + void CefRuntimeInitialize(CefSettings* settings, CefRefPtr app); + int ContentMainInitialize(const CefMainArgs& args, + void* windows_sandbox_info, + int* no_sandbox); + bool ContentMainRun(bool* initialized, base::OnceClosure context_initialized); + + // CefMainDelegate::Runner methods: + void PreCreateMainMessageLoop() override; + int RunMainProcess( + const content::MainFunctionParams& main_function_params) override; + + // Create the UI thread when running with multi-threaded message loop mode. + bool CreateUIThread(base::OnceClosure setup_callback); + + // Called on the UI thread after the context is initialized. + void OnContextInitialized(base::OnceClosure context_initialized); + + // Performs shutdown actions that need to occur on the UI thread before any + // threads are destroyed. + void FinishShutdownOnUIThread(base::OnceClosure shutdown_on_ui_thread, + base::WaitableEvent* uithread_shutdown_event); + + // Destroys the runtime and related objects. + void FinalizeShutdown(base::OnceClosure finalize_shutdown); + + const bool multi_threaded_message_loop_; + const bool external_message_pump_; + + enum class RuntimeType { + UNINITIALIZED, + CEF, + }; + RuntimeType runtime_type_ = RuntimeType::UNINITIALIZED; + + std::unique_ptr main_delegate_; + std::unique_ptr sm_main_delegate_; + std::unique_ptr sm_main_params_; + + std::unique_ptr browser_runner_; + std::unique_ptr ui_thread_; + + DISALLOW_COPY_AND_ASSIGN(CefMainRunner); +}; + +#endif // CEF_LIBCEF_BROWSER_MAIN_RUNNER_H_ diff --git a/libcef/common/main_delegate.cc b/libcef/common/main_delegate.cc index a68020efb..bc9aff235 100644 --- a/libcef/common/main_delegate.cc +++ b/libcef/common/main_delegate.cc @@ -8,27 +8,24 @@ #include #endif -#include "libcef/browser/browser_message_loop.h" +#include "libcef/browser/chrome_browser_process_stub.h" #include "libcef/browser/content_browser_client.h" -#include "libcef/browser/context.h" #include "libcef/common/cef_switches.h" #include "libcef/common/command_line_impl.h" #include "libcef/common/crash_reporting.h" #include "libcef/common/extensions/extensions_util.h" +#include "libcef/common/widevine_loader.h" #include "libcef/renderer/content_renderer_client.h" -#include "base/at_exit.h" #include "base/base_switches.h" #include "base/command_line.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/path_service.h" -#include "base/run_loop.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/synchronization/waitable_event.h" -#include "base/threading/thread.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/media/router/media_router_feature.h" #include "chrome/child/pdf_child_init.h" @@ -40,8 +37,6 @@ #include "components/content_settings/core/common/content_settings_pattern.h" #include "components/viz/common/features.h" #include "content/browser/browser_process_sub_thread.h" -#include "content/browser/scheduler/browser_task_executor.h" -#include "content/public/browser/browser_main_runner.h" #include "content/public/browser/render_process_host.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" @@ -67,7 +62,6 @@ #endif #if defined(OS_WIN) -#include #include "base/win/registry.h" #endif @@ -245,10 +239,9 @@ bool GetDefaultUserDataDirectory(base::FilePath* result) { #endif -base::FilePath GetUserDataPath() { - const CefSettings& settings = CefContext::Get()->settings(); - if (settings.user_data_path.length > 0) - return base::FilePath(CefString(&settings.user_data_path)); +base::FilePath GetUserDataPath(CefSettings* settings) { + if (settings->user_data_path.length > 0) + return base::FilePath(CefString(&settings->user_data_path)); base::FilePath result; if (GetDefaultUserDataDirectory(&result)) @@ -328,117 +321,10 @@ void OverrideAssetPath() { } // namespace -// Used to run the UI on a separate thread. -class CefUIThread : public base::PlatformThread::Delegate { - public: - explicit CefUIThread(base::OnceClosure setup_callback) - : setup_callback_(std::move(setup_callback)) {} - ~CefUIThread() override { Stop(); } - - void Start() { - base::AutoLock lock(thread_lock_); - bool success = base::PlatformThread::CreateWithPriority( - 0, this, &thread_, base::ThreadPriority::NORMAL); - if (!success) { - LOG(FATAL) << "failed to UI create thread"; - } - } - - void Stop() { - base::AutoLock lock(thread_lock_); - - if (!stopping_) { - stopping_ = true; - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&CefUIThread::ThreadQuitHelper, Unretained(this))); - } - - // Can't join if the |thread_| is either already gone or is non-joinable. - if (thread_.is_null()) - return; - - base::PlatformThread::Join(thread_); - thread_ = base::PlatformThreadHandle(); - - stopping_ = false; - } - - bool WaitUntilThreadStarted() const { - DCHECK(owning_sequence_checker_.CalledOnValidSequence()); - start_event_.Wait(); - return true; - } - - void InitializeBrowserRunner( - const content::MainFunctionParams& main_function_params) { - // Use our own browser process runner. - browser_runner_ = content::BrowserMainRunner::Create(); - - // Initialize browser process state. Uses the current thread's message loop. - int exit_code = browser_runner_->Initialize(main_function_params); - CHECK_EQ(exit_code, -1); - } - - protected: - void ThreadMain() override { - base::PlatformThread::SetName("CefUIThread"); - -#if defined(OS_WIN) - // Initializes the COM library on the current thread. - CoInitialize(nullptr); -#endif - - start_event_.Signal(); - - std::move(setup_callback_).Run(); - - base::RunLoop run_loop; - run_loop_ = &run_loop; - run_loop.Run(); - - browser_runner_->Shutdown(); - browser_runner_.reset(nullptr); - - content::BrowserTaskExecutor::Shutdown(); - - // Run exit callbacks on the UI thread to avoid sequence check failures. - base::AtExitManager::ProcessCallbacksNow(); - -#if defined(OS_WIN) - // Closes the COM library on the current thread. CoInitialize must - // be balanced by a corresponding call to CoUninitialize. - CoUninitialize(); -#endif - - run_loop_ = nullptr; - } - - void ThreadQuitHelper() { - DCHECK(run_loop_); - run_loop_->QuitWhenIdle(); - } - - std::unique_ptr browser_runner_; - base::OnceClosure setup_callback_; - - bool stopping_ = false; - - // The thread's handle. - base::PlatformThreadHandle thread_; - mutable base::Lock thread_lock_; // Protects |thread_|. - - base::RunLoop* run_loop_ = nullptr; - - mutable base::WaitableEvent start_event_; - - // This class is not thread-safe, use this to verify access from the owning - // sequence of the Thread. - base::SequenceChecker owning_sequence_checker_; -}; - -CefMainDelegate::CefMainDelegate(CefRefPtr application) - : content_client_(application) { +CefMainDelegate::CefMainDelegate(Runner* runner, + CefSettings* settings, + CefRefPtr application) + : runner_(runner), settings_(settings), content_client_(application) { // Necessary so that exported functions from base_impl.cc will be included // in the binary. extern void base_impl_stub(); @@ -452,7 +338,7 @@ CefMainDelegate::CefMainDelegate(CefRefPtr application) CefMainDelegate::~CefMainDelegate() {} void CefMainDelegate::PreCreateMainMessageLoop() { - InitMessagePumpFactoryForUI(); + runner_->PreCreateMainMessageLoop(); } bool CefMainDelegate::BasicStartupComplete(int* exit_code) { @@ -468,9 +354,7 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) { if (process_type.empty()) { // In the browser process. Populate the global command-line object. - const CefSettings& settings = CefContext::Get()->settings(); - - if (settings.command_line_args_disabled) { + if (settings_->command_line_args_disabled) { // Remove any existing command-line arguments. base::CommandLine::StringVector argv; argv.push_back(command_line->GetProgram().value()); @@ -480,11 +364,11 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) { const_cast(&map)->clear(); } - bool no_sandbox = settings.no_sandbox ? true : false; + bool no_sandbox = settings_->no_sandbox ? true : false; - if (settings.browser_subprocess_path.length > 0) { + if (settings_->browser_subprocess_path.length > 0) { base::FilePath file_path = - base::FilePath(CefString(&settings.browser_subprocess_path)); + base::FilePath(CefString(&settings_->browser_subprocess_path)); if (!file_path.empty()) { command_line->AppendSwitchPath(switches::kBrowserSubprocessPath, file_path); @@ -498,16 +382,16 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) { } #if defined(OS_MACOSX) - if (settings.framework_dir_path.length > 0) { + if (settings_->framework_dir_path.length > 0) { base::FilePath file_path = - base::FilePath(CefString(&settings.framework_dir_path)); + base::FilePath(CefString(&settings_->framework_dir_path)); if (!file_path.empty()) command_line->AppendSwitchPath(switches::kFrameworkDirPath, file_path); } - if (settings.main_bundle_path.length > 0) { + if (settings_->main_bundle_path.length > 0) { base::FilePath file_path = - base::FilePath(CefString(&settings.main_bundle_path)); + base::FilePath(CefString(&settings_->main_bundle_path)); if (!file_path.empty()) command_line->AppendSwitchPath(switches::kMainBundlePath, file_path); } @@ -516,25 +400,25 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) { if (no_sandbox) command_line->AppendSwitch(service_manager::switches::kNoSandbox); - if (settings.user_agent.length > 0) { + if (settings_->user_agent.length > 0) { command_line->AppendSwitchASCII(switches::kUserAgent, - CefString(&settings.user_agent)); - } else if (settings.product_version.length > 0) { + CefString(&settings_->user_agent)); + } else if (settings_->product_version.length > 0) { command_line->AppendSwitchASCII(switches::kProductVersion, - CefString(&settings.product_version)); + CefString(&settings_->product_version)); } - if (settings.locale.length > 0) { + if (settings_->locale.length > 0) { command_line->AppendSwitchASCII(switches::kLang, - CefString(&settings.locale)); + CefString(&settings_->locale)); } else if (!command_line->HasSwitch(switches::kLang)) { command_line->AppendSwitchASCII(switches::kLang, "en-US"); } base::FilePath log_file; bool has_log_file_cmdline = false; - if (settings.log_file.length > 0) - log_file = base::FilePath(CefString(&settings.log_file)); + if (settings_->log_file.length > 0) + log_file = base::FilePath(CefString(&settings_->log_file)); if (log_file.empty() && command_line->HasSwitch(switches::kLogFile)) { log_file = command_line->GetSwitchValuePath(switches::kLogFile); if (!log_file.empty()) @@ -546,9 +430,9 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) { if (!has_log_file_cmdline) command_line->AppendSwitchPath(switches::kLogFile, log_file); - if (settings.log_severity != LOGSEVERITY_DEFAULT) { + if (settings_->log_severity != LOGSEVERITY_DEFAULT) { std::string log_severity; - switch (settings.log_severity) { + switch (settings_->log_severity) { case LOGSEVERITY_VERBOSE: log_severity = switches::kLogSeverity_Verbose; break; @@ -574,42 +458,42 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) { command_line->AppendSwitchASCII(switches::kLogSeverity, log_severity); } - if (settings.javascript_flags.length > 0) { + if (settings_->javascript_flags.length > 0) { command_line->AppendSwitchASCII(switches::kJavaScriptFlags, - CefString(&settings.javascript_flags)); + CefString(&settings_->javascript_flags)); } - if (settings.pack_loading_disabled) { + if (settings_->pack_loading_disabled) { command_line->AppendSwitch(switches::kDisablePackLoading); } else { - if (settings.resources_dir_path.length > 0) { + if (settings_->resources_dir_path.length > 0) { base::FilePath file_path = - base::FilePath(CefString(&settings.resources_dir_path)); + base::FilePath(CefString(&settings_->resources_dir_path)); if (!file_path.empty()) { command_line->AppendSwitchPath(switches::kResourcesDirPath, file_path); } } - if (settings.locales_dir_path.length > 0) { + if (settings_->locales_dir_path.length > 0) { base::FilePath file_path = - base::FilePath(CefString(&settings.locales_dir_path)); + base::FilePath(CefString(&settings_->locales_dir_path)); if (!file_path.empty()) command_line->AppendSwitchPath(switches::kLocalesDirPath, file_path); } } - if (settings.remote_debugging_port >= 1024 && - settings.remote_debugging_port <= 65535) { + if (settings_->remote_debugging_port >= 1024 && + settings_->remote_debugging_port <= 65535) { command_line->AppendSwitchASCII( switches::kRemoteDebuggingPort, - base::NumberToString(settings.remote_debugging_port)); + base::NumberToString(settings_->remote_debugging_port)); } - if (settings.uncaught_exception_stack_size > 0) { + if (settings_->uncaught_exception_stack_size > 0) { command_line->AppendSwitchASCII( switches::kUncaughtExceptionStackSize, - base::NumberToString(settings.uncaught_exception_stack_size)); + base::NumberToString(settings_->uncaught_exception_stack_size)); } std::vector disable_features; @@ -765,7 +649,7 @@ void CefMainDelegate::PreSandboxStartup() { dir_default_download_safe); } - const base::FilePath& user_data_path = GetUserDataPath(); + const base::FilePath& user_data_path = GetUserDataPath(settings_); base::PathService::Override(chrome::DIR_USER_DATA, user_data_path); // Path used for crash dumps. @@ -800,40 +684,12 @@ int CefMainDelegate::RunProcess( const std::string& process_type, const content::MainFunctionParams& main_function_params) { if (process_type.empty()) { - const CefSettings& settings = CefContext::Get()->settings(); - if (!settings.multi_threaded_message_loop) { - // Use our own browser process runner. - browser_runner_ = content::BrowserMainRunner::Create(); - - // Initialize browser process state. Results in a call to - // CefBrowserMain::PreMainMessageLoopStart() which creates the UI message - // loop. - int exit_code = browser_runner_->Initialize(main_function_params); - if (exit_code >= 0) - return exit_code; - } else { - // Running on the separate UI thread. - DCHECK(ui_thread_); - ui_thread_->InitializeBrowserRunner(main_function_params); - } - - return 0; + return runner_->RunMainProcess(main_function_params); } return -1; } -bool CefMainDelegate::CreateUIThread(base::OnceClosure setup_callback) { - DCHECK(!ui_thread_); - - ui_thread_.reset(new CefUIThread(std::move(setup_callback))); - ui_thread_->Start(); - ui_thread_->WaitUntilThreadStarted(); - - InitMessagePumpFactoryForUI(); - return true; -} - void CefMainDelegate::ProcessExiting(const std::string& process_type) { ui::ResourceBundle::CleanupSharedInstance(); } @@ -863,17 +719,38 @@ content::ContentUtilityClient* CefMainDelegate::CreateContentUtilityClient() { return utility_client_.get(); } -void CefMainDelegate::ShutdownBrowser() { - if (browser_runner_.get()) { - browser_runner_->Shutdown(); - browser_runner_.reset(nullptr); - } +// static +void CefMainDelegate::CefInitialize() { + g_browser_process = new ChromeBrowserProcessStub(); +} - if (ui_thread_.get()) { - // Blocks until the thread has stopped. - ui_thread_->Stop(); - ui_thread_.reset(); - } +// static +void CefMainDelegate::MainThreadInitialize() { + static_cast(g_browser_process)->Initialize(); +} + +// static +void CefMainDelegate::UIThreadInitialize() { +#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS) + CefWidevineLoader::GetInstance()->OnContextInitialized(); +#endif + + static_cast(g_browser_process) + ->OnContextInitialized(); +} + +// static +void CefMainDelegate::UIThreadShutdown() { + static_cast(g_browser_process) + ->CleanupOnUIThread(); + + ui::ResourceBundle::GetSharedInstance().CleanupOnUIThread(); +} + +// static +void CefMainDelegate::MainThreadShutdown() { + delete g_browser_process; + g_browser_process = nullptr; } void CefMainDelegate::InitializeResourceBundle() { diff --git a/libcef/common/main_delegate.h b/libcef/common/main_delegate.h index fecc697a4..77921a6a0 100644 --- a/libcef/common/main_delegate.h +++ b/libcef/common/main_delegate.h @@ -16,22 +16,30 @@ namespace base { class CommandLine; -class MessageLoop; -class Thread; -} // namespace base - -namespace content { -class BrowserMainRunner; } class CefContentBrowserClient; class CefContentRendererClient; -class CefUIThread; class ChromeContentUtilityClient; +// Manages state specific to the CEF runtime. class CefMainDelegate : public content::ContentMainDelegate { public: - explicit CefMainDelegate(CefRefPtr application); + class Runner { + public: + virtual void PreCreateMainMessageLoop() = 0; + virtual int RunMainProcess( + const content::MainFunctionParams& main_function_params) = 0; + + protected: + virtual ~Runner() {} + }; + + // |runner| and |settings| will be non-nullptr for the main process only, + // and will outlive this object. + CefMainDelegate(Runner* runner, + CefSettings* settings, + CefRefPtr application); ~CefMainDelegate() override; void PreCreateMainMessageLoop() override; @@ -49,19 +57,22 @@ class CefMainDelegate : public content::ContentMainDelegate { content::ContentRendererClient* CreateContentRendererClient() override; content::ContentUtilityClient* CreateContentUtilityClient() override; - bool CreateUIThread(base::OnceClosure setup_callback); - - // Shut down the browser runner. - void ShutdownBrowser(); - CefContentBrowserClient* browser_client() { return browser_client_.get(); } CefContentClient* content_client() { return &content_client_; } + // Called from MainRunner at various initialization/shutdown stages to create + // and clean up global state. + static void CefInitialize(); + static void MainThreadInitialize(); + static void UIThreadInitialize(); + static void UIThreadShutdown(); + static void MainThreadShutdown(); + private: void InitializeResourceBundle(); - std::unique_ptr browser_runner_; - std::unique_ptr ui_thread_; + Runner* const runner_; + CefSettings* const settings_; std::unique_ptr browser_client_; std::unique_ptr renderer_client_;