Fix a leak in wm::AnimateOnChildWindowVisibilityChanged().
[chromium-blink-merge.git] / chrome / app / client_util.cc
blobac6dbfc111bece74b0d214d6430157f94a3d2af1
1 // Copyright (c) 2012 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.
5 #include <windows.h>
6 #include <shlwapi.h>
8 #include "base/command_line.h"
9 #include "base/compiler_specific.h"
10 #include "base/debug/trace_event.h"
11 #include "base/environment.h"
12 #include "base/file_version_info.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/rand_util.h" // For PreRead experiment.
17 #include "base/sha1.h" // For PreRead experiment.
18 #include "base/strings/string16.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/version.h"
23 #include "base/win/windows_version.h"
24 #include "chrome/app/chrome_breakpad_client.h"
25 #include "chrome/app/client_util.h"
26 #include "chrome/app/image_pre_reader_win.h"
27 #include "chrome/common/chrome_constants.h"
28 #include "chrome/common/chrome_result_codes.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/env_vars.h"
31 #include "chrome/installer/util/google_update_constants.h"
32 #include "chrome/installer/util/google_update_settings.h"
33 #include "chrome/installer/util/install_util.h"
34 #include "chrome/installer/util/util_constants.h"
35 #include "components/breakpad/app/breakpad_client.h"
36 #include "components/breakpad/app/breakpad_win.h"
37 #include "content/public/app/startup_helper_win.h"
38 #include "sandbox/win/src/sandbox.h"
40 namespace {
41 // The entry point signature of chrome.dll.
42 typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*);
44 typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc)();
46 base::LazyInstance<chrome::ChromeBreakpadClient>::Leaky
47 g_chrome_breakpad_client = LAZY_INSTANCE_INITIALIZER;
49 // Returns true if the build date for this module precedes the expiry date
50 // for the pre-read experiment.
51 bool PreReadExperimentIsActive() {
52 const int kPreReadExpiryYear = 2014;
53 const int kPreReadExpiryMonth = 7;
54 const int kPreReadExpiryDay = 1;
55 const char kBuildTimeStr[] = __DATE__ " " __TIME__;
57 // Get the timestamp of the build.
58 base::Time build_time;
59 bool result = base::Time::FromString(kBuildTimeStr, &build_time);
60 DCHECK(result);
62 // Get the timestamp at which the experiment expires.
63 base::Time::Exploded exploded = {0};
64 exploded.year = kPreReadExpiryYear;
65 exploded.month = kPreReadExpiryMonth;
66 exploded.day_of_month = kPreReadExpiryDay;
67 base::Time expiration_time = base::Time::FromLocalExploded(exploded);
69 // Return true if the build time predates the expiration time..
70 return build_time < expiration_time;
73 // Get random unit values, i.e., in the range (0, 1), denoting a die-toss for
74 // being in an experiment population and experimental group thereof.
75 void GetPreReadPopulationAndGroup(double* population, double* group) {
76 // By default we use the metrics id for the user as stable pseudo-random
77 // input to a hash.
78 std::string metrics_id;
79 GoogleUpdateSettings::LoadMetricsClientId(&metrics_id);
81 // If this user has not metrics id, we fall back to a purely random value
82 // per browser session.
83 const size_t kLength = 16;
84 std::string random_value(metrics_id.empty() ? base::RandBytesAsString(kLength)
85 : metrics_id);
87 // To interpret the value as a random number we hash it and read the first 8
88 // bytes of the hash as a unit-interval representing a die-toss for being in
89 // the experiment population and the second 8 bytes as a die-toss for being
90 // in various experiment groups.
91 unsigned char sha1_hash[base::kSHA1Length];
92 base::SHA1HashBytes(
93 reinterpret_cast<const unsigned char*>(random_value.c_str()),
94 random_value.size() * sizeof(random_value[0]),
95 sha1_hash);
96 COMPILE_ASSERT(2 * sizeof(uint64) < sizeof(sha1_hash), need_more_data);
97 const uint64* random_bits = reinterpret_cast<uint64*>(&sha1_hash[0]);
99 // Convert the bits into unit-intervals and return.
100 *population = base::BitsToOpenEndedUnitInterval(random_bits[0]);
101 *group = base::BitsToOpenEndedUnitInterval(random_bits[1]);
104 // Gets the amount of pre-read to use as well as the experiment group in which
105 // the user falls.
106 size_t InitPreReadPercentage() {
107 // By default use the old behaviour: read 100%.
108 const int kDefaultPercentage = 100;
109 const char kDefaultFormatStr[] = "%d-pct-default";
110 const char kControlFormatStr[] = "%d-pct-control";
111 const char kGroupFormatStr[] = "%d-pct";
113 COMPILE_ASSERT(kDefaultPercentage <= 100, default_percentage_too_large);
114 COMPILE_ASSERT(kDefaultPercentage % 5 == 0, default_percentage_not_mult_5);
116 // Roll the dice to determine if this user is in the experiment and if so,
117 // in which experimental group.
118 double population = 0.0;
119 double group = 0.0;
120 GetPreReadPopulationAndGroup(&population, &group);
122 // We limit experiment populations to 1% of the Stable and 10% of each of
123 // the other channels.
124 const base::string16 channel(GoogleUpdateSettings::GetChromeChannel(
125 GoogleUpdateSettings::IsSystemInstall()));
126 double threshold = (channel == installer::kChromeChannelStable) ? 0.01 : 0.10;
128 // If the experiment has expired use the default pre-read level. Otherwise,
129 // those not in the experiment population also use the default pre-read level.
130 size_t value = kDefaultPercentage;
131 const char* format_str = kDefaultFormatStr;
132 if (PreReadExperimentIsActive() && (population <= threshold)) {
133 // We divide the experiment population into groups pre-reading at 5 percent
134 // increments in the range [0, 100].
135 value = static_cast<size_t>(group * 21.0) * 5;
136 DCHECK_LE(value, 100u);
137 DCHECK_EQ(0u, value % 5);
138 format_str =
139 (value == kDefaultPercentage) ? kControlFormatStr : kGroupFormatStr;
142 // Generate the group name corresponding to this percentage value.
143 std::string group_name;
144 base::SStringPrintf(&group_name, format_str, value);
146 // Persist the group name to the environment so that it can be used for
147 // reporting.
148 scoped_ptr<base::Environment> env(base::Environment::Create());
149 env->SetVar(chrome::kPreReadEnvironmentVariable, group_name);
151 // Return the percentage value to be used.
152 return value;
155 // Expects that |dir| has a trailing backslash. |dir| is modified so it
156 // contains the full path that was tried. Caller must check for the return
157 // value not being null to determine if this path contains a valid dll.
158 HMODULE LoadModuleWithDirectory(base::string16* dir,
159 const wchar_t* dll_name,
160 bool pre_read) {
161 ::SetCurrentDirectoryW(dir->c_str());
162 dir->append(dll_name);
164 if (pre_read) {
165 #if !defined(WIN_DISABLE_PREREAD)
166 // We pre-read the binary to warm the memory caches (fewer hard faults to
167 // page parts of the binary in).
168 const size_t kStepSize = 1024 * 1024;
169 size_t percentage = InitPreReadPercentage();
170 ImagePreReader::PartialPreReadImage(dir->c_str(), percentage, kStepSize);
171 #endif
174 return ::LoadLibraryExW(dir->c_str(), NULL,
175 LOAD_WITH_ALTERED_SEARCH_PATH);
178 void RecordDidRun(const base::string16& dll_path) {
179 bool system_level = !InstallUtil::IsPerUserInstall(dll_path.c_str());
180 GoogleUpdateSettings::UpdateDidRunState(true, system_level);
183 void ClearDidRun(const base::string16& dll_path) {
184 bool system_level = !InstallUtil::IsPerUserInstall(dll_path.c_str());
185 GoogleUpdateSettings::UpdateDidRunState(false, system_level);
188 bool InMetroMode() {
189 return (wcsstr(
190 ::GetCommandLineW(), L" -ServerName:DefaultBrowserServer") != NULL);
193 typedef int (*InitMetro)();
195 } // namespace
197 base::string16 GetExecutablePath() {
198 wchar_t path[MAX_PATH];
199 ::GetModuleFileNameW(NULL, path, MAX_PATH);
200 if (!::PathRemoveFileSpecW(path))
201 return base::string16();
202 base::string16 exe_path(path);
203 return exe_path.append(1, L'\\');
206 base::string16 GetCurrentModuleVersion() {
207 scoped_ptr<FileVersionInfo> file_version_info(
208 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
209 if (file_version_info.get()) {
210 base::string16 version_string(file_version_info->file_version());
211 if (Version(base::UTF16ToASCII(version_string)).IsValid())
212 return version_string;
214 return base::string16();
217 //=============================================================================
219 MainDllLoader::MainDllLoader()
220 : dll_(NULL), metro_mode_(InMetroMode()) {
223 MainDllLoader::~MainDllLoader() {
226 // Loading chrome is an interesting affair. First we try loading from the
227 // current directory to support run-what-you-compile and other development
228 // scenarios.
229 // If that fails then we look at the version resource in the current
230 // module. This is the expected path for chrome.exe browser instances in an
231 // installed build.
232 HMODULE MainDllLoader::Load(base::string16* version,
233 base::string16* out_file) {
234 const base::string16 executable_dir(GetExecutablePath());
235 *out_file = executable_dir;
237 const wchar_t* dll_name = metro_mode_ ?
238 installer::kChromeMetroDll :
239 #if !defined(CHROME_MULTIPLE_DLL)
240 installer::kChromeDll;
241 #else
242 (process_type_ == "service") || process_type_.empty() ?
243 installer::kChromeDll :
244 installer::kChromeChildDll;
245 #endif
246 const bool pre_read = !metro_mode_;
247 HMODULE dll = LoadModuleWithDirectory(out_file, dll_name, pre_read);
248 if (!dll) {
249 base::string16 version_string(GetCurrentModuleVersion());
250 if (version_string.empty()) {
251 LOG(ERROR) << "No valid Chrome version found";
252 return NULL;
254 *out_file = executable_dir;
255 *version = version_string;
256 out_file->append(version_string).append(1, L'\\');
257 dll = LoadModuleWithDirectory(out_file, dll_name, pre_read);
258 if (!dll) {
259 PLOG(ERROR) << "Failed to load Chrome DLL from " << *out_file;
260 return NULL;
264 DCHECK(dll);
265 return dll;
268 // Launching is a matter of loading the right dll, setting the CHROME_VERSION
269 // environment variable and just calling the entry point. Derived classes can
270 // add custom code in the OnBeforeLaunch callback.
271 int MainDllLoader::Launch(HINSTANCE instance) {
272 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
273 process_type_ = cmd_line.GetSwitchValueASCII(switches::kProcessType);
275 base::string16 version;
276 base::string16 file;
278 if (metro_mode_) {
279 HMODULE metro_dll = Load(&version, &file);
280 if (!metro_dll)
281 return chrome::RESULT_CODE_MISSING_DATA;
283 InitMetro chrome_metro_main =
284 reinterpret_cast<InitMetro>(::GetProcAddress(metro_dll, "InitMetro"));
285 return chrome_metro_main();
288 // Initialize the sandbox services.
289 sandbox::SandboxInterfaceInfo sandbox_info = {0};
290 content::InitializeSandboxInfo(&sandbox_info);
292 breakpad::SetBreakpadClient(g_chrome_breakpad_client.Pointer());
293 bool exit_now = true;
294 if (process_type_.empty()) {
295 if (breakpad::ShowRestartDialogIfCrashed(&exit_now)) {
296 // We restarted because of a previous crash. Ask user if we should
297 // Relaunch. Only for the browser process. See crbug.com/132119.
298 if (exit_now)
299 return content::RESULT_CODE_NORMAL_EXIT;
302 breakpad::InitCrashReporter(process_type_);
304 dll_ = Load(&version, &file);
305 if (!dll_)
306 return chrome::RESULT_CODE_MISSING_DATA;
308 scoped_ptr<base::Environment> env(base::Environment::Create());
309 env->SetVar(chrome::kChromeVersionEnvVar, base::WideToUTF8(version));
311 OnBeforeLaunch(file);
312 DLL_MAIN chrome_main =
313 reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain"));
314 int rc = chrome_main(instance, &sandbox_info);
315 return OnBeforeExit(rc, file);
318 void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
319 if (!dll_)
320 return;
322 RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function =
323 reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc>(
324 ::GetProcAddress(dll_,
325 "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
326 if (!relaunch_function) {
327 LOG(ERROR) << "Could not find exported function "
328 << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
329 } else {
330 relaunch_function();
334 //=============================================================================
336 class ChromeDllLoader : public MainDllLoader {
337 protected:
338 virtual void OnBeforeLaunch(const base::string16& dll_path) {
339 RecordDidRun(dll_path);
342 virtual int OnBeforeExit(int return_code, const base::string16& dll_path) {
343 // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
344 // so we need to reset the did_run signal so omaha does not count
345 // this run as active usage.
346 if (chrome::RESULT_CODE_NORMAL_EXIT_CANCEL == return_code) {
347 ClearDidRun(dll_path);
349 return return_code;
353 //=============================================================================
355 class ChromiumDllLoader : public MainDllLoader {
356 protected:
357 virtual void OnBeforeLaunch(const base::string16& dll_path) OVERRIDE {
359 virtual int OnBeforeExit(int return_code,
360 const base::string16& dll_path) OVERRIDE {
361 return return_code;
365 MainDllLoader* MakeMainDllLoader() {
366 #if defined(GOOGLE_CHROME_BUILD)
367 return new ChromeDllLoader();
368 #else
369 return new ChromiumDllLoader();
370 #endif