• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "libcef/common/alloy/alloy_main_delegate.h"
6 
7 #include <tuple>
8 
9 #include "libcef/browser/alloy/alloy_browser_context.h"
10 #include "libcef/browser/alloy/alloy_content_browser_client.h"
11 #include "libcef/common/cef_switches.h"
12 #include "libcef/common/command_line_impl.h"
13 #include "libcef/common/crash_reporting.h"
14 #include "libcef/common/extensions/extensions_util.h"
15 #include "libcef/common/resource_util.h"
16 #include "libcef/renderer/alloy/alloy_content_renderer_client.h"
17 
18 #include "base/base_switches.h"
19 #include "base/command_line.h"
20 #include "base/files/file_path.h"
21 #include "base/files/file_util.h"
22 #include "base/path_service.h"
23 #include "base/stl_util.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/string_util.h"
26 #include "base/synchronization/waitable_event.h"
27 #include "chrome/browser/browser_process.h"
28 #include "chrome/child/pdf_child_init.h"
29 #include "chrome/common/chrome_constants.h"
30 #include "chrome/common/chrome_paths.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/utility/chrome_content_utility_client.h"
33 #include "components/component_updater/component_updater_paths.h"
34 #include "components/content_settings/core/common/content_settings_pattern.h"
35 #include "components/embedder_support/switches.h"
36 #include "components/spellcheck/common/spellcheck_features.h"
37 #include "components/viz/common/features.h"
38 #include "content/public/common/content_features.h"
39 #include "content/public/common/content_switches.h"
40 #include "content/public/common/main_function_params.h"
41 #include "content/public/common/url_constants.h"
42 #include "extensions/common/constants.h"
43 #include "net/base/features.h"
44 #include "pdf/pdf_ppapi.h"
45 #include "sandbox/policy/switches.h"
46 #include "services/network/public/cpp/features.h"
47 #include "third_party/blink/public/common/switches.h"
48 #include "ui/base/resource/resource_bundle.h"
49 #include "ui/base/ui_base_features.h"
50 #include "ui/base/ui_base_paths.h"
51 #include "ui/base/ui_base_switches.h"
52 
53 #if BUILDFLAG(IS_MAC)
54 #include "libcef/common/util_mac.h"
55 #endif
56 
57 #if BUILDFLAG(IS_WIN)
58 #include "ui/base/resource/resource_bundle_win.h"
59 #endif
60 
61 namespace {
62 
63 const char* const kNonWildcardDomainNonPortSchemes[] = {
64     extensions::kExtensionScheme, content::kChromeDevToolsScheme,
65     content::kChromeUIScheme, content::kChromeUIUntrustedScheme};
66 const size_t kNonWildcardDomainNonPortSchemesSize =
67     base::size(kNonWildcardDomainNonPortSchemes);
68 
69 }  // namespace
70 
AlloyMainDelegate(CefMainRunnerHandler * runner,CefSettings * settings,CefRefPtr<CefApp> application)71 AlloyMainDelegate::AlloyMainDelegate(CefMainRunnerHandler* runner,
72                                      CefSettings* settings,
73                                      CefRefPtr<CefApp> application)
74     : runner_(runner), settings_(settings), application_(application) {
75   // Necessary so that exported functions from base_impl.cc will be included
76   // in the binary.
77   extern void base_impl_stub();
78   base_impl_stub();
79 
80 #if BUILDFLAG(IS_LINUX)
81   resource_util::OverrideAssetPath();
82 #endif
83 }
84 
~AlloyMainDelegate()85 AlloyMainDelegate::~AlloyMainDelegate() {}
86 
PreBrowserMain()87 void AlloyMainDelegate::PreBrowserMain() {
88   runner_->PreBrowserMain();
89 }
90 
BasicStartupComplete(int * exit_code)91 bool AlloyMainDelegate::BasicStartupComplete(int* exit_code) {
92   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
93   std::string process_type =
94       command_line->GetSwitchValueASCII(switches::kProcessType);
95 
96 #if BUILDFLAG(IS_POSIX)
97   // Read the crash configuration file. Platforms using Breakpad also add a
98   // command-line switch. On Windows this is done from chrome_elf.
99   crash_reporting::BasicStartupComplete(command_line);
100 #endif
101 
102   if (process_type.empty()) {
103     // In the browser process. Populate the global command-line object.
104     if (settings_->command_line_args_disabled) {
105       // Remove any existing command-line arguments.
106       base::CommandLine::StringVector argv;
107       argv.push_back(command_line->GetProgram().value());
108       command_line->InitFromArgv(argv);
109 
110       const base::CommandLine::SwitchMap& map = command_line->GetSwitches();
111       const_cast<base::CommandLine::SwitchMap*>(&map)->clear();
112     }
113 
114     bool no_sandbox = settings_->no_sandbox ? true : false;
115 
116     if (settings_->browser_subprocess_path.length > 0) {
117       base::FilePath file_path =
118           base::FilePath(CefString(&settings_->browser_subprocess_path));
119       if (!file_path.empty()) {
120         command_line->AppendSwitchPath(switches::kBrowserSubprocessPath,
121                                        file_path);
122 
123 #if BUILDFLAG(IS_WIN)
124         // The sandbox is not supported when using a separate subprocess
125         // executable on Windows.
126         no_sandbox = true;
127 #endif
128       }
129     }
130 
131 #if BUILDFLAG(IS_MAC)
132     if (settings_->framework_dir_path.length > 0) {
133       base::FilePath file_path =
134           base::FilePath(CefString(&settings_->framework_dir_path));
135       if (!file_path.empty())
136         command_line->AppendSwitchPath(switches::kFrameworkDirPath, file_path);
137     }
138 
139     if (settings_->main_bundle_path.length > 0) {
140       base::FilePath file_path =
141           base::FilePath(CefString(&settings_->main_bundle_path));
142       if (!file_path.empty())
143         command_line->AppendSwitchPath(switches::kMainBundlePath, file_path);
144     }
145 #endif
146 
147     if (no_sandbox)
148       command_line->AppendSwitch(sandbox::policy::switches::kNoSandbox);
149 
150     if (settings_->user_agent.length > 0) {
151       command_line->AppendSwitchASCII(
152           embedder_support::kUserAgent,
153           CefString(&settings_->user_agent).ToString());
154     } else if (settings_->user_agent_product.length > 0) {
155       command_line->AppendSwitchASCII(
156           switches::kUserAgentProductAndVersion,
157           CefString(&settings_->user_agent_product).ToString());
158     }
159 
160     if (settings_->locale.length > 0) {
161       command_line->AppendSwitchASCII(switches::kLang,
162                                       CefString(&settings_->locale).ToString());
163     } else if (!command_line->HasSwitch(switches::kLang)) {
164       command_line->AppendSwitchASCII(switches::kLang, "en-US");
165     }
166 
167     base::FilePath log_file;
168     bool has_log_file_cmdline = false;
169     if (settings_->log_file.length > 0)
170       log_file = base::FilePath(CefString(&settings_->log_file));
171     if (log_file.empty() && command_line->HasSwitch(switches::kLogFile)) {
172       log_file = command_line->GetSwitchValuePath(switches::kLogFile);
173       if (!log_file.empty())
174         has_log_file_cmdline = true;
175     }
176     if (log_file.empty())
177       log_file = resource_util::GetDefaultLogFilePath();
178     DCHECK(!log_file.empty());
179     if (!has_log_file_cmdline)
180       command_line->AppendSwitchPath(switches::kLogFile, log_file);
181 
182     if (settings_->log_severity != LOGSEVERITY_DEFAULT) {
183       std::string log_severity;
184       switch (settings_->log_severity) {
185         case LOGSEVERITY_VERBOSE:
186           log_severity = switches::kLogSeverity_Verbose;
187           break;
188         case LOGSEVERITY_INFO:
189           log_severity = switches::kLogSeverity_Info;
190           break;
191         case LOGSEVERITY_WARNING:
192           log_severity = switches::kLogSeverity_Warning;
193           break;
194         case LOGSEVERITY_ERROR:
195           log_severity = switches::kLogSeverity_Error;
196           break;
197         case LOGSEVERITY_FATAL:
198           log_severity = switches::kLogSeverity_Fatal;
199           break;
200         case LOGSEVERITY_DISABLE:
201           log_severity = switches::kLogSeverity_Disable;
202           break;
203         default:
204           break;
205       }
206       if (!log_severity.empty())
207         command_line->AppendSwitchASCII(switches::kLogSeverity, log_severity);
208     }
209 
210     if (settings_->javascript_flags.length > 0) {
211       command_line->AppendSwitchASCII(
212           blink::switches::kJavaScriptFlags,
213           CefString(&settings_->javascript_flags).ToString());
214     }
215 
216     if (settings_->pack_loading_disabled) {
217       command_line->AppendSwitch(switches::kDisablePackLoading);
218     } else {
219       if (settings_->resources_dir_path.length > 0) {
220         base::FilePath file_path =
221             base::FilePath(CefString(&settings_->resources_dir_path));
222         if (!file_path.empty()) {
223           command_line->AppendSwitchPath(switches::kResourcesDirPath,
224                                          file_path);
225         }
226       }
227 
228       if (settings_->locales_dir_path.length > 0) {
229         base::FilePath file_path =
230             base::FilePath(CefString(&settings_->locales_dir_path));
231         if (!file_path.empty())
232           command_line->AppendSwitchPath(switches::kLocalesDirPath, file_path);
233       }
234     }
235 
236     if (settings_->remote_debugging_port >= 1024 &&
237         settings_->remote_debugging_port <= 65535) {
238       command_line->AppendSwitchASCII(
239           switches::kRemoteDebuggingPort,
240           base::NumberToString(settings_->remote_debugging_port));
241     }
242 
243     if (settings_->uncaught_exception_stack_size > 0) {
244       command_line->AppendSwitchASCII(
245           switches::kUncaughtExceptionStackSize,
246           base::NumberToString(settings_->uncaught_exception_stack_size));
247     }
248 
249 #if BUILDFLAG(IS_WIN)
250     std::vector<std::string> disable_features;
251 
252     if (features::kCalculateNativeWinOcclusion.default_state ==
253         base::FEATURE_ENABLED_BY_DEFAULT) {
254       // TODO: Add support for occlusion detection in combination with native
255       // parent windows (see issue #2805).
256       disable_features.push_back(features::kCalculateNativeWinOcclusion.name);
257     }
258 
259     if (spellcheck::kWinUseBrowserSpellChecker.default_state ==
260         base::FEATURE_ENABLED_BY_DEFAULT) {
261       // TODO: Add support for windows spellcheck service (see issue #3055).
262       disable_features.push_back(spellcheck::kWinUseBrowserSpellChecker.name);
263     }
264 
265     if (!disable_features.empty()) {
266       DCHECK(!base::FeatureList::GetInstance());
267       std::string disable_features_str =
268           command_line->GetSwitchValueASCII(switches::kDisableFeatures);
269       for (auto feature_str : disable_features) {
270         if (!disable_features_str.empty())
271           disable_features_str += ",";
272         disable_features_str += feature_str;
273       }
274       command_line->AppendSwitchASCII(switches::kDisableFeatures,
275                                       disable_features_str);
276     }
277 #endif  // BUILDFLAG(IS_WIN)
278   }
279 
280   if (application_) {
281     // Give the application a chance to view/modify the command line.
282     CefRefPtr<CefCommandLineImpl> commandLinePtr(
283         new CefCommandLineImpl(command_line, false, false));
284     application_->OnBeforeCommandLineProcessing(CefString(process_type),
285                                                 commandLinePtr.get());
286     std::ignore = commandLinePtr->Detach(nullptr);
287   }
288 
289   // Initialize logging.
290   logging::LoggingSettings log_settings;
291 
292   const base::FilePath& log_file =
293       command_line->GetSwitchValuePath(switches::kLogFile);
294   DCHECK(!log_file.empty());
295   log_settings.log_file_path = log_file.value().c_str();
296 
297   log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
298   log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
299 
300   logging::LogSeverity log_severity = logging::LOG_INFO;
301 
302   std::string log_severity_str =
303       command_line->GetSwitchValueASCII(switches::kLogSeverity);
304   if (!log_severity_str.empty()) {
305     if (base::LowerCaseEqualsASCII(log_severity_str,
306                                    switches::kLogSeverity_Verbose)) {
307       log_severity = logging::LOG_VERBOSE;
308     } else if (base::LowerCaseEqualsASCII(log_severity_str,
309                                           switches::kLogSeverity_Warning)) {
310       log_severity = logging::LOG_WARNING;
311     } else if (base::LowerCaseEqualsASCII(log_severity_str,
312                                           switches::kLogSeverity_Error)) {
313       log_severity = logging::LOG_ERROR;
314     } else if (base::LowerCaseEqualsASCII(log_severity_str,
315                                           switches::kLogSeverity_Fatal)) {
316       log_severity = logging::LOG_FATAL;
317     } else if (base::LowerCaseEqualsASCII(log_severity_str,
318                                           switches::kLogSeverity_Disable)) {
319       log_severity = LOGSEVERITY_DISABLE;
320     }
321   }
322 
323   if (log_severity == LOGSEVERITY_DISABLE) {
324     log_settings.logging_dest = logging::LOG_NONE;
325     // By default, ERROR and FATAL messages will always be output to stderr due
326     // to the kAlwaysPrintErrorLevel value in base/logging.cc. We change the log
327     // level here so that only FATAL messages are output.
328     logging::SetMinLogLevel(logging::LOG_FATAL);
329   } else {
330     log_settings.logging_dest = logging::LOG_TO_ALL;
331     logging::SetMinLogLevel(log_severity);
332   }
333 
334   logging::InitLogging(log_settings);
335 
336   ContentSettingsPattern::SetNonWildcardDomainNonPortSchemes(
337       kNonWildcardDomainNonPortSchemes, kNonWildcardDomainNonPortSchemesSize);
338 
339   content::SetContentClient(&content_client_);
340 
341 #if BUILDFLAG(IS_MAC)
342   util_mac::BasicStartupComplete();
343 #endif
344 
345   return false;
346 }
347 
PreSandboxStartup()348 void AlloyMainDelegate::PreSandboxStartup() {
349   const base::CommandLine* command_line =
350       base::CommandLine::ForCurrentProcess();
351   const std::string& process_type =
352       command_line->GetSwitchValueASCII(switches::kProcessType);
353 
354   if (process_type.empty()) {
355 // Only override these paths when executing the main process.
356 #if BUILDFLAG(IS_MAC)
357     util_mac::PreSandboxStartup();
358 #endif
359 
360     resource_util::OverrideDefaultDownloadDir();
361   }
362 
363   resource_util::OverrideUserDataDir(settings_, command_line);
364 
365   if (command_line->HasSwitch(switches::kDisablePackLoading))
366     resource_bundle_delegate_.set_pack_loading_disabled(true);
367 
368   // Initialize crash reporting state for this process/module.
369   // chrome::DIR_CRASH_DUMPS must be configured before calling this function.
370   crash_reporting::PreSandboxStartup(*command_line, process_type);
371 
372   // Register the component_updater PathProvider.
373   component_updater::RegisterPathProvider(chrome::DIR_COMPONENTS,
374                                           chrome::DIR_INTERNAL_PLUGINS,
375                                           chrome::DIR_USER_DATA);
376 
377   InitializeResourceBundle();
378   MaybePatchGdiGetFontData();
379 }
380 
SandboxInitialized(const std::string & process_type)381 void AlloyMainDelegate::SandboxInitialized(const std::string& process_type) {
382   AlloyContentClient::SetPDFEntryFunctions(chrome_pdf::PPP_GetInterface,
383                                            chrome_pdf::PPP_InitializeModule,
384                                            chrome_pdf::PPP_ShutdownModule);
385 }
386 
RunProcess(const std::string & process_type,content::MainFunctionParams main_function_params)387 absl::variant<int, content::MainFunctionParams> AlloyMainDelegate::RunProcess(
388     const std::string& process_type,
389     content::MainFunctionParams main_function_params) {
390   if (process_type.empty()) {
391     return runner_->RunMainProcess(std::move(main_function_params));
392   }
393 
394   return std::move(main_function_params);
395 }
396 
ProcessExiting(const std::string & process_type)397 void AlloyMainDelegate::ProcessExiting(const std::string& process_type) {
398   ui::ResourceBundle::CleanupSharedInstance();
399 }
400 
401 #if BUILDFLAG(IS_LINUX)
ZygoteForked()402 void AlloyMainDelegate::ZygoteForked() {
403   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
404   const std::string& process_type =
405       command_line->GetSwitchValueASCII(switches::kProcessType);
406   // Initialize crash reporting state for the newly forked process.
407   crash_reporting::ZygoteForked(command_line, process_type);
408 }
409 #endif
410 
CreateContentBrowserClient()411 content::ContentBrowserClient* AlloyMainDelegate::CreateContentBrowserClient() {
412   browser_client_.reset(new AlloyContentBrowserClient);
413   return browser_client_.get();
414 }
415 
416 content::ContentRendererClient*
CreateContentRendererClient()417 AlloyMainDelegate::CreateContentRendererClient() {
418   renderer_client_.reset(new AlloyContentRendererClient);
419   return renderer_client_.get();
420 }
421 
CreateContentUtilityClient()422 content::ContentUtilityClient* AlloyMainDelegate::CreateContentUtilityClient() {
423   utility_client_.reset(new ChromeContentUtilityClient);
424   return utility_client_.get();
425 }
426 
GetGlobalRequestContext()427 CefRefPtr<CefRequestContext> AlloyMainDelegate::GetGlobalRequestContext() {
428   if (!browser_client_)
429     return nullptr;
430   return browser_client_->request_context();
431 }
432 
CreateNewBrowserContext(const CefRequestContextSettings & settings,base::OnceClosure initialized_cb)433 CefBrowserContext* AlloyMainDelegate::CreateNewBrowserContext(
434     const CefRequestContextSettings& settings,
435     base::OnceClosure initialized_cb) {
436   auto context = new AlloyBrowserContext(settings);
437   context->Initialize();
438   std::move(initialized_cb).Run();
439   return context;
440 }
441 
442 scoped_refptr<base::SingleThreadTaskRunner>
GetBackgroundTaskRunner()443 AlloyMainDelegate::GetBackgroundTaskRunner() {
444   if (browser_client_)
445     return browser_client_->background_task_runner();
446   return nullptr;
447 }
448 
449 scoped_refptr<base::SingleThreadTaskRunner>
GetUserVisibleTaskRunner()450 AlloyMainDelegate::GetUserVisibleTaskRunner() {
451   if (browser_client_)
452     return browser_client_->user_visible_task_runner();
453   return nullptr;
454 }
455 
456 scoped_refptr<base::SingleThreadTaskRunner>
GetUserBlockingTaskRunner()457 AlloyMainDelegate::GetUserBlockingTaskRunner() {
458   if (browser_client_)
459     return browser_client_->user_blocking_task_runner();
460   return nullptr;
461 }
462 
463 scoped_refptr<base::SingleThreadTaskRunner>
GetRenderTaskRunner()464 AlloyMainDelegate::GetRenderTaskRunner() {
465   if (renderer_client_)
466     return renderer_client_->render_task_runner();
467   return nullptr;
468 }
469 
470 scoped_refptr<base::SingleThreadTaskRunner>
GetWebWorkerTaskRunner()471 AlloyMainDelegate::GetWebWorkerTaskRunner() {
472   if (renderer_client_)
473     return renderer_client_->GetCurrentTaskRunner();
474   return nullptr;
475 }
476 
InitializeResourceBundle()477 void AlloyMainDelegate::InitializeResourceBundle() {
478   const base::CommandLine* command_line =
479       base::CommandLine::ForCurrentProcess();
480   base::FilePath resources_pak_file, chrome_100_percent_pak_file,
481       chrome_200_percent_pak_file, locales_dir;
482 
483   base::FilePath resources_dir;
484   if (command_line->HasSwitch(switches::kResourcesDirPath)) {
485     resources_dir =
486         command_line->GetSwitchValuePath(switches::kResourcesDirPath);
487   }
488   if (resources_dir.empty())
489     resources_dir = resource_util::GetResourcesDir();
490   if (!resources_dir.empty())
491     base::PathService::Override(chrome::DIR_RESOURCES, resources_dir);
492 
493   if (!resource_bundle_delegate_.pack_loading_disabled()) {
494     if (!resources_dir.empty()) {
495       CHECK(resources_dir.IsAbsolute());
496       resources_pak_file =
497           resources_dir.Append(FILE_PATH_LITERAL("resources.pak"));
498       chrome_100_percent_pak_file =
499           resources_dir.Append(FILE_PATH_LITERAL("chrome_100_percent.pak"));
500       chrome_200_percent_pak_file =
501           resources_dir.Append(FILE_PATH_LITERAL("chrome_200_percent.pak"));
502     }
503 
504     if (command_line->HasSwitch(switches::kLocalesDirPath))
505       locales_dir = command_line->GetSwitchValuePath(switches::kLocalesDirPath);
506 
507     if (!locales_dir.empty())
508       base::PathService::Override(ui::DIR_LOCALES, locales_dir);
509   }
510 
511 #if BUILDFLAG(IS_WIN)
512   // From chrome/app/chrome_main_delegate.cc
513   // Throbber icons and cursors are still stored in chrome.dll,
514   // this can be killed once those are merged into resources.pak. See
515   // GlassBrowserFrameView::InitThrobberIcons(), https://crbug.com/368327 and
516   // https://crbug.com/1178117.
517   auto module_handle =
518       ::GetModuleHandle(CefAppManager::Get()->GetResourceDllName());
519   if (!module_handle)
520     module_handle = ::GetModuleHandle(NULL);
521 
522   ui::SetResourcesDataDLL(module_handle);
523 #endif
524 
525   std::string locale = command_line->GetSwitchValueASCII(switches::kLang);
526   DCHECK(!locale.empty());
527 
528   const std::string loaded_locale =
529       ui::ResourceBundle::InitSharedInstanceWithLocale(
530           locale, &resource_bundle_delegate_,
531           ui::ResourceBundle::LOAD_COMMON_RESOURCES);
532   if (!loaded_locale.empty() && g_browser_process)
533     g_browser_process->SetApplicationLocale(loaded_locale);
534 
535   ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
536 
537   if (!resource_bundle_delegate_.pack_loading_disabled()) {
538     if (loaded_locale.empty())
539       LOG(ERROR) << "Could not load locale pak for " << locale;
540 
541     resource_bundle_delegate_.set_allow_pack_file_load(true);
542 
543     if (base::PathExists(resources_pak_file)) {
544       resource_bundle.AddDataPackFromPath(resources_pak_file,
545                                           ui::kScaleFactorNone);
546     } else {
547       LOG(ERROR) << "Could not load resources.pak";
548     }
549 
550     // Always load the 1x data pack first as the 2x data pack contains both 1x
551     // and 2x images. The 1x data pack only has 1x images, thus passes in an
552     // accurate scale factor to gfx::ImageSkia::AddRepresentation.
553     if (resource_util::IsScaleFactorSupported(ui::k100Percent)) {
554       if (base::PathExists(chrome_100_percent_pak_file)) {
555         resource_bundle.AddDataPackFromPath(chrome_100_percent_pak_file,
556                                             ui::k100Percent);
557       } else {
558         LOG(ERROR) << "Could not load chrome_100_percent.pak";
559       }
560     }
561 
562     if (resource_util::IsScaleFactorSupported(ui::k200Percent)) {
563       if (base::PathExists(chrome_200_percent_pak_file)) {
564         resource_bundle.AddDataPackFromPath(chrome_200_percent_pak_file,
565                                             ui::k200Percent);
566       } else {
567         LOG(ERROR) << "Could not load chrome_200_percent.pak";
568       }
569     }
570 
571     // Skip the default pak file loading that would otherwise occur in
572     // ResourceBundle::LoadChromeResources().
573     resource_bundle_delegate_.set_allow_pack_file_load(false);
574   }
575 }
576