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