// 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/main_delegate.h" #include "libcef/browser/content_browser_client.h" #include "libcef/browser/context.h" #include "libcef/common/breakpad_client.h" #include "libcef/common/cef_switches.h" #include "libcef/common/command_line_impl.h" #include "libcef/renderer/content_renderer_client.h" #include "libcef/utility/content_utility_client.h" #include "base/base_switches.h" #include "base/command_line.h" #include "base/files/file_path.h" #include "base/file_util.h" #include "base/lazy_instance.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "content/public/browser/browser_main_runner.h" #include "content/public/browser/render_process_host.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" #include "ui/base/layout.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_switches.h" #if defined(OS_WIN) #include // NOLINT(build/include_order) #include "components/breakpad/app/breakpad_win.h" #endif #if defined(OS_MACOSX) #include "base/mac/os_crash_dumps.h" #include "base/mac/bundle_locations.h" #include "base/mac/foundation_util.h" #include "components/breakpad/app/breakpad_mac.h" #include "content/public/common/content_paths.h" #endif #if defined(OS_POSIX) && !defined(OS_MACOSX) #include "components/breakpad/app/breakpad_linux.h" #endif namespace { base::LazyInstance::Leaky g_shell_breakpad_client = LAZY_INSTANCE_INITIALIZER; #if defined(OS_MACOSX) base::FilePath GetFrameworksPath() { // Start out with the path to the running executable. base::FilePath execPath; PathService::Get(base::FILE_EXE, &execPath); // Get the main bundle path. base::FilePath bundlePath = base::mac::GetAppBundlePath(execPath); // Go into the Contents/Frameworks directory. return bundlePath.Append(FILE_PATH_LITERAL("Contents")) .Append(FILE_PATH_LITERAL("Frameworks")); } // The framework bundle path is used for loading resources, libraries, etc. base::FilePath GetFrameworkBundlePath() { return GetFrameworksPath().Append( FILE_PATH_LITERAL("Chromium Embedded Framework.framework")); } base::FilePath GetResourcesFilePath() { return GetFrameworkBundlePath().Append(FILE_PATH_LITERAL("Resources")); } base::FilePath GetLibrariesFilePath() { return GetFrameworkBundlePath().Append(FILE_PATH_LITERAL("Libraries")); } void OverrideFrameworkBundlePath() { base::mac::SetOverrideFrameworkBundlePath(GetFrameworkBundlePath()); } void OverrideChildProcessPath() { // Retrieve the name of the running executable. base::FilePath path; PathService::Get(base::FILE_EXE, &path); std::string name = path.BaseName().value(); base::FilePath helper_path = GetFrameworksPath() .Append(FILE_PATH_LITERAL(name+" Helper.app")) .Append(FILE_PATH_LITERAL("Contents")) .Append(FILE_PATH_LITERAL("MacOS")) .Append(FILE_PATH_LITERAL(name+" Helper")); PathService::Override(content::CHILD_PROCESS_EXE, helper_path); } #else // !defined(OS_MACOSX) base::FilePath GetResourcesFilePath() { base::FilePath pak_dir; PathService::Get(base::DIR_MODULE, &pak_dir); return pak_dir; } base::FilePath GetLibrariesFilePath() { return GetResourcesFilePath(); } #endif // !defined(OS_MACOSX) // File name of the internal PDF plugin on different platforms. const base::FilePath::CharType kInternalPDFPluginFileName[] = #if defined(OS_WIN) FILE_PATH_LITERAL("pdf.dll"); #elif defined(OS_MACOSX) FILE_PATH_LITERAL("PDF.plugin"); #else // Linux and Chrome OS FILE_PATH_LITERAL("libpdf.so"); #endif void OverridePdfPluginPath() { base::FilePath plugin_path = GetLibrariesFilePath(); plugin_path = plugin_path.Append(kInternalPDFPluginFileName); PathService::Override(chrome::FILE_PDF_PLUGIN, plugin_path); } // Returns true if |scale_factor| is supported by this platform. // Same as ResourceBundle::IsScaleFactorSupported. bool IsScaleFactorSupported(ui::ScaleFactor scale_factor) { const std::vector& supported_scale_factors = ui::GetSupportedScaleFactors(); return std::find(supported_scale_factors.begin(), supported_scale_factors.end(), scale_factor) != supported_scale_factors.end(); } // Used to run the UI on a separate thread. class CefUIThread : public base::Thread { public: explicit CefUIThread(const content::MainFunctionParams& main_function_params) : base::Thread("CefUIThread"), main_function_params_(main_function_params) { } virtual void Init() OVERRIDE { #if defined(OS_WIN) // Initializes the COM library on the current thread. CoInitialize(NULL); #endif // Use our own browser process runner. browser_runner_.reset(content::BrowserMainRunner::Create()); // Initialize browser process state. Uses the current thread's mesage loop. int exit_code = browser_runner_->Initialize(main_function_params_); CHECK_EQ(exit_code, -1); } virtual void CleanUp() OVERRIDE { browser_runner_->Shutdown(); browser_runner_.reset(NULL); #if defined(OS_WIN) // Closes the COM library on the current thread. CoInitialize must // be balanced by a corresponding call to CoUninitialize. CoUninitialize(); #endif } protected: content::MainFunctionParams main_function_params_; scoped_ptr browser_runner_; }; } // namespace CefMainDelegate::CefMainDelegate(CefRefPtr application) : content_client_(application) { } CefMainDelegate::~CefMainDelegate() { } bool CefMainDelegate::BasicStartupComplete(int* exit_code) { #if defined(OS_MACOSX) OverrideFrameworkBundlePath(); #endif CommandLine* command_line = CommandLine::ForCurrentProcess(); std::string process_type = command_line->GetSwitchValueASCII(switches::kProcessType); if (process_type.empty()) { // In the browser process. Populate the global command-line object. const CefSettings& settings = CefContext::Get()->settings(); if (settings.command_line_args_disabled) { // Remove any existing command-line arguments. CommandLine::StringVector argv; argv.push_back(command_line->GetProgram().value()); command_line->InitFromArgv(argv); const CommandLine::SwitchMap& map = command_line->GetSwitches(); const_cast(&map)->clear(); } if (settings.single_process) command_line->AppendSwitch(switches::kSingleProcess); 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 defined(OS_WIN) // The sandbox is not supported when using a separate subprocess // executable on Windows. no_sandbox = true; #endif } } if (no_sandbox) command_line->AppendSwitch(switches::kNoSandbox); if (settings.user_agent.length > 0) { command_line->AppendSwitchASCII(switches::kUserAgent, CefString(&settings.user_agent)); } else if (settings.product_version.length > 0) { command_line->AppendSwitchASCII(switches::kProductVersion, CefString(&settings.product_version)); } if (settings.locale.length > 0) { command_line->AppendSwitchASCII(switches::kLang, CefString(&settings.locale)); } else if (!command_line->HasSwitch(switches::kLang)) { command_line->AppendSwitchASCII(switches::kLang, "en-US"); } if (settings.log_file.length > 0) { base::FilePath file_path = base::FilePath(CefString(&settings.log_file)); if (!file_path.empty()) command_line->AppendSwitchPath(switches::kLogFile, file_path); } 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_DISABLE: log_severity = switches::kLogSeverity_Disable; break; default: break; } if (!log_severity.empty()) command_line->AppendSwitchASCII(switches::kLogSeverity, log_severity); } if (settings.javascript_flags.length > 0) { command_line->AppendSwitchASCII(switches::kJavaScriptFlags, CefString(&settings.javascript_flags)); } 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::IntToString(settings.remote_debugging_port)); } if (settings.uncaught_exception_stack_size > 0) { command_line->AppendSwitchASCII(switches::kUncaughtExceptionStackSize, base::IntToString(settings.uncaught_exception_stack_size)); } if (settings.context_safety_implementation != 0) { command_line->AppendSwitchASCII(switches::kContextSafetyImplementation, base::IntToString(settings.context_safety_implementation)); } if (settings.windowless_rendering_enabled) { #if defined(OS_MACOSX) // The delegated renderer is not yet enabled by default on OS X. command_line->AppendSwitch(switches::kEnableDelegatedRenderer); #endif } } if (content_client_.application().get()) { // Give the application a chance to view/modify the command line. CefRefPtr commandLinePtr( new CefCommandLineImpl(command_line, false, false)); content_client_.application()->OnBeforeCommandLineProcessing( CefString(process_type), commandLinePtr.get()); commandLinePtr->Detach(NULL); } // Initialize logging. logging::LoggingSettings log_settings; const base::FilePath& log_file = command_line->GetSwitchValuePath(switches::kLogFile); log_settings.log_file = log_file.value().c_str(); log_settings.lock_log = logging::DONT_LOCK_LOG_FILE; log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE; logging::LogSeverity log_severity = logging::LOG_INFO; std::string log_severity_str = command_line->GetSwitchValueASCII(switches::kLogSeverity); if (!log_severity_str.empty()) { if (LowerCaseEqualsASCII(log_severity_str, switches::kLogSeverity_Verbose)) { log_severity = logging::LOG_VERBOSE; } else if (LowerCaseEqualsASCII(log_severity_str, switches::kLogSeverity_Warning)) { log_severity = logging::LOG_WARNING; } else if (LowerCaseEqualsASCII(log_severity_str, switches::kLogSeverity_Error)) { log_severity = logging::LOG_ERROR; } else if (LowerCaseEqualsASCII(log_severity_str, switches::kLogSeverity_Disable)) { log_severity = LOGSEVERITY_DISABLE; } } if (log_severity == LOGSEVERITY_DISABLE) { log_settings.logging_dest = logging::LOG_NONE; } else { log_settings.logging_dest = logging::LOG_TO_ALL; logging::SetMinLogLevel(log_severity); } logging::InitLogging(log_settings); content::SetContentClient(&content_client_); return false; } void CefMainDelegate::PreSandboxStartup() { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); const std::string& process_type = command_line.GetSwitchValueASCII(switches::kProcessType); if (command_line.HasSwitch(switches::kEnableCrashReporter)) { breakpad::SetBreakpadClient(g_shell_breakpad_client.Pointer()); #if defined(OS_MACOSX) base::mac::DisableOSCrashDumps(); breakpad::InitCrashReporter(process_type); breakpad::InitCrashProcessInfo(process_type); #elif defined(OS_POSIX) && !defined(OS_MACOSX) if (process_type != switches::kZygoteProcess) breakpad::InitCrashReporter(process_type); #elif defined(OS_WIN) UINT new_flags = SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX; UINT existing_flags = SetErrorMode(new_flags); SetErrorMode(existing_flags | new_flags); breakpad::InitCrashReporter(process_type); #endif } #if defined(OS_MACOSX) if (!command_line.HasSwitch(switches::kProcessType)) { // Only override the child process path when executing the main process. OverrideChildProcessPath(); } #endif OverridePdfPluginPath(); if (command_line.HasSwitch(switches::kDisablePackLoading)) content_client_.set_pack_loading_disabled(true); InitializeResourceBundle(); if (process_type == switches::kUtilityProcess || process_type == switches::kZygoteProcess) { CefContentUtilityClient::PreSandboxStartup(); } } int CefMainDelegate::RunProcess( const std::string& process_type, const content::MainFunctionParams& main_function_params) { if (process_type.empty()) { const CefSettings& settings = CefContext::Get()->settings(); if (!settings.multi_threaded_message_loop) { // Use our own browser process runner. browser_runner_.reset(content::BrowserMainRunner::Create()); // Initialize browser process state. Results in a call to // CefBrowserMain::PreMainMessageLoopStart() which creates the UI message // loop. int exit_code = browser_runner_->Initialize(main_function_params); if (exit_code >= 0) return exit_code; } else { // Run the UI on a separate thread. scoped_ptr thread; thread.reset(new CefUIThread(main_function_params)); base::Thread::Options options; options.message_loop_type = base::MessageLoop::TYPE_UI; if (!thread->StartWithOptions(options)) { NOTREACHED() << "failed to start UI thread"; return 1; } ui_thread_.swap(thread); } return 0; } return -1; } void CefMainDelegate::ProcessExiting(const std::string& process_type) { ResourceBundle::CleanupSharedInstance(); } #if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) void CefMainDelegate::ZygoteForked() { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kEnableCrashReporter)) { const std::string& process_type = command_line.GetSwitchValueASCII( switches::kProcessType); breakpad::InitCrashReporter(process_type); } } #endif content::ContentBrowserClient* CefMainDelegate::CreateContentBrowserClient() { browser_client_.reset(new CefContentBrowserClient); return browser_client_.get(); } content::ContentRendererClient* CefMainDelegate::CreateContentRendererClient() { renderer_client_.reset(new CefContentRendererClient); return renderer_client_.get(); } content::ContentUtilityClient* CefMainDelegate::CreateContentUtilityClient() { utility_client_.reset(new CefContentUtilityClient); return utility_client_.get(); } void CefMainDelegate::ShutdownBrowser() { if (browser_runner_.get()) { browser_runner_->Shutdown(); browser_runner_.reset(NULL); } if (ui_thread_.get()) { // Blocks until the thread has stopped. ui_thread_->Stop(); ui_thread_.reset(); } } void CefMainDelegate::InitializeResourceBundle() { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); base::FilePath cef_pak_file, cef_100_percent_pak_file, cef_200_percent_pak_file, devtools_pak_file, locales_dir; if (!content_client_.pack_loading_disabled()) { base::FilePath resources_dir; if (command_line.HasSwitch(switches::kResourcesDirPath)) { resources_dir = command_line.GetSwitchValuePath(switches::kResourcesDirPath); } if (resources_dir.empty()) resources_dir = GetResourcesFilePath(); if (!resources_dir.empty()) { cef_pak_file = resources_dir.Append(FILE_PATH_LITERAL("cef.pak")); cef_100_percent_pak_file = resources_dir.Append(FILE_PATH_LITERAL("cef_100_percent.pak")); cef_200_percent_pak_file = resources_dir.Append(FILE_PATH_LITERAL("cef_200_percent.pak")); devtools_pak_file = resources_dir.Append(FILE_PATH_LITERAL("devtools_resources.pak")); } if (command_line.HasSwitch(switches::kLocalesDirPath)) locales_dir = command_line.GetSwitchValuePath(switches::kLocalesDirPath); if (!locales_dir.empty()) PathService::Override(ui::DIR_LOCALES, locales_dir); } std::string locale = command_line.GetSwitchValueASCII(switches::kLang); DCHECK(!locale.empty()); const std::string loaded_locale = ui::ResourceBundle::InitSharedInstanceWithLocale(locale, &content_client_); ResourceBundle& resource_bundle = ResourceBundle::GetSharedInstance(); if (!content_client_.pack_loading_disabled()) { if (loaded_locale.empty()) LOG(ERROR) << "Could not load locale pak for " << locale; content_client_.set_allow_pack_file_load(true); if (base::PathExists(cef_pak_file)) { resource_bundle.AddDataPackFromPath(cef_pak_file, ui::SCALE_FACTOR_NONE); } else { LOG(ERROR) << "Could not load cef.pak"; } // On OS X and Linux/Aura always load the 1x data pack first as the 2x data // pack contains both 1x and 2x images. const bool load_100_percent = #if defined(OS_WIN) IsScaleFactorSupported(ui::SCALE_FACTOR_100P); #else true; #endif if (load_100_percent) { if (base::PathExists(cef_100_percent_pak_file)) { resource_bundle.AddDataPackFromPath( cef_100_percent_pak_file, ui::SCALE_FACTOR_100P); } else { LOG(ERROR) << "Could not load cef_100_percent.pak"; } } if (IsScaleFactorSupported(ui::SCALE_FACTOR_200P)) { if (base::PathExists(cef_200_percent_pak_file)) { resource_bundle.AddDataPackFromPath( cef_200_percent_pak_file, ui::SCALE_FACTOR_200P); } else { LOG(ERROR) << "Could not load cef_200_percent.pak"; } } if (base::PathExists(devtools_pak_file)) { resource_bundle.AddDataPackFromPath( devtools_pak_file, ui::SCALE_FACTOR_NONE); } content_client_.set_allow_pack_file_load(false); } }