blob: aa151815203f547ac93043fa408985c0d5ab8687 [file] [log] [blame]
[email protected]3b63f8f42011-03-28 01:54:151// Copyright (c) 2011 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"
[email protected]3b63f8f42011-03-28 01:54:1513#include "base/memory/scoped_ptr.h"
[email protected]2544a7a2011-03-14 18:25:1914#include "base/win/registry.h"
[email protected]36ee0322010-12-23 21:27:1215#include "base/string_util.h"
[email protected]ae0f0772010-08-13 04:54:1016#include "base/utf_string_conversions.h"
[email protected]85769352010-11-16 14:24:3017#include "base/version.h"
[email protected]4512f3ac2009-11-04 03:39:2218#include "chrome/app/breakpad_win.h"
19#include "chrome/app/client_util.h"
[email protected]03d21b82010-12-07 19:37:2220#include "chrome/common/chrome_constants.h"
[email protected]103607e2010-02-01 18:57:0921#include "chrome/common/chrome_switches.h"
[email protected]74d1eec2009-11-04 22:18:5722#include "chrome/installer/util/browser_distribution.h"
[email protected]2544a7a2011-03-14 18:25:1923#include "chrome/installer/util/channel_info.h"
[email protected]4512f3ac2009-11-04 03:39:2224#include "chrome/installer/util/install_util.h"
[email protected]2414e842008-11-07 01:27:5725#include "chrome/installer/util/google_update_constants.h"
[email protected]fd59f822011-05-12 18:07:1826#include "chrome/installer/util/google_update_settings.h"
[email protected]28c19692008-11-07 23:40:3827#include "chrome/installer/util/util_constants.h"
[email protected]4dd57932011-03-17 06:06:1228#include "content/common/result_codes.h"
[email protected]28c19692008-11-07 23:40:3829
30namespace {
[email protected]4512f3ac2009-11-04 03:39:2231// The entry point signature of chrome.dll.
32typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*, wchar_t*);
[email protected]28c19692008-11-07 23:40:3833
[email protected]3cdacd42010-04-30 18:55:5334typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc)();
35
[email protected]4512f3ac2009-11-04 03:39:2236// Gets chrome version according to the load path. |exe_path| must be the
37// backslash terminated directory of the current chrome.exe.
38bool GetVersion(const wchar_t* exe_path, const wchar_t* key_path,
39 std::wstring* version) {
[email protected]48d43872008-11-04 23:38:3240 HKEY reg_root = InstallUtil::IsPerUserInstall(exe_path) ? HKEY_CURRENT_USER :
[email protected]28c19692008-11-07 23:40:3841 HKEY_LOCAL_MACHINE;
[email protected]2544a7a2011-03-14 18:25:1942 bool success = false;
[email protected]28c19692008-11-07 23:40:3843
[email protected]2544a7a2011-03-14 18:25:1944 base::win::RegKey key(reg_root, key_path, KEY_QUERY_VALUE);
45 if (key.Valid()) {
46 // If 'new_chrome.exe' is present it means chrome was auto-updated while
47 // running. We need to consult the opv value so we can load the old dll.
48 // TODO(cpu) : This is solving the same problem as the environment variable
49 // so one of them will eventually be deprecated.
50 std::wstring new_chrome_exe(exe_path);
51 new_chrome_exe.append(installer::kChromeNewExe);
52 if (::PathFileExistsW(new_chrome_exe.c_str()) &&
53 key.ReadValue(google_update::kRegOldVersionField,
54 version) == ERROR_SUCCESS) {
55 success = true;
56 } else {
57 success = (key.ReadValue(google_update::kRegVersionField,
58 version) == ERROR_SUCCESS);
59 }
[email protected]2414e842008-11-07 01:27:5760 }
61
[email protected]2544a7a2011-03-14 18:25:1962 return success;
[email protected]c9349d082008-08-22 21:16:4763}
64
[email protected]4512f3ac2009-11-04 03:39:2265// Gets the path of the current exe with a trailing backslash.
[email protected]604004042009-10-21 23:10:2666std::wstring GetExecutablePath() {
[email protected]4512f3ac2009-11-04 03:39:2267 wchar_t path[MAX_PATH];
68 ::GetModuleFileNameW(NULL, path, MAX_PATH);
69 if (!::PathRemoveFileSpecW(path))
70 return std::wstring();
71 std::wstring exe_path(path);
72 return exe_path.append(L"\\");
[email protected]c9349d082008-08-22 21:16:4773}
74
[email protected]bae4e382010-08-22 04:44:0575// Not generic, we only handle strings up to 128 chars.
76bool EnvQueryStr(const wchar_t* key_name, std::wstring* value) {
77 wchar_t out[128];
78 DWORD count = sizeof(out)/sizeof(out[0]);
79 DWORD rv = ::GetEnvironmentVariableW(key_name, out, count);
80 if ((rv == 0) || (rv >= count))
81 return false;
82 *value = out;
83 return true;
84}
85
[email protected]4512f3ac2009-11-04 03:39:2286// Expects that |dir| has a trailing backslash. |dir| is modified so it
87// contains the full path that was tried. Caller must check for the return
[email protected]85769352010-11-16 14:24:3088// value not being null to determine if this path contains a valid dll.
[email protected]4512f3ac2009-11-04 03:39:2289HMODULE LoadChromeWithDirectory(std::wstring* dir) {
90 ::SetCurrentDirectoryW(dir->c_str());
[email protected]103607e2010-02-01 18:57:0991 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
[email protected]7b1bf812010-06-21 21:15:5392#ifdef _WIN64
[email protected]103607e2010-02-01 18:57:0993 if ((cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
94 switches::kNaClBrokerProcess) ||
95 (cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
96 switches::kNaClLoaderProcess)) {
97 // Load the 64-bit DLL when running in a 64-bit process.
[email protected]74ca0442010-12-15 14:44:5098 dir->append(installer::kChromeNaCl64Dll);
[email protected]103607e2010-02-01 18:57:0999 } else {
100 // Only NaCl broker and loader can be launched as Win64 processes.
101 NOTREACHED();
102 return NULL;
103 }
104#else
[email protected]74ca0442010-12-15 14:44:50105 dir->append(installer::kChromeDll);
[email protected]103607e2010-02-01 18:57:09106#endif
[email protected]00b73412010-06-21 21:01:36107
[email protected]58ac0242010-07-09 18:31:15108#ifdef NDEBUG
[email protected]00b73412010-06-21 21:01:36109 // Experimental pre-reading optimization
110 // The idea is to pre read significant portion of chrome.dll in advance
111 // so that subsequent hard page faults are avoided.
[email protected]bdf411b2010-07-13 22:21:59112 if (!cmd_line.HasSwitch(switches::kProcessType)) {
[email protected]85286b52010-07-03 06:14:45113 // The kernel brings in 8 pages for the code section at a time and 4 pages
114 // for other sections. We can skip over these pages to avoid a soft page
115 // fault which may not occur during code execution. However skipping 4K at
116 // a time still has better performance over 32K and 16K according to data.
117 // TODO(ananta)
118 // Investigate this and tune.
119 const size_t kStepSize = 4 * 1024;
120
121 DWORD pre_read_size = 0;
122 DWORD pre_read_step_size = kStepSize;
123 DWORD pre_read = 1;
124
[email protected]2544a7a2011-03-14 18:25:19125 base::win::RegKey key(HKEY_CURRENT_USER, L"Software\\Google\\ChromeFrame",
126 KEY_QUERY_VALUE);
127 if (key.Valid()) {
128 key.ReadValueDW(L"PreReadSize", &pre_read_size);
129 key.ReadValueDW(L"PreReadStepSize", &pre_read_step_size);
130 key.ReadValueDW(L"PreRead", &pre_read);
131 key.Close();
[email protected]00b73412010-06-21 21:01:36132 }
[email protected]85286b52010-07-03 06:14:45133 if (pre_read) {
[email protected]366ae242011-05-10 02:23:58134 TRACE_EVENT_BEGIN_ETW("PreReadImage", 0, "");
[email protected]85286b52010-07-03 06:14:45135 file_util::PreReadImage(dir->c_str(), pre_read_size, pre_read_step_size);
[email protected]366ae242011-05-10 02:23:58136 TRACE_EVENT_END_ETW("PreReadImage", 0, "");
[email protected]00b73412010-06-21 21:01:36137 }
138 }
[email protected]58ac0242010-07-09 18:31:15139#endif // NDEBUG
[email protected]00b73412010-06-21 21:01:36140
[email protected]4512f3ac2009-11-04 03:39:22141 return ::LoadLibraryExW(dir->c_str(), NULL,
142 LOAD_WITH_ALTERED_SEARCH_PATH);
143}
144
[email protected]2544a7a2011-03-14 18:25:19145void RecordDidRun(const std::wstring& dll_path) {
[email protected]2544a7a2011-03-14 18:25:19146 bool system_level = !InstallUtil::IsPerUserInstall(dll_path.c_str());
[email protected]fd59f822011-05-12 18:07:18147 GoogleUpdateSettings::UpdateDidRunState(true, system_level);
[email protected]4512f3ac2009-11-04 03:39:22148}
149
[email protected]2544a7a2011-03-14 18:25:19150void ClearDidRun(const std::wstring& dll_path) {
[email protected]2544a7a2011-03-14 18:25:19151 bool system_level = !InstallUtil::IsPerUserInstall(dll_path.c_str());
[email protected]fd59f822011-05-12 18:07:18152 GoogleUpdateSettings::UpdateDidRunState(false, system_level);
[email protected]2544a7a2011-03-14 18:25:19153}
[email protected]fd59f822011-05-12 18:07:18154
[email protected]42d7510c2009-12-19 01:48:30155}
[email protected]4512f3ac2009-11-04 03:39:22156//=============================================================================
157
158MainDllLoader::MainDllLoader() : dll_(NULL) {
159}
160
161MainDllLoader::~MainDllLoader() {
162#ifdef PURIFY
163 // We should never unload the dll. There is only risk and no gain from
164 // doing so. The singleton dtors have been already run by AtExitManager.
165 ::FreeLibrary(dll_);
166#endif
167}
168
[email protected]85769352010-11-16 14:24:30169// Loading chrome is an interesting affair. First we try loading from the
170// current directory to support run-what-you-compile and other development
171// scenarios.
172// If that fails then we look at the --chrome-version command line flag followed
173// by the 'CHROME_VERSION' env variable to determine if we should stick with an
174// older dll version even if a new one is available to support upgrade-in-place
175// scenarios.
176// If that fails then finally we look at the registry which should point us
177// to the latest version. This is the expected path for the first chrome.exe
178// browser instance in an installed build.
179HMODULE MainDllLoader::Load(std::wstring* out_version, std::wstring* out_file) {
[email protected]4512f3ac2009-11-04 03:39:22180 std::wstring dir(GetExecutablePath());
[email protected]85769352010-11-16 14:24:30181 *out_file = dir;
182 HMODULE dll = LoadChromeWithDirectory(out_file);
[email protected]4512f3ac2009-11-04 03:39:22183 if (dll)
184 return dll;
185
[email protected]36ee0322010-12-23 21:27:12186 std::wstring version_string;
[email protected]85769352010-11-16 14:24:30187 scoped_ptr<Version> version;
188 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
189 if (cmd_line.HasSwitch(switches::kChromeVersion)) {
[email protected]36ee0322010-12-23 21:27:12190 version_string = cmd_line.GetSwitchValueNative(switches::kChromeVersion);
191 version.reset(Version::GetVersionFromString(WideToASCII(version_string)));
[email protected]85769352010-11-16 14:24:30192
193 if (!version.get()) {
194 // If a bogus command line flag was given, then abort.
195 LOG(ERROR) << "Invalid version string received on command line: "
[email protected]36ee0322010-12-23 21:27:12196 << version_string;
[email protected]4512f3ac2009-11-04 03:39:22197 return NULL;
[email protected]85769352010-11-16 14:24:30198 }
[email protected]4512f3ac2009-11-04 03:39:22199 }
200
[email protected]85769352010-11-16 14:24:30201 if (!version.get()) {
[email protected]03d21b82010-12-07 19:37:22202 if (EnvQueryStr(ASCIIToWide(chrome::kChromeVersionEnvVar).c_str(),
[email protected]36ee0322010-12-23 21:27:12203 &version_string)) {
204 version.reset(Version::GetVersionFromString(WideToASCII(version_string)));
[email protected]85769352010-11-16 14:24:30205 }
206 }
207
208 if (!version.get()) {
209 std::wstring reg_path(GetRegistryPath());
210 // Look into the registry to find the latest version. We don't validate
211 // this by building a Version object to avoid harming normal case startup
212 // time.
[email protected]36ee0322010-12-23 21:27:12213 version_string.clear();
214 GetVersion(dir.c_str(), reg_path.c_str(), &version_string);
[email protected]85769352010-11-16 14:24:30215 }
216
[email protected]36ee0322010-12-23 21:27:12217 if (version.get() || !version_string.empty()) {
[email protected]85769352010-11-16 14:24:30218 *out_file = dir;
[email protected]36ee0322010-12-23 21:27:12219 *out_version = version_string;
[email protected]85769352010-11-16 14:24:30220 out_file->append(*out_version).append(L"\\");
[email protected]3e092a572011-05-02 20:47:08221 dll = LoadChromeWithDirectory(out_file);
222 if (!dll) {
223 LOG(ERROR) << "Failed to load Chrome DLL from " << out_file;
224 }
225 return dll;
[email protected]85769352010-11-16 14:24:30226 } else {
[email protected]3e092a572011-05-02 20:47:08227 LOG(ERROR) << "Could not get Chrome DLL version.";
[email protected]85769352010-11-16 14:24:30228 return NULL;
229 }
[email protected]4512f3ac2009-11-04 03:39:22230}
231
232// Launching is a matter of loading the right dll, setting the CHROME_VERSION
233// environment variable and just calling the entry point. Derived classes can
234// add custom code in the OnBeforeLaunch callback.
235int MainDllLoader::Launch(HINSTANCE instance,
236 sandbox::SandboxInterfaceInfo* sbox_info) {
237 std::wstring version;
238 std::wstring file;
239 dll_ = Load(&version, &file);
240 if (!dll_)
241 return ResultCodes::MISSING_DATA;
242
[email protected]ae0f0772010-08-13 04:54:10243 scoped_ptr<base::Environment> env(base::Environment::Create());
[email protected]03d21b82010-12-07 19:37:22244 env->SetVar(chrome::kChromeVersionEnvVar, WideToUTF8(version));
[email protected]4512f3ac2009-11-04 03:39:22245
246 InitCrashReporterWithDllPath(file);
[email protected]2544a7a2011-03-14 18:25:19247 OnBeforeLaunch(file);
[email protected]4512f3ac2009-11-04 03:39:22248
249 DLL_MAIN entry_point =
250 reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain"));
251 if (!entry_point)
252 return ResultCodes::BAD_PROCESS_TYPE;
253
[email protected]42d7510c2009-12-19 01:48:30254 int rc = entry_point(instance, sbox_info, ::GetCommandLineW());
[email protected]2544a7a2011-03-14 18:25:19255 return OnBeforeExit(rc, file);
[email protected]4512f3ac2009-11-04 03:39:22256}
257
[email protected]3cdacd42010-04-30 18:55:53258void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
259 RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function =
260 reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc>(
261 ::GetProcAddress(dll_,
262 "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
263 if (!relaunch_function) {
264 LOG(ERROR) << "Could not find exported function "
265 << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
266 } else {
267 relaunch_function();
268 }
269}
270
[email protected]4512f3ac2009-11-04 03:39:22271//=============================================================================
272
273class ChromeDllLoader : public MainDllLoader {
274 public:
275 virtual std::wstring GetRegistryPath() {
276 std::wstring key(google_update::kRegPathClients);
[email protected]5f2cee82009-11-05 04:59:48277 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
278 key.append(L"\\").append(dist->GetAppGuid());
[email protected]4512f3ac2009-11-04 03:39:22279 return key;
280 }
281
[email protected]2544a7a2011-03-14 18:25:19282 virtual void OnBeforeLaunch(const std::wstring& dll_path) {
283 RecordDidRun(dll_path);
[email protected]4512f3ac2009-11-04 03:39:22284 }
[email protected]42d7510c2009-12-19 01:48:30285
[email protected]2544a7a2011-03-14 18:25:19286 virtual int OnBeforeExit(int return_code, const std::wstring& dll_path) {
[email protected]42d7510c2009-12-19 01:48:30287 // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
288 // so we need to reset the did_run signal so omaha does not count
289 // this run as active usage.
290 if (ResultCodes::NORMAL_EXIT_CANCEL == return_code) {
[email protected]2544a7a2011-03-14 18:25:19291 ClearDidRun(dll_path);
[email protected]42d7510c2009-12-19 01:48:30292 }
293 return return_code;
294 }
[email protected]4512f3ac2009-11-04 03:39:22295};
296
297//=============================================================================
298
299class ChromiumDllLoader : public MainDllLoader {
300 public:
301 virtual std::wstring GetRegistryPath() {
[email protected]74d1eec2009-11-04 22:18:57302 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
303 return dist->GetVersionKey();
[email protected]4512f3ac2009-11-04 03:39:22304 }
305};
306
307MainDllLoader* MakeMainDllLoader() {
308#if defined(GOOGLE_CHROME_BUILD)
309 return new ChromeDllLoader();
310#else
311 return new ChromiumDllLoader();
312#endif
313}