diff options
author | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-28 17:27:17 +0000 |
---|---|---|
committer | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-28 17:27:17 +0000 |
commit | 79b663c6e6ddf89e85cdc566b5d4f368465bb858 (patch) | |
tree | 868c35191ee02c8bd7da15b69edc7114007e211a /chrome_frame | |
parent | 84b5647e4a43a55b9b0e8b211684f48222569218 (diff) | |
download | chromium_src-79b663c6e6ddf89e85cdc566b5d4f368465bb858.zip chromium_src-79b663c6e6ddf89e85cdc566b5d4f368465bb858.tar.gz chromium_src-79b663c6e6ddf89e85cdc566b5d4f368465bb858.tar.bz2 |
Rewrite of chrome_launcher.exe. ETW-based perf tests suggest that this is on average about 50% faster than the previous version.
Review URL: http://codereview.chromium.org/2278003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@48500 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame')
-rw-r--r-- | chrome_frame/chrome_frame.gyp | 44 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.cc | 2 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_launcher.gyp | 84 | ||||
-rw-r--r-- | chrome_frame/chrome_launcher.cc | 270 | ||||
-rw-r--r-- | chrome_frame/chrome_launcher.h | 45 | ||||
-rw-r--r-- | chrome_frame/chrome_launcher_main.cc | 146 | ||||
-rw-r--r-- | chrome_frame/chrome_launcher_unittest.cc | 83 | ||||
-rw-r--r-- | chrome_frame/chrome_launcher_utils.cc | 68 | ||||
-rw-r--r-- | chrome_frame/chrome_launcher_utils.h | 32 | ||||
-rw-r--r-- | chrome_frame/chrome_tab.cc | 2 | ||||
-rw-r--r-- | chrome_frame/chrome_tab.def | 1 |
11 files changed, 516 insertions, 261 deletions
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp index 947769a..141a88e 100644 --- a/chrome_frame/chrome_frame.gyp +++ b/chrome_frame/chrome_frame.gyp @@ -137,6 +137,8 @@ 'chrome_frame_histograms.cc', 'chrome_frame_npapi_unittest.cc', 'chrome_frame_unittest_main.cc', + 'chrome_launcher.cc', + 'chrome_launcher.h', 'chrome_launcher_unittest.cc', 'function_stub_unittest.cc', 'test/chrome_frame_test_utils.h', @@ -498,7 +500,7 @@ 'chrome_frame_plugin.h', 'chrome_frame_npapi.cc', 'chrome_frame_npapi.h', - 'custom_sync_call_context.h', + 'custom_sync_call_context.h', 'ff_30_privilege_check.cc', 'ff_privilege_check.h', 'html_utils.cc', @@ -525,40 +527,6 @@ ], }, { - 'target_name': 'chrome_launcher', - 'type': 'executable', - 'msvs_guid': 'B7E540C1-49D9-4350-ACBC-FB8306316D16', - 'dependencies': [], - 'sources': [ - 'chrome_launcher_main.cc', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'OutputFile': - '$(OutDir)\\servers\\$(ProjectName).exe', - # Set /SUBSYSTEM:WINDOWS since this is not a command-line program. - 'SubSystem': '2', - # We're going for minimal size, so no standard library (in release - # builds). - 'IgnoreAllDefaultLibraries': "true", - }, - 'VCCLCompilerTool': { - # Requires standard library, so disable it. - 'BufferSecurityCheck': "false", - }, - }, - 'configurations': { - # Bring back the standard library in debug buidls. - 'Debug_Base': { - 'msvs_settings': { - 'VCLinkerTool': { - 'IgnoreAllDefaultLibraries': "false", - }, - }, - }, - }, - }, - { 'target_name': 'chrome_frame_strings', 'type': 'none', 'rules': [ @@ -642,8 +610,8 @@ 'chrome_frame_reporting.h', 'chrome_imported_resources.cc', 'chrome_imported_resources.h', - 'chrome_launcher.cc', - 'chrome_launcher.h', + 'chrome_launcher_utils.cc', + 'chrome_launcher_utils.h', 'chrome_protocol.cc', 'chrome_protocol.h', 'chrome_protocol.rgs', @@ -752,8 +720,8 @@ 'chrome_frame_npapi', 'chrome_frame_strings', 'chrome_frame_utils', - 'chrome_launcher', 'xulrunner_sdk', + 'chrome_frame_launcher.gyp:chrome_launcher', '../chrome/chrome.gyp:chrome_version_info', '../chrome/chrome.gyp:chrome_version_header', '../chrome/chrome.gyp:common', diff --git a/chrome_frame/chrome_frame_automation.cc b/chrome_frame/chrome_frame_automation.cc index 33ab786..9f58181 100644 --- a/chrome_frame/chrome_frame_automation.cc +++ b/chrome_frame/chrome_frame_automation.cc @@ -22,7 +22,7 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/automation/tab_proxy.h" -#include "chrome_frame/chrome_launcher.h" +#include "chrome_frame/chrome_launcher_utils.h" #include "chrome_frame/custom_sync_call_context.h" #include "chrome_frame/utils.h" diff --git a/chrome_frame/chrome_frame_launcher.gyp b/chrome_frame/chrome_frame_launcher.gyp new file mode 100644 index 0000000..1466399 --- /dev/null +++ b/chrome_frame/chrome_frame_launcher.gyp @@ -0,0 +1,84 @@ +# Copyright (c) 2010 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+
+ # Keep the archive builder happy.
+ 'chrome_personalization%': 1,
+ 'use_syncapi_stub%': 0,
+
+ 'conditions': [
+ ['OS=="win"', {
+ 'python': [
+ '<(DEPTH)\\third_party\\python_24\\setup_env.bat && python'
+ ],
+ }, { # OS != win
+ 'python': [
+ 'python'
+ ],
+ }],
+ ],
+ },
+ 'includes': [
+ '../build/common.gypi',
+ ],
+ 'target_defaults': {
+ 'include_dirs': [
+ # all our own includes are relative to src/
+ '..',
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'chrome_launcher',
+ 'type': 'executable',
+ 'msvs_guid': 'B7E540C1-49D9-4350-ACBC-FB8306316D16',
+ 'dependencies': [
+ '../breakpad/breakpad.gyp:breakpad_handler',
+ ],
+ 'sources': [
+ 'chrome_launcher_main.cc',
+ 'chrome_launcher.cc',
+ 'chrome_launcher.h',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'OutputFile':
+ '$(OutDir)\\servers\\$(ProjectName).exe',
+ # Set /SUBSYSTEM:WINDOWS since this is not a command-line program.
+ 'SubSystem': '2',
+ 'AdditionalDependencies': [
+ 'shlwapi.lib',
+ ],
+
+ },
+ },
+ 'configurations': {
+ 'Release_Base': {
+ # Set flags to unconditionally optimize chrome_frame_launcher.exe
+ # for release builds.
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'LinkTimeCodeGeneration': '1',
+ },
+ 'VCCLCompilerTool': {
+ 'Optimization': '3',
+ 'InlineFunctionExpansion': '2',
+ 'EnableIntrinsicFunctions': 'true',
+ 'FavorSizeOrSpeed': '2',
+ 'OmitFramePointers': 'true',
+ 'EnableFiberSafeOptimizations': 'true',
+ 'WholeProgramOptimization': 'true',
+ },
+ 'VCLibrarianTool': {
+ 'AdditionalOptions': ['/ltcg', '/expectedoutputsize:120000000'],
+ },
+ },
+ },
+ },
+ },
+ ],
+}
\ No newline at end of file diff --git a/chrome_frame/chrome_launcher.cc b/chrome_frame/chrome_launcher.cc index 34f1e9f..930b0c0 100644 --- a/chrome_frame/chrome_launcher.cc +++ b/chrome_frame/chrome_launcher.cc @@ -1,140 +1,200 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2009 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_frame/chrome_launcher.h" -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/file_util.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/win_util.h" -#include "chrome/common/chrome_constants.h" -#include "chrome/common/chrome_switches.h" -#include "chrome_frame/chrome_frame_automation.h" -#include "chrome_frame/chrome_frame_reporting.h" +#include <windows.h> +#include <shellapi.h> +#include <shlwapi.h> -namespace chrome_launcher { - -const wchar_t kLauncherExeBaseName[] = L"chrome_launcher.exe"; +// Herein lies stuff selectively stolen from Chrome. We don't pull it in +// directly because all of it results in many things we don't want being +// included as well. +namespace { // These are the switches we will allow (along with their values) in the // safe-for-Low-Integrity version of the Chrome command line. -const char* kAllowedSwitches[] = { - switches::kAutomationClientChannelID, - switches::kChromeFrame, - switches::kEnableRendererAccessibility, - switches::kEnableExperimentalExtensionApis, - switches::kNoDefaultBrowserCheck, - switches::kNoErrorDialogs, - switches::kNoFirstRun, - switches::kUserDataDir, - switches::kDisablePopupBlocking, - switches::kFullMemoryCrashReport, +// Including the chrome switch files pulls in a bunch of dependencies sadly, so +// we redefine things here: +const wchar_t* kAllowedSwitches[] = { + L"automation-channel", + L"chrome-frame", + L"enable-renderer-accessibility", + L"enable-experimental-extension-apis", + L"no-default-browser-check", + L"noerrdialogs", + L"no-first-run", + L"user-data-dir", + L"disable-popup-blocking", + L"full-memory-crash-report", }; -CommandLine* CreateLaunchCommandLine() { - // Shortcut for OS versions that don't need the integrity broker. - if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) { - return new CommandLine(GetChromeExecutablePath()); - } +const wchar_t kWhitespaceChars[] = { + 0x0009, /* <control-0009> to <control-000D> */ + 0x000A, + 0x000B, + 0x000C, + 0x000D, + 0x0020, /* Space */ + 0x0085, /* <control-0085> */ + 0x00A0, /* No-Break Space */ + 0x1680, /* Ogham Space Mark */ + 0x180E, /* Mongolian Vowel Separator */ + 0x2000, /* En Quad to Hair Space */ + 0x2001, + 0x2002, + 0x2003, + 0x2004, + 0x2005, + 0x2006, + 0x2007, + 0x2008, + 0x2009, + 0x200A, + 0x200C, /* Zero Width Non-Joiner */ + 0x2028, /* Line Separator */ + 0x2029, /* Paragraph Separator */ + 0x202F, /* Narrow No-Break Space */ + 0x205F, /* Medium Mathematical Space */ + 0x3000, /* Ideographic Space */ + 0 +}; + +const wchar_t kLauncherExeBaseName[] = L"chrome_launcher.exe"; +const wchar_t kBrowserProcessExecutableName[] = L"chrome.exe"; + +} // end namespace - // The launcher EXE will be in the same directory as the Chrome Frame DLL, - // so create a full path to it based on this assumption. Since our unit - // tests also use this function, and live in the directory above, we test - // existence of the file and try the path that includes the /servers/ - // directory if needed. - FilePath module_path; - if (PathService::Get(base::FILE_MODULE, &module_path)) { - FilePath current_dir = module_path.DirName(); - FilePath same_dir_path = current_dir.Append(kLauncherExeBaseName); - if (file_util::PathExists(same_dir_path)) { - return new CommandLine(same_dir_path); - } else { - FilePath servers_path = - current_dir.Append(L"servers").Append(kLauncherExeBaseName); - DCHECK(file_util::PathExists(servers_path)) << - "What module is this? It's not in 'servers' or main output dir."; - return new CommandLine(servers_path); + +namespace chrome_launcher { + +std::wstring TrimWhiteSpace(const wchar_t* input_str) { + std::wstring output; + if (input_str != NULL) { + std::wstring str(input_str); + + const std::wstring::size_type first_good_char = + str.find_first_not_of(kWhitespaceChars); + const std::wstring::size_type last_good_char = + str.find_last_not_of(kWhitespaceChars); + + if (first_good_char != std::wstring::npos && + last_good_char != std::wstring::npos && + last_good_char >= first_good_char) { + // + 1 because find_last_not_of returns the index, and we want the count + output = str.substr(first_good_char, + last_good_char - first_good_char + 1); } - } else { - NOTREACHED(); - return NULL; } + + return output; } -void SanitizeCommandLine(const CommandLine& original, CommandLine* sanitized) { - size_t num_sanitized_switches = 0; +bool IsValidArgument(const std::wstring& arg) { + if (arg.length() < 2) { + return false; + } + for (int i = 0; i < arraysize(kAllowedSwitches); ++i) { - const char* current_switch = kAllowedSwitches[i]; - if (original.HasSwitch(current_switch)) { - ++num_sanitized_switches; - std::wstring switch_value = original.GetSwitchValue(current_switch); - if (0 == switch_value.length()) { - sanitized->AppendSwitch(current_switch); - } else { - sanitized->AppendSwitchWithValue(current_switch, switch_value); + size_t arg_length = lstrlenW(kAllowedSwitches[i]); + if (arg.find(kAllowedSwitches[i], 2) == 2) { + // The argument starts off right, now it must either end here, or be + // followed by an equals sign. + if (arg.length() == (arg_length + 2) || + (arg.length() > (arg_length + 2) && arg[arg_length+2] == L'=')) { + return true; } } } - if (num_sanitized_switches != original.GetSwitchCount()) { - NOTREACHED(); - LOG(ERROR) << "Original command line from Low Integrity had switches " - << "that are not on our whitelist."; - } + + return false; } -bool SanitizeAndLaunchChrome(const wchar_t* command_line) { - std::wstring command_line_with_program(L"dummy.exe "); - command_line_with_program += command_line; - CommandLine original = CommandLine::FromString(command_line_with_program); - CommandLine sanitized(GetChromeExecutablePath()); - SanitizeCommandLine(original, &sanitized); +bool IsValidCommandLine(const wchar_t* command_line) { + if (command_line == NULL) { + return false; + } - DLOG(INFO) << sanitized.command_line_string(); - sanitized.AppendSwitchWithValue("log-level", "0"); + int num_args = 0; + wchar_t** args = NULL; + args = CommandLineToArgvW(command_line, &num_args); + + bool success = true; + // Note that we skip args[0] since that is just our executable name and + // doesn't get passed through to Chrome. + for (int i = 1; i < num_args; ++i) { + std::wstring trimmed_arg = TrimWhiteSpace(args[i]); + if (!IsValidArgument(trimmed_arg.c_str())) { + success = false; + break; + } + } - return base::LaunchApp(sanitized.command_line_string(), false, false, NULL); + return success; } -FilePath GetChromeExecutablePath() { - FilePath cur_path; - PathService::Get(base::DIR_MODULE, &cur_path); - cur_path = cur_path.Append(chrome::kBrowserProcessExecutableName); - - // The installation model for Chrome places the DLLs in a versioned - // sub-folder one down from the Chrome executable. If we fail to find - // chrome.exe in the current path, try looking one up and launching that - // instead. - if (!file_util::PathExists(cur_path)) { - PathService::Get(base::DIR_MODULE, &cur_path); - cur_path = cur_path.DirName().Append(chrome::kBrowserProcessExecutableName); +bool SanitizeAndLaunchChrome(const wchar_t* command_line) { + bool success = false; + if (IsValidCommandLine(command_line)) { + std::wstring chrome_path; + if (GetChromeExecutablePath(&chrome_path)) { + const wchar_t* args = PathGetArgs(command_line); + if (args != NULL) { + chrome_path += L" "; + chrome_path += args; + } + + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags = STARTF_USESHOWWINDOW; + startup_info.wShowWindow = SW_SHOW; + PROCESS_INFORMATION process_info = {0}; + if (CreateProcess(NULL, &chrome_path[0], + NULL, NULL, FALSE, 0, NULL, NULL, + &startup_info, &process_info)) { + // Close handles. + CloseHandle(process_info.hThread); + CloseHandle(process_info.hProcess); + success = true; + } else { + _ASSERT(FALSE); + } + } } - return cur_path; + return success; } -} // namespace chrome_launcher - -// Entrypoint that implements the logic of chrome_launcher.exe. -int CALLBACK CfLaunchChrome() { - int result = ERROR_OPEN_FAILED; +bool GetChromeExecutablePath(std::wstring* chrome_path) { + _ASSERT(chrome_path); + + wchar_t cur_path[MAX_PATH * 4] = {0}; + // Assume that we are always built into an exe. + GetModuleFileName(NULL, cur_path, arraysize(cur_path) / 2); + + PathRemoveFileSpec(cur_path); + + bool success = false; + if (PathAppend(cur_path, kBrowserProcessExecutableName)) { + if (!PathFileExists(cur_path)) { + // The installation model for Chrome places the DLLs in a versioned + // sub-folder one down from the Chrome executable. If we fail to find + // chrome.exe in the current path, try looking one up and launching that + // instead. In practice, that means we back up two and append the + // executable name again. + PathRemoveFileSpec(cur_path); + PathRemoveFileSpec(cur_path); + PathAppend(cur_path, kBrowserProcessExecutableName); + } - if (chrome_launcher::SanitizeAndLaunchChrome(::GetCommandLine())) { - result = ERROR_SUCCESS; + if (PathFileExists(cur_path)) { + *chrome_path = cur_path; + success = true; + } } - // Regardless of what just happened, shut down crash reporting now to avoid a - // hang when we are unloaded. - ShutdownCrashReporting(); - - return result; + return success; } -// Compile-time check to see that the type CfLaunchChromeProc is correct. -#ifndef NODEBUG -namespace { -chrome_launcher::CfLaunchChromeProc cf_launch_chrome = CfLaunchChrome; -} // namespace -#endif // NODEBUG +} // namespace chrome_launcher diff --git a/chrome_frame/chrome_launcher.h b/chrome_frame/chrome_launcher.h index 86dc03f..da2540e 100644 --- a/chrome_frame/chrome_launcher.h +++ b/chrome_frame/chrome_launcher.h @@ -7,40 +7,45 @@ #include <string> -#include "base/file_path.h" +// arraysize macro shamelessly stolen from base\basictypes.h +template <typename T, size_t N> +char (&ArraySizeHelper(T (&array)[N]))[N]; -class CommandLine; +#define arraysize(array) (sizeof(ArraySizeHelper(array))) namespace chrome_launcher { // The base name of the chrome_launcher.exe file. extern const wchar_t kLauncherExeBaseName[]; -// Creates a command line suitable for launching Chrome. You can add any -// flags needed before launching. -// -// The command-line may use the Chrome executable directly, or use an in-between -// process if needed for security/elevation purposes. You must delete the -// returned command line. -CommandLine* CreateLaunchCommandLine(); - -// Fills in a new command line from the flags on this process's command line -// that we are allowing Low Integrity to invoke. -// -// Logs a warning for any flags that were passed that are not allowed to be -// invoked by Low Integrity. -void SanitizeCommandLine(const CommandLine& original, CommandLine* sanitized); +// Returns true if command_line contains only flags that we allow through. +// Returns false if command_line contains any unrecognized flags. +bool IsValidCommandLine(const wchar_t* command_line); // Given a command-line without an initial program part, launch our associated // chrome.exe with a sanitized version of that command line. Returns true iff // successful. bool SanitizeAndLaunchChrome(const wchar_t* command_line); -// Returns the full path to the Chrome executable. -FilePath GetChromeExecutablePath(); +// Returns a pointer to the position in command_line the string right after the +// name of the executable. This is equivalent to the second element of the +// array returned by CommandLineToArgvW. Returns NULL if there are no further +// arguments. +const wchar_t* GetArgumentsStart(const wchar_t* command_line); -// The type of the CfLaunchChrome entrypoint exported from this DLL. -typedef int (__stdcall *CfLaunchChromeProc)(); +// Returns the full path to the Chrome executable. +bool GetChromeExecutablePath(std::wstring* chrome_path); + +// Returns whether a given argument is considered a valid flag. Only accepts +// flags of the forms: +// --foo +// --foo=bar +bool IsValidArgument(const std::wstring& argument); + +// Returns a string that is equivalent in input_str without any leading +// or trailing whitespace. Returns an empty string if input_str contained only +// whitespace. +std::wstring TrimWhiteSpace(const wchar_t* input_str); } // namespace chrome_launcher diff --git a/chrome_frame/chrome_launcher_main.cc b/chrome_frame/chrome_launcher_main.cc index 8eaedae..e682675 100644 --- a/chrome_frame/chrome_launcher_main.cc +++ b/chrome_frame/chrome_launcher_main.cc @@ -2,78 +2,92 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. + #include <windows.h> +#include <DbgHelp.h> +#include <string> #include "chrome_frame/chrome_launcher.h" +#include "breakpad/src/client/windows/handler/exception_handler.h" + +// TODO(robertshield): Much of the crash report init code is shared with CF and +// probably Chrome too. If this pans out, consider refactoring it into a +// common lib. + +namespace { + +// Possible names for Pipes: +// Headless (testing) mode: "NamedPipe\ChromeCrashServices" +// System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18" +// Per-user install: "NamedPipe\GoogleCrashServices\<user SID>" +const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; +const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; +const wchar_t kSystemPrincipalSid[] = L"S-1-5-18"; + +// Assume this implies headless mode and use kChromePipeName if it shows +// up in the command line. +const wchar_t kFullMemoryCrashReport[] = L"full-memory-crash-report"; + +const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithUnloadedModules | // Get unloaded modules when available. + MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. + +} // namespace + +google_breakpad::CustomClientInfo* GetCustomInfo() { + // TODO(robertshield): Populate this with actual data. + std::wstring product(L"ChromeFrame"); + std::wstring version(L"0.1.0.0"); + static google_breakpad::CustomInfoEntry ver_entry(L"ver", version.c_str()); + static google_breakpad::CustomInfoEntry prod_entry(L"prod", product.c_str()); + static google_breakpad::CustomInfoEntry plat_entry(L"plat", L"Win32"); + static google_breakpad::CustomInfoEntry type_entry(L"ptype", L"chrome_frame"); + static google_breakpad::CustomInfoEntry entries[] = { + ver_entry, prod_entry, plat_entry, type_entry }; + static google_breakpad::CustomClientInfo custom_info = { + entries, arraysize(entries) }; + return &custom_info; +} + +google_breakpad::ExceptionHandler* InitializeCrashReporting( + const wchar_t* cmd_line) { + if (cmd_line == NULL) { + return NULL; + } + + wchar_t temp_path[MAX_PATH + 1] = {0}; + DWORD path_len = ::GetTempPath(MAX_PATH, temp_path); + + std::wstring pipe_name; + if (wcsstr(cmd_line, kFullMemoryCrashReport) != NULL) { + pipe_name = kChromePipeName; + } else { + // TODO(robertshield): Figure out if we're a per-user install and connect + // to the per-user named pipe instead. + pipe_name = kGoogleUpdatePipeName; + pipe_name += kSystemPrincipalSid; + } + + google_breakpad::ExceptionHandler* breakpad = + new google_breakpad::ExceptionHandler( + temp_path, NULL, NULL, NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL, kLargerDumpType, + pipe_name.c_str(), GetCustomInfo()); + + return breakpad; +} -// We want to keep this EXE tiny, so we avoid all dependencies and link to no -// libraries, and we do not use the C runtime. -// -// To catch errors in debug builds, we define an extremely simple assert macro. -#ifndef NDEBUG -#define CLM_ASSERT(x) do { if (!(x)) { ::DebugBreak(); } } while (false) -#else -#define CLM_ASSERT(x) -#endif // NDEBUG - -// In release builds, we skip the standard library completely to minimize -// size. This is more work in debug builds, and unnecessary, hence the -// different signatures. -#ifndef NDEBUG int APIENTRY wWinMain(HINSTANCE, HINSTANCE, wchar_t*, int) { -#else -extern "C" void __cdecl WinMainCRTStartup() { -#endif // NDEBUG - // This relies on the chrome_launcher.exe residing in the same directory - // as our DLL. We build a full path to avoid loading it from any other - // directory in the DLL search path. - // - // The code is a bit verbose because we can't use the standard library. - const wchar_t kBaseName[] = L"npchrome_frame.dll"; - wchar_t file_path[MAX_PATH + (sizeof(kBaseName) / sizeof(kBaseName[0])) + 1]; - file_path[0] = L'\0'; - ::GetModuleFileName(::GetModuleHandle(NULL), file_path, MAX_PATH); - - // Find index of last slash, and null-terminate the string after it. - // - // Proof for security purposes, since we can't use the safe string - // manipulation functions from the runtime: - // - File_path is always null-terminated, by us initially and by - // ::GetModuleFileName if it puts anything into the buffer. - // - If there is no slash in the path then it's a relative path, not an - // absolute one, and the code ends up creating a relative path to - // npchrome_frame.dll. - // - It's safe to use lstrcatW since we know the maximum length of both - // parts we are concatenating, and we know the buffer will fit them in - // the worst case. - int slash_index = lstrlenW(file_path); - // Invariant: 0 <= slash_index < MAX_PATH - CLM_ASSERT(slash_index > 0); - while (slash_index > 0 && file_path[slash_index] != L'\\') - --slash_index; - // Invariant: 0 <= slash_index < MAX_PATH and it is either the index of - // the last \ in the path, or 0. - if (slash_index != 0) - ++slash_index; // don't remove the last '\' - file_path[slash_index] = L'\0'; - - lstrcatW(file_path, kBaseName); + const wchar_t* cmd_line = ::GetCommandLine(); + + google_breakpad::scoped_ptr<google_breakpad::ExceptionHandler> breakpad( + InitializeCrashReporting(cmd_line)); UINT exit_code = ERROR_FILE_NOT_FOUND; - HMODULE chrome_tab = ::LoadLibrary(file_path); - CLM_ASSERT(chrome_tab); - if (chrome_tab) { - chrome_launcher::CfLaunchChromeProc proc = - reinterpret_cast<chrome_launcher::CfLaunchChromeProc>( - ::GetProcAddress(chrome_tab, "CfLaunchChrome")); - CLM_ASSERT(proc); - if (proc) { - exit_code = proc(); - } else { - exit_code = ERROR_INVALID_FUNCTION; - } - - ::FreeLibrary(chrome_tab); + if (chrome_launcher::SanitizeAndLaunchChrome(::GetCommandLine())) { + exit_code = ERROR_SUCCESS; } - ::ExitProcess(exit_code); + + return exit_code; } diff --git a/chrome_frame/chrome_launcher_unittest.cc b/chrome_frame/chrome_launcher_unittest.cc index fa6efd8..c577d1e 100644 --- a/chrome_frame/chrome_launcher_unittest.cc +++ b/chrome_frame/chrome_launcher_unittest.cc @@ -8,41 +8,66 @@ #include "chrome_frame/chrome_launcher.h" #include "testing/gtest/include/gtest/gtest.h" -namespace { +TEST(ChromeLauncher, IsValidCommandLine) { + CommandLine bad(FilePath(L"dummy.exe")); + bad.AppendSwitch(switches::kNoFirstRun); // in whitelist + bad.AppendSwitch("no-such-switch"); // does not exist + bad.AppendSwitch(switches::kHomePage); // exists but not in whitelist -// Utility class to disable logging. Required to disable DCHECKs that some -// of our tests would otherwise trigger. -class LogDisabler { - public: - LogDisabler() { - initial_log_level_ = logging::GetMinLogLevel(); - logging::SetMinLogLevel(logging::LOG_FATAL + 1); - } + EXPECT_FALSE(chrome_launcher::IsValidCommandLine( + bad.command_line_string().c_str())); - ~LogDisabler() { - logging::SetMinLogLevel(initial_log_level_); - } + CommandLine good(FilePath(L"dummy.exe")); + good.AppendSwitch(switches::kNoFirstRun); // in whitelist + good.AppendSwitchWithValue(switches::kUserDataDir, L"foo"); // in whitelist - private: - int initial_log_level_; -}; + EXPECT_TRUE(chrome_launcher::IsValidCommandLine( + good.command_line_string().c_str())); -} // namespace + CommandLine no_params(FilePath(L"dummy.exe")); + EXPECT_TRUE(chrome_launcher::IsValidCommandLine( + no_params.command_line_string().c_str())); -TEST(ChromeLauncher, SanitizeCommandLine) { - CommandLine bad(FilePath(L"dummy.exe")); - bad.AppendSwitch(switches::kNoFirstRun); // in whitelist - bad.AppendSwitchWithValue(switches::kLoadExtension, L"foo"); // in whitelist - bad.AppendSwitch("no-such-switch"); // does not exist - bad.AppendSwitch(switches::kHomePage); // exists but not in whitelist + CommandLine empty(FilePath(L"")); + EXPECT_TRUE(chrome_launcher::IsValidCommandLine( + empty.command_line_string().c_str())); +} + +TEST(ChromeLauncher, TrimWhiteSpace) { + std::wstring trimmed(chrome_launcher::TrimWhiteSpace(L" \t some text \n\t")); + EXPECT_STREQ(L"some text", trimmed.c_str()); + + std::wstring now_empty(chrome_launcher::TrimWhiteSpace(L"\t\t \n\t")); + EXPECT_STREQ(L"", now_empty.c_str()); + + std::wstring empty(chrome_launcher::TrimWhiteSpace(L"")); + EXPECT_STREQ(L"", empty.c_str()); - LogDisabler no_dchecks; + std::wstring not_trimmed(chrome_launcher::TrimWhiteSpace(L"foo bar")); + EXPECT_STREQ(L"foo bar", not_trimmed.c_str()); - CommandLine sanitized(FilePath(L"dumbo.exe")); - chrome_launcher::SanitizeCommandLine(bad, &sanitized); - EXPECT_TRUE(sanitized.HasSwitch(switches::kNoFirstRun)); - EXPECT_FALSE(sanitized.HasSwitch(switches::kLoadExtension)); - EXPECT_FALSE(sanitized.HasSwitch("no-such-switch")); - EXPECT_FALSE(sanitized.HasSwitch(switches::kHomePage)); + std::wstring trimmed_right(chrome_launcher::TrimWhiteSpace(L"foo bar\t")); + EXPECT_STREQ(L"foo bar", trimmed_right.c_str()); + + std::wstring trimmed_left(chrome_launcher::TrimWhiteSpace(L"\nfoo bar")); + EXPECT_STREQ(L"foo bar", trimmed_right.c_str()); } +TEST(ChromeLauncher, IsValidArgument) { + EXPECT_TRUE(chrome_launcher::IsValidArgument(L"--chrome-frame")); + EXPECT_FALSE(chrome_launcher::IsValidArgument(L"--invalid-arg")); + + EXPECT_TRUE(chrome_launcher::IsValidArgument(L"--chrome-frame=")); + EXPECT_TRUE(chrome_launcher::IsValidArgument(L"--chrome-frame=foo")); + EXPECT_TRUE(chrome_launcher::IsValidArgument(L"--chrome-frame=foo=foo")); + + EXPECT_FALSE(chrome_launcher::IsValidArgument(L"chrome-frame")); + EXPECT_FALSE(chrome_launcher::IsValidArgument(L"-chrome-frame")); + EXPECT_FALSE(chrome_launcher::IsValidArgument(L"---chrome-frame")); + EXPECT_FALSE(chrome_launcher::IsValidArgument(L" --chrome-frame")); + EXPECT_FALSE(chrome_launcher::IsValidArgument(L"--chrome-framefoobar")); + EXPECT_FALSE(chrome_launcher::IsValidArgument(L"foobar--chrome-frame")); + EXPECT_FALSE(chrome_launcher::IsValidArgument(L"--chrome-frames")); + EXPECT_FALSE(chrome_launcher::IsValidArgument(L"--Chrome-frame")); + EXPECT_FALSE(chrome_launcher::IsValidArgument(L"")); +} diff --git a/chrome_frame/chrome_launcher_utils.cc b/chrome_frame/chrome_launcher_utils.cc new file mode 100644 index 0000000..913ff14 --- /dev/null +++ b/chrome_frame/chrome_launcher_utils.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2009 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_frame/chrome_launcher_utils.h" + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/win_util.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome_frame/chrome_frame_automation.h" + +namespace chrome_launcher { + +const wchar_t kLauncherExeBaseName[] = L"chrome_launcher.exe"; + +CommandLine* CreateLaunchCommandLine() { + // Shortcut for OS versions that don't need the integrity broker. + if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) { + return new CommandLine(GetChromeExecutablePath()); + } + + // The launcher EXE will be in the same directory as the Chrome Frame DLL, + // so create a full path to it based on this assumption. Since our unit + // tests also use this function, and live in the directory above, we test + // existence of the file and try the path that includes the /servers/ + // directory if needed. + FilePath module_path; + if (PathService::Get(base::FILE_MODULE, &module_path)) { + FilePath current_dir = module_path.DirName(); + FilePath same_dir_path = current_dir.Append(kLauncherExeBaseName); + if (file_util::PathExists(same_dir_path)) { + return new CommandLine(same_dir_path); + } else { + FilePath servers_path = + current_dir.Append(L"servers").Append(kLauncherExeBaseName); + DCHECK(file_util::PathExists(servers_path)) << + "What module is this? It's not in 'servers' or main output dir."; + return new CommandLine(servers_path); + } + } else { + NOTREACHED(); + return NULL; + } +} + +FilePath GetChromeExecutablePath() { + FilePath cur_path; + PathService::Get(base::DIR_MODULE, &cur_path); + cur_path = cur_path.Append(chrome::kBrowserProcessExecutableName); + + // The installation model for Chrome places the DLLs in a versioned + // sub-folder one down from the Chrome executable. If we fail to find + // chrome.exe in the current path, try looking one up and launching that + // instead. + if (!file_util::PathExists(cur_path)) { + PathService::Get(base::DIR_MODULE, &cur_path); + cur_path = cur_path.DirName().Append(chrome::kBrowserProcessExecutableName); + } + + return cur_path; +} + +} // namespace chrome_launcher diff --git a/chrome_frame/chrome_launcher_utils.h b/chrome_frame/chrome_launcher_utils.h new file mode 100644 index 0000000..c72569b --- /dev/null +++ b/chrome_frame/chrome_launcher_utils.h @@ -0,0 +1,32 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_FRAME_CHROME_LAUNCHER_H_ +#define CHROME_FRAME_CHROME_LAUNCHER_H_ + +#include <string> + +#include "base/file_path.h" + +class CommandLine; + +namespace chrome_launcher { + +// The base name of the chrome_launcher.exe file. +extern const wchar_t kLauncherExeBaseName[]; + +// Creates a command line suitable for launching Chrome. You can add any +// flags needed before launching. +// +// The command-line may use the Chrome executable directly, or use an in-between +// process if needed for security/elevation purposes. You must delete the +// returned command line. +CommandLine* CreateLaunchCommandLine(); + +// Returns the full path to the Chrome executable. +FilePath GetChromeExecutablePath(); + +} // namespace chrome_launcher + +#endif // CHROME_FRAME_CHROME_LAUNCHER_H_ diff --git a/chrome_frame/chrome_tab.cc b/chrome_frame/chrome_tab.cc index f44d475..26ffe7b 100644 --- a/chrome_frame/chrome_tab.cc +++ b/chrome_frame/chrome_tab.cc @@ -30,7 +30,7 @@ #include "chrome_frame/chrome_frame_automation.h" #include "chrome_frame/exception_barrier.h" #include "chrome_frame/chrome_frame_reporting.h" -#include "chrome_frame/chrome_launcher.h" +#include "chrome_frame/chrome_launcher_utils.h" #include "chrome_frame/chrome_protocol.h" #include "chrome_frame/module_utils.h" #include "chrome_frame/resource.h" diff --git a/chrome_frame/chrome_tab.def b/chrome_frame/chrome_tab.def index 98e6899..cbea59f 100644 --- a/chrome_frame/chrome_tab.def +++ b/chrome_frame/chrome_tab.def @@ -10,6 +10,5 @@ EXPORTS NP_Initialize PRIVATE NP_GetEntryPoints PRIVATE NP_Shutdown PRIVATE - CfLaunchChrome PRIVATE RegisterNPAPIPlugin PRIVATE UnregisterNPAPIPlugin PRIVATE |