• 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 defined(OS_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 defined(OS_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  // defined(OS_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 defined(OS_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 defined(OS_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 defined(OS_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 defined(OS_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     base::CurrentThread::Get()->set_os_modal_loop(osModalLoop);
299   else
300     CEF_POST_TASK(CEF_UIT, base::Bind(CefSetOSModalLoop, osModalLoop));
301 #endif  // defined(OS_WIN)
302 }
303 
304 // CefContext
305 
CefContext()306 CefContext::CefContext()
307     : initialized_(false), shutting_down_(false), init_thread_id_(0) {}
308 
~CefContext()309 CefContext::~CefContext() {}
310 
311 // static
Get()312 CefContext* CefContext::Get() {
313   return g_context;
314 }
315 
Initialize(const CefMainArgs & args,const CefSettings & settings,CefRefPtr<CefApp> application,void * windows_sandbox_info)316 bool CefContext::Initialize(const CefMainArgs& args,
317                             const CefSettings& settings,
318                             CefRefPtr<CefApp> application,
319                             void* windows_sandbox_info) {
320   init_thread_id_ = base::PlatformThread::CurrentId();
321   settings_ = settings;
322   application_ = application;
323 
324 #if !(defined(OS_WIN) || defined(OS_LINUX))
325   if (settings.multi_threaded_message_loop) {
326     NOTIMPLEMENTED() << "multi_threaded_message_loop is not supported.";
327     return false;
328   }
329 #endif
330 
331 #if defined(OS_WIN)
332   // Signal Chrome Elf that Chrome has begun to start.
333   SignalChromeElf();
334 #endif
335 
336   const base::FilePath& root_cache_path =
337       NormalizePathAndSet(settings_.root_cache_path, "root_cache_path");
338   const base::FilePath& cache_path =
339       NormalizeCachePathAndSet(settings_.cache_path, root_cache_path);
340   if (root_cache_path.empty() && !cache_path.empty()) {
341     CefString(&settings_.root_cache_path) = cache_path.value();
342   }
343 
344   // All other paths that need to be normalized.
345   NormalizePathAndSet(settings_.browser_subprocess_path,
346                       "browser_subprocess_path");
347   NormalizePathAndSet(settings_.framework_dir_path, "framework_dir_path");
348   NormalizePathAndSet(settings_.main_bundle_path, "main_bundle_path");
349   NormalizePathAndSet(settings_.user_data_path, "user_data_path");
350   NormalizePathAndSet(settings_.resources_dir_path, "resources_dir_path");
351   NormalizePathAndSet(settings_.locales_dir_path, "locales_dir_path");
352 
353   browser_info_manager_.reset(new CefBrowserInfoManager);
354 
355   main_runner_.reset(new CefMainRunner(settings_.multi_threaded_message_loop,
356                                        settings_.external_message_pump));
357   return main_runner_->Initialize(
358       &settings_, application, args, windows_sandbox_info, &initialized_,
359       base::BindOnce(&CefContext::OnContextInitialized,
360                      base::Unretained(this)));
361 }
362 
RunMessageLoop()363 void CefContext::RunMessageLoop() {
364   // Must always be called on the same thread as Initialize.
365   DCHECK(OnInitThread());
366 
367   // Blocks until QuitMessageLoop() is called.
368   main_runner_->RunMessageLoop();
369 }
370 
QuitMessageLoop()371 void CefContext::QuitMessageLoop() {
372   // Must always be called on the same thread as Initialize.
373   DCHECK(OnInitThread());
374 
375   main_runner_->QuitMessageLoop();
376 }
377 
Shutdown()378 void CefContext::Shutdown() {
379   // Must always be called on the same thread as Initialize.
380   DCHECK(OnInitThread());
381 
382   shutting_down_ = true;
383 
384   main_runner_->Shutdown(
385       base::BindOnce(&CefContext::ShutdownOnUIThread, base::Unretained(this)),
386       base::BindOnce(&CefContext::FinalizeShutdown, base::Unretained(this)));
387 }
388 
OnInitThread()389 bool CefContext::OnInitThread() {
390   return (base::PlatformThread::CurrentId() == init_thread_id_);
391 }
392 
GetBackgroundColor(const CefBrowserSettings * browser_settings,cef_state_t windowless_state) const393 SkColor CefContext::GetBackgroundColor(
394     const CefBrowserSettings* browser_settings,
395     cef_state_t windowless_state) const {
396   bool is_windowless = windowless_state == STATE_ENABLED
397                            ? true
398                            : (windowless_state == STATE_DISABLED
399                                   ? false
400                                   : !!settings_.windowless_rendering_enabled);
401 
402   // Default to opaque white if no acceptable color values are found.
403   SkColor sk_color = SK_ColorWHITE;
404 
405   if (!browser_settings ||
406       !GetColor(browser_settings->background_color, is_windowless, &sk_color)) {
407     GetColor(settings_.background_color, is_windowless, &sk_color);
408   }
409   return sk_color;
410 }
411 
GetTraceSubscriber()412 CefTraceSubscriber* CefContext::GetTraceSubscriber() {
413   CEF_REQUIRE_UIT();
414   if (shutting_down_)
415     return nullptr;
416   if (!trace_subscriber_.get())
417     trace_subscriber_.reset(new CefTraceSubscriber());
418   return trace_subscriber_.get();
419 }
420 
PopulateGlobalRequestContextSettings(CefRequestContextSettings * settings)421 void CefContext::PopulateGlobalRequestContextSettings(
422     CefRequestContextSettings* settings) {
423   CefRefPtr<CefCommandLine> command_line =
424       CefCommandLine::GetGlobalCommandLine();
425 
426   // This value was already normalized in Initialize.
427   CefString(&settings->cache_path) = CefString(&settings_.cache_path);
428 
429   settings->persist_session_cookies =
430       settings_.persist_session_cookies ||
431       command_line->HasSwitch(switches::kPersistSessionCookies);
432   settings->persist_user_preferences =
433       settings_.persist_user_preferences ||
434       command_line->HasSwitch(switches::kPersistUserPreferences);
435   settings->ignore_certificate_errors =
436       settings_.ignore_certificate_errors ||
437       command_line->HasSwitch(switches::kIgnoreCertificateErrors);
438 
439   CefString(&settings->cookieable_schemes_list) =
440       CefString(&settings_.cookieable_schemes_list);
441   settings->cookieable_schemes_exclude_defaults =
442       settings_.cookieable_schemes_exclude_defaults;
443 }
444 
NormalizeRequestContextSettings(CefRequestContextSettings * settings)445 void CefContext::NormalizeRequestContextSettings(
446     CefRequestContextSettings* settings) {
447   // The |root_cache_path| value was already normalized in Initialize.
448   const base::FilePath& root_cache_path = CefString(&settings_.root_cache_path);
449   NormalizeCachePathAndSet(settings->cache_path, root_cache_path);
450 }
451 
AddObserver(Observer * observer)452 void CefContext::AddObserver(Observer* observer) {
453   CEF_REQUIRE_UIT();
454   observers_.AddObserver(observer);
455 }
456 
RemoveObserver(Observer * observer)457 void CefContext::RemoveObserver(Observer* observer) {
458   CEF_REQUIRE_UIT();
459   observers_.RemoveObserver(observer);
460 }
461 
HasObserver(Observer * observer) const462 bool CefContext::HasObserver(Observer* observer) const {
463   CEF_REQUIRE_UIT();
464   return observers_.HasObserver(observer);
465 }
466 
OnContextInitialized()467 void CefContext::OnContextInitialized() {
468   CEF_REQUIRE_UIT();
469 
470   if (application_) {
471     // Notify the handler after the global browser context has initialized.
472     CefRefPtr<CefRequestContext> request_context =
473         CefRequestContext::GetGlobalContext();
474     auto impl = static_cast<CefRequestContextImpl*>(request_context.get());
475     impl->ExecuteWhenBrowserContextInitialized(base::BindOnce(
476         [](CefRefPtr<CefApp> app) {
477           CefRefPtr<CefBrowserProcessHandler> handler =
478               app->GetBrowserProcessHandler();
479           if (handler)
480             handler->OnContextInitialized();
481         },
482         application_));
483   }
484 }
485 
ShutdownOnUIThread()486 void CefContext::ShutdownOnUIThread() {
487   CEF_REQUIRE_UIT();
488 
489   browser_info_manager_->DestroyAllBrowsers();
490 
491   for (auto& observer : observers_)
492     observer.OnContextDestroyed();
493 
494   if (trace_subscriber_.get())
495     trace_subscriber_.reset(nullptr);
496 }
497 
FinalizeShutdown()498 void CefContext::FinalizeShutdown() {
499   browser_info_manager_.reset(nullptr);
500   application_ = nullptr;
501 }
502