1 // Copyright 2020 The Chromium Embedded Framework Authors.
2 // Portions copyright 2014 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include "libcef/browser/main_runner.h"
7
8 #include "libcef/browser/browser_message_loop.h"
9 #include "libcef/browser/thread_util.h"
10 #include "libcef/common/alloy/alloy_main_runner_delegate.h"
11 #include "libcef/common/cef_switches.h"
12 #include "libcef/common/chrome/chrome_main_runner_delegate.h"
13 #include "libcef/features/runtime.h"
14
15 #include "base/at_exit.h"
16 #include "base/base_switches.h"
17 #include "base/command_line.h"
18 #include "base/debug/debugger.h"
19 #include "base/run_loop.h"
20 #include "base/sequence_checker.h"
21 #include "base/synchronization/lock.h"
22 #include "base/synchronization/waitable_event.h"
23 #include "base/threading/thread.h"
24 #include "content/app/content_main_runner_impl.h"
25 #include "content/browser/scheduler/browser_task_executor.h"
26 #include "content/public/app/content_main.h"
27 #include "content/public/app/content_main_runner.h"
28 #include "content/public/browser/render_process_host.h"
29 #include "content/public/common/content_switches.h"
30
31 #if BUILDFLAG(IS_WIN)
32 #include <Objbase.h>
33 #include <windows.h>
34 #include "content/public/app/sandbox_helper_win.h"
35 #include "sandbox/win/src/sandbox_types.h"
36 #endif
37
38 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
39 #include "components/crash/core/app/crash_switches.h"
40 #include "third_party/crashpad/crashpad/handler/handler_main.h"
41 #endif
42
43 namespace {
44
45 enum class RuntimeType {
46 UNINITIALIZED,
47 ALLOY,
48 CHROME,
49 };
50 RuntimeType g_runtime_type = RuntimeType::UNINITIALIZED;
51
MakeDelegate(RuntimeType type,CefMainRunnerHandler * runner,CefSettings * settings,CefRefPtr<CefApp> application)52 std::unique_ptr<CefMainRunnerDelegate> MakeDelegate(
53 RuntimeType type,
54 CefMainRunnerHandler* runner,
55 CefSettings* settings,
56 CefRefPtr<CefApp> application) {
57 if (type == RuntimeType::ALLOY) {
58 g_runtime_type = RuntimeType::ALLOY;
59 return std::make_unique<AlloyMainRunnerDelegate>(runner, settings,
60 application);
61 } else {
62 g_runtime_type = RuntimeType::CHROME;
63 return std::make_unique<ChromeMainRunnerDelegate>(runner, settings,
64 application);
65 }
66 }
67
68 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
69
70 // Based on components/crash/core/app/run_as_crashpad_handler_win.cc
71 // Remove the "--type=crashpad-handler" command-line flag that will otherwise
72 // confuse the crashpad handler.
73 // Chrome uses an embedded crashpad handler on Windows only and imports this
74 // function via the existing "run_as_crashpad_handler" target defined in
75 // components/crash/core/app/BUILD.gn. CEF uses an embedded handler on both
76 // Windows and macOS so we define the function here instead of using the
77 // existing target (because we can't use that target on macOS).
RunAsCrashpadHandler(const base::CommandLine & command_line)78 int RunAsCrashpadHandler(const base::CommandLine& command_line) {
79 base::CommandLine::StringVector argv = command_line.argv();
80 const base::CommandLine::StringType process_type =
81 FILE_PATH_LITERAL("--type=");
82 argv.erase(
83 std::remove_if(argv.begin(), argv.end(),
84 [&process_type](const base::CommandLine::StringType& str) {
85 return base::StartsWith(str, process_type,
86 base::CompareCase::SENSITIVE) ||
87 (!str.empty() && str[0] == L'/');
88 }),
89 argv.end());
90
91 #if BUILDFLAG(IS_MAC)
92 // HandlerMain on macOS uses the system version of getopt_long which expects
93 // the first argument to be the program name.
94 argv.insert(argv.begin(), command_line.GetProgram().value());
95 #endif
96
97 std::unique_ptr<char*[]> argv_as_utf8(new char*[argv.size() + 1]);
98 std::vector<std::string> storage;
99 storage.reserve(argv.size());
100 for (size_t i = 0; i < argv.size(); ++i) {
101 #if BUILDFLAG(IS_WIN)
102 storage.push_back(base::WideToUTF8(argv[i]));
103 #else
104 storage.push_back(argv[i]);
105 #endif
106 argv_as_utf8[i] = &storage[i][0];
107 }
108 argv_as_utf8[argv.size()] = nullptr;
109 argv.clear();
110 return crashpad::HandlerMain(static_cast<int>(storage.size()),
111 argv_as_utf8.get(), nullptr);
112 }
113
114 #endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
115
116 } // namespace
117
118 // Used to run the UI on a separate thread.
119 class CefUIThread : public base::PlatformThread::Delegate {
120 public:
CefUIThread(CefMainRunner * runner,base::OnceClosure setup_callback)121 CefUIThread(CefMainRunner* runner, base::OnceClosure setup_callback)
122 : runner_(runner), setup_callback_(std::move(setup_callback)) {}
~CefUIThread()123 ~CefUIThread() override { Stop(); }
124
Start()125 void Start() {
126 base::AutoLock lock(thread_lock_);
127 bool success = base::PlatformThread::CreateWithPriority(
128 0, this, &thread_, base::ThreadPriority::NORMAL);
129 if (!success) {
130 LOG(FATAL) << "failed to UI create thread";
131 }
132 }
133
Stop()134 void Stop() {
135 base::AutoLock lock(thread_lock_);
136
137 if (!stopping_) {
138 stopping_ = true;
139 CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefMainRunner::QuitMessageLoop,
140 base::Unretained(runner_)));
141 }
142
143 // Can't join if the |thread_| is either already gone or is non-joinable.
144 if (thread_.is_null())
145 return;
146
147 base::PlatformThread::Join(thread_);
148 thread_ = base::PlatformThreadHandle();
149
150 stopping_ = false;
151 }
152
WaitUntilThreadStarted() const153 bool WaitUntilThreadStarted() const {
154 DCHECK(owning_sequence_checker_.CalledOnValidSequence());
155 start_event_.Wait();
156 return true;
157 }
158
InitializeBrowserRunner(content::MainFunctionParams main_function_params)159 void InitializeBrowserRunner(
160 content::MainFunctionParams main_function_params) {
161 // Use our own browser process runner.
162 browser_runner_ = content::BrowserMainRunner::Create();
163
164 // Initialize browser process state. Uses the current thread's message loop.
165 int exit_code =
166 browser_runner_->Initialize(std::move(main_function_params));
167 CHECK_EQ(exit_code, -1);
168 }
169
170 protected:
ThreadMain()171 void ThreadMain() override {
172 base::PlatformThread::SetName("CefUIThread");
173
174 #if BUILDFLAG(IS_WIN)
175 // Initializes the COM library on the current thread.
176 CoInitialize(nullptr);
177 #endif
178
179 start_event_.Signal();
180
181 std::move(setup_callback_).Run();
182
183 runner_->RunMessageLoop();
184
185 browser_runner_->Shutdown();
186 browser_runner_.reset();
187
188 content::BrowserTaskExecutor::Shutdown();
189
190 // Run exit callbacks on the UI thread to avoid sequence check failures.
191 base::AtExitManager::ProcessCallbacksNow();
192
193 #if BUILDFLAG(IS_WIN)
194 // Closes the COM library on the current thread. CoInitialize must
195 // be balanced by a corresponding call to CoUninitialize.
196 CoUninitialize();
197 #endif
198 }
199
200 CefMainRunner* const runner_;
201 base::OnceClosure setup_callback_;
202
203 std::unique_ptr<content::BrowserMainRunner> browser_runner_;
204
205 bool stopping_ = false;
206
207 // The thread's handle.
208 base::PlatformThreadHandle thread_;
209 mutable base::Lock thread_lock_; // Protects |thread_|.
210
211 mutable base::WaitableEvent start_event_;
212
213 // This class is not thread-safe, use this to verify access from the owning
214 // sequence of the Thread.
215 base::SequenceChecker owning_sequence_checker_;
216 };
217
CefMainRunner(bool multi_threaded_message_loop,bool external_message_pump)218 CefMainRunner::CefMainRunner(bool multi_threaded_message_loop,
219 bool external_message_pump)
220 : multi_threaded_message_loop_(multi_threaded_message_loop),
221 external_message_pump_(external_message_pump) {}
222
223 CefMainRunner::~CefMainRunner() = default;
224
Initialize(CefSettings * settings,CefRefPtr<CefApp> application,const CefMainArgs & args,void * windows_sandbox_info,bool * initialized,base::OnceClosure context_initialized)225 bool CefMainRunner::Initialize(CefSettings* settings,
226 CefRefPtr<CefApp> application,
227 const CefMainArgs& args,
228 void* windows_sandbox_info,
229 bool* initialized,
230 base::OnceClosure context_initialized) {
231 DCHECK(!main_delegate_);
232 main_delegate_ = MakeDelegate(
233 settings->chrome_runtime ? RuntimeType::CHROME : RuntimeType::ALLOY, this,
234 settings, application);
235
236 const int exit_code =
237 ContentMainInitialize(args, windows_sandbox_info, &settings->no_sandbox);
238 if (exit_code >= 0) {
239 NOTREACHED() << "ContentMainInitialize failed";
240 return false;
241 }
242
243 if (!ContentMainRun(initialized, std::move(context_initialized))) {
244 NOTREACHED() << "ContentMainRun failed";
245 return false;
246 }
247
248 return true;
249 }
250
Shutdown(base::OnceClosure shutdown_on_ui_thread,base::OnceClosure finalize_shutdown)251 void CefMainRunner::Shutdown(base::OnceClosure shutdown_on_ui_thread,
252 base::OnceClosure finalize_shutdown) {
253 if (multi_threaded_message_loop_) {
254 // Events that will be used to signal when shutdown is complete. Start in
255 // non-signaled mode so that the event will block.
256 base::WaitableEvent uithread_shutdown_event(
257 base::WaitableEvent::ResetPolicy::AUTOMATIC,
258 base::WaitableEvent::InitialState::NOT_SIGNALED);
259
260 // Finish shutdown on the UI thread.
261 CEF_POST_TASK(
262 CEF_UIT,
263 base::BindOnce(&CefMainRunner::FinishShutdownOnUIThread,
264 base::Unretained(this), std::move(shutdown_on_ui_thread),
265 &uithread_shutdown_event));
266
267 /// Block until UI thread shutdown is complete.
268 uithread_shutdown_event.Wait();
269
270 FinalizeShutdown(std::move(finalize_shutdown));
271 } else {
272 // Finish shutdown on the current thread, which should be the UI thread.
273 FinishShutdownOnUIThread(std::move(shutdown_on_ui_thread), nullptr);
274
275 FinalizeShutdown(std::move(finalize_shutdown));
276 }
277 }
278
RunMessageLoop()279 void CefMainRunner::RunMessageLoop() {
280 base::RunLoop run_loop;
281
282 DCHECK(quit_when_idle_callback_.is_null());
283 quit_when_idle_callback_ = run_loop.QuitWhenIdleClosure();
284
285 main_delegate_->BeforeMainMessageLoopRun(&run_loop);
286
287 // Blocks until QuitMessageLoop() is called.
288 run_loop.Run();
289 }
290
QuitMessageLoop()291 void CefMainRunner::QuitMessageLoop() {
292 if (!quit_when_idle_callback_.is_null()) {
293 if (main_delegate_->HandleMainMessageLoopQuit())
294 return;
295 std::move(quit_when_idle_callback_).Run();
296 }
297 }
298
299 // static
RunAsHelperProcess(const CefMainArgs & args,CefRefPtr<CefApp> application,void * windows_sandbox_info)300 int CefMainRunner::RunAsHelperProcess(const CefMainArgs& args,
301 CefRefPtr<CefApp> application,
302 void* windows_sandbox_info) {
303 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
304 #if BUILDFLAG(IS_WIN)
305 command_line.ParseFromString(::GetCommandLineW());
306 #else
307 command_line.InitFromArgv(args.argc, args.argv);
308 #endif
309
310 // Wait for the debugger as early in process initialization as possible.
311 if (command_line.HasSwitch(switches::kWaitForDebugger))
312 base::debug::WaitForDebugger(60, true);
313
314 // If no process type is specified then it represents the browser process and
315 // we do nothing.
316 const std::string& process_type =
317 command_line.GetSwitchValueASCII(switches::kProcessType);
318 if (process_type.empty())
319 return -1;
320
321 auto runtime_type = command_line.HasSwitch(switches::kEnableChromeRuntime)
322 ? RuntimeType::CHROME
323 : RuntimeType::ALLOY;
324 auto main_delegate = MakeDelegate(runtime_type, /*runner=*/nullptr,
325 /*settings=*/nullptr, application);
326 main_delegate->BeforeExecuteProcess(args);
327
328 int result;
329
330 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
331 if (process_type == crash_reporter::switches::kCrashpadHandler) {
332 result = RunAsCrashpadHandler(command_line);
333 main_delegate->AfterExecuteProcess();
334 return result;
335 }
336 #endif
337
338 // Execute the secondary process.
339 content::ContentMainParams main_params(
340 main_delegate->GetContentMainDelegate());
341 #if BUILDFLAG(IS_WIN)
342 sandbox::SandboxInterfaceInfo sandbox_info = {nullptr};
343 if (windows_sandbox_info == nullptr) {
344 content::InitializeSandboxInfo(&sandbox_info);
345 windows_sandbox_info = &sandbox_info;
346 }
347
348 main_params.instance = args.instance;
349 main_params.sandbox_info =
350 static_cast<sandbox::SandboxInterfaceInfo*>(windows_sandbox_info);
351 #else
352 main_params.argc = args.argc;
353 main_params.argv = const_cast<const char**>(args.argv);
354 #endif
355 result = content::ContentMain(std::move(main_params));
356
357 main_delegate->AfterExecuteProcess();
358
359 return result;
360 }
361
ContentMainInitialize(const CefMainArgs & args,void * windows_sandbox_info,int * no_sandbox)362 int CefMainRunner::ContentMainInitialize(const CefMainArgs& args,
363 void* windows_sandbox_info,
364 int* no_sandbox) {
365 main_delegate_->BeforeMainThreadInitialize(args);
366
367 // Initialize the content runner.
368 main_runner_ = content::ContentMainRunner::Create();
369 content::ContentMainParams main_params(
370 main_delegate_->GetContentMainDelegate());
371
372 #if BUILDFLAG(IS_WIN)
373 sandbox::SandboxInterfaceInfo sandbox_info = {nullptr};
374 if (windows_sandbox_info == nullptr) {
375 windows_sandbox_info = &sandbox_info;
376 *no_sandbox = true;
377 }
378
379 main_params.instance = args.instance;
380 main_params.sandbox_info =
381 static_cast<sandbox::SandboxInterfaceInfo*>(windows_sandbox_info);
382 #else
383 main_params.argc = args.argc;
384 main_params.argv = const_cast<const char**>(args.argv);
385 #endif
386
387 return content::ContentMainInitialize(std::move(main_params),
388 main_runner_.get());
389 }
390
ContentMainRun(bool * initialized,base::OnceClosure context_initialized)391 bool CefMainRunner::ContentMainRun(bool* initialized,
392 base::OnceClosure context_initialized) {
393 main_delegate_->BeforeMainThreadRun();
394
395 if (multi_threaded_message_loop_) {
396 base::WaitableEvent uithread_startup_event(
397 base::WaitableEvent::ResetPolicy::AUTOMATIC,
398 base::WaitableEvent::InitialState::NOT_SIGNALED);
399
400 if (!CreateUIThread(base::BindOnce(
401 [](CefMainRunner* runner, base::WaitableEvent* event) {
402 content::ContentMainRun(runner->main_runner_.get());
403 event->Signal();
404 },
405 base::Unretained(this),
406 base::Unretained(&uithread_startup_event)))) {
407 return false;
408 }
409
410 *initialized = true;
411
412 // We need to wait until content::ContentMainRun has finished.
413 uithread_startup_event.Wait();
414 } else {
415 *initialized = true;
416 content::ContentMainRun(main_runner_.get());
417 }
418
419 if (CEF_CURRENTLY_ON_UIT()) {
420 OnContextInitialized(std::move(context_initialized));
421 } else {
422 // Continue initialization on the UI thread.
423 CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefMainRunner::OnContextInitialized,
424 base::Unretained(this),
425 std::move(context_initialized)));
426 }
427
428 return true;
429 }
430
PreBrowserMain()431 void CefMainRunner::PreBrowserMain() {
432 if (external_message_pump_) {
433 InitExternalMessagePumpFactoryForUI();
434 }
435 }
436
RunMainProcess(content::MainFunctionParams main_function_params)437 int CefMainRunner::RunMainProcess(
438 content::MainFunctionParams main_function_params) {
439 if (!multi_threaded_message_loop_) {
440 // Use our own browser process runner.
441 browser_runner_ = content::BrowserMainRunner::Create();
442
443 // Initialize browser process state. Results in a call to
444 // AlloyBrowserMain::PreBrowserMain() which creates the UI message
445 // loop.
446 int exit_code =
447 browser_runner_->Initialize(std::move(main_function_params));
448 if (exit_code >= 0)
449 return exit_code;
450 } else {
451 // Running on the separate UI thread.
452 DCHECK(ui_thread_);
453 ui_thread_->InitializeBrowserRunner(std::move(main_function_params));
454 }
455
456 return 0;
457 }
458
CreateUIThread(base::OnceClosure setup_callback)459 bool CefMainRunner::CreateUIThread(base::OnceClosure setup_callback) {
460 DCHECK(!ui_thread_);
461
462 ui_thread_.reset(new CefUIThread(this, std::move(setup_callback)));
463 ui_thread_->Start();
464 ui_thread_->WaitUntilThreadStarted();
465
466 if (external_message_pump_) {
467 InitExternalMessagePumpFactoryForUI();
468 }
469 return true;
470 }
471
OnContextInitialized(base::OnceClosure context_initialized)472 void CefMainRunner::OnContextInitialized(
473 base::OnceClosure context_initialized) {
474 CEF_REQUIRE_UIT();
475
476 main_delegate_->AfterUIThreadInitialize();
477 std::move(context_initialized).Run();
478 }
479
FinishShutdownOnUIThread(base::OnceClosure shutdown_on_ui_thread,base::WaitableEvent * uithread_shutdown_event)480 void CefMainRunner::FinishShutdownOnUIThread(
481 base::OnceClosure shutdown_on_ui_thread,
482 base::WaitableEvent* uithread_shutdown_event) {
483 CEF_REQUIRE_UIT();
484
485 // Execute all pending tasks now before proceeding with shutdown. Otherwise,
486 // objects bound to tasks and released at the end of shutdown via
487 // BrowserTaskExecutor::Shutdown may attempt to access other objects that have
488 // already been destroyed (for example, if teardown results in a call to
489 // RenderProcessHostImpl::Cleanup).
490 content::BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(
491 content::BrowserThread::UI);
492 content::BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(
493 content::BrowserThread::IO);
494
495 static_cast<content::ContentMainRunnerImpl*>(main_runner_.get())
496 ->ShutdownOnUIThread();
497
498 std::move(shutdown_on_ui_thread).Run();
499 main_delegate_->AfterUIThreadShutdown();
500
501 if (uithread_shutdown_event)
502 uithread_shutdown_event->Signal();
503 }
504
FinalizeShutdown(base::OnceClosure finalize_shutdown)505 void CefMainRunner::FinalizeShutdown(base::OnceClosure finalize_shutdown) {
506 main_delegate_->BeforeMainThreadShutdown();
507
508 if (browser_runner_.get()) {
509 browser_runner_->Shutdown();
510 browser_runner_.reset();
511 }
512
513 if (ui_thread_.get()) {
514 // Blocks until the thread has stopped.
515 ui_thread_->Stop();
516 ui_thread_.reset();
517 }
518
519 // Shut down the content runner.
520 content::ContentMainShutdown(main_runner_.get());
521
522 main_runner_.reset();
523
524 std::move(finalize_shutdown).Run();
525 main_delegate_->AfterMainThreadShutdown();
526
527 main_delegate_.reset();
528 g_runtime_type = RuntimeType::UNINITIALIZED;
529 }
530
531 // From libcef/features/runtime.h:
532 namespace cef {
533
IsAlloyRuntimeEnabled()534 bool IsAlloyRuntimeEnabled() {
535 return g_runtime_type == RuntimeType::ALLOY;
536 }
537
IsChromeRuntimeEnabled()538 bool IsChromeRuntimeEnabled() {
539 return g_runtime_type == RuntimeType::CHROME;
540 }
541
542 } // namespace cef
543