blob: 06864a34e290cd99cce270382026af38bb2a1eb5 [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]103607e2010-02-01 18:57:0918#include "chrome/common/chrome_switches.h"
[email protected]4512f3ac2009-11-04 03:39:2219#include "chrome/common/result_codes.h"
[email protected]74d1eec2009-11-04 22:18:5720#include "chrome/installer/util/browser_distribution.h"
[email protected]4512f3ac2009-11-04 03:39:2221#include "chrome/installer/util/install_util.h"
[email protected]2414e842008-11-07 01:27:5722#include "chrome/installer/util/google_update_constants.h"
[email protected]28c19692008-11-07 23:40:3823#include "chrome/installer/util/util_constants.h"
24
25namespace {
[email protected]4512f3ac2009-11-04 03:39:2226// The entry point signature of chrome.dll.
27typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*, wchar_t*);
[email protected]28c19692008-11-07 23:40:3828
[email protected]3cdacd42010-04-30 18:55:5329typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc)();
30
[email protected]4512f3ac2009-11-04 03:39:2231// Not generic, we only handle strings up to 128 chars.
32bool ReadRegistryStr(HKEY key, const wchar_t* name, std::wstring* value) {
33 BYTE out[128 * sizeof(wchar_t)];
34 DWORD size = sizeof(out);
35 DWORD type = 0;
36 if (ERROR_SUCCESS != ::RegQueryValueExW(key, name, NULL, &type, out, &size))
[email protected]28c19692008-11-07 23:40:3837 return false;
[email protected]4512f3ac2009-11-04 03:39:2238 if (type != REG_SZ)
39 return false;
40 value->assign(reinterpret_cast<wchar_t*>(out));
[email protected]85769352010-11-16 14:24:3041
[email protected]28c19692008-11-07 23:40:3842 return true;
43}
[email protected]c9349d082008-08-22 21:16:4744
[email protected]4512f3ac2009-11-04 03:39:2245// Gets chrome version according to the load path. |exe_path| must be the
46// backslash terminated directory of the current chrome.exe.
47bool GetVersion(const wchar_t* exe_path, const wchar_t* key_path,
48 std::wstring* version) {
[email protected]48d43872008-11-04 23:38:3249 HKEY reg_root = InstallUtil::IsPerUserInstall(exe_path) ? HKEY_CURRENT_USER :
[email protected]28c19692008-11-07 23:40:3850 HKEY_LOCAL_MACHINE;
[email protected]4512f3ac2009-11-04 03:39:2251 HKEY key;
52 if (::RegOpenKeyEx(reg_root, key_path, 0, KEY_READ, &key) != ERROR_SUCCESS)
[email protected]c9349d082008-08-22 21:16:4753 return false;
[email protected]28c19692008-11-07 23:40:3854
[email protected]4512f3ac2009-11-04 03:39:2255 // If 'new_chrome.exe' is present it means chrome was auto-updated while
56 // running. We need to consult the opv value so we can load the old dll.
57 // TODO(cpu) : This is solving the same problem as the environment variable
58 // so one of them will eventually be deprecated.
[email protected]28c19692008-11-07 23:40:3859 std::wstring new_chrome_exe(exe_path);
60 new_chrome_exe.append(installer_util::kChromeNewExe);
[email protected]4512f3ac2009-11-04 03:39:2261 if (::PathFileExistsW(new_chrome_exe.c_str()) &&
62 ReadRegistryStr(key, google_update::kRegOldVersionField, version)) {
63 ::RegCloseKey(key);
[email protected]28c19692008-11-07 23:40:3864 return true;
[email protected]2414e842008-11-07 01:27:5765 }
66
[email protected]4512f3ac2009-11-04 03:39:2267 bool ret = ReadRegistryStr(key, google_update::kRegVersionField, version);
68 ::RegCloseKey(key);
[email protected]c9349d082008-08-22 21:16:4769 return ret;
70}
71
[email protected]4512f3ac2009-11-04 03:39:2272// Gets the path of the current exe with a trailing backslash.
[email protected]604004042009-10-21 23:10:2673std::wstring GetExecutablePath() {
[email protected]4512f3ac2009-11-04 03:39:2274 wchar_t path[MAX_PATH];
75 ::GetModuleFileNameW(NULL, path, MAX_PATH);
76 if (!::PathRemoveFileSpecW(path))
77 return std::wstring();
78 std::wstring exe_path(path);
79 return exe_path.append(L"\\");
[email protected]c9349d082008-08-22 21:16:4780}
81
[email protected]bae4e382010-08-22 04:44:0582// Not generic, we only handle strings up to 128 chars.
83bool EnvQueryStr(const wchar_t* key_name, std::wstring* value) {
84 wchar_t out[128];
85 DWORD count = sizeof(out)/sizeof(out[0]);
86 DWORD rv = ::GetEnvironmentVariableW(key_name, out, count);
87 if ((rv == 0) || (rv >= count))
88 return false;
89 *value = out;
90 return true;
91}
92
[email protected]4512f3ac2009-11-04 03:39:2293// Expects that |dir| has a trailing backslash. |dir| is modified so it
94// contains the full path that was tried. Caller must check for the return
[email protected]85769352010-11-16 14:24:3095// value not being null to determine if this path contains a valid dll.
[email protected]4512f3ac2009-11-04 03:39:2296HMODULE LoadChromeWithDirectory(std::wstring* dir) {
97 ::SetCurrentDirectoryW(dir->c_str());
[email protected]103607e2010-02-01 18:57:0998 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
[email protected]7b1bf812010-06-21 21:15:5399#ifdef _WIN64
[email protected]103607e2010-02-01 18:57:09100 if ((cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
101 switches::kNaClBrokerProcess) ||
102 (cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
103 switches::kNaClLoaderProcess)) {
104 // Load the 64-bit DLL when running in a 64-bit process.
105 dir->append(installer_util::kChromeNaCl64Dll);
106 } else {
107 // Only NaCl broker and loader can be launched as Win64 processes.
108 NOTREACHED();
109 return NULL;
110 }
111#else
[email protected]4512f3ac2009-11-04 03:39:22112 dir->append(installer_util::kChromeDll);
[email protected]103607e2010-02-01 18:57:09113#endif
[email protected]00b73412010-06-21 21:01:36114
[email protected]58ac0242010-07-09 18:31:15115#ifdef NDEBUG
[email protected]00b73412010-06-21 21:01:36116 // Experimental pre-reading optimization
117 // The idea is to pre read significant portion of chrome.dll in advance
118 // so that subsequent hard page faults are avoided.
[email protected]bdf411b2010-07-13 22:21:59119 if (!cmd_line.HasSwitch(switches::kProcessType)) {
[email protected]85286b52010-07-03 06:14:45120 // The kernel brings in 8 pages for the code section at a time and 4 pages
121 // for other sections. We can skip over these pages to avoid a soft page
122 // fault which may not occur during code execution. However skipping 4K at
123 // a time still has better performance over 32K and 16K according to data.
124 // TODO(ananta)
125 // Investigate this and tune.
126 const size_t kStepSize = 4 * 1024;
127
128 DWORD pre_read_size = 0;
129 DWORD pre_read_step_size = kStepSize;
130 DWORD pre_read = 1;
131
[email protected]00b73412010-06-21 21:01:36132 HKEY key = NULL;
133 if (::RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Google\\ChromeFrame",
134 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
[email protected]85286b52010-07-03 06:14:45135 DWORD unused = sizeof(pre_read_size);
136 RegQueryValueEx(key, L"PreReadSize", NULL, NULL,
137 reinterpret_cast<LPBYTE>(&pre_read_size), &unused);
138 RegQueryValueEx(key, L"PreReadStepSize", NULL, NULL,
139 reinterpret_cast<LPBYTE>(&pre_read_step_size), &unused);
140 RegQueryValueEx(key, L"PreRead", NULL, NULL,
141 reinterpret_cast<LPBYTE>(&pre_read), &unused);
[email protected]00b73412010-06-21 21:01:36142 RegCloseKey(key);
143 key = NULL;
[email protected]00b73412010-06-21 21:01:36144 }
[email protected]85286b52010-07-03 06:14:45145 if (pre_read) {
[email protected]eb64b402010-08-12 01:37:35146 TRACE_EVENT_BEGIN("PreReadImage", 0, "");
[email protected]85286b52010-07-03 06:14:45147 file_util::PreReadImage(dir->c_str(), pre_read_size, pre_read_step_size);
[email protected]eb64b402010-08-12 01:37:35148 TRACE_EVENT_END("PreReadImage", 0, "");
[email protected]00b73412010-06-21 21:01:36149 }
150 }
[email protected]58ac0242010-07-09 18:31:15151#endif // NDEBUG
[email protected]00b73412010-06-21 21:01:36152
[email protected]4512f3ac2009-11-04 03:39:22153 return ::LoadLibraryExW(dir->c_str(), NULL,
154 LOAD_WITH_ALTERED_SEARCH_PATH);
155}
156
[email protected]42d7510c2009-12-19 01:48:30157// Set did_run "dr" in omaha's client state for this product.
158bool SetDidRunState(const wchar_t* guid, const wchar_t* value) {
[email protected]4512f3ac2009-11-04 03:39:22159 std::wstring key_path(google_update::kRegPathClientState);
160 key_path.append(L"\\").append(guid);
161 HKEY reg_key;
162 if (::RegCreateKeyExW(HKEY_CURRENT_USER, key_path.c_str(), 0, NULL,
163 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
164 &reg_key, NULL) == ERROR_SUCCESS) {
[email protected]203c6872010-01-30 15:59:45165 // Note that the length here must be in bytes and must account for the
166 // terminating null char.
[email protected]4512f3ac2009-11-04 03:39:22167 ::RegSetValueExW(reg_key, google_update::kRegDidRunField, 0, REG_SZ,
[email protected]203c6872010-01-30 15:59:45168 reinterpret_cast<const BYTE *>(value),
169 (::lstrlenW(value) + 1) * sizeof(wchar_t));
[email protected]4512f3ac2009-11-04 03:39:22170 ::RegCloseKey(reg_key);
171 return true;
172 }
173 return false;
174}
[email protected]42d7510c2009-12-19 01:48:30175
176bool RecordDidRun(const wchar_t* guid) {
177 return SetDidRunState(guid, L"1");
[email protected]4512f3ac2009-11-04 03:39:22178}
179
[email protected]42d7510c2009-12-19 01:48:30180bool ClearDidRun(const wchar_t* guid) {
181 return SetDidRunState(guid, L"0");
182}
183
184}
[email protected]4512f3ac2009-11-04 03:39:22185//=============================================================================
186
187MainDllLoader::MainDllLoader() : dll_(NULL) {
188}
189
190MainDllLoader::~MainDllLoader() {
191#ifdef PURIFY
192 // We should never unload the dll. There is only risk and no gain from
193 // doing so. The singleton dtors have been already run by AtExitManager.
194 ::FreeLibrary(dll_);
195#endif
196}
197
[email protected]85769352010-11-16 14:24:30198// Loading chrome is an interesting affair. First we try loading from the
199// current directory to support run-what-you-compile and other development
200// scenarios.
201// If that fails then we look at the --chrome-version command line flag followed
202// by the 'CHROME_VERSION' env variable to determine if we should stick with an
203// older dll version even if a new one is available to support upgrade-in-place
204// scenarios.
205// If that fails then finally we look at the registry which should point us
206// to the latest version. This is the expected path for the first chrome.exe
207// browser instance in an installed build.
208HMODULE MainDllLoader::Load(std::wstring* out_version, std::wstring* out_file) {
[email protected]4512f3ac2009-11-04 03:39:22209 std::wstring dir(GetExecutablePath());
[email protected]85769352010-11-16 14:24:30210 *out_file = dir;
211 HMODULE dll = LoadChromeWithDirectory(out_file);
[email protected]4512f3ac2009-11-04 03:39:22212 if (dll)
213 return dll;
214
[email protected]85769352010-11-16 14:24:30215 std::wstring version_env_string;
216 scoped_ptr<Version> version;
217 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
218 if (cmd_line.HasSwitch(switches::kChromeVersion)) {
219 version_env_string = cmd_line.GetSwitchValueNative(
220 switches::kChromeVersion);
221 version.reset(Version::GetVersionFromString(version_env_string));
222
223 if (!version.get()) {
224 // If a bogus command line flag was given, then abort.
225 LOG(ERROR) << "Invalid version string received on command line: "
226 << version_env_string;
[email protected]4512f3ac2009-11-04 03:39:22227 return NULL;
[email protected]85769352010-11-16 14:24:30228 }
[email protected]4512f3ac2009-11-04 03:39:22229 }
230
[email protected]85769352010-11-16 14:24:30231 if (!version.get()) {
232 if (EnvQueryStr(
233 BrowserDistribution::GetDistribution()->GetEnvVersionKey().c_str(),
234 &version_env_string)) {
235 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());
270 env->SetVar(WideToUTF8(
271 BrowserDistribution::GetDistribution()->GetEnvVersionKey()).c_str(),
272 WideToUTF8(version));
[email protected]4512f3ac2009-11-04 03:39:22273
274 InitCrashReporterWithDllPath(file);
[email protected]85769352010-11-16 14:24:30275 OnBeforeLaunch();
[email protected]4512f3ac2009-11-04 03:39:22276
277 DLL_MAIN entry_point =
278 reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain"));
279 if (!entry_point)
280 return ResultCodes::BAD_PROCESS_TYPE;
281
[email protected]42d7510c2009-12-19 01:48:30282 int rc = entry_point(instance, sbox_info, ::GetCommandLineW());
283 return OnBeforeExit(rc);
[email protected]4512f3ac2009-11-04 03:39:22284}
285
[email protected]3cdacd42010-04-30 18:55:53286void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
287 RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function =
288 reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc>(
289 ::GetProcAddress(dll_,
290 "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
291 if (!relaunch_function) {
292 LOG(ERROR) << "Could not find exported function "
293 << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
294 } else {
295 relaunch_function();
296 }
297}
298
[email protected]4512f3ac2009-11-04 03:39:22299//=============================================================================
300
301class ChromeDllLoader : public MainDllLoader {
302 public:
303 virtual std::wstring GetRegistryPath() {
304 std::wstring key(google_update::kRegPathClients);
[email protected]5f2cee82009-11-05 04:59:48305 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
306 key.append(L"\\").append(dist->GetAppGuid());
[email protected]4512f3ac2009-11-04 03:39:22307 return key;
308 }
309
[email protected]85769352010-11-16 14:24:30310 virtual void OnBeforeLaunch() {
[email protected]5f2cee82009-11-05 04:59:48311 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
312 RecordDidRun(dist->GetAppGuid().c_str());
[email protected]4512f3ac2009-11-04 03:39:22313 }
[email protected]42d7510c2009-12-19 01:48:30314
315 virtual int OnBeforeExit(int return_code) {
316 // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
317 // so we need to reset the did_run signal so omaha does not count
318 // this run as active usage.
319 if (ResultCodes::NORMAL_EXIT_CANCEL == return_code) {
320 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
321 ClearDidRun(dist->GetAppGuid().c_str());
322 }
323 return return_code;
324 }
[email protected]4512f3ac2009-11-04 03:39:22325};
326
327//=============================================================================
328
329class ChromiumDllLoader : public MainDllLoader {
330 public:
331 virtual std::wstring GetRegistryPath() {
[email protected]74d1eec2009-11-04 22:18:57332 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
333 return dist->GetVersionKey();
[email protected]4512f3ac2009-11-04 03:39:22334 }
335};
336
337MainDllLoader* MakeMainDllLoader() {
338#if defined(GOOGLE_CHROME_BUILD)
339 return new ChromeDllLoader();
340#else
341 return new ChromiumDllLoader();
342#endif
343}