// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "libcef/common/alloy/alloy_main_delegate.h" #include #include #include "libcef/browser/alloy/alloy_browser_context.h" #include "libcef/browser/alloy/alloy_content_browser_client.h" #include "libcef/common/cef_switches.h" #include "libcef/common/command_line_impl.h" #include "libcef/common/crash_reporting.h" #include "libcef/common/extensions/extensions_util.h" #include "libcef/common/resource_util.h" #include "libcef/renderer/alloy/alloy_content_renderer_client.h" #include "base/base_switches.h" #include "base/command_line.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/path_service.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_process_singleton.h" #include "chrome/child/pdf_child_init.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_result_codes.h" #include "chrome/common/chrome_switches.h" #include "chrome/grit/generated_resources.h" #include "chrome/utility/chrome_content_utility_client.h" #include "components/component_updater/component_updater_paths.h" #include "components/content_settings/core/common/content_settings_pattern.h" #include "components/embedder_support/switches.h" #include "components/metrics/persistent_histograms.h" #include "components/viz/common/features.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" #include "content/public/common/url_constants.h" #include "extensions/common/constants.h" #include "net/base/features.h" #include "sandbox/policy/switches.h" #include "services/network/public/cpp/features.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/switches.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/scoped_startup_resource_bundle.h" #include "ui/base/ui_base_features.h" #include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_switches.h" #if BUILDFLAG(IS_MAC) #include "components/crash/core/common/objc_zombie.h" #include "libcef/common/util_mac.h" #elif BUILDFLAG(IS_POSIX) #include "libcef/common/util_linux.h" #endif #if BUILDFLAG(IS_WIN) #include "base/win/scoped_handle.h" #include "base/win/win_util.h" #include "ui/base/resource/resource_bundle_win.h" #endif namespace { const char* const kNonWildcardDomainNonPortSchemes[] = { extensions::kExtensionScheme, content::kChromeDevToolsScheme, content::kChromeUIScheme, content::kChromeUIUntrustedScheme}; const size_t kNonWildcardDomainNonPortSchemesSize = std::size(kNonWildcardDomainNonPortSchemes); std::optional AcquireProcessSingleton( const base::FilePath& user_data_dir) { // Take the Chrome process singleton lock. The process can become the // Browser process if it succeed to take the lock. Otherwise, the // command-line is sent to the actual Browser process and the current // process can be exited. ChromeProcessSingleton::CreateInstance(user_data_dir); ProcessSingleton::NotifyResult notify_result = ChromeProcessSingleton::GetInstance()->NotifyOtherProcessOrCreate(); switch (notify_result) { case ProcessSingleton::PROCESS_NONE: break; case ProcessSingleton::PROCESS_NOTIFIED: { // Ensure there is an instance of ResourceBundle that is initialized for // localized string resource accesses. ui::ScopedStartupResourceBundle startup_resource_bundle; printf("%s\n", base::SysWideToNativeMB( base::UTF16ToWide(l10n_util::GetStringUTF16( IDS_USED_EXISTING_BROWSER))) .c_str()); return chrome::RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED; } case ProcessSingleton::PROFILE_IN_USE: return chrome::RESULT_CODE_PROFILE_IN_USE; case ProcessSingleton::LOCK_ERROR: LOG(ERROR) << "Failed to create a ProcessSingleton for your profile " "directory. This means that running multiple instances " "would start multiple browser processes rather than " "opening a new window in the existing process. Aborting " "now to avoid profile corruption."; return chrome::RESULT_CODE_PROFILE_IN_USE; } return std::nullopt; } } // namespace AlloyMainDelegate::AlloyMainDelegate(CefMainRunnerHandler* runner, CefSettings* settings, CefRefPtr application) : runner_(runner), settings_(settings), application_(application) { #if BUILDFLAG(IS_LINUX) resource_util::OverrideAssetPath(); #endif } AlloyMainDelegate::~AlloyMainDelegate() = default; std::optional AlloyMainDelegate::PreBrowserMain() { runner_->PreBrowserMain(); return std::nullopt; } std::optional AlloyMainDelegate::PostEarlyInitialization( InvokedIn invoked_in) { const auto* invoked_in_browser = absl::get_if(&invoked_in); if (!invoked_in_browser) { return std::nullopt; } // Based on ChromeMainDelegate::PostEarlyInitialization. // The User Data dir is guaranteed to be valid as per PreSandboxStartup. base::FilePath user_data_dir = base::PathService::CheckedGet(chrome::DIR_USER_DATA); // On platforms that support the process rendezvous, acquire the process // singleton. In case of failure, it means there is already a running browser // instance that handled the command-line. if (auto process_singleton_result = AcquireProcessSingleton(user_data_dir); process_singleton_result.has_value()) { // To ensure that the histograms emitted in this process are reported in // case of early exit, report the metrics accumulated this session with a // future session's metrics. DeferBrowserMetrics(user_data_dir); return process_singleton_result; } return std::nullopt; } std::optional AlloyMainDelegate::BasicStartupComplete() { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); std::string process_type = command_line->GetSwitchValueASCII(switches::kProcessType); #if BUILDFLAG(IS_POSIX) // Read the crash configuration file. On Windows this is done from chrome_elf. crash_reporting::BasicStartupComplete(command_line); #endif const bool is_browser = process_type.empty(); if (is_browser) { // In the browser process. Populate the global command-line object. if (settings_->command_line_args_disabled) { // Remove any existing command-line arguments. base::CommandLine::StringVector argv; argv.push_back(command_line->GetProgram().value()); command_line->InitFromArgv(argv); const base::CommandLine::SwitchMap& map = command_line->GetSwitches(); const_cast(&map)->clear(); } bool no_sandbox = settings_->no_sandbox ? true : false; if (settings_->browser_subprocess_path.length > 0) { base::FilePath file_path = base::FilePath(CefString(&settings_->browser_subprocess_path)); if (!file_path.empty()) { command_line->AppendSwitchPath(switches::kBrowserSubprocessPath, file_path); #if BUILDFLAG(IS_WIN) // The sandbox is not supported when using a separate subprocess // executable on Windows. no_sandbox = true; #endif } } #if BUILDFLAG(IS_MAC) if (settings_->framework_dir_path.length > 0) { base::FilePath file_path = base::FilePath(CefString(&settings_->framework_dir_path)); if (!file_path.empty()) { command_line->AppendSwitchPath(switches::kFrameworkDirPath, file_path); } } if (settings_->main_bundle_path.length > 0) { base::FilePath file_path = base::FilePath(CefString(&settings_->main_bundle_path)); if (!file_path.empty()) { command_line->AppendSwitchPath(switches::kMainBundlePath, file_path); } } #endif if (no_sandbox) { command_line->AppendSwitch(sandbox::policy::switches::kNoSandbox); } if (settings_->user_agent.length > 0) { command_line->AppendSwitchASCII( embedder_support::kUserAgent, CefString(&settings_->user_agent).ToString()); } else if (settings_->user_agent_product.length > 0) { command_line->AppendSwitchASCII( switches::kUserAgentProductAndVersion, CefString(&settings_->user_agent_product).ToString()); } if (settings_->locale.length > 0) { command_line->AppendSwitchASCII(switches::kLang, CefString(&settings_->locale).ToString()); } else if (!command_line->HasSwitch(switches::kLang)) { command_line->AppendSwitchASCII(switches::kLang, "en-US"); } base::FilePath log_file; bool has_log_file_cmdline = false; if (settings_->log_file.length > 0) { log_file = base::FilePath(CefString(&settings_->log_file)); } if (log_file.empty() && command_line->HasSwitch(switches::kLogFile)) { log_file = command_line->GetSwitchValuePath(switches::kLogFile); if (!log_file.empty()) { has_log_file_cmdline = true; } } if (log_file.empty()) { log_file = resource_util::GetDefaultLogFilePath(); } DCHECK(!log_file.empty()); if (!has_log_file_cmdline) { command_line->AppendSwitchPath(switches::kLogFile, log_file); } if (settings_->log_severity != LOGSEVERITY_DEFAULT) { std::string log_severity; switch (settings_->log_severity) { case LOGSEVERITY_VERBOSE: log_severity = switches::kLogSeverity_Verbose; break; case LOGSEVERITY_INFO: log_severity = switches::kLogSeverity_Info; break; case LOGSEVERITY_WARNING: log_severity = switches::kLogSeverity_Warning; break; case LOGSEVERITY_ERROR: log_severity = switches::kLogSeverity_Error; break; case LOGSEVERITY_FATAL: log_severity = switches::kLogSeverity_Fatal; break; case LOGSEVERITY_DISABLE: log_severity = switches::kLogSeverity_Disable; break; default: break; } if (!log_severity.empty()) { command_line->AppendSwitchASCII(switches::kLogSeverity, log_severity); } } if (settings_->log_items != LOG_ITEMS_DEFAULT) { std::string log_items_str; if (settings_->log_items == LOG_ITEMS_NONE) { log_items_str = std::string(switches::kLogItems_None); } else { std::vector added_items; if (settings_->log_items & LOG_ITEMS_FLAG_PROCESS_ID) { added_items.emplace_back(switches::kLogItems_PId); } if (settings_->log_items & LOG_ITEMS_FLAG_THREAD_ID) { added_items.emplace_back(switches::kLogItems_TId); } if (settings_->log_items & LOG_ITEMS_FLAG_TIME_STAMP) { added_items.emplace_back(switches::kLogItems_TimeStamp); } if (settings_->log_items & LOG_ITEMS_FLAG_TICK_COUNT) { added_items.emplace_back(switches::kLogItems_TickCount); } if (!added_items.empty()) { log_items_str = base::JoinString(added_items, ","); } } if (!log_items_str.empty()) { command_line->AppendSwitchASCII(switches::kLogItems, log_items_str); } } if (settings_->javascript_flags.length > 0) { command_line->AppendSwitchASCII( blink::switches::kJavaScriptFlags, CefString(&settings_->javascript_flags).ToString()); } if (settings_->pack_loading_disabled) { command_line->AppendSwitch(switches::kDisablePackLoading); } else { if (settings_->resources_dir_path.length > 0) { base::FilePath file_path = base::FilePath(CefString(&settings_->resources_dir_path)); if (!file_path.empty()) { command_line->AppendSwitchPath(switches::kResourcesDirPath, file_path); } } if (settings_->locales_dir_path.length > 0) { base::FilePath file_path = base::FilePath(CefString(&settings_->locales_dir_path)); if (!file_path.empty()) { command_line->AppendSwitchPath(switches::kLocalesDirPath, file_path); } } } if (settings_->remote_debugging_port >= 1024 && settings_->remote_debugging_port <= 65535) { command_line->AppendSwitchASCII( switches::kRemoteDebuggingPort, base::NumberToString(settings_->remote_debugging_port)); } if (settings_->uncaught_exception_stack_size > 0) { command_line->AppendSwitchASCII( switches::kUncaughtExceptionStackSize, base::NumberToString(settings_->uncaught_exception_stack_size)); } std::vector disable_features; #if BUILDFLAG(IS_WIN) if (features::kCalculateNativeWinOcclusion.default_state == base::FEATURE_ENABLED_BY_DEFAULT) { // TODO: Add support for occlusion detection in combination with native // parent windows (see issue #2805). disable_features.push_back(features::kCalculateNativeWinOcclusion.name); } #endif // BUILDFLAG(IS_WIN) if (features::kBackForwardCache.default_state == base::FEATURE_ENABLED_BY_DEFAULT) { // Disable BackForwardCache globally so that // blink::RuntimeEnabledFeatures::BackForwardCacheEnabled reports the // correct value in the renderer process (see issue #3374). disable_features.push_back(features::kBackForwardCache.name); } if (blink::features::kDocumentPictureInPictureAPI.default_state == base::FEATURE_ENABLED_BY_DEFAULT) { // Disable DocumentPictureInPictureAPI globally so that // blink::RuntimeEnabledFeatures::DocumentPictureInPictureAPIEnabled // reports the correct value in the renderer process (see issue #3448). disable_features.push_back( blink::features::kDocumentPictureInPictureAPI.name); } if (!disable_features.empty()) { DCHECK(!base::FeatureList::GetInstance()); std::string disable_features_str = command_line->GetSwitchValueASCII(switches::kDisableFeatures); for (auto feature_str : disable_features) { if (!disable_features_str.empty()) { disable_features_str += ","; } disable_features_str += feature_str; } command_line->AppendSwitchASCII(switches::kDisableFeatures, disable_features_str); } } if (application_) { // Give the application a chance to view/modify the command line. CefRefPtr commandLinePtr( new CefCommandLineImpl(command_line, false, false)); application_->OnBeforeCommandLineProcessing(CefString(process_type), commandLinePtr.get()); std::ignore = commandLinePtr->Detach(nullptr); } #if BUILDFLAG(IS_MAC) // Turns all deallocated Objective-C objects into zombies. Give the browser // process a longer treadmill, since crashes there have more impact. ObjcEvilDoers::ZombieEnable(true, is_browser ? 10000 : 1000); #endif // Initialize logging. logging::LoggingSettings log_settings; enum class LoggingDest { kFile, kStderr, #if BUILDFLAG(IS_WIN) kHandle, #endif }; LoggingDest dest = LoggingDest::kFile; if (command_line->GetSwitchValueASCII(switches::kEnableLogging) == "stderr") { dest = LoggingDest::kStderr; } #if BUILDFLAG(IS_WIN) // On Windows child process may be given a handle in the --log-file switch. base::win::ScopedHandle log_handle; if (command_line->GetSwitchValueASCII(switches::kEnableLogging) == "handle") { auto handle_str = command_line->GetSwitchValueNative(switches::kLogFile); uint32_t handle_value = 0; if (base::StringToUint(handle_str, &handle_value)) { // This handle is owned by the logging framework and is closed when the // process exits. HANDLE duplicate = nullptr; if (::DuplicateHandle(GetCurrentProcess(), base::win::Uint32ToHandle(handle_value), GetCurrentProcess(), &duplicate, 0, FALSE, DUPLICATE_SAME_ACCESS)) { log_handle.Set(duplicate); dest = LoggingDest::kHandle; } } } #endif // BUILDFLAG(IS_WIN) base::FilePath log_file; if (dest == LoggingDest::kFile) { log_file = command_line->GetSwitchValuePath(switches::kLogFile); DCHECK(!log_file.empty()); } #if BUILDFLAG(IS_WIN) if (dest == LoggingDest::kHandle) { // TODO(crbug.com/328285906) Use a ScopedHandle in logging settings. log_settings.log_file = log_handle.release(); } else { log_settings.log_file = nullptr; } #endif // BUILDFLAG(IS_WIN) if (dest == LoggingDest::kFile) { log_settings.log_file_path = log_file.value(); } log_settings.lock_log = logging::DONT_LOCK_LOG_FILE; log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE; logging::LogSeverity log_severity = logging::LOGGING_INFO; std::string log_severity_str = command_line->GetSwitchValueASCII(switches::kLogSeverity); if (!log_severity_str.empty()) { if (base::EqualsCaseInsensitiveASCII(log_severity_str, switches::kLogSeverity_Verbose)) { log_severity = logging::LOGGING_VERBOSE; } else if (base::EqualsCaseInsensitiveASCII( log_severity_str, switches::kLogSeverity_Warning)) { log_severity = logging::LOGGING_WARNING; } else if (base::EqualsCaseInsensitiveASCII(log_severity_str, switches::kLogSeverity_Error)) { log_severity = logging::LOGGING_ERROR; } else if (base::EqualsCaseInsensitiveASCII(log_severity_str, switches::kLogSeverity_Fatal)) { log_severity = logging::LOGGING_FATAL; } else if (base::EqualsCaseInsensitiveASCII( log_severity_str, switches::kLogSeverity_Disable)) { log_severity = LOGSEVERITY_DISABLE; } } if (log_severity == LOGSEVERITY_DISABLE) { log_settings.logging_dest = logging::LOG_NONE; // By default, ERROR and FATAL messages will always be output to stderr due // to the kAlwaysPrintErrorLevel value in base/logging.cc. We change the log // level here so that only FATAL messages are output. logging::SetMinLogLevel(logging::LOGGING_FATAL); } else { if (dest == LoggingDest::kStderr) { log_settings.logging_dest = logging::LOG_TO_STDERR | logging::LOG_TO_SYSTEM_DEBUG_LOG; } else { // Includes both handle or provided filename on Windows. log_settings.logging_dest = logging::LOG_TO_ALL; } logging::SetMinLogLevel(log_severity); } // Customization of items automatically prepended to log lines. std::string log_items_str = command_line->GetSwitchValueASCII(switches::kLogItems); if (!log_items_str.empty()) { bool enable_log_of_process_id, enable_log_of_thread_id, enable_log_of_time_stamp, enable_log_of_tick_count; enable_log_of_process_id = enable_log_of_thread_id = enable_log_of_time_stamp = enable_log_of_tick_count = false; for (const auto& cur_item_to_log : base::SplitStringPiece(log_items_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { // if "none" mode is present, all items are disabled. if (base::EqualsCaseInsensitiveASCII(cur_item_to_log, switches::kLogItems_None)) { enable_log_of_process_id = enable_log_of_thread_id = enable_log_of_time_stamp = enable_log_of_tick_count = false; break; } else if (base::EqualsCaseInsensitiveASCII(cur_item_to_log, switches::kLogItems_PId)) { enable_log_of_process_id = true; } else if (base::EqualsCaseInsensitiveASCII(cur_item_to_log, switches::kLogItems_TId)) { enable_log_of_thread_id = true; } else if (base::EqualsCaseInsensitiveASCII( cur_item_to_log, switches::kLogItems_TimeStamp)) { enable_log_of_time_stamp = true; } else if (base::EqualsCaseInsensitiveASCII( cur_item_to_log, switches::kLogItems_TickCount)) { enable_log_of_tick_count = true; } } logging::SetLogItems(enable_log_of_process_id, enable_log_of_thread_id, enable_log_of_time_stamp, enable_log_of_tick_count); } logging::InitLogging(log_settings); ContentSettingsPattern::SetNonWildcardDomainNonPortSchemes( kNonWildcardDomainNonPortSchemes, kNonWildcardDomainNonPortSchemesSize); content::SetContentClient(&content_client_); #if BUILDFLAG(IS_MAC) util_mac::BasicStartupComplete(); #endif return std::nullopt; } void AlloyMainDelegate::PreSandboxStartup() { const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); const std::string& process_type = command_line->GetSwitchValueASCII(switches::kProcessType); if (process_type.empty()) { // Only override these paths when executing the main process. #if BUILDFLAG(IS_MAC) util_mac::PreSandboxStartup(); #elif BUILDFLAG(IS_POSIX) util_linux::PreSandboxStartup(); #endif resource_util::OverrideDefaultDownloadDir(); } resource_util::OverrideUserDataDir(settings_, command_line); if (command_line->HasSwitch(switches::kDisablePackLoading)) { resource_bundle_delegate_.set_pack_loading_disabled(true); } // Initialize crash reporting state for this process/module. // chrome::DIR_CRASH_DUMPS must be configured before calling this function. crash_reporting::PreSandboxStartup(*command_line, process_type); // Register the component_updater PathProvider. component_updater::RegisterPathProvider(chrome::DIR_COMPONENTS, chrome::DIR_INTERNAL_PLUGINS, chrome::DIR_USER_DATA); InitializeResourceBundle(); MaybePatchGdiGetFontData(); } absl::variant AlloyMainDelegate::RunProcess( const std::string& process_type, content::MainFunctionParams main_function_params) { if (process_type.empty()) { return runner_->RunMainProcess(std::move(main_function_params)); } return std::move(main_function_params); } void AlloyMainDelegate::ProcessExiting(const std::string& process_type) { ui::ResourceBundle::CleanupSharedInstance(); } #if BUILDFLAG(IS_LINUX) void AlloyMainDelegate::ZygoteForked() { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); const std::string& process_type = command_line->GetSwitchValueASCII(switches::kProcessType); // Initialize crash reporting state for the newly forked process. crash_reporting::ZygoteForked(command_line, process_type); } #endif content::ContentBrowserClient* AlloyMainDelegate::CreateContentBrowserClient() { browser_client_ = std::make_unique(); return browser_client_.get(); } content::ContentRendererClient* AlloyMainDelegate::CreateContentRendererClient() { renderer_client_ = std::make_unique(); return renderer_client_.get(); } content::ContentUtilityClient* AlloyMainDelegate::CreateContentUtilityClient() { utility_client_ = std::make_unique(); return utility_client_.get(); } CefRefPtr AlloyMainDelegate::GetGlobalRequestContext() { if (!browser_client_) { return nullptr; } return browser_client_->request_context(); } CefBrowserContext* AlloyMainDelegate::CreateNewBrowserContext( const CefRequestContextSettings& settings, base::OnceClosure initialized_cb) { auto context = new AlloyBrowserContext(settings); context->Initialize(); std::move(initialized_cb).Run(); return context; } scoped_refptr AlloyMainDelegate::GetBackgroundTaskRunner() { if (browser_client_) { return browser_client_->background_task_runner(); } return nullptr; } scoped_refptr AlloyMainDelegate::GetUserVisibleTaskRunner() { if (browser_client_) { return browser_client_->user_visible_task_runner(); } return nullptr; } scoped_refptr AlloyMainDelegate::GetUserBlockingTaskRunner() { if (browser_client_) { return browser_client_->user_blocking_task_runner(); } return nullptr; } scoped_refptr AlloyMainDelegate::GetRenderTaskRunner() { if (renderer_client_) { return renderer_client_->render_task_runner(); } return nullptr; } scoped_refptr AlloyMainDelegate::GetWebWorkerTaskRunner() { if (renderer_client_) { return renderer_client_->GetCurrentTaskRunner(); } return nullptr; } void AlloyMainDelegate::InitializeResourceBundle() { const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); base::FilePath resources_pak_file, chrome_100_percent_pak_file, chrome_200_percent_pak_file, locales_dir; base::FilePath resources_dir; if (command_line->HasSwitch(switches::kResourcesDirPath)) { resources_dir = command_line->GetSwitchValuePath(switches::kResourcesDirPath); } if (resources_dir.empty()) { resources_dir = resource_util::GetResourcesDir(); } if (!resources_dir.empty()) { base::PathService::Override(chrome::DIR_RESOURCES, resources_dir); } if (!resource_bundle_delegate_.pack_loading_disabled()) { if (!resources_dir.empty()) { CHECK(resources_dir.IsAbsolute()); resources_pak_file = resources_dir.Append(FILE_PATH_LITERAL("resources.pak")); chrome_100_percent_pak_file = resources_dir.Append(FILE_PATH_LITERAL("chrome_100_percent.pak")); chrome_200_percent_pak_file = resources_dir.Append(FILE_PATH_LITERAL("chrome_200_percent.pak")); } if (command_line->HasSwitch(switches::kLocalesDirPath)) { locales_dir = command_line->GetSwitchValuePath(switches::kLocalesDirPath); } if (!locales_dir.empty()) { base::PathService::Override(ui::DIR_LOCALES, locales_dir); } } #if BUILDFLAG(IS_WIN) // From chrome/app/chrome_main_delegate.cc // Throbber icons and cursors are still stored in chrome.dll, // this can be killed once those are merged into resources.pak. See // GlassBrowserFrameView::InitThrobberIcons(), https://crbug.com/368327 and // https://crbug.com/1178117. auto module_handle = ::GetModuleHandle(CefAppManager::Get()->GetResourceDllName()); if (!module_handle) { module_handle = ::GetModuleHandle(nullptr); } ui::SetResourcesDataDLL(module_handle); #endif std::string locale = command_line->GetSwitchValueASCII(switches::kLang); DCHECK(!locale.empty()); const std::string loaded_locale = ui::ResourceBundle::InitSharedInstanceWithLocale( locale, &resource_bundle_delegate_, ui::ResourceBundle::LOAD_COMMON_RESOURCES); if (!loaded_locale.empty() && g_browser_process) { g_browser_process->SetApplicationLocale(loaded_locale); } ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance(); if (!resource_bundle_delegate_.pack_loading_disabled()) { if (loaded_locale.empty()) { LOG(ERROR) << "Could not load locale pak for " << locale; } resource_bundle_delegate_.set_allow_pack_file_load(true); if (base::PathExists(resources_pak_file)) { resource_bundle.AddDataPackFromPath(resources_pak_file, ui::kScaleFactorNone); } else { LOG(ERROR) << "Could not load resources.pak"; } // Always load the 1x data pack first as the 2x data pack contains both 1x // and 2x images. The 1x data pack only has 1x images, thus passes in an // accurate scale factor to gfx::ImageSkia::AddRepresentation. if (resource_util::IsScaleFactorSupported(ui::k100Percent)) { if (base::PathExists(chrome_100_percent_pak_file)) { resource_bundle.AddDataPackFromPath(chrome_100_percent_pak_file, ui::k100Percent); } else { LOG(ERROR) << "Could not load chrome_100_percent.pak"; } } if (resource_util::IsScaleFactorSupported(ui::k200Percent)) { if (base::PathExists(chrome_200_percent_pak_file)) { resource_bundle.AddDataPackFromPath(chrome_200_percent_pak_file, ui::k200Percent); } else { LOG(ERROR) << "Could not load chrome_200_percent.pak"; } } // Skip the default pak file loading that would otherwise occur in // ResourceBundle::LoadChromeResources(). resource_bundle_delegate_.set_allow_pack_file_load(false); } }