[Android] Added UMA for search by image context menu.
[chromium-blink-merge.git] / chrome / installer / util / install_util.cc
blobfd1ad19847dd9986c0afdf5a15e2d7b8689b605d
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.
4 //
5 // See the corresponding header file for description of the functions in this
6 // file.
8 #include "chrome/installer/util/install_util.h"
10 #include <shellapi.h>
11 #include <shlobj.h>
12 #include <shlwapi.h>
14 #include <algorithm>
16 #include "base/command_line.h"
17 #include "base/file_util.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/path_service.h"
21 #include "base/process/launch.h"
22 #include "base/strings/string_util.h"
23 #include "base/sys_info.h"
24 #include "base/values.h"
25 #include "base/version.h"
26 #include "base/win/metro.h"
27 #include "base/win/registry.h"
28 #include "base/win/windows_version.h"
29 #include "chrome/installer/util/browser_distribution.h"
30 #include "chrome/installer/util/google_update_constants.h"
31 #include "chrome/installer/util/helper.h"
32 #include "chrome/installer/util/installation_state.h"
33 #include "chrome/installer/util/l10n_string_util.h"
34 #include "chrome/installer/util/util_constants.h"
35 #include "chrome/installer/util/work_item_list.h"
37 using base::win::RegKey;
38 using installer::ProductState;
40 namespace {
42 const wchar_t kStageBinaryPatching[] = L"binary_patching";
43 const wchar_t kStageBuilding[] = L"building";
44 const wchar_t kStageConfiguringAutoLaunch[] = L"configuring_auto_launch";
45 const wchar_t kStageCopyingPreferencesFile[] = L"copying_prefs";
46 const wchar_t kStageCreatingShortcuts[] = L"creating_shortcuts";
47 const wchar_t kStageEnsemblePatching[] = L"ensemble_patching";
48 const wchar_t kStageExecuting[] = L"executing";
49 const wchar_t kStageFinishing[] = L"finishing";
50 const wchar_t kStagePreconditions[] = L"preconditions";
51 const wchar_t kStageRefreshingPolicy[] = L"refreshing_policy";
52 const wchar_t kStageRegisteringChrome[] = L"registering_chrome";
53 const wchar_t kStageRemovingOldVersions[] = L"removing_old_ver";
54 const wchar_t kStageRollingback[] = L"rollingback";
55 const wchar_t kStageUncompressing[] = L"uncompressing";
56 const wchar_t kStageUnpacking[] = L"unpacking";
57 const wchar_t kStageUpdatingChannels[] = L"updating_channels";
58 const wchar_t kStageCreatingVisualManifest[] = L"creating_visual_manifest";
59 const wchar_t kStageDeferringToHigherVersion[] = L"deferring_to_higher_version";
61 const wchar_t* const kStages[] = {
62 NULL,
63 kStagePreconditions,
64 kStageUncompressing,
65 kStageEnsemblePatching,
66 kStageBinaryPatching,
67 kStageUnpacking,
68 kStageBuilding,
69 kStageExecuting,
70 kStageRollingback,
71 kStageRefreshingPolicy,
72 kStageUpdatingChannels,
73 kStageCopyingPreferencesFile,
74 kStageCreatingShortcuts,
75 kStageRegisteringChrome,
76 kStageRemovingOldVersions,
77 kStageFinishing,
78 kStageConfiguringAutoLaunch,
79 kStageCreatingVisualManifest,
80 kStageDeferringToHigherVersion,
83 COMPILE_ASSERT(installer::NUM_STAGES == arraysize(kStages),
84 kStages_disagrees_with_Stage_comma_they_must_match_bang);
86 // Creates a zero-sized non-decorated foreground window that doesn't appear
87 // in the taskbar. This is used as a parent window for calls to ShellExecuteEx
88 // in order for the UAC dialog to appear in the foreground and for focus
89 // to be returned to this process once the UAC task is dismissed. Returns
90 // NULL on failure, a handle to the UAC window on success.
91 HWND CreateUACForegroundWindow() {
92 HWND foreground_window = ::CreateWindowEx(WS_EX_TOOLWINDOW,
93 L"STATIC",
94 NULL,
95 WS_POPUP | WS_VISIBLE,
96 0, 0, 0, 0,
97 NULL, NULL,
98 ::GetModuleHandle(NULL),
99 NULL);
100 if (foreground_window) {
101 HMONITOR monitor = ::MonitorFromWindow(foreground_window,
102 MONITOR_DEFAULTTONEAREST);
103 if (monitor) {
104 MONITORINFO mi = {0};
105 mi.cbSize = sizeof(mi);
106 ::GetMonitorInfo(monitor, &mi);
107 RECT screen_rect = mi.rcWork;
108 int x_offset = (screen_rect.right - screen_rect.left) / 2;
109 int y_offset = (screen_rect.bottom - screen_rect.top) / 2;
110 ::MoveWindow(foreground_window,
111 screen_rect.left + x_offset,
112 screen_rect.top + y_offset,
113 0, 0, FALSE);
114 } else {
115 NOTREACHED() << "Unable to get default monitor";
117 ::SetForegroundWindow(foreground_window);
119 return foreground_window;
122 } // namespace
124 string16 InstallUtil::GetActiveSetupPath(BrowserDistribution* dist) {
125 static const wchar_t kInstalledComponentsPath[] =
126 L"Software\\Microsoft\\Active Setup\\Installed Components\\";
127 return kInstalledComponentsPath + dist->GetActiveSetupGuid();
130 void InstallUtil::TriggerActiveSetupCommand() {
131 string16 active_setup_reg(
132 GetActiveSetupPath(BrowserDistribution::GetDistribution()));
133 base::win::RegKey active_setup_key(
134 HKEY_LOCAL_MACHINE, active_setup_reg.c_str(), KEY_QUERY_VALUE);
135 string16 cmd_str;
136 LONG read_status = active_setup_key.ReadValue(L"StubPath", &cmd_str);
137 if (read_status != ERROR_SUCCESS) {
138 LOG(ERROR) << active_setup_reg << ", " << read_status;
139 // This should never fail if Chrome is registered at system-level, but if it
140 // does there is not much else to be done.
141 return;
144 CommandLine cmd(CommandLine::FromString(cmd_str));
145 // Force creation of shortcuts as the First Run beacon might land between now
146 // and the time setup.exe checks for it.
147 cmd.AppendSwitch(installer::switches::kForceConfigureUserSettings);
149 base::LaunchOptions launch_options;
150 if (base::win::IsMetroProcess())
151 launch_options.force_breakaway_from_job_ = true;
152 if (!base::LaunchProcess(cmd.GetCommandLineString(), launch_options, NULL))
153 PLOG(ERROR) << cmd.GetCommandLineString();
156 bool InstallUtil::ExecuteExeAsAdmin(const CommandLine& cmd, DWORD* exit_code) {
157 base::FilePath::StringType program(cmd.GetProgram().value());
158 DCHECK(!program.empty());
159 DCHECK_NE(program[0], L'\"');
161 CommandLine::StringType params(cmd.GetCommandLineString());
162 if (params[0] == '"') {
163 DCHECK_EQ('"', params[program.length() + 1]);
164 DCHECK_EQ(program, params.substr(1, program.length()));
165 params = params.substr(program.length() + 2);
166 } else {
167 DCHECK_EQ(program, params.substr(0, program.length()));
168 params = params.substr(program.length());
171 TrimWhitespace(params, TRIM_ALL, &params);
173 HWND uac_foreground_window = CreateUACForegroundWindow();
175 SHELLEXECUTEINFO info = {0};
176 info.cbSize = sizeof(SHELLEXECUTEINFO);
177 info.fMask = SEE_MASK_NOCLOSEPROCESS;
178 info.hwnd = uac_foreground_window;
179 info.lpVerb = L"runas";
180 info.lpFile = program.c_str();
181 info.lpParameters = params.c_str();
182 info.nShow = SW_SHOW;
184 bool success = false;
185 if (::ShellExecuteEx(&info) == TRUE) {
186 ::WaitForSingleObject(info.hProcess, INFINITE);
187 DWORD ret_val = 0;
188 if (::GetExitCodeProcess(info.hProcess, &ret_val)) {
189 success = true;
190 if (exit_code)
191 *exit_code = ret_val;
195 if (uac_foreground_window) {
196 DestroyWindow(uac_foreground_window);
199 return success;
202 CommandLine InstallUtil::GetChromeUninstallCmd(
203 bool system_install, BrowserDistribution::Type distribution_type) {
204 ProductState state;
205 if (state.Initialize(system_install, distribution_type)) {
206 return state.uninstall_command();
208 return CommandLine(CommandLine::NO_PROGRAM);
211 void InstallUtil::GetChromeVersion(BrowserDistribution* dist,
212 bool system_install,
213 Version* version) {
214 DCHECK(dist);
215 RegKey key;
216 HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
217 LONG result = key.Open(reg_root, dist->GetVersionKey().c_str(),
218 KEY_QUERY_VALUE);
220 string16 version_str;
221 if (result == ERROR_SUCCESS)
222 result = key.ReadValue(google_update::kRegVersionField, &version_str);
224 *version = Version();
225 if (result == ERROR_SUCCESS && !version_str.empty()) {
226 VLOG(1) << "Existing " << dist->GetAppShortCutName() << " version found "
227 << version_str;
228 *version = Version(WideToASCII(version_str));
229 } else {
230 DCHECK_EQ(ERROR_FILE_NOT_FOUND, result);
231 VLOG(1) << "No existing " << dist->GetAppShortCutName()
232 << " install found.";
236 void InstallUtil::GetCriticalUpdateVersion(BrowserDistribution* dist,
237 bool system_install,
238 Version* version) {
239 DCHECK(dist);
240 RegKey key;
241 HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
242 LONG result =
243 key.Open(reg_root, dist->GetVersionKey().c_str(), KEY_QUERY_VALUE);
245 string16 version_str;
246 if (result == ERROR_SUCCESS)
247 result = key.ReadValue(google_update::kRegCriticalVersionField,
248 &version_str);
250 *version = Version();
251 if (result == ERROR_SUCCESS && !version_str.empty()) {
252 VLOG(1) << "Critical Update version for " << dist->GetAppShortCutName()
253 << " found " << version_str;
254 *version = Version(WideToASCII(version_str));
255 } else {
256 DCHECK_EQ(ERROR_FILE_NOT_FOUND, result);
257 VLOG(1) << "No existing " << dist->GetAppShortCutName()
258 << " install found.";
262 bool InstallUtil::IsOSSupported() {
263 // We do not support Win2K or older, or XP without service pack 2.
264 VLOG(1) << base::SysInfo::OperatingSystemName() << ' '
265 << base::SysInfo::OperatingSystemVersion();
266 base::win::Version version = base::win::GetVersion();
267 return (version > base::win::VERSION_XP) ||
268 ((version == base::win::VERSION_XP) &&
269 (base::win::OSInfo::GetInstance()->service_pack().major >= 2));
272 void InstallUtil::AddInstallerResultItems(bool system_install,
273 const string16& state_key,
274 installer::InstallStatus status,
275 int string_resource_id,
276 const string16* const launch_cmd,
277 WorkItemList* install_list) {
278 DCHECK(install_list);
279 const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
280 DWORD installer_result = (GetInstallReturnCode(status) == 0) ? 0 : 1;
281 install_list->AddCreateRegKeyWorkItem(root, state_key);
282 install_list->AddSetRegValueWorkItem(root, state_key,
283 installer::kInstallerResult,
284 installer_result, true);
285 install_list->AddSetRegValueWorkItem(root, state_key,
286 installer::kInstallerError,
287 static_cast<DWORD>(status), true);
288 if (string_resource_id != 0) {
289 string16 msg = installer::GetLocalizedString(string_resource_id);
290 install_list->AddSetRegValueWorkItem(root, state_key,
291 installer::kInstallerResultUIString, msg, true);
293 if (launch_cmd != NULL && !launch_cmd->empty()) {
294 install_list->AddSetRegValueWorkItem(root, state_key,
295 installer::kInstallerSuccessLaunchCmdLine, *launch_cmd, true);
299 void InstallUtil::UpdateInstallerStage(bool system_install,
300 const string16& state_key_path,
301 installer::InstallerStage stage) {
302 DCHECK_LE(static_cast<installer::InstallerStage>(0), stage);
303 DCHECK_GT(installer::NUM_STAGES, stage);
304 const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
305 RegKey state_key;
306 LONG result = state_key.Open(root, state_key_path.c_str(),
307 KEY_QUERY_VALUE | KEY_SET_VALUE);
308 if (result == ERROR_SUCCESS) {
309 if (stage == installer::NO_STAGE) {
310 result = state_key.DeleteValue(installer::kInstallerExtraCode1);
311 LOG_IF(ERROR, result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
312 << "Failed deleting installer stage from " << state_key_path
313 << "; result: " << result;
314 } else {
315 const DWORD extra_code_1 = static_cast<DWORD>(stage);
316 result = state_key.WriteValue(installer::kInstallerExtraCode1,
317 extra_code_1);
318 LOG_IF(ERROR, result != ERROR_SUCCESS)
319 << "Failed writing installer stage to " << state_key_path
320 << "; result: " << result;
322 // TODO(grt): Remove code below here once we're convinced that our use of
323 // Google Update's new InstallerExtraCode1 value is good.
324 installer::ChannelInfo channel_info;
325 // This will return false if the "ap" value isn't present, which is fine.
326 channel_info.Initialize(state_key);
327 if (channel_info.SetStage(kStages[stage]) &&
328 !channel_info.Write(&state_key)) {
329 LOG(ERROR) << "Failed writing installer stage to " << state_key_path;
331 } else {
332 LOG(ERROR) << "Failed opening " << state_key_path
333 << " to update installer stage; result: " << result;
337 bool InstallUtil::IsPerUserInstall(const wchar_t* const exe_path) {
338 wchar_t program_files_path[MAX_PATH] = {0};
339 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL,
340 SHGFP_TYPE_CURRENT, program_files_path))) {
341 return !StartsWith(exe_path, program_files_path, false);
342 } else {
343 NOTREACHED();
345 return true;
348 bool InstallUtil::IsMultiInstall(BrowserDistribution* dist,
349 bool system_install) {
350 DCHECK(dist);
351 ProductState state;
352 return state.Initialize(system_install, dist->GetType()) &&
353 state.is_multi_install();
356 bool CheckIsChromeSxSProcess() {
357 CommandLine* command_line = CommandLine::ForCurrentProcess();
358 CHECK(command_line);
360 if (command_line->HasSwitch(installer::switches::kChromeSxS))
361 return true;
363 // Also return true if we are running from Chrome SxS installed path.
364 base::FilePath exe_dir;
365 PathService::Get(base::DIR_EXE, &exe_dir);
366 string16 chrome_sxs_dir(installer::kGoogleChromeInstallSubDir2);
367 chrome_sxs_dir.append(installer::kSxSSuffix);
368 return base::FilePath::CompareEqualIgnoreCase(
369 exe_dir.BaseName().value(), installer::kInstallBinaryDir) &&
370 base::FilePath::CompareEqualIgnoreCase(
371 exe_dir.DirName().BaseName().value(), chrome_sxs_dir);
374 bool InstallUtil::IsChromeSxSProcess() {
375 static bool sxs = CheckIsChromeSxSProcess();
376 return sxs;
379 bool InstallUtil::GetSentinelFilePath(const base::FilePath::CharType* file,
380 BrowserDistribution* dist,
381 base::FilePath* path) {
382 base::FilePath exe_path;
383 if (!PathService::Get(base::DIR_EXE, &exe_path))
384 return false;
386 if (IsPerUserInstall(exe_path.value().c_str())) {
387 const base::FilePath maybe_product_dir(exe_path.DirName().DirName());
388 if (base::PathExists(exe_path.Append(installer::kChromeExe))) {
389 // DIR_EXE is most likely Chrome's directory in which case |exe_path| is
390 // the user-level sentinel path.
391 *path = exe_path;
392 } else if (base::PathExists(
393 maybe_product_dir.Append(installer::kChromeExe))) {
394 // DIR_EXE can also be the Installer directory if this is called from a
395 // setup.exe running from Application\<version>\Installer (see
396 // InstallerState::GetInstallerDirectory) in which case Chrome's directory
397 // is two levels up.
398 *path = maybe_product_dir;
399 } else {
400 NOTREACHED();
401 return false;
403 } else {
404 std::vector<base::FilePath> user_data_dir_paths;
405 installer::GetChromeUserDataPaths(dist, &user_data_dir_paths);
407 if (!user_data_dir_paths.empty())
408 *path = user_data_dir_paths[0];
409 else
410 return false;
413 *path = path->Append(file);
414 return true;
417 // This method tries to delete a registry key and logs an error message
418 // in case of failure. It returns true if deletion is successful (or the key did
419 // not exist), otherwise false.
420 bool InstallUtil::DeleteRegistryKey(HKEY root_key,
421 const string16& key_path) {
422 VLOG(1) << "Deleting registry key " << key_path;
423 LONG result = ::SHDeleteKey(root_key, key_path.c_str());
424 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
425 LOG(ERROR) << "Failed to delete registry key: " << key_path
426 << " error: " << result;
427 return false;
429 return true;
432 // This method tries to delete a registry value and logs an error message
433 // in case of failure. It returns true if deletion is successful (or the key did
434 // not exist), otherwise false.
435 bool InstallUtil::DeleteRegistryValue(HKEY reg_root,
436 const string16& key_path,
437 const string16& value_name) {
438 RegKey key;
439 LONG result = key.Open(reg_root, key_path.c_str(), KEY_SET_VALUE);
440 if (result == ERROR_SUCCESS)
441 result = key.DeleteValue(value_name.c_str());
442 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
443 LOG(ERROR) << "Failed to delete registry value: " << value_name
444 << " error: " << result;
445 return false;
447 return true;
450 // static
451 InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryKeyIf(
452 HKEY root_key,
453 const string16& key_to_delete_path,
454 const string16& key_to_test_path,
455 const wchar_t* value_name,
456 const RegistryValuePredicate& predicate) {
457 DCHECK(root_key);
458 ConditionalDeleteResult delete_result = NOT_FOUND;
459 RegKey key;
460 string16 actual_value;
461 if (key.Open(root_key, key_to_test_path.c_str(),
462 KEY_QUERY_VALUE) == ERROR_SUCCESS &&
463 key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
464 predicate.Evaluate(actual_value)) {
465 key.Close();
466 delete_result = DeleteRegistryKey(root_key, key_to_delete_path)
467 ? DELETED : DELETE_FAILED;
469 return delete_result;
472 // static
473 InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryValueIf(
474 HKEY root_key,
475 const wchar_t* key_path,
476 const wchar_t* value_name,
477 const RegistryValuePredicate& predicate) {
478 DCHECK(root_key);
479 DCHECK(key_path);
480 ConditionalDeleteResult delete_result = NOT_FOUND;
481 RegKey key;
482 string16 actual_value;
483 if (key.Open(root_key, key_path,
484 KEY_QUERY_VALUE | KEY_SET_VALUE) == ERROR_SUCCESS &&
485 key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
486 predicate.Evaluate(actual_value)) {
487 LONG result = key.DeleteValue(value_name);
488 if (result != ERROR_SUCCESS) {
489 LOG(ERROR) << "Failed to delete registry value: "
490 << (value_name ? value_name : L"(Default)")
491 << " error: " << result;
492 delete_result = DELETE_FAILED;
494 delete_result = DELETED;
496 return delete_result;
499 bool InstallUtil::ValueEquals::Evaluate(const string16& value) const {
500 return value == value_to_match_;
503 // static
504 int InstallUtil::GetInstallReturnCode(installer::InstallStatus status) {
505 switch (status) {
506 case installer::FIRST_INSTALL_SUCCESS:
507 case installer::INSTALL_REPAIRED:
508 case installer::NEW_VERSION_UPDATED:
509 case installer::IN_USE_UPDATED:
510 return 0;
511 default:
512 return status;
516 // static
517 void InstallUtil::MakeUninstallCommand(const string16& program,
518 const string16& arguments,
519 CommandLine* command_line) {
520 *command_line = CommandLine::FromString(L"\"" + program + L"\" " + arguments);
523 // static
524 string16 InstallUtil::GetCurrentDate() {
525 static const wchar_t kDateFormat[] = L"yyyyMMdd";
526 wchar_t date_str[arraysize(kDateFormat)] = {0};
527 int len = GetDateFormatW(LOCALE_INVARIANT, 0, NULL, kDateFormat,
528 date_str, arraysize(date_str));
529 if (len) {
530 --len; // Subtract terminating \0.
531 } else {
532 PLOG(DFATAL) << "GetDateFormat";
535 return string16(date_str, len);
538 // Open |path| with minimal access to obtain information about it, returning
539 // true and populating |handle| on success.
540 // static
541 bool InstallUtil::ProgramCompare::OpenForInfo(const base::FilePath& path,
542 base::win::ScopedHandle* handle) {
543 DCHECK(handle);
544 handle->Set(base::CreatePlatformFile(path, base::PLATFORM_FILE_OPEN, NULL,
545 NULL));
546 return handle->IsValid();
549 // Populate |info| for |handle|, returning true on success.
550 // static
551 bool InstallUtil::ProgramCompare::GetInfo(const base::win::ScopedHandle& handle,
552 BY_HANDLE_FILE_INFORMATION* info) {
553 DCHECK(handle.IsValid());
554 return GetFileInformationByHandle(
555 const_cast<base::win::ScopedHandle&>(handle), info) != 0;
558 InstallUtil::ProgramCompare::ProgramCompare(const base::FilePath& path_to_match)
559 : path_to_match_(path_to_match),
560 file_handle_(base::kInvalidPlatformFileValue),
561 file_info_() {
562 DCHECK(!path_to_match_.empty());
563 if (!OpenForInfo(path_to_match_, &file_handle_)) {
564 PLOG(WARNING) << "Failed opening " << path_to_match_.value()
565 << "; falling back to path string comparisons.";
566 } else if (!GetInfo(file_handle_, &file_info_)) {
567 PLOG(WARNING) << "Failed getting information for "
568 << path_to_match_.value()
569 << "; falling back to path string comparisons.";
570 file_handle_.Close();
574 InstallUtil::ProgramCompare::~ProgramCompare() {
577 bool InstallUtil::ProgramCompare::Evaluate(const string16& value) const {
578 // Suss out the exe portion of the value, which is expected to be a command
579 // line kinda (or exactly) like:
580 // "c:\foo\bar\chrome.exe" -- "%1"
581 base::FilePath program(CommandLine::FromString(value).GetProgram());
582 if (program.empty()) {
583 LOG(WARNING) << "Failed to parse an executable name from command line: \""
584 << value << "\"";
585 return false;
588 return EvaluatePath(program);
591 bool InstallUtil::ProgramCompare::EvaluatePath(
592 const base::FilePath& path) const {
593 // Try the simple thing first: do the paths happen to match?
594 if (base::FilePath::CompareEqualIgnoreCase(path_to_match_.value(),
595 path.value()))
596 return true;
598 // If the paths don't match and we couldn't open the expected file, we've done
599 // our best.
600 if (!file_handle_.IsValid())
601 return false;
603 // Open the program and see if it references the expected file.
604 base::win::ScopedHandle handle;
605 BY_HANDLE_FILE_INFORMATION info = {};
607 return (OpenForInfo(path, &handle) &&
608 GetInfo(handle, &info) &&
609 info.dwVolumeSerialNumber == file_info_.dwVolumeSerialNumber &&
610 info.nFileIndexHigh == file_info_.nFileIndexHigh &&
611 info.nFileIndexLow == file_info_.nFileIndexLow);