Remove support for specifying version on command line.
[chromium-blink-merge.git] / chrome / browser / first_run / upgrade_util_win.cc
blobb1293360a38a62ec16427c4277891f7d72b1fc29
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 "chrome/browser/first_run/upgrade_util.h"
7 #include <windows.h>
8 #include <shellapi.h>
10 #include <algorithm>
11 #include <string>
13 #include "base/base_paths.h"
14 #include "base/command_line.h"
15 #include "base/environment.h"
16 #include "base/file_util.h"
17 #include "base/files/file_path.h"
18 #include "base/logging.h"
19 #include "base/path_service.h"
20 #include "base/process/launch.h"
21 #include "base/process/process_handle.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/win/metro.h"
26 #include "base/win/registry.h"
27 #include "base/win/scoped_comptr.h"
28 #include "base/win/windows_version.h"
29 #include "chrome/browser/first_run/upgrade_util_win.h"
30 #include "chrome/browser/shell_integration.h"
31 #include "chrome/common/chrome_constants.h"
32 #include "chrome/common/chrome_switches.h"
33 #include "chrome/installer/util/browser_distribution.h"
34 #include "chrome/installer/util/google_update_constants.h"
35 #include "chrome/installer/util/install_util.h"
36 #include "chrome/installer/util/shell_util.h"
37 #include "chrome/installer/util/util_constants.h"
38 #include "google_update/google_update_idl.h"
40 namespace {
42 bool GetNewerChromeFile(base::FilePath* path) {
43 if (!PathService::Get(base::DIR_EXE, path))
44 return false;
45 *path = path->Append(installer::kChromeNewExe);
46 return true;
49 bool InvokeGoogleUpdateForRename() {
50 base::win::ScopedComPtr<IProcessLauncher> ipl;
51 if (!FAILED(ipl.CreateInstance(__uuidof(ProcessLauncherClass)))) {
52 ULONG_PTR phandle = NULL;
53 DWORD id = GetCurrentProcessId();
54 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
55 if (!FAILED(ipl->LaunchCmdElevated(dist->GetAppGuid().c_str(),
56 google_update::kRegRenameCmdField,
57 id,
58 &phandle))) {
59 HANDLE handle = HANDLE(phandle);
60 WaitForSingleObject(handle, INFINITE);
61 DWORD exit_code;
62 ::GetExitCodeProcess(handle, &exit_code);
63 ::CloseHandle(handle);
64 if (exit_code == installer::RENAME_SUCCESSFUL)
65 return true;
68 return false;
71 base::FilePath GetMetroRelauncherPath(const base::FilePath& chrome_exe,
72 const std::string& version_str) {
73 base::FilePath path(chrome_exe.DirName());
75 // The relauncher is ordinarily in the version directory. When running in a
76 // build tree however (where CHROME_VERSION is not set in the environment)
77 // look for it in Chrome's directory.
78 if (!version_str.empty())
79 path = path.AppendASCII(version_str);
81 return path.Append(installer::kDelegateExecuteExe);
84 } // namespace
86 namespace upgrade_util {
88 const char kRelaunchModeMetro[] = "relaunch.mode.metro";
89 const char kRelaunchModeDesktop[] = "relaunch.mode.desktop";
90 const char kRelaunchModeDefault[] = "relaunch.mode.default";
92 // TODO(shrikant): Have a map/array to quickly map enum to strings.
93 std::string RelaunchModeEnumToString(const RelaunchMode relaunch_mode) {
94 if (relaunch_mode == RELAUNCH_MODE_METRO)
95 return kRelaunchModeMetro;
97 if (relaunch_mode == RELAUNCH_MODE_DESKTOP)
98 return kRelaunchModeDesktop;
100 // For the purpose of code flow, even in case of wrong value we will
101 // return default re-launch mode.
102 return kRelaunchModeDefault;
105 RelaunchMode RelaunchModeStringToEnum(const std::string& relaunch_mode) {
106 if (relaunch_mode == kRelaunchModeMetro)
107 return RELAUNCH_MODE_METRO;
109 if (relaunch_mode == kRelaunchModeDesktop)
110 return RELAUNCH_MODE_DESKTOP;
112 return RELAUNCH_MODE_DEFAULT;
115 bool RelaunchChromeHelper(const CommandLine& command_line,
116 const RelaunchMode& relaunch_mode) {
117 scoped_ptr<base::Environment> env(base::Environment::Create());
118 std::string version_str;
120 // Get the version variable and remove it from the environment.
121 if (env->GetVar(chrome::kChromeVersionEnvVar, &version_str))
122 env->UnSetVar(chrome::kChromeVersionEnvVar);
123 else
124 version_str.clear();
126 if (base::win::GetVersion() < base::win::VERSION_WIN8)
127 return base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
129 // On Windows 8 we always use the delegate_execute for re-launching chrome.
131 // Pass this Chrome's Start Menu shortcut path to the relauncher so it can
132 // re-activate chrome via ShellExecute.
133 base::FilePath chrome_exe;
134 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
135 NOTREACHED();
136 return false;
139 // We need to use ShellExecute to launch the relauncher, which will wait until
140 // we exit. But ShellExecute does not support handle passing to the child
141 // process so we create a uniquely named mutex that we aquire and never
142 // release. So when we exit, Windows marks our mutex as abandoned and the
143 // wait is satisfied.
144 // The format of the named mutex is important. See DelegateExecuteOperation
145 // for more details.
146 base::string16 mutex_name =
147 base::StringPrintf(L"chrome.relaunch.%d", ::GetCurrentProcessId());
148 HANDLE mutex = ::CreateMutexW(NULL, TRUE, mutex_name.c_str());
149 // The |mutex| handle needs to be leaked. See comment above.
150 if (!mutex) {
151 NOTREACHED();
152 return false;
154 if (::GetLastError() == ERROR_ALREADY_EXISTS) {
155 NOTREACHED() << "Relaunch mutex already exists";
156 return false;
159 CommandLine relaunch_cmd(CommandLine::NO_PROGRAM);
160 relaunch_cmd.AppendSwitchPath(switches::kRelaunchShortcut,
161 ShellIntegration::GetStartMenuShortcut(chrome_exe));
162 relaunch_cmd.AppendSwitchNative(switches::kWaitForMutex, mutex_name);
164 if (relaunch_mode != RELAUNCH_MODE_DEFAULT) {
165 relaunch_cmd.AppendSwitch(relaunch_mode == RELAUNCH_MODE_METRO?
166 switches::kForceImmersive : switches::kForceDesktop);
169 base::string16 params(relaunch_cmd.GetCommandLineString());
170 base::string16 path(GetMetroRelauncherPath(chrome_exe, version_str).value());
172 SHELLEXECUTEINFO sei = { sizeof(sei) };
173 sei.fMask = SEE_MASK_FLAG_LOG_USAGE | SEE_MASK_NOCLOSEPROCESS;
174 sei.nShow = SW_SHOWNORMAL;
175 sei.lpFile = path.c_str();
176 sei.lpParameters = params.c_str();
178 if (!::ShellExecuteExW(&sei)) {
179 NOTREACHED() << "ShellExecute failed with " << GetLastError();
180 return false;
182 DWORD pid = ::GetProcessId(sei.hProcess);
183 CloseHandle(sei.hProcess);
184 if (!pid)
185 return false;
186 // The next call appears to be needed if we are relaunching from desktop into
187 // metro mode. The observed effect if not done is that chrome starts in metro
188 // mode but it is not given focus and it gets killed by windows after a few
189 // seconds.
190 ::AllowSetForegroundWindow(pid);
191 return true;
194 bool RelaunchChromeBrowser(const CommandLine& command_line) {
195 return RelaunchChromeHelper(command_line, RELAUNCH_MODE_DEFAULT);
198 bool RelaunchChromeWithMode(const CommandLine& command_line,
199 const RelaunchMode& relaunch_mode) {
200 return RelaunchChromeHelper(command_line, relaunch_mode);
203 bool IsUpdatePendingRestart() {
204 base::FilePath new_chrome_exe;
205 if (!GetNewerChromeFile(&new_chrome_exe))
206 return false;
207 return base::PathExists(new_chrome_exe);
210 bool SwapNewChromeExeIfPresent() {
211 base::FilePath new_chrome_exe;
212 if (!GetNewerChromeFile(&new_chrome_exe))
213 return false;
214 if (!base::PathExists(new_chrome_exe))
215 return false;
216 base::FilePath cur_chrome_exe;
217 if (!PathService::Get(base::FILE_EXE, &cur_chrome_exe))
218 return false;
220 // Open up the registry key containing current version and rename information.
221 bool user_install =
222 InstallUtil::IsPerUserInstall(cur_chrome_exe.value().c_str());
223 HKEY reg_root = user_install ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
224 BrowserDistribution *dist = BrowserDistribution::GetDistribution();
225 base::win::RegKey key;
226 if (key.Open(reg_root, dist->GetVersionKey().c_str(),
227 KEY_QUERY_VALUE) == ERROR_SUCCESS) {
228 // First try to rename exe by launching rename command ourselves.
229 std::wstring rename_cmd;
230 if (key.ReadValue(google_update::kRegRenameCmdField,
231 &rename_cmd) == ERROR_SUCCESS) {
232 base::win::ScopedHandle handle;
233 base::LaunchOptions options;
234 options.wait = true;
235 options.start_hidden = true;
236 if (base::LaunchProcess(rename_cmd, options, &handle)) {
237 DWORD exit_code;
238 ::GetExitCodeProcess(handle, &exit_code);
239 if (exit_code == installer::RENAME_SUCCESSFUL)
240 return true;
245 // Rename didn't work so try to rename by calling Google Update
246 return InvokeGoogleUpdateForRename();
249 bool DoUpgradeTasks(const CommandLine& command_line) {
250 // The DelegateExecute verb handler finalizes pending in-use updates for
251 // metro mode launches, as Chrome cannot be gracefully relaunched when
252 // running in this mode.
253 if (base::win::IsMetroProcess())
254 return false;
255 if (!SwapNewChromeExeIfPresent())
256 return false;
257 // At this point the chrome.exe has been swapped with the new one.
258 if (!RelaunchChromeBrowser(command_line)) {
259 // The re-launch fails. Feel free to panic now.
260 NOTREACHED();
262 return true;
265 } // namespace upgrade_util