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 defined(OS_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 defined(OS_MAC) || defined(OS_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 defined(OS_MAC) || defined(OS_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 defined(OS_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 defined(OS_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 // defined(OS_MAC) || defined(OS_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(const content::MainFunctionParams & main_function_params)159 void InitializeBrowserRunner(
160 const 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 = browser_runner_->Initialize(main_function_params);
166 CHECK_EQ(exit_code, -1);
167 }
168
169 protected:
ThreadMain()170 void ThreadMain() override {
171 base::PlatformThread::SetName("CefUIThread");
172
173 #if defined(OS_WIN)
174 // Initializes the COM library on the current thread.
175 CoInitialize(nullptr);
176 #endif
177
178 start_event_.Signal();
179
180 std::move(setup_callback_).Run();
181
182 runner_->RunMessageLoop();
183
184 browser_runner_->Shutdown();
185 browser_runner_.reset();
186
187 content::BrowserTaskExecutor::Shutdown();
188
189 // Run exit callbacks on the UI thread to avoid sequence check failures.
190 base::AtExitManager::ProcessCallbacksNow();
191
192 #if defined(OS_WIN)
193 // Closes the COM library on the current thread. CoInitialize must
194 // be balanced by a corresponding call to CoUninitialize.
195 CoUninitialize();
196 #endif
197 }
198
199 CefMainRunner* const runner_;
200 base::OnceClosure setup_callback_;
201
202 std::unique_ptr<content::BrowserMainRunner> browser_runner_;
203
204 bool stopping_ = false;
205
206 // The thread's handle.
207 base::PlatformThreadHandle thread_;
208 mutable base::Lock thread_lock_; // Protects |thread_|.
209
210 mutable base::WaitableEvent start_event_;
211
212 // This class is not thread-safe, use this to verify access from the owning
213 // sequence of the Thread.
214 base::SequenceChecker owning_sequence_checker_;
215 };
216
CefMainRunner(bool multi_threaded_message_loop,bool external_message_pump)217 CefMainRunner::CefMainRunner(bool multi_threaded_message_loop,
218 bool external_message_pump)
219 : multi_threaded_message_loop_(multi_threaded_message_loop),
220 external_message_pump_(external_message_pump) {}
221
222 CefMainRunner::~CefMainRunner() = default;
223
Initialize(CefSettings * settings,CefRefPtr<CefApp> application,const CefMainArgs & args,void * windows_sandbox_info,bool * initialized,base::OnceClosure context_initialized)224 bool CefMainRunner::Initialize(CefSettings* settings,
225 CefRefPtr<CefApp> application,
226 const CefMainArgs& args,
227 void* windows_sandbox_info,
228 bool* initialized,
229 base::OnceClosure context_initialized) {
230 DCHECK(!main_delegate_);
231 main_delegate_ = MakeDelegate(
232 settings->chrome_runtime ? RuntimeType::CHROME : RuntimeType::ALLOY, this,
233 settings, application);
234
235 const int exit_code =
236 ContentMainInitialize(args, windows_sandbox_info, &settings->no_sandbox);
237 if (exit_code >= 0) {
238 NOTREACHED() << "ContentMainInitialize failed";
239 return false;
240 }
241
242 if (!ContentMainRun(initialized, std::move(context_initialized))) {
243 NOTREACHED() << "ContentMainRun failed";
244 return false;
245 }
246
247 return true;
248 }
249
Shutdown(base::OnceClosure shutdown_on_ui_thread,base::OnceClosure finalize_shutdown)250 void CefMainRunner::Shutdown(base::OnceClosure shutdown_on_ui_thread,
251 base::OnceClosure finalize_shutdown) {
252 if (multi_threaded_message_loop_) {
253 // Events that will be used to signal when shutdown is complete. Start in
254 // non-signaled mode so that the event will block.
255 base::WaitableEvent uithread_shutdown_event(
256 base::WaitableEvent::ResetPolicy::AUTOMATIC,
257 base::WaitableEvent::InitialState::NOT_SIGNALED);
258
259 // Finish shutdown on the UI thread.
260 CEF_POST_TASK(
261 CEF_UIT,
262 base::BindOnce(&CefMainRunner::FinishShutdownOnUIThread,
263 base::Unretained(this), std::move(shutdown_on_ui_thread),
264 &uithread_shutdown_event));
265
266 /// Block until UI thread shutdown is complete.
267 uithread_shutdown_event.Wait();
268
269 FinalizeShutdown(std::move(finalize_shutdown));
270 } else {
271 // Finish shutdown on the current thread, which should be the UI thread.
272 FinishShutdownOnUIThread(std::move(shutdown_on_ui_thread), nullptr);
273
274 FinalizeShutdown(std::move(finalize_shutdown));
275 }
276 }
277
RunMessageLoop()278 void CefMainRunner::RunMessageLoop() {
279 base::RunLoop run_loop;
280
281 DCHECK(quit_when_idle_callback_.is_null());
282 quit_when_idle_callback_ = run_loop.QuitWhenIdleClosure();
283
284 main_delegate_->BeforeMainMessageLoopRun(&run_loop);
285
286 // Blocks until QuitMessageLoop() is called.
287 run_loop.Run();
288 }
289
QuitMessageLoop()290 void CefMainRunner::QuitMessageLoop() {
291 if (!quit_when_idle_callback_.is_null()) {
292 if (main_delegate_->HandleMainMessageLoopQuit())
293 return;
294 std::move(quit_when_idle_callback_).Run();
295 }
296 }
297
298 // static
RunAsHelperProcess(const CefMainArgs & args,CefRefPtr<CefApp> application,void * windows_sandbox_info)299 int CefMainRunner::RunAsHelperProcess(const CefMainArgs& args,
300 CefRefPtr<CefApp> application,
301 void* windows_sandbox_info) {
302 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
303 #if defined(OS_WIN)
304 command_line.ParseFromString(::GetCommandLineW());
305 #else
306 command_line.InitFromArgv(args.argc, args.argv);
307 #endif
308
309 // Wait for the debugger as early in process initialization as possible.
310 if (command_line.HasSwitch(switches::kWaitForDebugger))
311 base::debug::WaitForDebugger(60, true);
312
313 // If no process type is specified then it represents the browser process and
314 // we do nothing.
315 const std::string& process_type =
316 command_line.GetSwitchValueASCII(switches::kProcessType);
317 if (process_type.empty())
318 return -1;
319
320 auto runtime_type = command_line.HasSwitch(switches::kEnableChromeRuntime)
321 ? RuntimeType::CHROME
322 : RuntimeType::ALLOY;
323 auto main_delegate = MakeDelegate(runtime_type, /*runner=*/nullptr,
324 /*settings=*/nullptr, application);
325 main_delegate->BeforeExecuteProcess(args);
326
327 int result;
328
329 #if defined(OS_MAC) || defined(OS_WIN)
330 if (process_type == crash_reporter::switches::kCrashpadHandler) {
331 result = RunAsCrashpadHandler(command_line);
332 main_delegate->AfterExecuteProcess();
333 return result;
334 }
335 #endif
336
337 // Execute the secondary process.
338 content::ContentMainParams params(main_delegate->GetContentMainDelegate());
339 #if defined(OS_WIN)
340 sandbox::SandboxInterfaceInfo sandbox_info = {0};
341 if (windows_sandbox_info == nullptr) {
342 content::InitializeSandboxInfo(&sandbox_info);
343 windows_sandbox_info = &sandbox_info;
344 }
345
346 params.instance = args.instance;
347 params.sandbox_info =
348 static_cast<sandbox::SandboxInterfaceInfo*>(windows_sandbox_info);
349 #else
350 params.argc = args.argc;
351 params.argv = const_cast<const char**>(args.argv);
352 #endif
353 result = content::ContentMain(params);
354
355 main_delegate->AfterExecuteProcess();
356
357 return result;
358 }
359
ContentMainInitialize(const CefMainArgs & args,void * windows_sandbox_info,int * no_sandbox)360 int CefMainRunner::ContentMainInitialize(const CefMainArgs& args,
361 void* windows_sandbox_info,
362 int* no_sandbox) {
363 main_delegate_->BeforeMainThreadInitialize(args);
364
365 // Initialize the content runner.
366 main_runner_ = content::ContentMainRunner::Create();
367 main_params_ = std::make_unique<content::ContentMainParams>(
368 main_delegate_->GetContentMainDelegate());
369
370 #if defined(OS_WIN)
371 sandbox::SandboxInterfaceInfo sandbox_info = {0};
372 if (windows_sandbox_info == nullptr) {
373 windows_sandbox_info = &sandbox_info;
374 *no_sandbox = true;
375 }
376
377 main_params_->instance = args.instance;
378 main_params_->sandbox_info =
379 static_cast<sandbox::SandboxInterfaceInfo*>(windows_sandbox_info);
380 #else
381 main_params_->argc = args.argc;
382 main_params_->argv = const_cast<const char**>(args.argv);
383 #endif
384
385 return content::ContentMainInitialize(*main_params_, main_runner_.get());
386 }
387
ContentMainRun(bool * initialized,base::OnceClosure context_initialized)388 bool CefMainRunner::ContentMainRun(bool* initialized,
389 base::OnceClosure context_initialized) {
390 main_delegate_->BeforeMainThreadRun();
391
392 if (multi_threaded_message_loop_) {
393 base::WaitableEvent uithread_startup_event(
394 base::WaitableEvent::ResetPolicy::AUTOMATIC,
395 base::WaitableEvent::InitialState::NOT_SIGNALED);
396
397 if (!CreateUIThread(base::BindOnce(
398 [](CefMainRunner* runner, base::WaitableEvent* event) {
399 content::ContentMainRun(*runner->main_params_,
400 runner->main_runner_.get());
401 event->Signal();
402 },
403 base::Unretained(this),
404 base::Unretained(&uithread_startup_event)))) {
405 return false;
406 }
407
408 *initialized = true;
409
410 // We need to wait until content::ContentMainRun has finished.
411 uithread_startup_event.Wait();
412 } else {
413 *initialized = true;
414 content::ContentMainRun(*main_params_, main_runner_.get());
415 }
416
417 if (CEF_CURRENTLY_ON_UIT()) {
418 OnContextInitialized(std::move(context_initialized));
419 } else {
420 // Continue initialization on the UI thread.
421 CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefMainRunner::OnContextInitialized,
422 base::Unretained(this),
423 std::move(context_initialized)));
424 }
425
426 return true;
427 }
428
PreCreateMainMessageLoop()429 void CefMainRunner::PreCreateMainMessageLoop() {
430 if (external_message_pump_) {
431 InitExternalMessagePumpFactoryForUI();
432 }
433 }
434
RunMainProcess(const content::MainFunctionParams & main_function_params)435 int CefMainRunner::RunMainProcess(
436 const content::MainFunctionParams& main_function_params) {
437 if (!multi_threaded_message_loop_) {
438 // Use our own browser process runner.
439 browser_runner_ = content::BrowserMainRunner::Create();
440
441 // Initialize browser process state. Results in a call to
442 // AlloyBrowserMain::PreMainMessageLoopStart() which creates the UI message
443 // loop.
444 int exit_code = browser_runner_->Initialize(main_function_params);
445 if (exit_code >= 0)
446 return exit_code;
447 } else {
448 // Running on the separate UI thread.
449 DCHECK(ui_thread_);
450 ui_thread_->InitializeBrowserRunner(main_function_params);
451 }
452
453 return 0;
454 }
455
CreateUIThread(base::OnceClosure setup_callback)456 bool CefMainRunner::CreateUIThread(base::OnceClosure setup_callback) {
457 DCHECK(!ui_thread_);
458
459 ui_thread_.reset(new CefUIThread(this, std::move(setup_callback)));
460 ui_thread_->Start();
461 ui_thread_->WaitUntilThreadStarted();
462
463 if (external_message_pump_) {
464 InitExternalMessagePumpFactoryForUI();
465 }
466 return true;
467 }
468
OnContextInitialized(base::OnceClosure context_initialized)469 void CefMainRunner::OnContextInitialized(
470 base::OnceClosure context_initialized) {
471 CEF_REQUIRE_UIT();
472
473 main_delegate_->AfterUIThreadInitialize();
474 std::move(context_initialized).Run();
475 }
476
FinishShutdownOnUIThread(base::OnceClosure shutdown_on_ui_thread,base::WaitableEvent * uithread_shutdown_event)477 void CefMainRunner::FinishShutdownOnUIThread(
478 base::OnceClosure shutdown_on_ui_thread,
479 base::WaitableEvent* uithread_shutdown_event) {
480 CEF_REQUIRE_UIT();
481
482 static_cast<content::ContentMainRunnerImpl*>(main_runner_.get())
483 ->ShutdownOnUIThread();
484
485 std::move(shutdown_on_ui_thread).Run();
486 main_delegate_->AfterUIThreadShutdown();
487
488 if (uithread_shutdown_event)
489 uithread_shutdown_event->Signal();
490 }
491
FinalizeShutdown(base::OnceClosure finalize_shutdown)492 void CefMainRunner::FinalizeShutdown(base::OnceClosure finalize_shutdown) {
493 main_delegate_->BeforeMainThreadShutdown();
494
495 if (browser_runner_.get()) {
496 browser_runner_->Shutdown();
497 browser_runner_.reset();
498 }
499
500 if (ui_thread_.get()) {
501 // Blocks until the thread has stopped.
502 ui_thread_->Stop();
503 ui_thread_.reset();
504 }
505
506 // Shut down the content runner.
507 content::ContentMainShutdown(*main_params_, main_runner_.get());
508
509 main_params_.reset();
510 main_runner_.reset();
511
512 std::move(finalize_shutdown).Run();
513 main_delegate_->AfterMainThreadShutdown();
514
515 main_delegate_.reset();
516 g_runtime_type = RuntimeType::UNINITIALIZED;
517 }
518
519 // From libcef/features/runtime.h:
520 namespace cef {
521
IsAlloyRuntimeEnabled()522 bool IsAlloyRuntimeEnabled() {
523 return g_runtime_type == RuntimeType::ALLOY;
524 }
525
IsChromeRuntimeEnabled()526 bool IsChromeRuntimeEnabled() {
527 return g_runtime_type == RuntimeType::CHROME;
528 }
529
530 } // namespace cef
531