blob: d78feb26295c184b14116c84efbed3ff690100b6 [file] [log] [blame]
[email protected]a21d8082012-01-12 19:23:201// Copyright (c) 2012 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]6a0464c82012-08-07 05:58:2812#include "base/file_version_info.h"
[email protected]ae0f0772010-08-13 04:54:1013#include "base/logging.h"
[email protected]3b63f8f42011-03-28 01:54:1514#include "base/memory/scoped_ptr.h"
[email protected]28f576f2011-08-26 20:46:5515#include "base/rand_util.h" // For PreRead experiment.
[email protected]5bca9ee2011-09-03 00:18:4216#include "base/sha1.h" // For PreRead experiment.
[email protected]6a0464c82012-08-07 05:58:2817#include "base/string16.h"
[email protected]36ee0322010-12-23 21:27:1218#include "base/string_util.h"
[email protected]48078e52012-02-09 00:02:2619#include "base/stringprintf.h"
[email protected]ae0f0772010-08-13 04:54:1020#include "base/utf_string_conversions.h"
[email protected]85769352010-11-16 14:24:3021#include "base/version.h"
[email protected]85acecb2011-08-05 05:28:0522#include "base/win/registry.h"
[email protected]4512f3ac2009-11-04 03:39:2223#include "chrome/app/breakpad_win.h"
24#include "chrome/app/client_util.h"
[email protected]6bb2628d2012-02-03 19:55:1125#include "chrome/app/image_pre_reader_win.h"
[email protected]03d21b82010-12-07 19:37:2226#include "chrome/common/chrome_constants.h"
[email protected]1fcfb202011-07-19 19:53:1427#include "chrome/common/chrome_result_codes.h"
[email protected]103607e2010-02-01 18:57:0928#include "chrome/common/chrome_switches.h"
[email protected]e3824ec2013-02-08 01:02:4029#include "chrome/common/env_vars.h"
[email protected]74d1eec2009-11-04 22:18:5730#include "chrome/installer/util/browser_distribution.h"
[email protected]2544a7a2011-03-14 18:25:1931#include "chrome/installer/util/channel_info.h"
[email protected]4512f3ac2009-11-04 03:39:2232#include "chrome/installer/util/install_util.h"
[email protected]2414e842008-11-07 01:27:5733#include "chrome/installer/util/google_update_constants.h"
[email protected]fd59f822011-05-12 18:07:1834#include "chrome/installer/util/google_update_settings.h"
[email protected]28c19692008-11-07 23:40:3835#include "chrome/installer/util/util_constants.h"
36
37namespace {
[email protected]4512f3ac2009-11-04 03:39:2238// The entry point signature of chrome.dll.
[email protected]d85d6702011-09-01 15:51:5339typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*);
[email protected]28c19692008-11-07 23:40:3840
[email protected]3cdacd42010-04-30 18:55:5341typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc)();
42
[email protected]4512f3ac2009-11-04 03:39:2243// Gets chrome version according to the load path. |exe_path| must be the
44// backslash terminated directory of the current chrome.exe.
[email protected]f068b412012-04-02 20:24:4845bool GetChromeVersion(const wchar_t* exe_dir, const wchar_t* key_path,
[email protected]6a0464c82012-08-07 05:58:2846 string16* version) {
[email protected]f068b412012-04-02 20:24:4847 HKEY reg_root = InstallUtil::IsPerUserInstall(exe_dir) ? HKEY_CURRENT_USER :
[email protected]a049b24a2012-06-13 17:37:1348 HKEY_LOCAL_MACHINE;
[email protected]2544a7a2011-03-14 18:25:1949 bool success = false;
[email protected]28c19692008-11-07 23:40:3850
[email protected]2544a7a2011-03-14 18:25:1951 base::win::RegKey key(reg_root, key_path, KEY_QUERY_VALUE);
52 if (key.Valid()) {
53 // If 'new_chrome.exe' is present it means chrome was auto-updated while
54 // running. We need to consult the opv value so we can load the old dll.
55 // TODO(cpu) : This is solving the same problem as the environment variable
56 // so one of them will eventually be deprecated.
[email protected]6a0464c82012-08-07 05:58:2857 string16 new_chrome_exe(exe_dir);
[email protected]2544a7a2011-03-14 18:25:1958 new_chrome_exe.append(installer::kChromeNewExe);
59 if (::PathFileExistsW(new_chrome_exe.c_str()) &&
60 key.ReadValue(google_update::kRegOldVersionField,
61 version) == ERROR_SUCCESS) {
62 success = true;
63 } else {
64 success = (key.ReadValue(google_update::kRegVersionField,
65 version) == ERROR_SUCCESS);
66 }
[email protected]2414e842008-11-07 01:27:5767 }
68
[email protected]2544a7a2011-03-14 18:25:1969 return success;
[email protected]c9349d082008-08-22 21:16:4770}
71
[email protected]bae4e382010-08-22 04:44:0572// Not generic, we only handle strings up to 128 chars.
[email protected]6a0464c82012-08-07 05:58:2873bool EnvQueryStr(const wchar_t* key_name, string16* value) {
[email protected]bae4e382010-08-22 04:44:0574 wchar_t out[128];
75 DWORD count = sizeof(out)/sizeof(out[0]);
76 DWORD rv = ::GetEnvironmentVariableW(key_name, out, count);
77 if ((rv == 0) || (rv >= count))
78 return false;
79 *value = out;
80 return true;
81}
82
[email protected]28f576f2011-08-26 20:46:5583#if defined(OS_WIN)
[email protected]5bca9ee2011-09-03 00:18:4284#if defined(GOOGLE_CHROME_BUILD)
[email protected]28f576f2011-08-26 20:46:5585// These constants are used by the PreRead experiment.
86const wchar_t kPreReadRegistryValue[] = L"PreReadExperimentGroup";
87const int kPreReadExpiryYear = 2012;
[email protected]a21d8082012-01-12 19:23:2088const int kPreReadExpiryMonth = 7;
[email protected]28f576f2011-08-26 20:46:5589const int kPreReadExpiryDay = 1;
90
[email protected]48078e52012-02-09 00:02:2691// These are control values.
92const DWORD kPreReadExperimentFull = 100;
93const DWORD kPreReadExperimentNone = 0;
94
95// Modulate these for different experiments. These values should be a multiple
96// of 5 between 0 and 100, exclusive.
97const DWORD kPreReadExperimentA = 25;
98const DWORD kPreReadExperimentB = 40;
99
100void StaticAssertions() {
101 COMPILE_ASSERT(
102 kPreReadExperimentA <= 100, kPreReadExperimentA_exceeds_100);
103 COMPILE_ASSERT(
104 kPreReadExperimentA % 5 == 0, kPreReadExperimentA_is_not_a_multiple_of_5);
105 COMPILE_ASSERT(
106 kPreReadExperimentB <= 100, kPreReadExperimentB_exceeds_100);
107 COMPILE_ASSERT(
108 kPreReadExperimentB % 5 == 0, kPreReadExperimentB_is_not_a_multiple_of_5);
109}
110
111bool PreReadExperimentShouldRun() {
[email protected]28f576f2011-08-26 20:46:55112 base::Time::Exploded exploded = { 0 };
113 exploded.year = kPreReadExpiryYear;
114 exploded.month = kPreReadExpiryMonth;
115 exploded.day_of_month = kPreReadExpiryDay;
116
117 base::Time expiration_time = base::Time::FromLocalExploded(exploded);
118
119 // Get the build time. This code is copied from
120 // base::FieldTrial::GetBuildTime. We can't use MetricsLogBase::GetBuildTime
121 // because that's in seconds since Unix epoch, which base::Time can't use.
122 base::Time build_time;
123 const char* kDateTime = __DATE__ " " __TIME__;
124 bool result = base::Time::FromString(kDateTime, &build_time);
125 DCHECK(result);
126
127 // If the experiment is expired, don't run it.
[email protected]48078e52012-02-09 00:02:26128 return (build_time <= expiration_time);
[email protected]28f576f2011-08-26 20:46:55129}
130
[email protected]48078e52012-02-09 00:02:26131// For channels with small populations we just divide the population evenly
132// across the 4 buckets.
133DWORD GetSmallPopulationPreReadBucket(double rand_unit) {
134 DCHECK_GE(rand_unit, 0.0);
135 DCHECK_LT(rand_unit, 1.0);
136 if (rand_unit < 0.25 || rand_unit > 1.0)
137 return kPreReadExperimentFull; // The default pre-read amount.
138 if (rand_unit < 0.50)
139 return kPreReadExperimentA;
140 if (rand_unit < 0.75)
141 return kPreReadExperimentB;
142 return kPreReadExperimentNone;
143}
[email protected]28f576f2011-08-26 20:46:55144
[email protected]48078e52012-02-09 00:02:26145// For channels with large populations, we allocate a small percentage of the
146// population to each of the experimental buckets, and the rest to the current
147// default pre-read behaviour
148DWORD GetLargePopulationPreReadBucket(double rand_unit) {
149 DCHECK_GE(rand_unit, 0.0);
150 DCHECK_LT(rand_unit, 1.0);
151 if (rand_unit < 0.97 || rand_unit > 1.0)
152 return kPreReadExperimentFull; // The default pre-read amount.
153 if (rand_unit < 0.98)
154 return kPreReadExperimentA;
155 if (rand_unit < 0.99)
156 return kPreReadExperimentB;
157 return kPreReadExperimentNone;
158}
159
160// Returns true and the |pre_read_percentage| IFF the experiment should run.
161// Otherwise, returns false and |pre_read_percentage| is not modified.
162bool GetPreReadExperimentGroup(DWORD* pre_read_percentage) {
163 DCHECK(pre_read_percentage != NULL);
164
165 // Check if the experiment has expired.
166 if (!PreReadExperimentShouldRun())
[email protected]28f576f2011-08-26 20:46:55167 return false;
[email protected]28f576f2011-08-26 20:46:55168
[email protected]5bca9ee2011-09-03 00:18:42169 // Get the MetricsId of the installation. This is only set if the user has
170 // opted in to reporting. Doing things this way ensures that we only enable
171 // the experiment if its results are actually going to be reported.
[email protected]6a0464c82012-08-07 05:58:28172 string16 metrics_id;
[email protected]5bca9ee2011-09-03 00:18:42173 if (!GoogleUpdateSettings::GetMetricsId(&metrics_id) || metrics_id.empty())
174 return false;
[email protected]28f576f2011-08-26 20:46:55175
[email protected]5bca9ee2011-09-03 00:18:42176 // We use the same technique as FieldTrial::HashClientId.
[email protected]b13b9bd2011-09-28 21:15:27177 unsigned char sha1_hash[base::kSHA1Length];
[email protected]5bca9ee2011-09-03 00:18:42178 base::SHA1HashBytes(
179 reinterpret_cast<const unsigned char*>(metrics_id.c_str()),
180 metrics_id.size() * sizeof(metrics_id[0]),
181 sha1_hash);
182 COMPILE_ASSERT(sizeof(uint64) < sizeof(sha1_hash), need_more_data);
183 uint64* bits = reinterpret_cast<uint64*>(&sha1_hash[0]);
184 double rand_unit = base::BitsToOpenEndedUnitInterval(*bits);
[email protected]28f576f2011-08-26 20:46:55185
[email protected]48078e52012-02-09 00:02:26186 // We carve up the bucket sizes based on the population of the channel.
187 const string16 channel(
188 GoogleUpdateSettings::GetChromeChannel(
189 GoogleUpdateSettings::IsSystemInstall()));
190
191 // For our purposes, Stable has a large population, everything else is small.
192 *pre_read_percentage = (channel == installer::kChromeChannelStable ?
193 GetLargePopulationPreReadBucket(rand_unit) :
194 GetSmallPopulationPreReadBucket(rand_unit));
[email protected]5bca9ee2011-09-03 00:18:42195
196 return true;
[email protected]28f576f2011-08-26 20:46:55197}
[email protected]5bca9ee2011-09-03 00:18:42198#endif // if defined(GOOGLE_CHROME_BUILD)
[email protected]28f576f2011-08-26 20:46:55199#endif // if defined(OS_WIN)
200
[email protected]4512f3ac2009-11-04 03:39:22201// Expects that |dir| has a trailing backslash. |dir| is modified so it
202// contains the full path that was tried. Caller must check for the return
[email protected]85769352010-11-16 14:24:30203// value not being null to determine if this path contains a valid dll.
[email protected]6a0464c82012-08-07 05:58:28204HMODULE LoadChromeWithDirectory(string16* dir) {
[email protected]4512f3ac2009-11-04 03:39:22205 ::SetCurrentDirectoryW(dir->c_str());
[email protected]103607e2010-02-01 18:57:09206 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
[email protected]74ca0442010-12-15 14:44:50207 dir->append(installer::kChromeDll);
[email protected]00b73412010-06-21 21:01:36208
[email protected]88ca34a2011-07-19 12:57:58209#ifndef WIN_DISABLE_PREREAD
[email protected]58ac0242010-07-09 18:31:15210#ifdef NDEBUG
[email protected]00b73412010-06-21 21:01:36211 // Experimental pre-reading optimization
[email protected]88ca34a2011-07-19 12:57:58212 // The idea is to pre-read a significant portion of chrome.dll in advance
[email protected]00b73412010-06-21 21:01:36213 // so that subsequent hard page faults are avoided.
[email protected]88ca34a2011-07-19 12:57:58214 //
215 // Pre-read may be disabled at compile time by defining WIN_DISABLE_PREREAD,
216 // but by default it is enabled in release builds. The ability to disable it
217 // is useful for evaluating competing optimization techniques.
[email protected]bdf411b2010-07-13 22:21:59218 if (!cmd_line.HasSwitch(switches::kProcessType)) {
[email protected]85286b52010-07-03 06:14:45219 // The kernel brings in 8 pages for the code section at a time and 4 pages
220 // for other sections. We can skip over these pages to avoid a soft page
221 // fault which may not occur during code execution. However skipping 4K at
222 // a time still has better performance over 32K and 16K according to data.
[email protected]88ca34a2011-07-19 12:57:58223 // TODO(ananta): Investigate this and tune.
[email protected]85286b52010-07-03 06:14:45224 const size_t kStepSize = 4 * 1024;
225
[email protected]6bb2628d2012-02-03 19:55:11226 // We hypothesize that pre-reading only the bytes actually touched during
227 // startup should improve startup time. The Syzygy toolchain attempts to
228 // optimize the binary layout of chrome.dll, rearranging the code and data
229 // blocks such that temporally related blocks (i.e., code and data used in
230 // startup, browser, renderer, etc) are grouped together, and that blocks
231 // used early in the process lifecycle occur earlier in their sections.
[email protected]48078e52012-02-09 00:02:26232 DWORD pre_read_percentage = 100;
[email protected]85286b52010-07-03 06:14:45233 DWORD pre_read_step_size = kStepSize;
[email protected]48078e52012-02-09 00:02:26234 bool is_eligible_for_experiment = true;
[email protected]85286b52010-07-03 06:14:45235
[email protected]85acecb2011-08-05 05:28:05236 // TODO(chrisha): This path should not be ChromeFrame specific, and it
237 // should not be hard-coded with 'Google' in the path. Rather, it should
238 // use the product name.
[email protected]48078e52012-02-09 00:02:26239 base::win::RegKey key(HKEY_CURRENT_USER,
240 L"Software\\Google\\ChromeFrame",
[email protected]2544a7a2011-03-14 18:25:19241 KEY_QUERY_VALUE);
[email protected]48078e52012-02-09 00:02:26242
243 // Check if there are any pre-read settings in the registry. If so, then
244 // the pre-read settings have been forcibly set and this instance is not
245 // eligible for the pre-read experiment.
[email protected]2544a7a2011-03-14 18:25:19246 if (key.Valid()) {
[email protected]48078e52012-02-09 00:02:26247 DWORD value = 0;
248 if (key.ReadValueDW(L"PreRead", &value) == ERROR_SUCCESS) {
249 is_eligible_for_experiment = false;
250 pre_read_percentage = (value != 0) ? 100 : 0;
251 }
252
253 if (key.ReadValueDW(L"PreReadPercentage", &value) == ERROR_SUCCESS) {
254 is_eligible_for_experiment = false;
255 pre_read_percentage = value;
256 }
257
258 if (key.ReadValueDW(L"PreReadStepSize", &value) == ERROR_SUCCESS) {
259 is_eligible_for_experiment = false;
260 pre_read_step_size = value;
[email protected]6bb2628d2012-02-03 19:55:11261 }
[email protected]2544a7a2011-03-14 18:25:19262 key.Close();
[email protected]00b73412010-06-21 21:01:36263 }
[email protected]85acecb2011-08-05 05:28:05264
[email protected]5bca9ee2011-09-03 00:18:42265#if defined(OS_WIN)
266#if defined(GOOGLE_CHROME_BUILD)
[email protected]28f576f2011-08-26 20:46:55267 // The PreRead experiment is unable to use the standard FieldTrial
268 // mechanism as pre-reading happens in chrome.exe prior to loading
269 // chrome.dll. As such, we use a custom approach. If the experiment is
[email protected]48078e52012-02-09 00:02:26270 // running (not expired) then we look to the registry for the BreakPad/UMA
271 // metricsid. We use this to seed a random unit, and select a bucket
272 // (percentage to pre-read) for the experiment. The selected bucket is
273 // communicated to chrome.dll via an environment variable, which alerts
274 // chrome.dll that the experiment is running, causing it to report
275 // sub-histogram results.
[email protected]6bb2628d2012-02-03 19:55:11276 //
277 // If we've read pre-read settings from the registry, then someone has
[email protected]48078e52012-02-09 00:02:26278 // specifically forced their pre-read options and is not participating in
[email protected]6bb2628d2012-02-03 19:55:11279 // the experiment.
280 //
[email protected]5bca9ee2011-09-03 00:18:42281 // If the experiment is running, indicate it to chrome.dll via an
[email protected]48078e52012-02-09 00:02:26282 // environment variable that contains the percentage of chrome that
283 // was pre-read. Allowable values are all multiples of 5 between
284 // 0 and 100, inclusive.
285 if (is_eligible_for_experiment &&
286 GetPreReadExperimentGroup(&pre_read_percentage)) {
287 DCHECK_LE(pre_read_percentage, 100U);
288 DCHECK_EQ(pre_read_percentage % 5, 0U);
[email protected]5bca9ee2011-09-03 00:18:42289 scoped_ptr<base::Environment> env(base::Environment::Create());
290 env->SetVar(chrome::kPreReadEnvironmentVariable,
[email protected]48078e52012-02-09 00:02:26291 base::StringPrintf("%d", pre_read_percentage));
[email protected]28f576f2011-08-26 20:46:55292 }
[email protected]5bca9ee2011-09-03 00:18:42293#endif // if defined(GOOGLE_CHROME_BUILD)
294#endif // if defined(OS_WIN)
[email protected]28f576f2011-08-26 20:46:55295
[email protected]6bb2628d2012-02-03 19:55:11296 // Clamp the DWORD percentage to fit into a uint8 that's <= 100.
[email protected]48078e52012-02-09 00:02:26297 pre_read_percentage = std::min(pre_read_percentage, 100UL);
[email protected]6bb2628d2012-02-03 19:55:11298
299 // Perform the full or partial pre-read.
300 TRACE_EVENT_BEGIN_ETW("PreReadImage", 0, "");
301 ImagePreReader::PartialPreReadImage(dir->c_str(),
[email protected]48078e52012-02-09 00:02:26302 static_cast<uint8>(pre_read_percentage),
[email protected]6bb2628d2012-02-03 19:55:11303 pre_read_step_size);
304 TRACE_EVENT_END_ETW("PreReadImage", 0, "");
[email protected]00b73412010-06-21 21:01:36305 }
[email protected]58ac0242010-07-09 18:31:15306#endif // NDEBUG
[email protected]88ca34a2011-07-19 12:57:58307#endif // WIN_DISABLE_PREREAD
[email protected]00b73412010-06-21 21:01:36308
[email protected]4512f3ac2009-11-04 03:39:22309 return ::LoadLibraryExW(dir->c_str(), NULL,
310 LOAD_WITH_ALTERED_SEARCH_PATH);
311}
312
[email protected]6a0464c82012-08-07 05:58:28313void RecordDidRun(const string16& dll_path) {
[email protected]2544a7a2011-03-14 18:25:19314 bool system_level = !InstallUtil::IsPerUserInstall(dll_path.c_str());
[email protected]fd59f822011-05-12 18:07:18315 GoogleUpdateSettings::UpdateDidRunState(true, system_level);
[email protected]4512f3ac2009-11-04 03:39:22316}
317
[email protected]6a0464c82012-08-07 05:58:28318void ClearDidRun(const string16& dll_path) {
[email protected]2544a7a2011-03-14 18:25:19319 bool system_level = !InstallUtil::IsPerUserInstall(dll_path.c_str());
[email protected]fd59f822011-05-12 18:07:18320 GoogleUpdateSettings::UpdateDidRunState(false, system_level);
[email protected]2544a7a2011-03-14 18:25:19321}
[email protected]fd59f822011-05-12 18:07:18322
[email protected]28f576f2011-08-26 20:46:55323} // namespace
[email protected]f068b412012-04-02 20:24:48324
[email protected]6a0464c82012-08-07 05:58:28325string16 GetExecutablePath() {
[email protected]f068b412012-04-02 20:24:48326 wchar_t path[MAX_PATH];
327 ::GetModuleFileNameW(NULL, path, MAX_PATH);
328 if (!::PathRemoveFileSpecW(path))
[email protected]6a0464c82012-08-07 05:58:28329 return string16();
330 string16 exe_path(path);
[email protected]f068b412012-04-02 20:24:48331 return exe_path.append(1, L'\\');
332}
333
[email protected]4512f3ac2009-11-04 03:39:22334//=============================================================================
335
336MainDllLoader::MainDllLoader() : dll_(NULL) {
337}
338
339MainDllLoader::~MainDllLoader() {
[email protected]4512f3ac2009-11-04 03:39:22340}
341
[email protected]85769352010-11-16 14:24:30342// Loading chrome is an interesting affair. First we try loading from the
343// current directory to support run-what-you-compile and other development
344// scenarios.
345// If that fails then we look at the --chrome-version command line flag followed
346// by the 'CHROME_VERSION' env variable to determine if we should stick with an
347// older dll version even if a new one is available to support upgrade-in-place
348// scenarios.
349// If that fails then finally we look at the registry which should point us
350// to the latest version. This is the expected path for the first chrome.exe
351// browser instance in an installed build.
[email protected]6a0464c82012-08-07 05:58:28352HMODULE MainDllLoader::Load(string16* out_version, string16* out_file) {
353 const string16 dir(GetExecutablePath());
[email protected]85769352010-11-16 14:24:30354 *out_file = dir;
[email protected]5bca9ee2011-09-03 00:18:42355 HMODULE dll = LoadChromeWithDirectory(out_file);
[email protected]4512f3ac2009-11-04 03:39:22356 if (dll)
357 return dll;
358
[email protected]6a0464c82012-08-07 05:58:28359 string16 version_string;
[email protected]12126d372012-07-11 18:40:53360 Version version;
[email protected]85769352010-11-16 14:24:30361 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
362 if (cmd_line.HasSwitch(switches::kChromeVersion)) {
[email protected]36ee0322010-12-23 21:27:12363 version_string = cmd_line.GetSwitchValueNative(switches::kChromeVersion);
[email protected]12126d372012-07-11 18:40:53364 version = Version(WideToASCII(version_string));
[email protected]85769352010-11-16 14:24:30365
[email protected]12126d372012-07-11 18:40:53366 if (!version.IsValid()) {
[email protected]85769352010-11-16 14:24:30367 // If a bogus command line flag was given, then abort.
[email protected]f068b412012-04-02 20:24:48368 LOG(ERROR) << "Invalid command line version: " << version_string;
[email protected]4512f3ac2009-11-04 03:39:22369 return NULL;
[email protected]85769352010-11-16 14:24:30370 }
[email protected]4512f3ac2009-11-04 03:39:22371 }
372
[email protected]6a0464c82012-08-07 05:58:28373 // If no version on the command line, then look at the version resource in
374 // the current module and try loading that.
375 if (!version.IsValid()) {
376 scoped_ptr<FileVersionInfo> file_version_info(
377 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
378 if (file_version_info.get()) {
379 version_string = file_version_info->file_version();
380 version = Version(WideToASCII(version_string));
381 }
382 }
383
384 // TODO(robertshield): in theory, these next two checks (env and registry)
385 // should never be needed. Remove them when I become 100% certain this is
386 // also true in practice.
387
388 // If no version in the current module, then look in the environment.
[email protected]12126d372012-07-11 18:40:53389 if (!version.IsValid()) {
[email protected]03d21b82010-12-07 19:37:22390 if (EnvQueryStr(ASCIIToWide(chrome::kChromeVersionEnvVar).c_str(),
[email protected]36ee0322010-12-23 21:27:12391 &version_string)) {
[email protected]12126d372012-07-11 18:40:53392 version = Version(WideToASCII(version_string));
393 LOG_IF(ERROR, !version.IsValid()) << "Invalid environment version: "
394 << version_string;
[email protected]85769352010-11-16 14:24:30395 }
396 }
397
[email protected]f068b412012-04-02 20:24:48398 // If no version in the environment, then look in the registry.
[email protected]12126d372012-07-11 18:40:53399 if (!version.IsValid()) {
[email protected]f068b412012-04-02 20:24:48400 version_string = GetVersion();
401 if (version_string.empty()) {
402 LOG(ERROR) << "Could not get Chrome DLL version.";
403 return NULL;
404 }
[email protected]85769352010-11-16 14:24:30405 }
406
[email protected]f068b412012-04-02 20:24:48407 *out_file = dir;
408 *out_version = version_string;
409 out_file->append(*out_version).append(1, L'\\');
410 dll = LoadChromeWithDirectory(out_file);
411 if (!dll) {
412 LOG(ERROR) << "Failed to load Chrome DLL from " << *out_file;
[email protected]85769352010-11-16 14:24:30413 return NULL;
414 }
[email protected]f068b412012-04-02 20:24:48415
416 return dll;
[email protected]4512f3ac2009-11-04 03:39:22417}
418
419// Launching is a matter of loading the right dll, setting the CHROME_VERSION
420// environment variable and just calling the entry point. Derived classes can
421// add custom code in the OnBeforeLaunch callback.
422int MainDllLoader::Launch(HINSTANCE instance,
423 sandbox::SandboxInterfaceInfo* sbox_info) {
[email protected]6a0464c82012-08-07 05:58:28424 string16 version;
425 string16 file;
[email protected]4512f3ac2009-11-04 03:39:22426 dll_ = Load(&version, &file);
427 if (!dll_)
[email protected]1fcfb202011-07-19 19:53:14428 return chrome::RESULT_CODE_MISSING_DATA;
[email protected]4512f3ac2009-11-04 03:39:22429
[email protected]ae0f0772010-08-13 04:54:10430 scoped_ptr<base::Environment> env(base::Environment::Create());
[email protected]03d21b82010-12-07 19:37:22431 env->SetVar(chrome::kChromeVersionEnvVar, WideToUTF8(version));
[email protected]e3824ec2013-02-08 01:02:40432 // TODO(erikwright): Remove this when http://crbug.com/174953 is fixed and
433 // widely deployed.
434 env->UnSetVar(env_vars::kGoogleUpdateIsMachineEnvVar);
[email protected]4512f3ac2009-11-04 03:39:22435
[email protected]82790582012-02-16 23:29:16436 InitCrashReporter();
[email protected]2544a7a2011-03-14 18:25:19437 OnBeforeLaunch(file);
[email protected]4512f3ac2009-11-04 03:39:22438
439 DLL_MAIN entry_point =
440 reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain"));
441 if (!entry_point)
[email protected]1fcfb202011-07-19 19:53:14442 return chrome::RESULT_CODE_BAD_PROCESS_TYPE;
[email protected]4512f3ac2009-11-04 03:39:22443
[email protected]d85d6702011-09-01 15:51:53444 int rc = entry_point(instance, sbox_info);
[email protected]2544a7a2011-03-14 18:25:19445 return OnBeforeExit(rc, file);
[email protected]4512f3ac2009-11-04 03:39:22446}
447
[email protected]6a0464c82012-08-07 05:58:28448string16 MainDllLoader::GetVersion() {
449 string16 reg_path(GetRegistryPath());
450 string16 version_string;
451 string16 dir(GetExecutablePath());
[email protected]f068b412012-04-02 20:24:48452 if (!GetChromeVersion(dir.c_str(), reg_path.c_str(), &version_string))
[email protected]6a0464c82012-08-07 05:58:28453 return string16();
[email protected]f068b412012-04-02 20:24:48454 return version_string;
455}
456
[email protected]3cdacd42010-04-30 18:55:53457void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
458 RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function =
459 reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc>(
460 ::GetProcAddress(dll_,
461 "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
462 if (!relaunch_function) {
463 LOG(ERROR) << "Could not find exported function "
464 << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
465 } else {
466 relaunch_function();
467 }
468}
469
[email protected]4512f3ac2009-11-04 03:39:22470//=============================================================================
471
472class ChromeDllLoader : public MainDllLoader {
473 public:
[email protected]6a0464c82012-08-07 05:58:28474 virtual string16 GetRegistryPath() {
475 string16 key(google_update::kRegPathClients);
[email protected]5f2cee82009-11-05 04:59:48476 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
477 key.append(L"\\").append(dist->GetAppGuid());
[email protected]4512f3ac2009-11-04 03:39:22478 return key;
479 }
480
[email protected]6a0464c82012-08-07 05:58:28481 virtual void OnBeforeLaunch(const string16& dll_path) {
[email protected]2544a7a2011-03-14 18:25:19482 RecordDidRun(dll_path);
[email protected]4512f3ac2009-11-04 03:39:22483 }
[email protected]42d7510c2009-12-19 01:48:30484
[email protected]6a0464c82012-08-07 05:58:28485 virtual int OnBeforeExit(int return_code, const string16& dll_path) {
[email protected]42d7510c2009-12-19 01:48:30486 // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
487 // so we need to reset the did_run signal so omaha does not count
488 // this run as active usage.
[email protected]1fcfb202011-07-19 19:53:14489 if (chrome::RESULT_CODE_NORMAL_EXIT_CANCEL == return_code) {
[email protected]2544a7a2011-03-14 18:25:19490 ClearDidRun(dll_path);
[email protected]42d7510c2009-12-19 01:48:30491 }
492 return return_code;
493 }
[email protected]4512f3ac2009-11-04 03:39:22494};
495
496//=============================================================================
497
498class ChromiumDllLoader : public MainDllLoader {
499 public:
[email protected]6a0464c82012-08-07 05:58:28500 virtual string16 GetRegistryPath() {
[email protected]74d1eec2009-11-04 22:18:57501 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
502 return dist->GetVersionKey();
[email protected]4512f3ac2009-11-04 03:39:22503 }
504};
505
506MainDllLoader* MakeMainDllLoader() {
507#if defined(GOOGLE_CHROME_BUILD)
508 return new ChromeDllLoader();
509#else
510 return new ChromiumDllLoader();
511#endif
512}