summaryrefslogtreecommitdiffstats
path: root/chrome_frame
diff options
context:
space:
mode:
authorrobertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-28 17:27:17 +0000
committerrobertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-28 17:27:17 +0000
commit79b663c6e6ddf89e85cdc566b5d4f368465bb858 (patch)
tree868c35191ee02c8bd7da15b69edc7114007e211a /chrome_frame
parent84b5647e4a43a55b9b0e8b211684f48222569218 (diff)
downloadchromium_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.gyp44
-rw-r--r--chrome_frame/chrome_frame_automation.cc2
-rw-r--r--chrome_frame/chrome_frame_launcher.gyp84
-rw-r--r--chrome_frame/chrome_launcher.cc270
-rw-r--r--chrome_frame/chrome_launcher.h45
-rw-r--r--chrome_frame/chrome_launcher_main.cc146
-rw-r--r--chrome_frame/chrome_launcher_unittest.cc83
-rw-r--r--chrome_frame/chrome_launcher_utils.cc68
-rw-r--r--chrome_frame/chrome_launcher_utils.h32
-rw-r--r--chrome_frame/chrome_tab.cc2
-rw-r--r--chrome_frame/chrome_tab.def1
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