• 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 "libcef/browser/alloy/alloy_browser_context.h"
8 #include "libcef/browser/alloy/alloy_content_browser_client.h"
9 #include "libcef/common/cef_switches.h"
10 #include "libcef/common/command_line_impl.h"
11 #include "libcef/common/crash_reporting.h"
12 #include "libcef/common/extensions/extensions_util.h"
13 #include "libcef/common/resource_util.h"
14 #include "libcef/renderer/alloy/alloy_content_renderer_client.h"
15 
16 #include "base/base_switches.h"
17 #include "base/command_line.h"
18 #include "base/files/file_path.h"
19 #include "base/files/file_util.h"
20 #include "base/path_service.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h"
24 #include "base/synchronization/waitable_event.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/media/router/media_router_feature.h"
27 #include "chrome/child/pdf_child_init.h"
28 #include "chrome/common/chrome_constants.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "chrome/utility/chrome_content_utility_client.h"
32 #include "components/content_settings/core/common/content_settings_pattern.h"
33 #include "components/embedder_support/switches.h"
34 #include "components/viz/common/features.h"
35 #include "content/browser/browser_process_sub_thread.h"
36 #include "content/public/common/content_features.h"
37 #include "content/public/common/content_switches.h"
38 #include "content/public/common/main_function_params.h"
39 #include "extensions/common/constants.h"
40 #include "ipc/ipc_buildflags.h"
41 #include "net/base/features.h"
42 #include "pdf/pdf_ppapi.h"
43 #include "sandbox/policy/switches.h"
44 #include "services/network/public/cpp/features.h"
45 #include "ui/base/resource/resource_bundle.h"
46 #include "ui/base/ui_base_features.h"
47 #include "ui/base/ui_base_paths.h"
48 #include "ui/base/ui_base_switches.h"
49 
50 #if defined(OS_MAC)
51 #include "libcef/common/util_mac.h"
52 #endif
53 
54 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
55 #define IPC_MESSAGE_MACROS_LOG_ENABLED
56 #include "content/public/common/content_ipc_logging.h"
57 #define IPC_LOG_TABLE_ADD_ENTRY(msg_id, logger) \
58   content::RegisterIPCLogger(msg_id, logger)
59 #include "libcef/common/cef_message_generator.h"
60 #endif
61 
62 namespace {
63 
64 const char* const kNonWildcardDomainNonPortSchemes[] = {
65     extensions::kExtensionScheme};
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 defined(OS_LINUX)
81   resource_util::OverrideAssetPath();
82 #endif
83 }
84 
~AlloyMainDelegate()85 AlloyMainDelegate::~AlloyMainDelegate() {}
86 
PreCreateMainMessageLoop()87 void AlloyMainDelegate::PreCreateMainMessageLoop() {
88   runner_->PreCreateMainMessageLoop();
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 defined(OS_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 defined(OS_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 defined(OS_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(embedder_support::kUserAgent,
152                                       CefString(&settings_->user_agent));
153     } else if (settings_->user_agent_product.length > 0) {
154       command_line->AppendSwitchASCII(
155           switches::kUserAgentProductAndVersion,
156           CefString(&settings_->user_agent_product));
157     }
158 
159     if (settings_->locale.length > 0) {
160       command_line->AppendSwitchASCII(switches::kLang,
161                                       CefString(&settings_->locale));
162     } else if (!command_line->HasSwitch(switches::kLang)) {
163       command_line->AppendSwitchASCII(switches::kLang, "en-US");
164     }
165 
166     base::FilePath log_file;
167     bool has_log_file_cmdline = false;
168     if (settings_->log_file.length > 0)
169       log_file = base::FilePath(CefString(&settings_->log_file));
170     if (log_file.empty() && command_line->HasSwitch(switches::kLogFile)) {
171       log_file = command_line->GetSwitchValuePath(switches::kLogFile);
172       if (!log_file.empty())
173         has_log_file_cmdline = true;
174     }
175     if (log_file.empty())
176       log_file = resource_util::GetDefaultLogFilePath();
177     DCHECK(!log_file.empty());
178     if (!has_log_file_cmdline)
179       command_line->AppendSwitchPath(switches::kLogFile, log_file);
180 
181     if (settings_->log_severity != LOGSEVERITY_DEFAULT) {
182       std::string log_severity;
183       switch (settings_->log_severity) {
184         case LOGSEVERITY_VERBOSE:
185           log_severity = switches::kLogSeverity_Verbose;
186           break;
187         case LOGSEVERITY_INFO:
188           log_severity = switches::kLogSeverity_Info;
189           break;
190         case LOGSEVERITY_WARNING:
191           log_severity = switches::kLogSeverity_Warning;
192           break;
193         case LOGSEVERITY_ERROR:
194           log_severity = switches::kLogSeverity_Error;
195           break;
196         case LOGSEVERITY_FATAL:
197           log_severity = switches::kLogSeverity_Fatal;
198           break;
199         case LOGSEVERITY_DISABLE:
200           log_severity = switches::kLogSeverity_Disable;
201           break;
202         default:
203           break;
204       }
205       if (!log_severity.empty())
206         command_line->AppendSwitchASCII(switches::kLogSeverity, log_severity);
207     }
208 
209     if (settings_->javascript_flags.length > 0) {
210       command_line->AppendSwitchASCII(switches::kJavaScriptFlags,
211                                       CefString(&settings_->javascript_flags));
212     }
213 
214     if (settings_->pack_loading_disabled) {
215       command_line->AppendSwitch(switches::kDisablePackLoading);
216     } else {
217       if (settings_->resources_dir_path.length > 0) {
218         base::FilePath file_path =
219             base::FilePath(CefString(&settings_->resources_dir_path));
220         if (!file_path.empty()) {
221           command_line->AppendSwitchPath(switches::kResourcesDirPath,
222                                          file_path);
223         }
224       }
225 
226       if (settings_->locales_dir_path.length > 0) {
227         base::FilePath file_path =
228             base::FilePath(CefString(&settings_->locales_dir_path));
229         if (!file_path.empty())
230           command_line->AppendSwitchPath(switches::kLocalesDirPath, file_path);
231       }
232     }
233 
234     if (settings_->remote_debugging_port >= 1024 &&
235         settings_->remote_debugging_port <= 65535) {
236       command_line->AppendSwitchASCII(
237           switches::kRemoteDebuggingPort,
238           base::NumberToString(settings_->remote_debugging_port));
239     }
240 
241     if (settings_->uncaught_exception_stack_size > 0) {
242       command_line->AppendSwitchASCII(
243           switches::kUncaughtExceptionStackSize,
244           base::NumberToString(settings_->uncaught_exception_stack_size));
245     }
246 
247     std::vector<std::string> disable_features;
248 
249 #if defined(OS_WIN)
250     if (features::kCalculateNativeWinOcclusion.default_state ==
251         base::FEATURE_ENABLED_BY_DEFAULT) {
252       // TODO: Add support for occlusion detection in combination with native
253       // parent windows (see issue #2805).
254       disable_features.push_back(features::kCalculateNativeWinOcclusion.name);
255     }
256 #endif  // defined(OS_WIN)
257 
258     if (!disable_features.empty()) {
259       DCHECK(!base::FeatureList::GetInstance());
260       std::string disable_features_str =
261           command_line->GetSwitchValueASCII(switches::kDisableFeatures);
262       for (auto feature_str : disable_features) {
263         if (!disable_features_str.empty())
264           disable_features_str += ",";
265         disable_features_str += feature_str;
266       }
267       command_line->AppendSwitchASCII(switches::kDisableFeatures,
268                                       disable_features_str);
269     }
270 
271     std::vector<std::string> enable_features;
272 
273     if (media_router::kDialMediaRouteProvider.default_state ==
274         base::FEATURE_DISABLED_BY_DEFAULT) {
275       // Enable discovery of DIAL devices.
276       enable_features.push_back(media_router::kDialMediaRouteProvider.name);
277     }
278 
279     if (media_router::kCastMediaRouteProvider.default_state ==
280         base::FEATURE_DISABLED_BY_DEFAULT) {
281       // Enable discovery of Cast devices.
282       enable_features.push_back(media_router::kCastMediaRouteProvider.name);
283     }
284 
285     if (!enable_features.empty()) {
286       DCHECK(!base::FeatureList::GetInstance());
287       std::string enable_features_str =
288           command_line->GetSwitchValueASCII(switches::kEnableFeatures);
289       for (auto feature_str : enable_features) {
290         if (!enable_features_str.empty())
291           enable_features_str += ",";
292         enable_features_str += feature_str;
293       }
294       command_line->AppendSwitchASCII(switches::kEnableFeatures,
295                                       enable_features_str);
296     }
297   }
298 
299   if (application_) {
300     // Give the application a chance to view/modify the command line.
301     CefRefPtr<CefCommandLineImpl> commandLinePtr(
302         new CefCommandLineImpl(command_line, false, false));
303     application_->OnBeforeCommandLineProcessing(CefString(process_type),
304                                                 commandLinePtr.get());
305     commandLinePtr->Detach(nullptr);
306   }
307 
308   // Initialize logging.
309   logging::LoggingSettings log_settings;
310 
311   const base::FilePath& log_file =
312       command_line->GetSwitchValuePath(switches::kLogFile);
313   DCHECK(!log_file.empty());
314   log_settings.log_file_path = log_file.value().c_str();
315 
316   log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
317   log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
318 
319   logging::LogSeverity log_severity = logging::LOG_INFO;
320 
321   std::string log_severity_str =
322       command_line->GetSwitchValueASCII(switches::kLogSeverity);
323   if (!log_severity_str.empty()) {
324     if (base::LowerCaseEqualsASCII(log_severity_str,
325                                    switches::kLogSeverity_Verbose)) {
326       log_severity = logging::LOG_VERBOSE;
327     } else if (base::LowerCaseEqualsASCII(log_severity_str,
328                                           switches::kLogSeverity_Warning)) {
329       log_severity = logging::LOG_WARNING;
330     } else if (base::LowerCaseEqualsASCII(log_severity_str,
331                                           switches::kLogSeverity_Error)) {
332       log_severity = logging::LOG_ERROR;
333     } else if (base::LowerCaseEqualsASCII(log_severity_str,
334                                           switches::kLogSeverity_Fatal)) {
335       log_severity = logging::LOG_FATAL;
336     } else if (base::LowerCaseEqualsASCII(log_severity_str,
337                                           switches::kLogSeverity_Disable)) {
338       log_severity = LOGSEVERITY_DISABLE;
339     }
340   }
341 
342   if (log_severity == LOGSEVERITY_DISABLE) {
343     log_settings.logging_dest = logging::LOG_NONE;
344     // By default, ERROR and FATAL messages will always be output to stderr due
345     // to the kAlwaysPrintErrorLevel value in base/logging.cc. We change the log
346     // level here so that only FATAL messages are output.
347     logging::SetMinLogLevel(logging::LOG_FATAL);
348   } else {
349     log_settings.logging_dest = logging::LOG_TO_ALL;
350     logging::SetMinLogLevel(log_severity);
351   }
352 
353   logging::InitLogging(log_settings);
354 
355   ContentSettingsPattern::SetNonWildcardDomainNonPortSchemes(
356       kNonWildcardDomainNonPortSchemes, kNonWildcardDomainNonPortSchemesSize);
357 
358   content::SetContentClient(&content_client_);
359 
360 #if defined(OS_MAC)
361   util_mac::BasicStartupComplete();
362 #endif
363 
364   return false;
365 }
366 
PreSandboxStartup()367 void AlloyMainDelegate::PreSandboxStartup() {
368   const base::CommandLine* command_line =
369       base::CommandLine::ForCurrentProcess();
370   const std::string& process_type =
371       command_line->GetSwitchValueASCII(switches::kProcessType);
372 
373   if (process_type.empty()) {
374 // Only override these paths when executing the main process.
375 #if defined(OS_MAC)
376     util_mac::PreSandboxStartup();
377 #endif
378 
379     resource_util::OverrideDefaultDownloadDir();
380     resource_util::OverrideUserDataDir(settings_, command_line);
381   }
382 
383   if (command_line->HasSwitch(switches::kDisablePackLoading))
384     resource_bundle_delegate_.set_pack_loading_disabled(true);
385 
386   // Initialize crash reporting state for this process/module.
387   // chrome::DIR_CRASH_DUMPS must be configured before calling this function.
388   crash_reporting::PreSandboxStartup(*command_line, process_type);
389 
390   InitializeResourceBundle();
391   MaybeInitializeGDI();
392 }
393 
SandboxInitialized(const std::string & process_type)394 void AlloyMainDelegate::SandboxInitialized(const std::string& process_type) {
395   AlloyContentClient::SetPDFEntryFunctions(chrome_pdf::PPP_GetInterface,
396                                            chrome_pdf::PPP_InitializeModule,
397                                            chrome_pdf::PPP_ShutdownModule);
398 }
399 
RunProcess(const std::string & process_type,const content::MainFunctionParams & main_function_params)400 int AlloyMainDelegate::RunProcess(
401     const std::string& process_type,
402     const content::MainFunctionParams& main_function_params) {
403   if (process_type.empty()) {
404     return runner_->RunMainProcess(main_function_params);
405   }
406 
407   return -1;
408 }
409 
ProcessExiting(const std::string & process_type)410 void AlloyMainDelegate::ProcessExiting(const std::string& process_type) {
411   ui::ResourceBundle::CleanupSharedInstance();
412 }
413 
414 #if defined(OS_LINUX)
ZygoteForked()415 void AlloyMainDelegate::ZygoteForked() {
416   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
417   const std::string& process_type =
418       command_line->GetSwitchValueASCII(switches::kProcessType);
419   // Initialize crash reporting state for the newly forked process.
420   crash_reporting::ZygoteForked(command_line, process_type);
421 }
422 #endif
423 
CreateContentBrowserClient()424 content::ContentBrowserClient* AlloyMainDelegate::CreateContentBrowserClient() {
425   browser_client_.reset(new AlloyContentBrowserClient);
426   return browser_client_.get();
427 }
428 
429 content::ContentRendererClient*
CreateContentRendererClient()430 AlloyMainDelegate::CreateContentRendererClient() {
431   renderer_client_.reset(new AlloyContentRendererClient);
432   return renderer_client_.get();
433 }
434 
CreateContentUtilityClient()435 content::ContentUtilityClient* AlloyMainDelegate::CreateContentUtilityClient() {
436   utility_client_.reset(new ChromeContentUtilityClient);
437   return utility_client_.get();
438 }
439 
GetGlobalRequestContext()440 CefRefPtr<CefRequestContext> AlloyMainDelegate::GetGlobalRequestContext() {
441   if (!browser_client_)
442     return nullptr;
443   return browser_client_->request_context();
444 }
445 
CreateNewBrowserContext(const CefRequestContextSettings & settings,base::OnceClosure initialized_cb)446 CefBrowserContext* AlloyMainDelegate::CreateNewBrowserContext(
447     const CefRequestContextSettings& settings,
448     base::OnceClosure initialized_cb) {
449   auto context = new AlloyBrowserContext(settings);
450   context->Initialize();
451   std::move(initialized_cb).Run();
452   return context;
453 }
454 
455 scoped_refptr<base::SingleThreadTaskRunner>
GetBackgroundTaskRunner()456 AlloyMainDelegate::GetBackgroundTaskRunner() {
457   if (browser_client_)
458     return browser_client_->background_task_runner();
459   return nullptr;
460 }
461 
462 scoped_refptr<base::SingleThreadTaskRunner>
GetUserVisibleTaskRunner()463 AlloyMainDelegate::GetUserVisibleTaskRunner() {
464   if (browser_client_)
465     return browser_client_->user_visible_task_runner();
466   return nullptr;
467 }
468 
469 scoped_refptr<base::SingleThreadTaskRunner>
GetUserBlockingTaskRunner()470 AlloyMainDelegate::GetUserBlockingTaskRunner() {
471   if (browser_client_)
472     return browser_client_->user_blocking_task_runner();
473   return nullptr;
474 }
475 
476 scoped_refptr<base::SingleThreadTaskRunner>
GetRenderTaskRunner()477 AlloyMainDelegate::GetRenderTaskRunner() {
478   if (renderer_client_)
479     return renderer_client_->render_task_runner();
480   return nullptr;
481 }
482 
483 scoped_refptr<base::SingleThreadTaskRunner>
GetWebWorkerTaskRunner()484 AlloyMainDelegate::GetWebWorkerTaskRunner() {
485   if (renderer_client_)
486     return renderer_client_->GetCurrentTaskRunner();
487   return nullptr;
488 }
489 
InitializeResourceBundle()490 void AlloyMainDelegate::InitializeResourceBundle() {
491   const base::CommandLine* command_line =
492       base::CommandLine::ForCurrentProcess();
493   base::FilePath resources_pak_file, chrome_100_percent_pak_file,
494       chrome_200_percent_pak_file, locales_dir;
495 
496   base::FilePath resources_dir;
497   if (command_line->HasSwitch(switches::kResourcesDirPath)) {
498     resources_dir =
499         command_line->GetSwitchValuePath(switches::kResourcesDirPath);
500   }
501   if (resources_dir.empty())
502     resources_dir = resource_util::GetResourcesDir();
503   if (!resources_dir.empty())
504     base::PathService::Override(chrome::DIR_RESOURCES, resources_dir);
505 
506   if (!resource_bundle_delegate_.pack_loading_disabled()) {
507     if (!resources_dir.empty()) {
508       CHECK(resources_dir.IsAbsolute());
509       resources_pak_file =
510           resources_dir.Append(FILE_PATH_LITERAL("resources.pak"));
511       chrome_100_percent_pak_file =
512           resources_dir.Append(FILE_PATH_LITERAL("chrome_100_percent.pak"));
513       chrome_200_percent_pak_file =
514           resources_dir.Append(FILE_PATH_LITERAL("chrome_200_percent.pak"));
515     }
516 
517     if (command_line->HasSwitch(switches::kLocalesDirPath))
518       locales_dir = command_line->GetSwitchValuePath(switches::kLocalesDirPath);
519 
520     if (!locales_dir.empty())
521       base::PathService::Override(ui::DIR_LOCALES, locales_dir);
522   }
523 
524   std::string locale = command_line->GetSwitchValueASCII(switches::kLang);
525   DCHECK(!locale.empty());
526 
527   const std::string loaded_locale =
528       ui::ResourceBundle::InitSharedInstanceWithLocale(
529           locale, &resource_bundle_delegate_,
530           ui::ResourceBundle::LOAD_COMMON_RESOURCES);
531   if (!loaded_locale.empty() && g_browser_process)
532     g_browser_process->SetApplicationLocale(loaded_locale);
533 
534   ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
535 
536   if (!resource_bundle_delegate_.pack_loading_disabled()) {
537     if (loaded_locale.empty())
538       LOG(ERROR) << "Could not load locale pak for " << locale;
539 
540     resource_bundle_delegate_.set_allow_pack_file_load(true);
541 
542     if (base::PathExists(resources_pak_file)) {
543       resource_bundle.AddDataPackFromPath(resources_pak_file,
544                                           ui::SCALE_FACTOR_NONE);
545     } else {
546       LOG(ERROR) << "Could not load resources.pak";
547     }
548 
549     // Always load the 1x data pack first as the 2x data pack contains both 1x
550     // and 2x images. The 1x data pack only has 1x images, thus passes in an
551     // accurate scale factor to gfx::ImageSkia::AddRepresentation.
552     if (resource_util::IsScaleFactorSupported(ui::SCALE_FACTOR_100P)) {
553       if (base::PathExists(chrome_100_percent_pak_file)) {
554         resource_bundle.AddDataPackFromPath(chrome_100_percent_pak_file,
555                                             ui::SCALE_FACTOR_100P);
556       } else {
557         LOG(ERROR) << "Could not load chrome_100_percent.pak";
558       }
559     }
560 
561     if (resource_util::IsScaleFactorSupported(ui::SCALE_FACTOR_200P)) {
562       if (base::PathExists(chrome_200_percent_pak_file)) {
563         resource_bundle.AddDataPackFromPath(chrome_200_percent_pak_file,
564                                             ui::SCALE_FACTOR_200P);
565       } else {
566         LOG(ERROR) << "Could not load chrome_200_percent.pak";
567       }
568     }
569 
570     // Skip the default pak file loading that would otherwise occur in
571     // ResourceBundle::LoadChromeResources().
572     resource_bundle_delegate_.set_allow_pack_file_load(false);
573   }
574 }
575