blob: a47f3cb9416c69c755bd6a8a83907a941f55195f [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// 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]85286b52010-07-03 06:14:458#include "base/file_util.h"
[email protected]4512f3ac2009-11-04 03:39:229#include "chrome/app/breakpad_win.h"
10#include "chrome/app/client_util.h"
[email protected]103607e2010-02-01 18:57:0911#include "chrome/common/chrome_switches.h"
[email protected]4512f3ac2009-11-04 03:39:2212#include "chrome/common/result_codes.h"
[email protected]74d1eec2009-11-04 22:18:5713#include "chrome/installer/util/browser_distribution.h"
[email protected]4512f3ac2009-11-04 03:39:2214#include "chrome/installer/util/install_util.h"
[email protected]2414e842008-11-07 01:27:5715#include "chrome/installer/util/google_update_constants.h"
[email protected]28c19692008-11-07 23:40:3816#include "chrome/installer/util/util_constants.h"
17
18namespace {
[email protected]4512f3ac2009-11-04 03:39:2219// The entry point signature of chrome.dll.
20typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*, wchar_t*);
[email protected]28c19692008-11-07 23:40:3821
[email protected]3cdacd42010-04-30 18:55:5322typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc)();
23
[email protected]4512f3ac2009-11-04 03:39:2224// Not generic, we only handle strings up to 128 chars.
25bool ReadRegistryStr(HKEY key, const wchar_t* name, std::wstring* value) {
26 BYTE out[128 * sizeof(wchar_t)];
27 DWORD size = sizeof(out);
28 DWORD type = 0;
29 if (ERROR_SUCCESS != ::RegQueryValueExW(key, name, NULL, &type, out, &size))
[email protected]28c19692008-11-07 23:40:3830 return false;
[email protected]4512f3ac2009-11-04 03:39:2231 if (type != REG_SZ)
32 return false;
33 value->assign(reinterpret_cast<wchar_t*>(out));
[email protected]28c19692008-11-07 23:40:3834 return true;
35}
[email protected]c9349d082008-08-22 21:16:4736
[email protected]4512f3ac2009-11-04 03:39:2237// Gets chrome version according to the load path. |exe_path| must be the
38// backslash terminated directory of the current chrome.exe.
39bool GetVersion(const wchar_t* exe_path, const wchar_t* key_path,
40 std::wstring* version) {
[email protected]48d43872008-11-04 23:38:3241 HKEY reg_root = InstallUtil::IsPerUserInstall(exe_path) ? HKEY_CURRENT_USER :
[email protected]28c19692008-11-07 23:40:3842 HKEY_LOCAL_MACHINE;
[email protected]4512f3ac2009-11-04 03:39:2243 HKEY key;
44 if (::RegOpenKeyEx(reg_root, key_path, 0, KEY_READ, &key) != ERROR_SUCCESS)
[email protected]c9349d082008-08-22 21:16:4745 return false;
[email protected]28c19692008-11-07 23:40:3846
[email protected]4512f3ac2009-11-04 03:39:2247 // If 'new_chrome.exe' is present it means chrome was auto-updated while
48 // running. We need to consult the opv value so we can load the old dll.
49 // TODO(cpu) : This is solving the same problem as the environment variable
50 // so one of them will eventually be deprecated.
[email protected]28c19692008-11-07 23:40:3851 std::wstring new_chrome_exe(exe_path);
52 new_chrome_exe.append(installer_util::kChromeNewExe);
[email protected]4512f3ac2009-11-04 03:39:2253 if (::PathFileExistsW(new_chrome_exe.c_str()) &&
54 ReadRegistryStr(key, google_update::kRegOldVersionField, version)) {
55 ::RegCloseKey(key);
[email protected]28c19692008-11-07 23:40:3856 return true;
[email protected]2414e842008-11-07 01:27:5757 }
58
[email protected]4512f3ac2009-11-04 03:39:2259 bool ret = ReadRegistryStr(key, google_update::kRegVersionField, version);
60 ::RegCloseKey(key);
[email protected]c9349d082008-08-22 21:16:4761 return ret;
62}
63
[email protected]4512f3ac2009-11-04 03:39:2264// Gets the path of the current exe with a trailing backslash.
[email protected]604004042009-10-21 23:10:2665std::wstring GetExecutablePath() {
[email protected]4512f3ac2009-11-04 03:39:2266 wchar_t path[MAX_PATH];
67 ::GetModuleFileNameW(NULL, path, MAX_PATH);
68 if (!::PathRemoveFileSpecW(path))
69 return std::wstring();
70 std::wstring exe_path(path);
71 return exe_path.append(L"\\");
[email protected]c9349d082008-08-22 21:16:4772}
73
[email protected]4512f3ac2009-11-04 03:39:2274// Not generic, we only handle strings up to 128 chars.
75bool EnvQueryStr(const wchar_t* key_name, std::wstring* value) {
76 wchar_t out[128];
77 DWORD count = sizeof(out)/sizeof(out[0]);
78 DWORD rv = ::GetEnvironmentVariableW(key_name, out, count);
79 if ((rv == 0) || (rv >= count))
80 return false;
81 *value = out;
82 return true;
83}
84
[email protected]00b73412010-06-21 21:01:3685bool IsRunningHeadless() {
86 return (0 != ::GetEnvironmentVariableW(L"CHROME_HEADLESS", NULL, 0));
87}
88
[email protected]4512f3ac2009-11-04 03:39:2289// Expects that |dir| has a trailing backslash. |dir| is modified so it
90// contains the full path that was tried. Caller must check for the return
91// value not being null to dermine if this path contains a valid dll.
92HMODULE LoadChromeWithDirectory(std::wstring* dir) {
93 ::SetCurrentDirectoryW(dir->c_str());
[email protected]103607e2010-02-01 18:57:0994 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
[email protected]7b1bf812010-06-21 21:15:5395#ifdef _WIN64
[email protected]103607e2010-02-01 18:57:0996 if ((cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
97 switches::kNaClBrokerProcess) ||
98 (cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
99 switches::kNaClLoaderProcess)) {
100 // Load the 64-bit DLL when running in a 64-bit process.
101 dir->append(installer_util::kChromeNaCl64Dll);
102 } else {
103 // Only NaCl broker and loader can be launched as Win64 processes.
104 NOTREACHED();
105 return NULL;
106 }
107#else
[email protected]4512f3ac2009-11-04 03:39:22108 dir->append(installer_util::kChromeDll);
[email protected]103607e2010-02-01 18:57:09109#endif
[email protected]00b73412010-06-21 21:01:36110
111 // Experimental pre-reading optimization
112 // The idea is to pre read significant portion of chrome.dll in advance
113 // so that subsequent hard page faults are avoided.
[email protected]00b73412010-06-21 21:01:36114 if (!cmd_line.HasSwitch(switches::kProcessType) &&
115 (IsRunningHeadless() || InstallUtil::IsChromeFrameProcess())) {
[email protected]85286b52010-07-03 06:14:45116 // The kernel brings in 8 pages for the code section at a time and 4 pages
117 // for other sections. We can skip over these pages to avoid a soft page
118 // fault which may not occur during code execution. However skipping 4K at
119 // a time still has better performance over 32K and 16K according to data.
120 // TODO(ananta)
121 // Investigate this and tune.
122 const size_t kStepSize = 4 * 1024;
123
124 DWORD pre_read_size = 0;
125 DWORD pre_read_step_size = kStepSize;
126 DWORD pre_read = 1;
127
[email protected]00b73412010-06-21 21:01:36128 HKEY key = NULL;
129 if (::RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Google\\ChromeFrame",
130 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
[email protected]85286b52010-07-03 06:14:45131 DWORD unused = sizeof(pre_read_size);
132 RegQueryValueEx(key, L"PreReadSize", NULL, NULL,
133 reinterpret_cast<LPBYTE>(&pre_read_size), &unused);
134 RegQueryValueEx(key, L"PreReadStepSize", NULL, NULL,
135 reinterpret_cast<LPBYTE>(&pre_read_step_size), &unused);
136 RegQueryValueEx(key, L"PreRead", NULL, NULL,
137 reinterpret_cast<LPBYTE>(&pre_read), &unused);
[email protected]00b73412010-06-21 21:01:36138 RegCloseKey(key);
139 key = NULL;
[email protected]00b73412010-06-21 21:01:36140 }
[email protected]85286b52010-07-03 06:14:45141 if (pre_read) {
142 file_util::PreReadImage(dir->c_str(), pre_read_size, pre_read_step_size);
[email protected]00b73412010-06-21 21:01:36143 }
144 }
145
[email protected]4512f3ac2009-11-04 03:39:22146 return ::LoadLibraryExW(dir->c_str(), NULL,
147 LOAD_WITH_ALTERED_SEARCH_PATH);
148}
149
[email protected]42d7510c2009-12-19 01:48:30150// Set did_run "dr" in omaha's client state for this product.
151bool SetDidRunState(const wchar_t* guid, const wchar_t* value) {
[email protected]4512f3ac2009-11-04 03:39:22152 std::wstring key_path(google_update::kRegPathClientState);
153 key_path.append(L"\\").append(guid);
154 HKEY reg_key;
155 if (::RegCreateKeyExW(HKEY_CURRENT_USER, key_path.c_str(), 0, NULL,
156 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
157 &reg_key, NULL) == ERROR_SUCCESS) {
[email protected]203c6872010-01-30 15:59:45158 // Note that the length here must be in bytes and must account for the
159 // terminating null char.
[email protected]4512f3ac2009-11-04 03:39:22160 ::RegSetValueExW(reg_key, google_update::kRegDidRunField, 0, REG_SZ,
[email protected]203c6872010-01-30 15:59:45161 reinterpret_cast<const BYTE *>(value),
162 (::lstrlenW(value) + 1) * sizeof(wchar_t));
[email protected]4512f3ac2009-11-04 03:39:22163 ::RegCloseKey(reg_key);
164 return true;
165 }
166 return false;
167}
[email protected]42d7510c2009-12-19 01:48:30168
169bool RecordDidRun(const wchar_t* guid) {
170 return SetDidRunState(guid, L"1");
[email protected]4512f3ac2009-11-04 03:39:22171}
172
[email protected]42d7510c2009-12-19 01:48:30173bool ClearDidRun(const wchar_t* guid) {
174 return SetDidRunState(guid, L"0");
175}
176
177}
[email protected]4512f3ac2009-11-04 03:39:22178//=============================================================================
179
180MainDllLoader::MainDllLoader() : dll_(NULL) {
181}
182
183MainDllLoader::~MainDllLoader() {
184#ifdef PURIFY
185 // We should never unload the dll. There is only risk and no gain from
186 // doing so. The singleton dtors have been already run by AtExitManager.
187 ::FreeLibrary(dll_);
188#endif
189}
190
191// Loading chrome is an interesting afair. First we try loading from the current
192// directory to support run-what-you-compile and other development scenarios.
193// If that fails then we look at the 'CHROME_VERSION' env variable to determine
194// if we should stick with an older dll version even if a new one is available
195// to support upgrade-in-place scenarios, and if that fails we finally we look
196// at the registry which should point us to the latest version.
197HMODULE MainDllLoader::Load(std::wstring* version, std::wstring* file) {
198 std::wstring dir(GetExecutablePath());
199 *file = dir;
200 HMODULE dll = LoadChromeWithDirectory(file);
201 if (dll)
202 return dll;
203
[email protected]9bfc86d2010-05-19 00:06:23204 if (!EnvQueryStr(
205 BrowserDistribution::GetDistribution()->GetEnvVersionKey().c_str(),
206 version)) {
[email protected]4512f3ac2009-11-04 03:39:22207 std::wstring reg_path(GetRegistryPath());
208 // Look into the registry to find the latest version.
209 if (!GetVersion(dir.c_str(), reg_path.c_str(), version))
210 return NULL;
211 }
212
213 *file = dir;
[email protected]f244388d2009-11-04 05:33:28214 file->append(*version).append(L"\\");
[email protected]4512f3ac2009-11-04 03:39:22215 return LoadChromeWithDirectory(file);
216}
217
218// Launching is a matter of loading the right dll, setting the CHROME_VERSION
219// environment variable and just calling the entry point. Derived classes can
220// add custom code in the OnBeforeLaunch callback.
221int MainDllLoader::Launch(HINSTANCE instance,
222 sandbox::SandboxInterfaceInfo* sbox_info) {
223 std::wstring version;
224 std::wstring file;
225 dll_ = Load(&version, &file);
226 if (!dll_)
227 return ResultCodes::MISSING_DATA;
228
[email protected]9bfc86d2010-05-19 00:06:23229 ::SetEnvironmentVariableW(
230 BrowserDistribution::GetDistribution()->GetEnvVersionKey().c_str(),
231 version.c_str());
[email protected]4512f3ac2009-11-04 03:39:22232
233 InitCrashReporterWithDllPath(file);
[email protected]4512f3ac2009-11-04 03:39:22234 OnBeforeLaunch(version);
235
236 DLL_MAIN entry_point =
237 reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain"));
238 if (!entry_point)
239 return ResultCodes::BAD_PROCESS_TYPE;
240
[email protected]42d7510c2009-12-19 01:48:30241 int rc = entry_point(instance, sbox_info, ::GetCommandLineW());
242 return OnBeforeExit(rc);
[email protected]4512f3ac2009-11-04 03:39:22243}
244
[email protected]3cdacd42010-04-30 18:55:53245void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
246 RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function =
247 reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc>(
248 ::GetProcAddress(dll_,
249 "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
250 if (!relaunch_function) {
251 LOG(ERROR) << "Could not find exported function "
252 << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
253 } else {
254 relaunch_function();
255 }
256}
257
[email protected]4512f3ac2009-11-04 03:39:22258//=============================================================================
259
260class ChromeDllLoader : public MainDllLoader {
261 public:
262 virtual std::wstring GetRegistryPath() {
263 std::wstring key(google_update::kRegPathClients);
[email protected]5f2cee82009-11-05 04:59:48264 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
265 key.append(L"\\").append(dist->GetAppGuid());
[email protected]4512f3ac2009-11-04 03:39:22266 return key;
267 }
268
269 virtual void OnBeforeLaunch(const std::wstring& version) {
[email protected]5f2cee82009-11-05 04:59:48270 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
271 RecordDidRun(dist->GetAppGuid().c_str());
[email protected]4512f3ac2009-11-04 03:39:22272 }
[email protected]42d7510c2009-12-19 01:48:30273
274 virtual int OnBeforeExit(int return_code) {
275 // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
276 // so we need to reset the did_run signal so omaha does not count
277 // this run as active usage.
278 if (ResultCodes::NORMAL_EXIT_CANCEL == return_code) {
279 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
280 ClearDidRun(dist->GetAppGuid().c_str());
281 }
282 return return_code;
283 }
[email protected]4512f3ac2009-11-04 03:39:22284};
285
286//=============================================================================
287
288class ChromiumDllLoader : public MainDllLoader {
289 public:
290 virtual std::wstring GetRegistryPath() {
[email protected]74d1eec2009-11-04 22:18:57291 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
292 return dist->GetVersionKey();
[email protected]4512f3ac2009-11-04 03:39:22293 }
294};
295
296MainDllLoader* MakeMainDllLoader() {
297#if defined(GOOGLE_CHROME_BUILD)
298 return new ChromeDllLoader();
299#else
300 return new ChromiumDllLoader();
301#endif
302}