• 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 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