• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that can
3 // be found in the LICENSE file.
4 
5 #include "libcef/browser/context.h"
6 
7 #include "libcef/browser/browser_info_manager.h"
8 #include "libcef/browser/request_context_impl.h"
9 #include "libcef/browser/thread_util.h"
10 #include "libcef/browser/trace_subscriber.h"
11 #include "libcef/common/cef_switches.h"
12 
13 #include "base/bind.h"
14 #include "base/files/file_util.h"
15 #include "base/run_loop.h"
16 #include "base/task/current_thread.h"
17 #include "base/threading/thread_restrictions.h"
18 #include "components/network_session_configurator/common/network_switches.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/notification_types.h"
21 #include "ui/base/ui_base_switches.h"
22 
23 #if BUILDFLAG(IS_WIN)
24 #include "base/strings/utf_string_conversions.h"
25 #include "chrome/chrome_elf/chrome_elf_main.h"
26 #include "chrome/install_static/initialize_from_primary_module.h"
27 #endif
28 
29 namespace {
30 
31 CefContext* g_context = nullptr;
32 
33 #if DCHECK_IS_ON()
34 // When the process terminates check if CefShutdown() has been called.
35 class CefShutdownChecker {
36  public:
~CefShutdownChecker()37   ~CefShutdownChecker() { DCHECK(!g_context) << "CefShutdown was not called"; }
38 } g_shutdown_checker;
39 #endif  // DCHECK_IS_ON()
40 
41 #if BUILDFLAG(IS_WIN)
42 #if defined(ARCH_CPU_X86_64)
43 // VS2013 only checks the existence of FMA3 instructions, not the enabled-ness
44 // of them at the OS level (this is fixed in VS2015). We force off usage of
45 // FMA3 instructions in the CRT to avoid using that path and hitting illegal
46 // instructions when running on CPUs that support FMA3, but OSs that don't.
DisableFMA3()47 void DisableFMA3() {
48   static bool disabled = false;
49   if (disabled)
50     return;
51   disabled = true;
52   _set_FMA3_enable(0);
53 }
54 #endif  // defined(ARCH_CPU_X86_64)
55 
56 // Transfer state from chrome_elf.dll to the libcef.dll. Accessed when
57 // loading chrome://system.
InitInstallDetails()58 void InitInstallDetails() {
59   static bool initialized = false;
60   if (initialized)
61     return;
62   initialized = true;
63   install_static::InitializeFromPrimaryModule();
64 }
65 
66 // Signal chrome_elf to initialize crash reporting, rather than doing it in
67 // DllMain. See https://crbug.com/656800 for details.
InitCrashReporter()68 void InitCrashReporter() {
69   static bool initialized = false;
70   if (initialized)
71     return;
72   initialized = true;
73   SignalInitializeCrashReporting();
74 }
75 #endif  // BUILDFLAG(IS_WIN)
76 
GetColor(const cef_color_t cef_in,bool is_windowless,SkColor * sk_out)77 bool GetColor(const cef_color_t cef_in, bool is_windowless, SkColor* sk_out) {
78   // Windowed browser colors must be fully opaque.
79   if (!is_windowless && CefColorGetA(cef_in) != SK_AlphaOPAQUE)
80     return false;
81 
82   // Windowless browser colors may be fully transparent.
83   if (is_windowless && CefColorGetA(cef_in) == SK_AlphaTRANSPARENT) {
84     *sk_out = SK_ColorTRANSPARENT;
85     return true;
86   }
87 
88   // Ignore the alpha component.
89   *sk_out = SkColorSetRGB(CefColorGetR(cef_in), CefColorGetG(cef_in),
90                           CefColorGetB(cef_in));
91   return true;
92 }
93 
94 // Convert |path_str| to a normalized FilePath.
NormalizePath(const cef_string_t & path_str,const char * name,bool * has_error=nullptr)95 base::FilePath NormalizePath(const cef_string_t& path_str,
96                              const char* name,
97                              bool* has_error = nullptr) {
98   if (has_error)
99     *has_error = false;
100 
101   base::FilePath path = base::FilePath(CefString(&path_str));
102   if (path.EndsWithSeparator()) {
103     // Remove the trailing separator because it will interfere with future
104     // equality checks.
105     path = path.StripTrailingSeparators();
106   }
107 
108   if (!path.empty() && !path.IsAbsolute()) {
109     LOG(ERROR) << "The " << name << " directory (" << path.value()
110                << ") is not an absolute path. Defaulting to empty.";
111     if (has_error)
112       *has_error = true;
113     path = base::FilePath();
114   }
115 
116   return path;
117 }
118 
SetPath(cef_string_t & path_str,const base::FilePath & path)119 void SetPath(cef_string_t& path_str, const base::FilePath& path) {
120 #if BUILDFLAG(IS_WIN)
121   CefString(&path_str).FromWString(path.value());
122 #else
123   CefString(&path_str).FromString(path.value());
124 #endif
125 }
126 
127 // Convert |path_str| to a normalized FilePath and update the |path_str| value.
NormalizePathAndSet(cef_string_t & path_str,const char * name)128 base::FilePath NormalizePathAndSet(cef_string_t& path_str, const char* name) {
129   const base::FilePath& path = NormalizePath(path_str, name);
130   SetPath(path_str, path);
131   return path;
132 }
133 
134 // Verify that |cache_path| is valid and create it if necessary.
ValidateCachePath(const base::FilePath & cache_path,const base::FilePath & root_cache_path)135 bool ValidateCachePath(const base::FilePath& cache_path,
136                        const base::FilePath& root_cache_path) {
137   if (cache_path.empty())
138     return true;
139 
140   if (!root_cache_path.empty() && root_cache_path != cache_path &&
141       !root_cache_path.IsParent(cache_path)) {
142     LOG(ERROR) << "The cache_path directory (" << cache_path.value()
143                << ") is not a child of the root_cache_path directory ("
144                << root_cache_path.value() << ")";
145     return false;
146   }
147 
148   base::ThreadRestrictions::ScopedAllowIO allow_io;
149   if (!base::DirectoryExists(cache_path) &&
150       !base::CreateDirectory(cache_path)) {
151     LOG(ERROR) << "The cache_path directory (" << cache_path.value()
152                << ") could not be created.";
153     return false;
154   }
155 
156   return true;
157 }
158 
159 // Like NormalizePathAndSet but with additional checks specific to the
160 // cache_path value.
NormalizeCachePathAndSet(cef_string_t & path_str,const base::FilePath & root_cache_path)161 base::FilePath NormalizeCachePathAndSet(cef_string_t& path_str,
162                                         const base::FilePath& root_cache_path) {
163   bool has_error = false;
164   base::FilePath path = NormalizePath(path_str, "cache_path", &has_error);
165   if (has_error || !ValidateCachePath(path, root_cache_path)) {
166     LOG(ERROR) << "The cache_path is invalid. Defaulting to in-memory storage.";
167     path = base::FilePath();
168   }
169   SetPath(path_str, path);
170   return path;
171 }
172 
173 }  // namespace
174 
CefExecuteProcess(const CefMainArgs & args,CefRefPtr<CefApp> application,void * windows_sandbox_info)175 int CefExecuteProcess(const CefMainArgs& args,
176                       CefRefPtr<CefApp> application,
177                       void* windows_sandbox_info) {
178 #if BUILDFLAG(IS_WIN)
179 #if defined(ARCH_CPU_X86_64)
180   DisableFMA3();
181 #endif
182   InitInstallDetails();
183   InitCrashReporter();
184 #endif
185 
186   return CefMainRunner::RunAsHelperProcess(args, application,
187                                            windows_sandbox_info);
188 }
189 
CefInitialize(const CefMainArgs & args,const CefSettings & settings,CefRefPtr<CefApp> application,void * windows_sandbox_info)190 bool CefInitialize(const CefMainArgs& args,
191                    const CefSettings& settings,
192                    CefRefPtr<CefApp> application,
193                    void* windows_sandbox_info) {
194 #if BUILDFLAG(IS_WIN)
195 #if defined(ARCH_CPU_X86_64)
196   DisableFMA3();
197 #endif
198   InitInstallDetails();
199   InitCrashReporter();
200 #endif
201 
202   // Return true if the global context already exists.
203   if (g_context)
204     return true;
205 
206   if (settings.size != sizeof(cef_settings_t)) {
207     NOTREACHED() << "invalid CefSettings structure size";
208     return false;
209   }
210 
211   // Create the new global context object.
212   g_context = new CefContext();
213 
214   // Initialize the global context.
215   return g_context->Initialize(args, settings, application,
216                                windows_sandbox_info);
217 }
218 
CefShutdown()219 void CefShutdown() {
220   // Verify that the context is in a valid state.
221   if (!CONTEXT_STATE_VALID()) {
222     NOTREACHED() << "context not valid";
223     return;
224   }
225 
226   // Must always be called on the same thread as Initialize.
227   if (!g_context->OnInitThread()) {
228     NOTREACHED() << "called on invalid thread";
229     return;
230   }
231 
232   // Shut down the global context. This will block until shutdown is complete.
233   g_context->Shutdown();
234 
235   // Delete the global context object.
236   delete g_context;
237   g_context = nullptr;
238 }
239 
CefDoMessageLoopWork()240 void CefDoMessageLoopWork() {
241   // Verify that the context is in a valid state.
242   if (!CONTEXT_STATE_VALID()) {
243     NOTREACHED() << "context not valid";
244     return;
245   }
246 
247   // Must always be called on the same thread as Initialize.
248   if (!g_context->OnInitThread()) {
249     NOTREACHED() << "called on invalid thread";
250     return;
251   }
252 
253   base::RunLoop run_loop;
254   run_loop.RunUntilIdle();
255 }
256 
CefRunMessageLoop()257 void CefRunMessageLoop() {
258   // Verify that the context is in a valid state.
259   if (!CONTEXT_STATE_VALID()) {
260     NOTREACHED() << "context not valid";
261     return;
262   }
263 
264   // Must always be called on the same thread as Initialize.
265   if (!g_context->OnInitThread()) {
266     NOTREACHED() << "called on invalid thread";
267     return;
268   }
269 
270   g_context->RunMessageLoop();
271 }
272 
CefQuitMessageLoop()273 void CefQuitMessageLoop() {
274   // Verify that the context is in a valid state.
275   if (!CONTEXT_STATE_VALID()) {
276     NOTREACHED() << "context not valid";
277     return;
278   }
279 
280   // Must always be called on the same thread as Initialize.
281   if (!g_context->OnInitThread()) {
282     NOTREACHED() << "called on invalid thread";
283     return;
284   }
285 
286   g_context->QuitMessageLoop();
287 }
288 
CefSetOSModalLoop(bool osModalLoop)289 void CefSetOSModalLoop(bool osModalLoop) {
290 #if BUILDFLAG(IS_WIN)
291   // Verify that the context is in a valid state.
292   if (!CONTEXT_STATE_VALID()) {
293     NOTREACHED() << "context not valid";
294     return;
295   }
296 
297   if (!CEF_CURRENTLY_ON_UIT()) {
298     CEF_POST_TASK(CEF_UIT, base::BindOnce(CefSetOSModalLoop, osModalLoop));
299     return;
300   }
301 
302   base::CurrentThread::Get()->set_os_modal_loop(osModalLoop);
303 #endif  // BUILDFLAG(IS_WIN)
304 }
305 
306 // CefContext
307 
CefContext()308 CefContext::CefContext()
309     : initialized_(false), shutting_down_(false), init_thread_id_(0) {}
310 
~CefContext()311 CefContext::~CefContext() {}
312 
313 // static
Get()314 CefContext* CefContext::Get() {
315   return g_context;
316 }
317 
Initialize(const CefMainArgs & args,const CefSettings & settings,CefRefPtr<CefApp> application,void * windows_sandbox_info)318 bool CefContext::Initialize(const CefMainArgs& args,
319                             const CefSettings& settings,
320                             CefRefPtr<CefApp> application,
321                             void* windows_sandbox_info) {
322   init_thread_id_ = base::PlatformThread::CurrentId();
323   settings_ = settings;
324   application_ = application;
325 
326 #if !(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX))
327   if (settings.multi_threaded_message_loop) {
328     NOTIMPLEMENTED() << "multi_threaded_message_loop is not supported.";
329     return false;
330   }
331 #endif
332 
333 #if BUILDFLAG(IS_WIN)
334   // Signal Chrome Elf that Chrome has begun to start.
335   SignalChromeElf();
336 #endif
337 
338   const base::FilePath& root_cache_path =
339       NormalizePathAndSet(settings_.root_cache_path, "root_cache_path");
340   const base::FilePath& cache_path =
341       NormalizeCachePathAndSet(settings_.cache_path, root_cache_path);
342   if (root_cache_path.empty() && !cache_path.empty()) {
343     CefString(&settings_.root_cache_path) = cache_path.value();
344   }
345 
346   // All other paths that need to be normalized.
347   NormalizePathAndSet(settings_.browser_subprocess_path,
348                       "browser_subprocess_path");
349   NormalizePathAndSet(settings_.framework_dir_path, "framework_dir_path");
350   NormalizePathAndSet(settings_.main_bundle_path, "main_bundle_path");
351   NormalizePathAndSet(settings_.user_data_path, "user_data_path");
352   NormalizePathAndSet(settings_.resources_dir_path, "resources_dir_path");
353   NormalizePathAndSet(settings_.locales_dir_path, "locales_dir_path");
354 
355   browser_info_manager_.reset(new CefBrowserInfoManager);
356 
357   main_runner_.reset(new CefMainRunner(settings_.multi_threaded_message_loop,
358                                        settings_.external_message_pump));
359   return main_runner_->Initialize(
360       &settings_, application, args, windows_sandbox_info, &initialized_,
361       base::BindOnce(&CefContext::OnContextInitialized,
362                      base::Unretained(this)));
363 }
364 
RunMessageLoop()365 void CefContext::RunMessageLoop() {
366   // Must always be called on the same thread as Initialize.
367   DCHECK(OnInitThread());
368 
369   // Blocks until QuitMessageLoop() is called.
370   main_runner_->RunMessageLoop();
371 }
372 
QuitMessageLoop()373 void CefContext::QuitMessageLoop() {
374   // Must always be called on the same thread as Initialize.
375   DCHECK(OnInitThread());
376 
377   main_runner_->QuitMessageLoop();
378 }
379 
Shutdown()380 void CefContext::Shutdown() {
381   // Must always be called on the same thread as Initialize.
382   DCHECK(OnInitThread());
383 
384   shutting_down_ = true;
385 
386   main_runner_->Shutdown(
387       base::BindOnce(&CefContext::ShutdownOnUIThread, base::Unretained(this)),
388       base::BindOnce(&CefContext::FinalizeShutdown, base::Unretained(this)));
389 }
390 
OnInitThread()391 bool CefContext::OnInitThread() {
392   return (base::PlatformThread::CurrentId() == init_thread_id_);
393 }
394 
GetBackgroundColor(const CefBrowserSettings * browser_settings,cef_state_t windowless_state) const395 SkColor CefContext::GetBackgroundColor(
396     const CefBrowserSettings* browser_settings,
397     cef_state_t windowless_state) const {
398   bool is_windowless = windowless_state == STATE_ENABLED
399                            ? true
400                            : (windowless_state == STATE_DISABLED
401                                   ? false
402                                   : !!settings_.windowless_rendering_enabled);
403 
404   // Default to opaque white if no acceptable color values are found.
405   SkColor sk_color = SK_ColorWHITE;
406 
407   if (!browser_settings ||
408       !GetColor(browser_settings->background_color, is_windowless, &sk_color)) {
409     GetColor(settings_.background_color, is_windowless, &sk_color);
410   }
411   return sk_color;
412 }
413 
GetTraceSubscriber()414 CefTraceSubscriber* CefContext::GetTraceSubscriber() {
415   CEF_REQUIRE_UIT();
416   if (shutting_down_)
417     return nullptr;
418   if (!trace_subscriber_.get())
419     trace_subscriber_.reset(new CefTraceSubscriber());
420   return trace_subscriber_.get();
421 }
422 
PopulateGlobalRequestContextSettings(CefRequestContextSettings * settings)423 void CefContext::PopulateGlobalRequestContextSettings(
424     CefRequestContextSettings* settings) {
425   CefRefPtr<CefCommandLine> command_line =
426       CefCommandLine::GetGlobalCommandLine();
427 
428   // This value was already normalized in Initialize.
429   CefString(&settings->cache_path) = CefString(&settings_.cache_path);
430 
431   settings->persist_session_cookies =
432       settings_.persist_session_cookies ||
433       command_line->HasSwitch(switches::kPersistSessionCookies);
434   settings->persist_user_preferences =
435       settings_.persist_user_preferences ||
436       command_line->HasSwitch(switches::kPersistUserPreferences);
437 
438   CefString(&settings->cookieable_schemes_list) =
439       CefString(&settings_.cookieable_schemes_list);
440   settings->cookieable_schemes_exclude_defaults =
441       settings_.cookieable_schemes_exclude_defaults;
442 }
443 
NormalizeRequestContextSettings(CefRequestContextSettings * settings)444 void CefContext::NormalizeRequestContextSettings(
445     CefRequestContextSettings* settings) {
446   // The |root_cache_path| value was already normalized in Initialize.
447   const base::FilePath& root_cache_path = CefString(&settings_.root_cache_path);
448   NormalizeCachePathAndSet(settings->cache_path, root_cache_path);
449 }
450 
AddObserver(Observer * observer)451 void CefContext::AddObserver(Observer* observer) {
452   CEF_REQUIRE_UIT();
453   observers_.AddObserver(observer);
454 }
455 
RemoveObserver(Observer * observer)456 void CefContext::RemoveObserver(Observer* observer) {
457   CEF_REQUIRE_UIT();
458   observers_.RemoveObserver(observer);
459 }
460 
HasObserver(Observer * observer) const461 bool CefContext::HasObserver(Observer* observer) const {
462   CEF_REQUIRE_UIT();
463   return observers_.HasObserver(observer);
464 }
465 
OnContextInitialized()466 void CefContext::OnContextInitialized() {
467   CEF_REQUIRE_UIT();
468 
469   if (application_) {
470     // Notify the handler after the global browser context has initialized.
471     CefRefPtr<CefRequestContext> request_context =
472         CefRequestContext::GetGlobalContext();
473     auto impl = static_cast<CefRequestContextImpl*>(request_context.get());
474     impl->ExecuteWhenBrowserContextInitialized(base::BindOnce(
475         [](CefRefPtr<CefApp> app) {
476           CefRefPtr<CefBrowserProcessHandler> handler =
477               app->GetBrowserProcessHandler();
478           if (handler)
479             handler->OnContextInitialized();
480         },
481         application_));
482   }
483 }
484 
ShutdownOnUIThread()485 void CefContext::ShutdownOnUIThread() {
486   CEF_REQUIRE_UIT();
487 
488   browser_info_manager_->DestroyAllBrowsers();
489 
490   for (auto& observer : observers_)
491     observer.OnContextDestroyed();
492 
493   if (trace_subscriber_.get())
494     trace_subscriber_.reset(nullptr);
495 }
496 
FinalizeShutdown()497 void CefContext::FinalizeShutdown() {
498   browser_info_manager_.reset(nullptr);
499   application_ = nullptr;
500 }
501