| // 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 "chrome/app/chrome_main.h" |
| |
| #include "base/at_exit.h" |
| #include "base/command_line.h" |
| #include "base/debug/debugger.h" |
| #include "base/i18n/icu_util.h" |
| #include "base/lazy_instance.h" |
| #include "base/mac/scoped_nsautorelease_pool.h" |
| #include "base/message_loop.h" |
| #include "base/metrics/stats_counters.h" |
| #include "base/metrics/stats_table.h" |
| #include "base/path_service.h" |
| #include "base/process_util.h" |
| #include "base/stringprintf.h" |
| #include "base/string_number_conversions.h" |
| #include "base/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "crypto/nss_util.h" |
| #include "chrome/browser/defaults.h" |
| #include "chrome/browser/diagnostics/diagnostics_main.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_content_client.h" |
| #include "chrome/common/chrome_content_plugin_client.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_paths_internal.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/chrome_version_info.h" |
| #include "chrome/common/logging_chrome.h" |
| #include "chrome/common/profiling.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/renderer/chrome_content_renderer_client.h" |
| #include "chrome/utility/chrome_content_utility_client.h" |
| #include "content/browser/renderer_host/render_process_host.h" |
| #include "content/common/content_client.h" |
| #include "content/common/content_counters.h" |
| #include "content/common/content_paths.h" |
| #include "content/common/main_function_params.h" |
| #include "content/common/sandbox_init_wrapper.h" |
| #include "content/common/set_process_title.h" |
| #include "ipc/ipc_switches.h" |
| #include "media/base/media.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 <algorithm> |
| #include <atlbase.h> |
| #include <malloc.h> |
| #include "base/string_util.h" |
| #include "base/win/registry.h" |
| #include "sandbox/src/sandbox.h" |
| #include "tools/memory_watcher/memory_watcher.h" |
| #endif |
| |
| #if defined(OS_MACOSX) |
| #include "base/mac/mac_util.h" |
| #include "base/mac/os_crash_dumps.h" |
| #include "base/mach_ipc_mac.h" |
| #include "base/system_monitor/system_monitor.h" |
| #include "chrome/app/breakpad_mac.h" |
| #include "chrome/browser/mac/relauncher.h" |
| #include "chrome/common/chrome_paths_internal.h" |
| #include "chrome/common/mac/cfbundle_blocker.h" |
| #include "content/browser/mach_broker_mac.h" |
| #include "grit/chromium_strings.h" |
| #include "third_party/WebKit/Source/WebKit/mac/WebCoreSupport/WebSystemInterface.h" |
| #include "ui/base/l10n/l10n_util_mac.h" |
| #endif |
| |
| #if defined(OS_POSIX) |
| #include <locale.h> |
| #include <signal.h> |
| #endif |
| |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| #include "chrome/common/nacl_fork_delegate_linux.h" |
| #include "content/common/zygote_fork_delegate_linux.h" |
| #endif |
| |
| #if defined(OS_CHROMEOS) |
| #include "base/sys_info.h" |
| #include "chrome/browser/chromeos/boot_times_loader.h" |
| #endif |
| |
| #if defined(USE_X11) |
| #include <gdk/gdk.h> |
| #include <glib.h> |
| #include <gtk/gtk.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "ui/base/x/x11_util.h" |
| #endif |
| |
| #if defined(USE_LINUX_BREAKPAD) |
| #include "chrome/app/breakpad_linux.h" |
| #endif |
| |
| #if !defined(NACL_WIN64) // We don't build the this code on win nacl64. |
| base::LazyInstance<chrome::ChromeContentRendererClient> |
| g_chrome_content_renderer_client(base::LINKER_INITIALIZED); |
| base::LazyInstance<chrome::ChromeContentUtilityClient> |
| g_chrome_content_utility_client(base::LINKER_INITIALIZED); |
| #endif // NACL_WIN64 |
| |
| base::LazyInstance<chrome::ChromeContentPluginClient> |
| g_chrome_content_plugin_client(base::LINKER_INITIALIZED); |
| |
| extern int BrowserMain(const MainFunctionParams&); |
| extern int RendererMain(const MainFunctionParams&); |
| extern int GpuMain(const MainFunctionParams&); |
| extern int PluginMain(const MainFunctionParams&); |
| extern int PpapiPluginMain(const MainFunctionParams&); |
| extern int PpapiBrokerMain(const MainFunctionParams&); |
| extern int WorkerMain(const MainFunctionParams&); |
| extern int NaClMain(const MainFunctionParams&); |
| extern int UtilityMain(const MainFunctionParams&); |
| extern int ProfileImportMain(const MainFunctionParams&); |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| extern int ZygoteMain(const MainFunctionParams&, |
| ZygoteForkDelegate* forkdelegate); |
| #endif |
| #if defined(_WIN64) |
| extern int NaClBrokerMain(const MainFunctionParams&); |
| #endif |
| extern int ServiceProcessMain(const MainFunctionParams&); |
| |
| #if defined(OS_WIN) |
| // TODO(erikkay): isn't this already defined somewhere? |
| #define DLLEXPORT __declspec(dllexport) |
| |
| // We use extern C for the prototype DLLEXPORT to avoid C++ name mangling. |
| extern "C" { |
| DLLEXPORT int __cdecl ChromeMain(HINSTANCE instance, |
| sandbox::SandboxInterfaceInfo* sandbox_info, |
| TCHAR* command_line_unused); |
| } |
| #elif defined(OS_POSIX) |
| extern "C" { |
| __attribute__((visibility("default"))) |
| int ChromeMain(int argc, char** argv); |
| } |
| #endif |
| |
| namespace { |
| |
| #if defined(OS_WIN) |
| const wchar_t kProfilingDll[] = L"memory_watcher.dll"; |
| |
| // Load the memory profiling DLL. All it needs to be activated |
| // is to be loaded. Return true on success, false otherwise. |
| bool LoadMemoryProfiler() { |
| HMODULE prof_module = LoadLibrary(kProfilingDll); |
| return prof_module != NULL; |
| } |
| |
| // Early versions of Chrome incorrectly registered a chromehtml: URL handler, |
| // which gives us nothing but trouble. Avoid launching chrome this way since |
| // some apps fail to properly escape arguments. |
| bool HasDeprecatedArguments(const std::wstring& command_line) { |
| const wchar_t kChromeHtml[] = L"chromehtml:"; |
| std::wstring command_line_lower = command_line; |
| // We are only searching for ASCII characters so this is OK. |
| StringToLowerASCII(&command_line_lower); |
| std::wstring::size_type pos = command_line_lower.find(kChromeHtml); |
| return (pos != std::wstring::npos); |
| } |
| |
| #endif // defined(OS_WIN) |
| |
| #if defined(OS_LINUX) |
| static void AdjustLinuxOOMScore(const std::string& process_type) { |
| // Browsers and zygotes should still be killable, but killed last. |
| const int kZygoteScore = 0; |
| // The minimum amount to bump a score by. This is large enough that |
| // even if it's translated into the old values, it will still go up |
| // by at least one. |
| const int kScoreBump = 100; |
| // This is the lowest score that renderers and extensions start with |
| // in the OomPriorityManager. |
| const int kRendererScore = chrome::kLowestRendererOomScore; |
| // For "miscellaneous" things, we want them after renderers, |
| // but before plugins. |
| const int kMiscScore = kRendererScore - kScoreBump; |
| // We want plugins to die after the renderers. |
| const int kPluginScore = kMiscScore - kScoreBump; |
| int score = -1; |
| |
| DCHECK(kMiscScore > 0); |
| DCHECK(kPluginScore > 0); |
| |
| if (process_type == switches::kPluginProcess || |
| process_type == switches::kPpapiPluginProcess) { |
| score = kPluginScore; |
| } else if (process_type == switches::kPpapiBrokerProcess) { |
| // The broker should be killed before the PPAPI plugin. |
| score = kPluginScore + kScoreBump; |
| } else if (process_type == switches::kUtilityProcess || |
| process_type == switches::kWorkerProcess || |
| process_type == switches::kGpuProcess || |
| process_type == switches::kServiceProcess) { |
| score = kMiscScore; |
| } else if (process_type == switches::kProfileImportProcess) { |
| NOTIMPLEMENTED(); |
| score = kZygoteScore; |
| #ifndef DISABLE_NACL |
| } else if (process_type == switches::kNaClLoaderProcess) { |
| score = kPluginScore; |
| #endif |
| } else if (process_type == switches::kZygoteProcess || |
| process_type.empty()) { |
| // For zygotes and unlabeled process types, we want to still make |
| // them killable by the OOM killer. |
| score = kZygoteScore; |
| } else if (process_type == switches::kExtensionProcess || |
| process_type == switches::kRendererProcess) { |
| LOG(WARNING) << "process type '" << process_type << "' " |
| << "should be created through the zygote."; |
| // When debugging, these process types can end up being run |
| // directly, but this isn't the typical path for assigning the OOM |
| // score for them. Still, we want to assign a score that is |
| // somewhat representative for debugging. |
| score = kRendererScore; |
| } else { |
| NOTREACHED() << "Unknown process type"; |
| } |
| if (score > -1) |
| base::AdjustOOMScore(base::GetCurrentProcId(), score); |
| } |
| #endif // defined(OS_LINUX) |
| |
| void SetupCRT(const CommandLine& command_line) { |
| #if defined(OS_WIN) |
| #if defined(_CRTDBG_MAP_ALLOC) |
| _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); |
| _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); |
| #else |
| if (!command_line.HasSwitch(switches::kDisableBreakpad)) { |
| _CrtSetReportMode(_CRT_ASSERT, 0); |
| } |
| #endif |
| #endif |
| } |
| |
| // Enable the heap profiler if the appropriate command-line switch is |
| // present, bailing out of the app we can't. |
| void EnableHeapProfiler(const CommandLine& command_line) { |
| #if defined(OS_WIN) |
| if (command_line.HasSwitch(switches::kMemoryProfiling)) |
| if (!LoadMemoryProfiler()) |
| exit(-1); |
| #endif |
| } |
| |
| void InitializeChromeContentRendererClient() { |
| #if !defined(NACL_WIN64) // We don't build the renderer code on win nacl64. |
| content::GetContentClient()->set_renderer( |
| &g_chrome_content_renderer_client.Get()); |
| #endif |
| } |
| |
| void InitializeChromeContentClient(const std::string& process_type) { |
| if (process_type == switches::kPluginProcess) { |
| content::GetContentClient()->set_plugin( |
| &g_chrome_content_plugin_client.Get()); |
| } else if (process_type == switches::kRendererProcess || |
| process_type == switches::kExtensionProcess) { |
| InitializeChromeContentRendererClient(); |
| } else if (process_type == switches::kUtilityProcess) { |
| #if !defined(NACL_WIN64) // We don't build this code on win nacl64. |
| content::GetContentClient()->set_utility( |
| &g_chrome_content_utility_client.Get()); |
| #endif |
| } |
| } |
| |
| void CommonSubprocessInit(const std::string& process_type) { |
| #if defined(OS_WIN) |
| // HACK: Let Windows know that we have started. This is needed to suppress |
| // the IDC_APPSTARTING cursor from being displayed for a prolonged period |
| // while a subprocess is starting. |
| PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0); |
| MSG msg; |
| PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); |
| #endif |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| // Various things break when you're using a locale where the decimal |
| // separator isn't a period. See e.g. bugs 22782 and 39964. For |
| // all processes except the browser process (where we call system |
| // APIs that may rely on the correct locale for formatting numbers |
| // when presenting them to the user), reset the locale for numeric |
| // formatting. |
| // Note that this is not correct for plugin processes -- they can |
| // surface UI -- but it's likely they get this wrong too so why not. |
| setlocale(LC_NUMERIC, "C"); |
| #endif |
| |
| #if defined(USE_LINUX_BREAKPAD) |
| // Needs to be called after we have chrome::DIR_USER_DATA. BrowserMain sets |
| // this up for the browser process in a different manner. Zygotes need to call |
| // InitCrashReporter() in RunZygote(). |
| if (process_type != switches::kZygoteProcess) |
| InitCrashReporter(); |
| #endif |
| |
| InitializeChromeContentClient(process_type); |
| } |
| |
| // Returns true if this subprocess type needs the ResourceBundle initialized |
| // and resources loaded. |
| bool SubprocessNeedsResourceBundle(const std::string& process_type) { |
| return |
| #if defined(OS_WIN) || defined(OS_MACOSX) |
| // Windows needs resources for the default/null plugin. |
| // Mac needs them for the plugin process name. |
| process_type == switches::kPluginProcess || |
| #endif |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| // The zygote process opens the resources for the renderers. |
| process_type == switches::kZygoteProcess || |
| #endif |
| process_type == switches::kRendererProcess || |
| process_type == switches::kExtensionProcess || |
| process_type == switches::kUtilityProcess; |
| } |
| |
| // Returns true if this process is a child of the browser process. |
| bool SubprocessIsBrowserChild(const std::string& process_type) { |
| if (process_type.empty() || |
| #if defined(OS_MACOSX) |
| process_type == switches::kRelauncherProcess || |
| #endif |
| process_type == switches::kServiceProcess) { |
| return false; |
| } |
| return true; |
| } |
| |
| #if defined(OS_MACOSX) |
| // Update the name shown in Activity Monitor so users are less likely to ask |
| // why Chrome has so many processes. |
| void SetMacProcessName(const std::string& process_type) { |
| // Don't worry about the browser process, its gets the stock name. |
| int name_id = 0; |
| if (process_type == switches::kRendererProcess) { |
| name_id = IDS_RENDERER_APP_NAME; |
| } else if (process_type == switches::kPluginProcess || |
| process_type == switches::kPpapiPluginProcess) { |
| name_id = IDS_PLUGIN_APP_NAME; |
| } else if (process_type == switches::kExtensionProcess) { |
| name_id = IDS_WORKER_APP_NAME; |
| } else if (process_type == switches::kUtilityProcess) { |
| name_id = IDS_UTILITY_APP_NAME; |
| } |
| if (name_id) { |
| NSString* app_name = l10n_util::GetNSString(name_id); |
| base::mac::SetProcessName(base::mac::NSToCFCast(app_name)); |
| } |
| } |
| |
| // Completes the Mach IPC handshake by sending this process' task port to the |
| // parent process. The parent is listening on the Mach port given by |
| // |GetMachPortName()|. The task port is used by the parent to get CPU/memory |
| // stats to display in the task manager. |
| void SendTaskPortToParentProcess() { |
| const mach_msg_timeout_t kTimeoutMs = 100; |
| const int32_t kMessageId = 0; |
| std::string mach_port_name = MachBroker::GetMachPortName(); |
| |
| base::MachSendMessage child_message(kMessageId); |
| if (!child_message.AddDescriptor(mach_task_self())) { |
| LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed."; |
| return; |
| } |
| |
| base::MachPortSender child_sender(mach_port_name.c_str()); |
| kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs); |
| if (err != KERN_SUCCESS) { |
| LOG(ERROR) << StringPrintf("child SendMessage() failed: 0x%x %s", err, |
| mach_error_string(err)); |
| } |
| } |
| #endif // defined(OS_MACOSX) |
| |
| void InitializeStatsTable(base::ProcessId browser_pid, |
| const CommandLine& command_line) { |
| // Initialize the Stats Counters table. With this initialized, |
| // the StatsViewer can be utilized to read counters outside of |
| // Chrome. These lines can be commented out to effectively turn |
| // counters 'off'. The table is created and exists for the life |
| // of the process. It is not cleaned up. |
| if (command_line.HasSwitch(switches::kEnableStatsTable) || |
| command_line.HasSwitch(switches::kEnableBenchmarking)) { |
| // NOTIMPLEMENTED: we probably need to shut this down correctly to avoid |
| // leaking shared memory regions on posix platforms. |
| std::string statsfile = |
| base::StringPrintf("%s-%u", |
| chrome::kStatsFilename, |
| static_cast<unsigned int>(browser_pid)); |
| base::StatsTable *stats_table = new base::StatsTable(statsfile, |
| chrome::kStatsMaxThreads, chrome::kStatsMaxCounters); |
| base::StatsTable::set_current(stats_table); |
| } |
| } |
| |
| #if defined(OS_POSIX) |
| // Check for --version and --product-version; return true if we encountered |
| // one of these switches and should exit now. |
| bool HandleVersionSwitches(const CommandLine& command_line) { |
| const chrome::VersionInfo version_info; |
| |
| #if !defined(OS_MACOSX) |
| if (command_line.HasSwitch(switches::kProductVersion)) { |
| printf("%s\n", version_info.Version().c_str()); |
| return true; |
| } |
| #endif |
| |
| if (command_line.HasSwitch(switches::kVersion)) { |
| printf("%s %s %s\n", |
| version_info.Name().c_str(), |
| version_info.Version().c_str(), |
| chrome::VersionInfo::GetVersionStringModifier().c_str()); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| #if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) |
| // Show the man page if --help or -h is on the command line. |
| void HandleHelpSwitches(const CommandLine& command_line) { |
| if (command_line.HasSwitch(switches::kHelp) || |
| command_line.HasSwitch(switches::kHelpShort)) { |
| FilePath binary(command_line.argv()[0]); |
| execlp("man", "man", binary.BaseName().value().c_str(), NULL); |
| PLOG(FATAL) << "execlp failed"; |
| } |
| } |
| #endif |
| |
| #endif // OS_POSIX |
| |
| // We dispatch to a process-type-specific FooMain() based on a command-line |
| // flag. This struct is used to build a table of (flag, main function) pairs. |
| struct MainFunction { |
| const char* name; |
| int (*function)(const MainFunctionParams&); |
| }; |
| |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| // On platforms that use the zygote, we have a special subset of |
| // subprocesses that are launched via the zygote. This function |
| // fills in some process-launching bits around ZygoteMain(). |
| // Returns the exit code of the subprocess. |
| int RunZygote(const MainFunctionParams& main_function_params) { |
| static const MainFunction kMainFunctions[] = { |
| { switches::kRendererProcess, RendererMain }, |
| { switches::kExtensionProcess, RendererMain }, |
| { switches::kWorkerProcess, WorkerMain }, |
| { switches::kPpapiPluginProcess, PpapiPluginMain }, |
| #if !defined(DISABLE_NACL) |
| { switches::kNaClLoaderProcess, NaClMain }, |
| #endif |
| }; |
| |
| // Each Renderer we spawn will re-attempt initialization of the media |
| // libraries, at which point failure will be detected and handled, so |
| // we do not need to cope with initialization failures here. |
| FilePath media_path; |
| if (PathService::Get(chrome::DIR_MEDIA_LIBS, &media_path)) |
| media::InitializeMediaLibrary(media_path); |
| |
| // This function call can return multiple times, once per fork(). |
| #if defined(DISABLE_NACL) |
| if (!ZygoteMain(main_function_params, NULL)) |
| return 1; |
| #else |
| NaClForkDelegate* nacl_delegate = new NaClForkDelegate(); |
| int rval = ZygoteMain(main_function_params, nacl_delegate); |
| if (nacl_delegate) |
| delete nacl_delegate; |
| if (!rval) |
| return 1; |
| #endif |
| |
| // Zygote::HandleForkRequest may have reallocated the command |
| // line so update it here with the new version. |
| const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| |
| // The StatsTable must be initialized in each process; we already |
| // initialized for the browser process, now we need to initialize |
| // within the new processes as well. |
| pid_t browser_pid = base::GetParentProcessId( |
| base::GetParentProcessId(base::GetCurrentProcId())); |
| InitializeStatsTable(browser_pid, command_line); |
| |
| MainFunctionParams main_params(command_line, |
| main_function_params.sandbox_info_, |
| main_function_params.autorelease_pool_); |
| // Get the new process type from the new command line. |
| std::string process_type = |
| command_line.GetSwitchValueASCII(switches::kProcessType); |
| |
| #if defined(USE_LINUX_BREAKPAD) |
| // Needs to be called after we have chrome::DIR_USER_DATA. BrowserMain sets |
| // this up for the browser process in a different manner. |
| InitCrashReporter(); |
| #endif |
| |
| InitializeChromeContentClient(process_type); |
| |
| for (size_t i = 0; i < arraysize(kMainFunctions); ++i) { |
| if (process_type == kMainFunctions[i].name) |
| return kMainFunctions[i].function(main_params); |
| } |
| |
| NOTREACHED() << "Unknown zygote process type: " << process_type; |
| return 1; |
| } |
| #endif // defined(OS_POSIX) && !defined(OS_MACOSX) |
| |
| // Run the FooMain() for a given process type. |
| // If |process_type| is empty, runs BrowserMain(). |
| // Returns the exit code for this process. |
| int RunNamedProcessTypeMain(const std::string& process_type, |
| const MainFunctionParams& main_function_params) { |
| static const MainFunction kMainFunctions[] = { |
| { "", BrowserMain }, |
| { switches::kRendererProcess, RendererMain }, |
| // An extension process is just a renderer process. We use a different |
| // command line argument to differentiate crash reports. |
| { switches::kExtensionProcess, RendererMain }, |
| { switches::kPluginProcess, PluginMain }, |
| { switches::kWorkerProcess, WorkerMain }, |
| { switches::kPpapiPluginProcess, PpapiPluginMain }, |
| { switches::kPpapiBrokerProcess, PpapiBrokerMain }, |
| { switches::kUtilityProcess, UtilityMain }, |
| { switches::kGpuProcess, GpuMain }, |
| { switches::kServiceProcess, ServiceProcessMain }, |
| |
| #if defined(OS_MACOSX) |
| // TODO(port): Use OOP profile import - http://crbug.com/22142 . |
| { switches::kProfileImportProcess, ProfileImportMain }, |
| { switches::kRelauncherProcess, mac_relauncher::internal::RelauncherMain }, |
| #endif |
| #if !defined(DISABLE_NACL) |
| { switches::kNaClLoaderProcess, NaClMain }, |
| #if defined(_WIN64) // The broker process is used only on Win64. |
| { switches::kNaClBrokerProcess, NaClBrokerMain }, |
| #endif |
| #endif // DISABLE_NACL |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| // Zygote startup is special -- see RunZygote comments above |
| // for why we don't use ZygoteMain directly. |
| { switches::kZygoteProcess, RunZygote }, |
| #endif |
| }; |
| |
| for (size_t i = 0; i < arraysize(kMainFunctions); ++i) { |
| if (process_type == kMainFunctions[i].name) |
| return kMainFunctions[i].function(main_function_params); |
| } |
| |
| NOTREACHED() << "Unknown process type: " << process_type; |
| return 1; |
| } |
| |
| } // namespace |
| |
| #if defined(OS_WIN) |
| DLLEXPORT int __cdecl ChromeMain(HINSTANCE instance, |
| sandbox::SandboxInterfaceInfo* sandbox_info, |
| TCHAR* command_line_unused) { |
| // argc/argv are ignored on Windows; see command_line.h for details. |
| int argc = 0; |
| char** argv = NULL; |
| #elif defined(OS_POSIX) |
| int ChromeMain(int argc, char** argv) { |
| // There is no HINSTANCE on non-Windows. |
| void* instance = NULL; |
| #endif |
| // LowLevelInit performs startup initialization before we |
| // e.g. allocate any memory. It must be the first call on startup. |
| chrome_main::LowLevelInit(instance); |
| |
| // The exit manager is in charge of calling the dtors of singleton objects. |
| base::AtExitManager exit_manager; |
| |
| // We need this pool for all the objects created before we get to the |
| // event loop, but we don't want to leave them hanging around until the |
| // app quits. Each "main" needs to flush this pool right before it goes into |
| // its main event loop to get rid of the cruft. |
| base::mac::ScopedNSAutoreleasePool autorelease_pool; |
| |
| #if defined(OS_CHROMEOS) |
| chromeos::BootTimesLoader::Get()->SaveChromeMainStats(); |
| #endif |
| |
| #if defined(OS_MACOSX) |
| chrome_main::SetUpBundleOverrides(); |
| chrome::common::mac::EnableCFBundleBlocker(); |
| #endif |
| |
| CommandLine::Init(argc, argv); |
| const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| |
| Profiling::ProcessStarted(); |
| |
| #if defined(OS_POSIX) |
| if (HandleVersionSwitches(command_line)) |
| return 0; // Got a --version switch; exit with a success error code. |
| #if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) |
| // This will directly exit if the user asked for help. |
| HandleHelpSwitches(command_line); |
| #endif |
| #endif // OS_POSIX |
| |
| std::string process_type = |
| command_line.GetSwitchValueASCII(switches::kProcessType); |
| |
| // If we are in diagnostics mode this is the end of the line. After the |
| // diagnostics are run the process will invariably exit. |
| if (command_line.HasSwitch(switches::kDiagnostics)) { |
| return DiagnosticsMain(command_line); |
| } |
| |
| #if defined(OS_WIN) |
| // Must do this before any other usage of command line! |
| if (HasDeprecatedArguments(command_line.GetCommandLineString())) |
| return 1; |
| #endif |
| |
| #if defined(OS_MACOSX) |
| // We need to allocate the IO Ports before the Sandbox is initialized or |
| // the first instance of SystemMonitor is created. |
| // It's important not to allocate the ports for processes which don't register |
| // with the system monitor - see crbug.com/88867. |
| if (process_type.empty() || |
| process_type == switches::kExtensionProcess || |
| process_type == switches::kNaClLoaderProcess || |
| process_type == switches::kPluginProcess || |
| process_type == switches::kRendererProcess || |
| process_type == switches::kUtilityProcess || |
| process_type == switches::kWorkerProcess) { |
| base::SystemMonitor::AllocateSystemIOPorts(); |
| } |
| #endif |
| |
| base::ProcessId browser_pid = base::GetCurrentProcId(); |
| if (SubprocessIsBrowserChild(process_type)) { |
| #if defined(OS_WIN) || defined(OS_MACOSX) |
| std::string channel_name = |
| command_line.GetSwitchValueASCII(switches::kProcessChannelID); |
| |
| int browser_pid_int; |
| base::StringToInt(channel_name, &browser_pid_int); |
| browser_pid = static_cast<base::ProcessId>(browser_pid_int); |
| DCHECK_NE(browser_pid_int, 0); |
| #elif defined(OS_POSIX) |
| // On linux, we're in the zygote here; so we need the parent process' id. |
| browser_pid = base::GetParentProcessId(base::GetCurrentProcId()); |
| #endif |
| |
| #if defined(OS_MACOSX) |
| SendTaskPortToParentProcess(); |
| #endif |
| } |
| |
| #if defined(OS_POSIX) |
| if (!process_type.empty()) { |
| // When you hit Ctrl-C in a terminal running the browser |
| // process, a SIGINT is delivered to the entire process group. |
| // When debugging the browser process via gdb, gdb catches the |
| // SIGINT for the browser process (and dumps you back to the gdb |
| // console) but doesn't for the child processes, killing them. |
| // The fix is to have child processes ignore SIGINT; they'll die |
| // on their own when the browser process goes away. |
| // |
| // Note that we *can't* rely on BeingDebugged to catch this case because we |
| // are the child process, which is not being debugged. |
| // TODO(evanm): move this to some shared subprocess-init function. |
| if (!base::debug::BeingDebugged()) |
| signal(SIGINT, SIG_IGN); |
| } |
| #endif |
| |
| SetupCRT(command_line); |
| |
| #if defined(USE_NSS) |
| crypto::EarlySetupForNSSInit(); |
| #endif |
| |
| // Initialize the Chrome path provider. |
| ui::RegisterPathProvider(); |
| chrome::RegisterPathProvider(); |
| content::RegisterPathProvider(); |
| |
| #if defined(OS_MACOSX) |
| // On the Mac, the child executable lives at a predefined location within |
| // the app bundle's versioned directory. |
| PathService::Override(content::CHILD_PROCESS_EXE, |
| chrome::GetVersionedDirectory(). |
| Append(chrome::kHelperProcessExecutablePath)); |
| #endif |
| |
| // Initialize the content client which that code uses to talk to Chrome. |
| chrome::ChromeContentClient chrome_content_client; |
| content::SetContentClient(&chrome_content_client); |
| |
| // Notice a user data directory override if any |
| FilePath user_data_dir = |
| command_line.GetSwitchValuePath(switches::kUserDataDir); |
| chrome_main::CheckUserDataDirPolicy(&user_data_dir); |
| if (!user_data_dir.empty()) |
| CHECK(PathService::Override(chrome::DIR_USER_DATA, user_data_dir)); |
| |
| #if defined(OS_MACOSX) |
| // TODO(mark): Right now, InitCrashReporter() needs to be called after |
| // CommandLine::Init() and chrome::RegisterPathProvider(). Ideally, Breakpad |
| // initialization could occur sooner, preferably even before the framework |
| // dylib is even loaded, to catch potential early crashes. |
| InitCrashReporter(); |
| |
| #if defined(NDEBUG) |
| bool is_debug_build = false; |
| #else |
| bool is_debug_build = true; |
| #endif |
| |
| // Details on when we enable Apple's Crash reporter. |
| // |
| // Motivation: |
| // In debug mode it takes Apple's crash reporter eons to generate a crash |
| // dump. |
| // |
| // What we do: |
| // * We only pass crashes for foreground processes to Apple's Crash reporter. |
| // At the time of this writing, that means just the Browser process. |
| // * If Breakpad is enabled, it will pass browser crashes to Crash Reporter |
| // itself. |
| // * If Breakpad is disabled, we only turn on Crash Reporter for the |
| // Browser process in release mode. |
| if (!command_line.HasSwitch(switches::kDisableBreakpad)) { |
| bool disable_apple_crash_reporter = is_debug_build |
| || base::mac::IsBackgroundOnlyProcess(); |
| if (!IsCrashReporterEnabled() && disable_apple_crash_reporter) { |
| base::mac::DisableOSCrashDumps(); |
| } |
| } |
| |
| // Mac Chrome is packaged with a main app bundle and a helper app bundle. |
| // The main app bundle should only be used for the browser process, so it |
| // should never see a --type switch (switches::kProcessType). Likewise, |
| // the helper should always have a --type switch. |
| // |
| // This check is done this late so there is already a call to |
| // base::mac::IsBackgroundOnlyProcess(), so there is no change in |
| // startup/initialization order. |
| |
| // The helper's Info.plist marks it as a background only app. |
| if (base::mac::IsBackgroundOnlyProcess()) { |
| CHECK(command_line.HasSwitch(switches::kProcessType)) |
| << "Helper application requires --type."; |
| } else { |
| CHECK(!command_line.HasSwitch(switches::kProcessType)) |
| << "Main application forbids --type, saw \"" << process_type << "\"."; |
| } |
| |
| if (IsCrashReporterEnabled()) |
| InitCrashProcessInfo(); |
| #endif // defined(OS_MACOSX) |
| |
| InitializeStatsTable(browser_pid, command_line); |
| |
| base::StatsScope<base::StatsCounterTimer> |
| startup_timer(content::Counters::chrome_main()); |
| |
| // Enable the heap profiler as early as possible! |
| EnableHeapProfiler(command_line); |
| |
| // Enable Message Loop related state asap. |
| if (command_line.HasSwitch(switches::kMessageLoopHistogrammer)) |
| MessageLoop::EnableHistogrammer(true); |
| |
| // Single-process is an unsupported and not fully tested mode, so |
| // don't enable it for official Chrome builds. |
| #if !defined(GOOGLE_CHROME_BUILD) |
| if (command_line.HasSwitch(switches::kSingleProcess)) { |
| RenderProcessHost::set_run_renderer_in_process(true); |
| #if defined(OS_MACOSX) |
| // TODO(port-mac): This is from renderer_main_platform_delegate.cc. |
| // shess tried to refactor things appropriately, but it sprawled out |
| // of control because different platforms needed different styles of |
| // initialization. Try again once we understand the process |
| // architecture needed and where it should live. |
| InitWebCoreSystemInterface(); |
| #endif |
| |
| InitializeChromeContentRendererClient(); |
| } |
| #endif // GOOGLE_CHROME_BUILD |
| |
| bool icu_result = icu_util::Initialize(); |
| CHECK(icu_result); |
| |
| logging::OldFileDeletionState file_state = |
| logging::APPEND_TO_OLD_LOG_FILE; |
| if (process_type.empty()) { |
| file_state = logging::DELETE_OLD_LOG_FILE; |
| } |
| logging::InitChromeLogging(command_line, file_state); |
| |
| // Register internal Chrome schemes so they'll be parsed correctly. This must |
| // happen before we process any URLs with the affected schemes, and must be |
| // done in all processes that work with these URLs (i.e. including renderers). |
| chrome::RegisterChromeSchemes(); |
| |
| #if defined(OS_WIN) |
| // TODO(darin): Kill this once http://crbug.com/52609 is fixed. |
| ResourceBundle::SetResourcesDataDLL(_AtlBaseModule.GetResourceInstance()); |
| #endif |
| |
| if (SubprocessNeedsResourceBundle(process_type)) { |
| // Initialize ResourceBundle which handles files loaded from external |
| // sources. The language should have been passed in to us from the |
| // browser process as a command line flag. |
| DCHECK(command_line.HasSwitch(switches::kLang) || |
| process_type == switches::kZygoteProcess); |
| |
| // TODO(markusheintz): The command line flag --lang is actually processed |
| // by the CommandLinePrefStore, and made available through the PrefService |
| // via the preference prefs::kApplicationLocale. The browser process uses |
| // the --lang flag to pass the value of the PrefService in here. Maybe this |
| // value could be passed in a different way. |
| const std::string locale = |
| command_line.GetSwitchValueASCII(switches::kLang); |
| const std::string loaded_locale = |
| ResourceBundle::InitSharedInstance(locale); |
| CHECK(!loaded_locale.empty()) << "Locale could not be found for " << locale; |
| |
| #if defined(OS_MACOSX) |
| // Update the process name (need resources to get the strings, so |
| // only do this when ResourcesBundle has been initialized). |
| SetMacProcessName(process_type); |
| #endif // defined(OS_MACOSX) |
| } |
| |
| if (!process_type.empty()) |
| CommonSubprocessInit(process_type); |
| |
| #if defined(OS_CHROMEOS) |
| { |
| // Read and cache ChromeOS version from file, |
| // to be used from inside the sandbox. |
| int32 major_version, minor_version, bugfix_version; |
| base::SysInfo::OperatingSystemVersionNumbers( |
| &major_version, &minor_version, &bugfix_version); |
| } |
| #endif |
| |
| // Initialize the sandbox for this process. |
| SandboxInitWrapper sandbox_wrapper; |
| bool initialize_sandbox = true; |
| |
| #if defined(OS_WIN) |
| sandbox_wrapper.SetServices(sandbox_info); |
| #elif defined(OS_MACOSX) |
| // On OS X the renderer sandbox needs to be initialized later in the startup |
| // sequence in RendererMainPlatformDelegate::EnableSandbox(). |
| // Same goes for NaClLoader, in NaClMainPlatformDelegate::EnableSandbox(). |
| if (process_type == switches::kRendererProcess || |
| process_type == switches::kExtensionProcess || |
| process_type == switches::kNaClLoaderProcess || |
| process_type == switches::kPpapiPluginProcess || |
| process_type == switches::kRelauncherProcess) { |
| initialize_sandbox = false; |
| } |
| #endif |
| |
| if (initialize_sandbox) { |
| bool sandbox_initialized_ok = |
| sandbox_wrapper.InitializeSandbox(command_line, process_type); |
| // Die if the sandbox can't be enabled. |
| CHECK(sandbox_initialized_ok) << "Error initializing sandbox for " |
| << process_type; |
| } |
| |
| startup_timer.Stop(); // End of Startup Time Measurement. |
| |
| MainFunctionParams main_params(command_line, sandbox_wrapper, |
| &autorelease_pool); |
| |
| // Note: If you are adding a new process type below, be sure to adjust the |
| // AdjustLinuxOOMScore function too. |
| #if defined(OS_LINUX) |
| AdjustLinuxOOMScore(process_type); |
| #endif |
| |
| #if defined(OS_POSIX) |
| SetProcessTitleFromCommandLine(argv); |
| #endif |
| |
| int exit_code = RunNamedProcessTypeMain(process_type, main_params); |
| |
| if (SubprocessNeedsResourceBundle(process_type)) |
| ResourceBundle::CleanupSharedInstance(); |
| |
| logging::CleanupChromeLogging(); |
| |
| chrome_main::LowLevelShutdown(); |
| |
| return exit_code; |
| } |