blob: 0c1a8ac4b3aebf7bae13ef31a401ac55bbad7947 [file] [log] [blame]
[email protected]ae0f0772010-08-13 04:54:101// Copyright (c) 2010 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
[email protected]c9349d082008-08-22 21:16:474
[email protected]4512f3ac2009-11-04 03:39:225#include <windows.h>
6#include <shlwapi.h>
[email protected]c9349d082008-08-22 21:16:477
[email protected]5d91c9e2010-07-28 17:25:288#include "base/command_line.h"
[email protected]58580352010-10-26 04:07:509#include "base/debug/trace_event.h"
[email protected]ae0f0772010-08-13 04:54:1010#include "base/environment.h"
[email protected]85286b52010-07-03 06:14:4511#include "base/file_util.h"
[email protected]ae0f0772010-08-13 04:54:1012#include "base/logging.h"
13#include "base/scoped_ptr.h"
14#include "base/utf_string_conversions.h"
[email protected]85769352010-11-16 14:24:3015#include "base/version.h"
[email protected]4512f3ac2009-11-04 03:39:2216#include "chrome/app/breakpad_win.h"
17#include "chrome/app/client_util.h"
[email protected]03d21b82010-12-07 19:37:2218#include "chrome/common/chrome_constants.h"
[email protected]103607e2010-02-01 18:57:0919#include "chrome/common/chrome_switches.h"
[email protected]4512f3ac2009-11-04 03:39:2220#include "chrome/common/result_codes.h"
[email protected]74d1eec2009-11-04 22:18:5721#include "chrome/installer/util/browser_distribution.h"
[email protected]4512f3ac2009-11-04 03:39:2222#include "chrome/installer/util/install_util.h"
[email protected]2414e842008-11-07 01:27:5723#include "chrome/installer/util/google_update_constants.h"
[email protected]28c19692008-11-07 23:40:3824#include "chrome/installer/util/util_constants.h"
25
26namespace {
[email protected]4512f3ac2009-11-04 03:39:2227// The entry point signature of chrome.dll.
28typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*, wchar_t*);
[email protected]28c19692008-11-07 23:40:3829
[email protected]3cdacd42010-04-30 18:55:5330typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc)();
31
[email protected]4512f3ac2009-11-04 03:39:2232// Not generic, we only handle strings up to 128 chars.
33bool ReadRegistryStr(HKEY key, const wchar_t* name, std::wstring* value) {
34 BYTE out[128 * sizeof(wchar_t)];
35 DWORD size = sizeof(out);
36 DWORD type = 0;
37 if (ERROR_SUCCESS != ::RegQueryValueExW(key, name, NULL, &type, out, &size))
[email protected]28c19692008-11-07 23:40:3838 return false;
[email protected]4512f3ac2009-11-04 03:39:2239 if (type != REG_SZ)
40 return false;
41 value->assign(reinterpret_cast<wchar_t*>(out));
[email protected]85769352010-11-16 14:24:3042
[email protected]28c19692008-11-07 23:40:3843 return true;
44}
[email protected]c9349d082008-08-22 21:16:4745
[email protected]4512f3ac2009-11-04 03:39:2246// Gets chrome version according to the load path. |exe_path| must be the
47// backslash terminated directory of the current chrome.exe.
48bool GetVersion(const wchar_t* exe_path, const wchar_t* key_path,
49 std::wstring* version) {
[email protected]48d43872008-11-04 23:38:3250 HKEY reg_root = InstallUtil::IsPerUserInstall(exe_path) ? HKEY_CURRENT_USER :
[email protected]28c19692008-11-07 23:40:3851 HKEY_LOCAL_MACHINE;
[email protected]4512f3ac2009-11-04 03:39:2252 HKEY key;
53 if (::RegOpenKeyEx(reg_root, key_path, 0, KEY_READ, &key) != ERROR_SUCCESS)
[email protected]c9349d082008-08-22 21:16:4754 return false;
[email protected]28c19692008-11-07 23:40:3855
[email protected]4512f3ac2009-11-04 03:39:2256 // If 'new_chrome.exe' is present it means chrome was auto-updated while
57 // running. We need to consult the opv value so we can load the old dll.
58 // TODO(cpu) : This is solving the same problem as the environment variable
59 // so one of them will eventually be deprecated.
[email protected]28c19692008-11-07 23:40:3860 std::wstring new_chrome_exe(exe_path);
[email protected]74ca0442010-12-15 14:44:5061 new_chrome_exe.append(installer::kChromeNewExe);
[email protected]4512f3ac2009-11-04 03:39:2262 if (::PathFileExistsW(new_chrome_exe.c_str()) &&
63 ReadRegistryStr(key, google_update::kRegOldVersionField, version)) {
64 ::RegCloseKey(key);
[email protected]28c19692008-11-07 23:40:3865 return true;
[email protected]2414e842008-11-07 01:27:5766 }
67
[email protected]4512f3ac2009-11-04 03:39:2268 bool ret = ReadRegistryStr(key, google_update::kRegVersionField, version);
69 ::RegCloseKey(key);
[email protected]c9349d082008-08-22 21:16:4770 return ret;
71}
72
[email protected]4512f3ac2009-11-04 03:39:2273// Gets the path of the current exe with a trailing backslash.
[email protected]604004042009-10-21 23:10:2674std::wstring GetExecutablePath() {
[email protected]4512f3ac2009-11-04 03:39:2275 wchar_t path[MAX_PATH];
76 ::GetModuleFileNameW(NULL, path, MAX_PATH);
77 if (!::PathRemoveFileSpecW(path))
78 return std::wstring();
79 std::wstring exe_path(path);
80 return exe_path.append(L"\\");
[email protected]c9349d082008-08-22 21:16:4781}
82
[email protected]bae4e382010-08-22 04:44:0583// Not generic, we only handle strings up to 128 chars.
84bool EnvQueryStr(const wchar_t* key_name, std::wstring* value) {
85 wchar_t out[128];
86 DWORD count = sizeof(out)/sizeof(out[0]);
87 DWORD rv = ::GetEnvironmentVariableW(key_name, out, count);
88 if ((rv == 0) || (rv >= count))
89 return false;
90 *value = out;
91 return true;
92}
93
[email protected]4512f3ac2009-11-04 03:39:2294// Expects that |dir| has a trailing backslash. |dir| is modified so it
95// contains the full path that was tried. Caller must check for the return
[email protected]85769352010-11-16 14:24:3096// value not being null to determine if this path contains a valid dll.
[email protected]4512f3ac2009-11-04 03:39:2297HMODULE LoadChromeWithDirectory(std::wstring* dir) {
98 ::SetCurrentDirectoryW(dir->c_str());
[email protected]103607e2010-02-01 18:57:0999 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
[email protected]7b1bf812010-06-21 21:15:53100#ifdef _WIN64
[email protected]103607e2010-02-01 18:57:09101 if ((cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
102 switches::kNaClBrokerProcess) ||
103 (cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
104 switches::kNaClLoaderProcess)) {
105 // Load the 64-bit DLL when running in a 64-bit process.
[email protected]74ca0442010-12-15 14:44:50106 dir->append(installer::kChromeNaCl64Dll);
[email protected]103607e2010-02-01 18:57:09107 } else {
108 // Only NaCl broker and loader can be launched as Win64 processes.
109 NOTREACHED();
110 return NULL;
111 }
112#else
[email protected]74ca0442010-12-15 14:44:50113 dir->append(installer::kChromeDll);
[email protected]103607e2010-02-01 18:57:09114#endif
[email protected]00b73412010-06-21 21:01:36115
[email protected]58ac0242010-07-09 18:31:15116#ifdef NDEBUG
[email protected]00b73412010-06-21 21:01:36117 // Experimental pre-reading optimization
118 // The idea is to pre read significant portion of chrome.dll in advance
119 // so that subsequent hard page faults are avoided.
[email protected]bdf411b2010-07-13 22:21:59120 if (!cmd_line.HasSwitch(switches::kProcessType)) {
[email protected]85286b52010-07-03 06:14:45121 // The kernel brings in 8 pages for the code section at a time and 4 pages
122 // for other sections. We can skip over these pages to avoid a soft page
123 // fault which may not occur during code execution. However skipping 4K at
124 // a time still has better performance over 32K and 16K according to data.
125 // TODO(ananta)
126 // Investigate this and tune.
127 const size_t kStepSize = 4 * 1024;
128
129 DWORD pre_read_size = 0;
130 DWORD pre_read_step_size = kStepSize;
131 DWORD pre_read = 1;
132
[email protected]00b73412010-06-21 21:01:36133 HKEY key = NULL;
134 if (::RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Google\\ChromeFrame",
135 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
[email protected]85286b52010-07-03 06:14:45136 DWORD unused = sizeof(pre_read_size);
137 RegQueryValueEx(key, L"PreReadSize", NULL, NULL,
138 reinterpret_cast<LPBYTE>(&pre_read_size), &unused);
139 RegQueryValueEx(key, L"PreReadStepSize", NULL, NULL,
140 reinterpret_cast<LPBYTE>(&pre_read_step_size), &unused);
141 RegQueryValueEx(key, L"PreRead", NULL, NULL,
142 reinterpret_cast<LPBYTE>(&pre_read), &unused);
[email protected]00b73412010-06-21 21:01:36143 RegCloseKey(key);
144 key = NULL;
[email protected]00b73412010-06-21 21:01:36145 }
[email protected]85286b52010-07-03 06:14:45146 if (pre_read) {
[email protected]eb64b402010-08-12 01:37:35147 TRACE_EVENT_BEGIN("PreReadImage", 0, "");
[email protected]85286b52010-07-03 06:14:45148 file_util::PreReadImage(dir->c_str(), pre_read_size, pre_read_step_size);
[email protected]eb64b402010-08-12 01:37:35149 TRACE_EVENT_END("PreReadImage", 0, "");
[email protected]00b73412010-06-21 21:01:36150 }
151 }
[email protected]58ac0242010-07-09 18:31:15152#endif // NDEBUG
[email protected]00b73412010-06-21 21:01:36153
[email protected]4512f3ac2009-11-04 03:39:22154 return ::LoadLibraryExW(dir->c_str(), NULL,
155 LOAD_WITH_ALTERED_SEARCH_PATH);
156}
157
[email protected]42d7510c2009-12-19 01:48:30158// Set did_run "dr" in omaha's client state for this product.
159bool SetDidRunState(const wchar_t* guid, const wchar_t* value) {
[email protected]4512f3ac2009-11-04 03:39:22160 std::wstring key_path(google_update::kRegPathClientState);
161 key_path.append(L"\\").append(guid);
162 HKEY reg_key;
163 if (::RegCreateKeyExW(HKEY_CURRENT_USER, key_path.c_str(), 0, NULL,
164 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
165 &reg_key, NULL) == ERROR_SUCCESS) {
[email protected]203c6872010-01-30 15:59:45166 // Note that the length here must be in bytes and must account for the
167 // terminating null char.
[email protected]4512f3ac2009-11-04 03:39:22168 ::RegSetValueExW(reg_key, google_update::kRegDidRunField, 0, REG_SZ,
[email protected]203c6872010-01-30 15:59:45169 reinterpret_cast<const BYTE *>(value),
170 (::lstrlenW(value) + 1) * sizeof(wchar_t));
[email protected]4512f3ac2009-11-04 03:39:22171 ::RegCloseKey(reg_key);
172 return true;
173 }
174 return false;
175}
[email protected]42d7510c2009-12-19 01:48:30176
177bool RecordDidRun(const wchar_t* guid) {
178 return SetDidRunState(guid, L"1");
[email protected]4512f3ac2009-11-04 03:39:22179}
180
[email protected]42d7510c2009-12-19 01:48:30181bool ClearDidRun(const wchar_t* guid) {
182 return SetDidRunState(guid, L"0");
183}
184
185}
[email protected]4512f3ac2009-11-04 03:39:22186//=============================================================================
187
188MainDllLoader::MainDllLoader() : dll_(NULL) {
189}
190
191MainDllLoader::~MainDllLoader() {
192#ifdef PURIFY
193 // We should never unload the dll. There is only risk and no gain from
194 // doing so. The singleton dtors have been already run by AtExitManager.
195 ::FreeLibrary(dll_);
196#endif
197}
198
[email protected]85769352010-11-16 14:24:30199// Loading chrome is an interesting affair. First we try loading from the
200// current directory to support run-what-you-compile and other development
201// scenarios.
202// If that fails then we look at the --chrome-version command line flag followed
203// by the 'CHROME_VERSION' env variable to determine if we should stick with an
204// older dll version even if a new one is available to support upgrade-in-place
205// scenarios.
206// If that fails then finally we look at the registry which should point us
207// to the latest version. This is the expected path for the first chrome.exe
208// browser instance in an installed build.
209HMODULE MainDllLoader::Load(std::wstring* out_version, std::wstring* out_file) {
[email protected]4512f3ac2009-11-04 03:39:22210 std::wstring dir(GetExecutablePath());
[email protected]85769352010-11-16 14:24:30211 *out_file = dir;
212 HMODULE dll = LoadChromeWithDirectory(out_file);
[email protected]4512f3ac2009-11-04 03:39:22213 if (dll)
214 return dll;
215
[email protected]85769352010-11-16 14:24:30216 std::wstring version_env_string;
217 scoped_ptr<Version> version;
218 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
219 if (cmd_line.HasSwitch(switches::kChromeVersion)) {
220 version_env_string = cmd_line.GetSwitchValueNative(
221 switches::kChromeVersion);
222 version.reset(Version::GetVersionFromString(version_env_string));
223
224 if (!version.get()) {
225 // If a bogus command line flag was given, then abort.
226 LOG(ERROR) << "Invalid version string received on command line: "
227 << version_env_string;
[email protected]4512f3ac2009-11-04 03:39:22228 return NULL;
[email protected]85769352010-11-16 14:24:30229 }
[email protected]4512f3ac2009-11-04 03:39:22230 }
231
[email protected]85769352010-11-16 14:24:30232 if (!version.get()) {
[email protected]03d21b82010-12-07 19:37:22233 if (EnvQueryStr(ASCIIToWide(chrome::kChromeVersionEnvVar).c_str(),
234 &version_env_string)) {
[email protected]85769352010-11-16 14:24:30235 version.reset(Version::GetVersionFromString(version_env_string));
236 }
237 }
238
239 if (!version.get()) {
240 std::wstring reg_path(GetRegistryPath());
241 // Look into the registry to find the latest version. We don't validate
242 // this by building a Version object to avoid harming normal case startup
243 // time.
244 version_env_string.clear();
245 GetVersion(dir.c_str(), reg_path.c_str(), &version_env_string);
246 }
247
248 if (version.get() || !version_env_string.empty()) {
249 *out_file = dir;
250 *out_version = version_env_string;
251 out_file->append(*out_version).append(L"\\");
252 return LoadChromeWithDirectory(out_file);
253 } else {
254 return NULL;
255 }
[email protected]4512f3ac2009-11-04 03:39:22256}
257
258// Launching is a matter of loading the right dll, setting the CHROME_VERSION
259// environment variable and just calling the entry point. Derived classes can
260// add custom code in the OnBeforeLaunch callback.
261int MainDllLoader::Launch(HINSTANCE instance,
262 sandbox::SandboxInterfaceInfo* sbox_info) {
263 std::wstring version;
264 std::wstring file;
265 dll_ = Load(&version, &file);
266 if (!dll_)
267 return ResultCodes::MISSING_DATA;
268
[email protected]ae0f0772010-08-13 04:54:10269 scoped_ptr<base::Environment> env(base::Environment::Create());
[email protected]03d21b82010-12-07 19:37:22270 env->SetVar(chrome::kChromeVersionEnvVar, WideToUTF8(version));
[email protected]4512f3ac2009-11-04 03:39:22271
272 InitCrashReporterWithDllPath(file);
[email protected]85769352010-11-16 14:24:30273 OnBeforeLaunch();
[email protected]4512f3ac2009-11-04 03:39:22274
275 DLL_MAIN entry_point =
276 reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain"));
277 if (!entry_point)
278 return ResultCodes::BAD_PROCESS_TYPE;
279
[email protected]42d7510c2009-12-19 01:48:30280 int rc = entry_point(instance, sbox_info, ::GetCommandLineW());
281 return OnBeforeExit(rc);
[email protected]4512f3ac2009-11-04 03:39:22282}
283
[email protected]3cdacd42010-04-30 18:55:53284void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
285 RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function =
286 reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc>(
287 ::GetProcAddress(dll_,
288 "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
289 if (!relaunch_function) {
290 LOG(ERROR) << "Could not find exported function "
291 << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
292 } else {
293 relaunch_function();
294 }
295}
296
[email protected]4512f3ac2009-11-04 03:39:22297//=============================================================================
298
299class ChromeDllLoader : public MainDllLoader {
300 public:
301 virtual std::wstring GetRegistryPath() {
302 std::wstring key(google_update::kRegPathClients);
[email protected]5f2cee82009-11-05 04:59:48303 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
304 key.append(L"\\").append(dist->GetAppGuid());
[email protected]4512f3ac2009-11-04 03:39:22305 return key;
306 }
307
[email protected]85769352010-11-16 14:24:30308 virtual void OnBeforeLaunch() {
[email protected]5f2cee82009-11-05 04:59:48309 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
310 RecordDidRun(dist->GetAppGuid().c_str());
[email protected]4512f3ac2009-11-04 03:39:22311 }
[email protected]42d7510c2009-12-19 01:48:30312
313 virtual int OnBeforeExit(int return_code) {
314 // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
315 // so we need to reset the did_run signal so omaha does not count
316 // this run as active usage.
317 if (ResultCodes::NORMAL_EXIT_CANCEL == return_code) {
318 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
319 ClearDidRun(dist->GetAppGuid().c_str());
320 }
321 return return_code;
322 }
[email protected]4512f3ac2009-11-04 03:39:22323};
324
325//=============================================================================
326
327class ChromiumDllLoader : public MainDllLoader {
328 public:
329 virtual std::wstring GetRegistryPath() {
[email protected]74d1eec2009-11-04 22:18:57330 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
331 return dist->GetVersionKey();
[email protected]4512f3ac2009-11-04 03:39:22332 }
333};
334
335MainDllLoader* MakeMainDllLoader() {
336#if defined(GOOGLE_CHROME_BUILD)
337 return new ChromeDllLoader();
338#else
339 return new ChromiumDllLoader();
340#endif
341}