• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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