| // Copyright (c) 2011 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 "content/browser/browser_main.h" |
| |
| #include "base/allocator/allocator_shim.h" |
| #include "base/command_line.h" |
| #include "base/debug/trace_event.h" |
| #include "base/logging.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/histogram.h" |
| #include "base/system_monitor/system_monitor.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/content_browser_client.h" |
| #include "content/common/content_switches.h" |
| #include "content/common/hi_res_timer_manager.h" |
| #include "content/common/main_function_params.h" |
| #include "content/common/notification_service.h" |
| #include "content/common/result_codes.h" |
| #include "content/common/sandbox_policy.h" |
| #include "crypto/nss_util.h" |
| #include "net/base/network_change_notifier.h" |
| #include "net/base/ssl_config_service.h" |
| #include "net/socket/client_socket_factory.h" |
| #include "net/socket/tcp_client_socket.h" |
| |
| #if defined(OS_WIN) |
| #include <windows.h> |
| #include <commctrl.h> |
| #include <ole2.h> |
| #include <shellapi.h> |
| |
| #include "base/win/scoped_com_initializer.h" |
| #include "net/base/winsock_init.h" |
| #include "sandbox/src/sandbox.h" |
| #include "ui/base/l10n/l10n_util_win.h" |
| #endif |
| |
| #if defined(OS_CHROMEOS) |
| #include <dbus/dbus-glib.h> |
| #endif |
| |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| #include <sys/stat.h> |
| |
| #include "content/browser/renderer_host/render_sandbox_host_linux.h" |
| #include "content/browser/zygote_host_linux.h" |
| #endif |
| |
| #if defined(TOOLKIT_USES_GTK) |
| #include "ui/gfx/gtk_util.h" |
| #endif |
| |
| namespace { |
| |
| #if defined(OS_WIN) |
| // Windows-specific initialization code for the sandbox broker services. |
| void InitializeBrokerServices(const MainFunctionParams& parameters, |
| const CommandLine& parsed_command_line) { |
| sandbox::BrokerServices* broker_services = |
| parameters.sandbox_info_.BrokerServices(); |
| if (broker_services) { |
| sandbox::InitBrokerServices(broker_services); |
| if (!parsed_command_line.HasSwitch(switches::kNoSandbox)) { |
| bool use_winsta = !parsed_command_line.HasSwitch( |
| switches::kDisableAltWinstation); |
| // Precreate the desktop and window station used by the renderers. |
| sandbox::TargetPolicy* policy = broker_services->CreatePolicy(); |
| sandbox::ResultCode result = policy->CreateAlternateDesktop(use_winsta); |
| CHECK(sandbox::SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION != result); |
| policy->Release(); |
| } |
| } |
| } |
| #elif defined(OS_POSIX) && !defined(OS_MACOSX) |
| void SetupSandbox(const CommandLine& parsed_command_line) { |
| // TODO(evanm): move this into SandboxWrapper; I'm just trying to move this |
| // code en masse out of chrome_main for now. |
| const char* sandbox_binary = NULL; |
| struct stat st; |
| |
| // In Chromium branded builds, developers can set an environment variable to |
| // use the development sandbox. See |
| // http://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment |
| if (stat("/proc/self/exe", &st) == 0 && st.st_uid == getuid()) |
| sandbox_binary = getenv("CHROME_DEVEL_SANDBOX"); |
| |
| #if defined(LINUX_SANDBOX_PATH) |
| if (!sandbox_binary) |
| sandbox_binary = LINUX_SANDBOX_PATH; |
| #endif |
| |
| std::string sandbox_cmd; |
| if (sandbox_binary && !parsed_command_line.HasSwitch(switches::kNoSandbox)) |
| sandbox_cmd = sandbox_binary; |
| |
| // Tickle the sandbox host and zygote host so they fork now. |
| RenderSandboxHostLinux* shost = RenderSandboxHostLinux::GetInstance(); |
| shost->Init(sandbox_cmd); |
| ZygoteHost* zhost = ZygoteHost::GetInstance(); |
| zhost->Init(sandbox_cmd); |
| } |
| #endif |
| |
| #if defined(TOOLKIT_USES_GTK) |
| static void GLibLogHandler(const gchar* log_domain, |
| GLogLevelFlags log_level, |
| const gchar* message, |
| gpointer userdata) { |
| if (!log_domain) |
| log_domain = "<unknown>"; |
| if (!message) |
| message = "<no message>"; |
| |
| if (strstr(message, "Loading IM context type") || |
| strstr(message, "wrong ELF class: ELFCLASS64")) { |
| // http://crbug.com/9643 |
| // Until we have a real 64-bit build or all of these 32-bit package issues |
| // are sorted out, don't fatal on ELF 32/64-bit mismatch warnings and don't |
| // spam the user with more than one of them. |
| static bool alerted = false; |
| if (!alerted) { |
| LOG(ERROR) << "Bug 9643: " << log_domain << ": " << message; |
| alerted = true; |
| } |
| } else if (strstr(message, "Theme file for default has no") || |
| strstr(message, "Theme directory") || |
| strstr(message, "theme pixmap")) { |
| LOG(ERROR) << "GTK theme error: " << message; |
| } else if (strstr(message, "gtk_drag_dest_leave: assertion")) { |
| LOG(ERROR) << "Drag destination deleted: http://crbug.com/18557"; |
| } else if (strstr(message, "Out of memory") && |
| strstr(log_domain, "<unknown>")) { |
| LOG(ERROR) << "DBus call timeout or out of memory: " |
| << "http://crosbug.com/15496"; |
| } else { |
| LOG(DFATAL) << log_domain << ": " << message; |
| } |
| } |
| |
| static void SetUpGLibLogHandler() { |
| // Register GLib-handled assertions to go through our logging system. |
| const char* kLogDomains[] = { NULL, "Gtk", "Gdk", "GLib", "GLib-GObject" }; |
| for (size_t i = 0; i < arraysize(kLogDomains); i++) { |
| g_log_set_handler(kLogDomains[i], |
| static_cast<GLogLevelFlags>(G_LOG_FLAG_RECURSION | |
| G_LOG_FLAG_FATAL | |
| G_LOG_LEVEL_ERROR | |
| G_LOG_LEVEL_CRITICAL | |
| G_LOG_LEVEL_WARNING), |
| GLibLogHandler, |
| NULL); |
| } |
| } |
| #endif |
| |
| } // namespace |
| |
| namespace content { |
| |
| BrowserMainParts::BrowserMainParts(const MainFunctionParams& parameters) |
| : parameters_(parameters), |
| parsed_command_line_(parameters.command_line_), |
| result_code_(content::RESULT_CODE_NORMAL_EXIT) { |
| } |
| |
| BrowserMainParts::~BrowserMainParts() { |
| } |
| |
| void BrowserMainParts::EarlyInitialization() { |
| PreEarlyInitialization(); |
| |
| #if defined(OS_WIN) |
| net::EnsureWinsockInit(); |
| #endif |
| |
| // Use NSS for SSL by default. |
| // The default client socket factory uses NSS for SSL by default on |
| // Windows and Mac. |
| #if defined(OS_WIN) || defined(OS_MACOSX) |
| if (parsed_command_line().HasSwitch(switches::kUseSystemSSL)) { |
| net::ClientSocketFactory::UseSystemSSL(); |
| } else { |
| #elif defined(USE_NSS) |
| if (true) { |
| #else |
| if (false) { |
| #endif |
| // We want to be sure to init NSPR on the main thread. |
| crypto::EnsureNSPRInit(); |
| } |
| |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| SetupSandbox(parsed_command_line()); |
| #endif |
| |
| if (parsed_command_line().HasSwitch(switches::kDisableSSLFalseStart)) |
| net::SSLConfigService::DisableFalseStart(); |
| if (parsed_command_line().HasSwitch(switches::kEnableSSLCachedInfo)) |
| net::SSLConfigService::EnableCachedInfo(); |
| if (parsed_command_line().HasSwitch(switches::kEnableOriginBoundCerts)) |
| net::SSLConfigService::EnableOriginBoundCerts(); |
| if (parsed_command_line().HasSwitch( |
| switches::kEnableDNSCertProvenanceChecking)) { |
| net::SSLConfigService::EnableDNSCertProvenanceChecking(); |
| } |
| |
| // TODO(abarth): Should this move to InitializeNetworkOptions? This doesn't |
| // seem dependent on SSL initialization(). |
| if (parsed_command_line().HasSwitch(switches::kEnableTcpFastOpen)) |
| net::set_tcp_fastopen_enabled(true); |
| |
| PostEarlyInitialization(); |
| } |
| |
| void BrowserMainParts::MainMessageLoopStart() { |
| PreMainMessageLoopStart(); |
| |
| #if defined(OS_WIN) |
| OleInitialize(NULL); |
| |
| // If we're running tests (ui_task is non-null), then the ResourceBundle |
| // has already been initialized. |
| if (!parameters().ui_task) { |
| // Override the configured locale with the user's preferred UI language. |
| l10n_util::OverrideLocaleWithUILanguageList(); |
| } |
| #endif |
| |
| main_message_loop_.reset(new MessageLoop(MessageLoop::TYPE_UI)); |
| |
| // TODO(viettrungluu): should these really go before setting the thread name? |
| system_monitor_.reset(new base::SystemMonitor); |
| hi_res_timer_manager_.reset(new HighResolutionTimerManager); |
| |
| InitializeMainThread(); |
| |
| network_change_notifier_.reset(net::NetworkChangeNotifier::Create()); |
| |
| PostMainMessageLoopStart(); |
| } |
| |
| void BrowserMainParts::RunMainMessageLoopParts() { |
| PreMainMessageLoopRun(); |
| |
| TRACE_EVENT_BEGIN_ETW("BrowserMain:MESSAGE_LOOP", 0, ""); |
| // If the UI thread blocks, the whole UI is unresponsive. |
| // Do not allow disk IO from the UI thread. |
| base::ThreadRestrictions::SetIOAllowed(false); |
| MainMessageLoopRun(); |
| TRACE_EVENT_END_ETW("BrowserMain:MESSAGE_LOOP", 0, ""); |
| |
| PostMainMessageLoopRun(); |
| } |
| |
| void BrowserMainParts::InitializeMainThread() { |
| const char* kThreadName = "CrBrowserMain"; |
| base::PlatformThread::SetName(kThreadName); |
| main_message_loop().set_thread_name(kThreadName); |
| |
| // Register the main thread by instantiating it, but don't call any methods. |
| main_thread_.reset(new BrowserThread(BrowserThread::UI, |
| MessageLoop::current())); |
| } |
| |
| void BrowserMainParts::InitializeToolkit() { |
| // TODO(evan): this function is rather subtle, due to the variety |
| // of intersecting ifdefs we have. To keep it easy to follow, there |
| // are no #else branches on any #ifs. |
| |
| #if defined(TOOLKIT_USES_GTK) |
| // We want to call g_thread_init(), but in some codepaths (tests) it |
| // is possible it has already been called. In older versions of |
| // GTK, it is an error to call g_thread_init twice; unfortunately, |
| // the API to tell whether it has been called already was also only |
| // added in a newer version of GTK! Thankfully, this non-intuitive |
| // check is actually equivalent and sufficient to work around the |
| // error. |
| if (!g_thread_supported()) |
| g_thread_init(NULL); |
| // Glib type system initialization. Needed at least for gconf, |
| // used in net/proxy/proxy_config_service_linux.cc. Most likely |
| // this is superfluous as gtk_init() ought to do this. It's |
| // definitely harmless, so retained as a reminder of this |
| // requirement for gconf. |
| g_type_init(); |
| #if defined(OS_CHROMEOS) |
| // ChromeOS still uses dbus-glib, so initialize its threading here. |
| // TODO(satorux, stevenjb): remove this once it is no longer needed. |
| dbus_g_thread_init(); |
| #endif |
| #if defined(USE_AURA) |
| // TODO(saintlou): We still need some GTK at the lowest level, so init here. |
| gtk_init(NULL, NULL); |
| #else |
| gfx::GtkInitFromCommandLine(parameters().command_line_); |
| #endif |
| SetUpGLibLogHandler(); |
| #endif |
| |
| #if defined(TOOLKIT_GTK) |
| // It is important for this to happen before the first run dialog, as it |
| // styles the dialog as well. |
| gfx::InitRCStyles(); |
| #endif |
| |
| #if defined(OS_WIN) |
| INITCOMMONCONTROLSEX config; |
| config.dwSize = sizeof(config); |
| config.dwICC = ICC_WIN95_CLASSES; |
| if (!InitCommonControlsEx(&config)) |
| LOG_GETLASTERROR(FATAL); |
| #endif |
| |
| ToolkitInitialized(); |
| } |
| |
| void BrowserMainParts::PreEarlyInitialization() { |
| } |
| |
| void BrowserMainParts::PostEarlyInitialization() { |
| } |
| |
| void BrowserMainParts::PreMainMessageLoopStart() { |
| } |
| |
| void BrowserMainParts::PostMainMessageLoopStart() { |
| } |
| |
| void BrowserMainParts::PreMainMessageLoopRun() { |
| } |
| |
| void BrowserMainParts::MainMessageLoopRun() { |
| #if defined(OS_MACOSX) |
| MessageLoopForUI::current()->Run(); |
| #else |
| MessageLoopForUI::current()->Run(NULL); |
| #endif |
| } |
| |
| void BrowserMainParts::PostMainMessageLoopRun() { |
| } |
| |
| void BrowserMainParts::ToolkitInitialized() { |
| } |
| |
| } // namespace content |
| |
| // Main routine for running as the Browser process. |
| int BrowserMain(const MainFunctionParams& parameters) { |
| TRACE_EVENT_BEGIN_ETW("BrowserMain", 0, ""); |
| |
| NotificationService main_notification_service; |
| |
| scoped_ptr<content::BrowserMainParts> parts( |
| content::GetContentClient()->browser()->CreateBrowserMainParts( |
| parameters)); |
| if (!parts.get()) |
| parts.reset(new content::BrowserMainParts(parameters)); |
| |
| parts->EarlyInitialization(); |
| |
| // Must happen before we try to use a message loop or display any UI. |
| parts->InitializeToolkit(); |
| |
| parts->MainMessageLoopStart(); |
| |
| // WARNING: If we get a WM_ENDSESSION, objects created on the stack here |
| // are NOT deleted. If you need something to run during WM_ENDSESSION add it |
| // to browser_shutdown::Shutdown or BrowserProcess::EndSession. |
| |
| // !!!!!!!!!! READ ME !!!!!!!!!! |
| // I (viettrungluu) am in the process of refactoring |BrowserMain()|. If you |
| // need to add something above this comment, read the documentation in |
| // browser_main.h. If you need to add something below, please do the |
| // following: |
| // - Figure out where you should add your code. Do NOT just pick a random |
| // location "which works". |
| // - Document the dependencies apart from compile-time-checkable ones. What |
| // must happen before your new code is executed? Does your new code need to |
| // run before something else? Are there performance reasons for executing |
| // your code at that point? |
| // - If you need to create a (persistent) object, heap allocate it and keep a |
| // |scoped_ptr| to it rather than allocating it on the stack. Otherwise |
| // I'll have to convert your code when I refactor. |
| // - Unless your new code is just a couple of lines, factor it out into a |
| // function with a well-defined purpose. Do NOT just add it inline in |
| // |BrowserMain()|. |
| // Thanks! |
| |
| // TODO(viettrungluu): put the remainder into BrowserMainParts |
| |
| #if defined(OS_WIN) |
| #if !defined(NO_TCMALLOC) |
| // When linking shared libraries, NO_TCMALLOC is defined, and dynamic |
| // allocator selection is not supported. |
| |
| // Make this call before going multithreaded, or spawning any subprocesses. |
| base::allocator::SetupSubprocessAllocator(); |
| #endif |
| // The broker service initialization needs to run early because it will |
| // initialize the sandbox broker, which requires the process to swap its |
| // window station. During this time all the UI will be broken. This has to |
| // run before threads and windows are created. |
| InitializeBrokerServices(parameters, parameters.command_line_); |
| |
| base::win::ScopedCOMInitializer com_initializer; |
| #endif // OS_WIN |
| |
| base::StatisticsRecorder statistics; |
| |
| parts->RunMainMessageLoopParts(); |
| |
| TRACE_EVENT_END_ETW("BrowserMain", 0, 0); |
| return parts->result_code(); |
| } |