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"
14 #include "base/base_paths.h"
15 #include "base/command_line.h"
16 #include "base/environment.h"
17 #include "base/files/file_path.h"
18 #include "base/files/file_util.h"
19 #include "base/logging.h"
20 #include "base/path_service.h"
21 #include "base/prefs/pref_service.h"
22 #include "base/process/launch.h"
23 #include "base/process/process_handle.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/win/metro.h"
28 #include "base/win/registry.h"
29 #include "base/win/scoped_comptr.h"
30 #include "base/win/windows_version.h"
31 #include "chrome/browser/browser_process.h"
32 #include "chrome/browser/first_run/upgrade_util_win.h"
33 #include "chrome/browser/shell_integration.h"
34 #include "chrome/common/chrome_constants.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/pref_names.h"
37 #include "chrome/installer/util/browser_distribution.h"
38 #include "chrome/installer/util/google_update_constants.h"
39 #include "chrome/installer/util/install_util.h"
40 #include "chrome/installer/util/shell_util.h"
41 #include "chrome/installer/util/util_constants.h"
42 #include "ui/base/ui_base_switches.h"
44 #if defined(GOOGLE_CHROME_BUILD)
45 #include "google_update/google_update_idl.h"
50 bool GetNewerChromeFile(base::FilePath
* path
) {
51 if (!PathService::Get(base::DIR_EXE
, path
))
53 *path
= path
->Append(installer::kChromeNewExe
);
57 bool InvokeGoogleUpdateForRename() {
58 #if defined(GOOGLE_CHROME_BUILD)
59 base::win::ScopedComPtr
<IProcessLauncher
> ipl
;
60 if (!FAILED(ipl
.CreateInstance(__uuidof(ProcessLauncherClass
)))) {
61 ULONG_PTR phandle
= NULL
;
62 DWORD id
= GetCurrentProcessId();
63 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
64 if (!FAILED(ipl
->LaunchCmdElevated(dist
->GetAppGuid().c_str(),
65 google_update::kRegRenameCmdField
,
68 HANDLE handle
= HANDLE(phandle
);
69 WaitForSingleObject(handle
, INFINITE
);
71 ::GetExitCodeProcess(handle
, &exit_code
);
72 ::CloseHandle(handle
);
73 if (exit_code
== installer::RENAME_SUCCESSFUL
)
77 #endif // GOOGLE_CHROME_BUILD
81 base::FilePath
GetMetroRelauncherPath(const base::FilePath
& chrome_exe
,
82 const std::string
& version_str
) {
83 base::FilePath
path(chrome_exe
.DirName());
85 // The relauncher is ordinarily in the version directory. When running in a
86 // build tree however (where CHROME_VERSION is not set in the environment)
87 // look for it in Chrome's directory.
88 if (!version_str
.empty())
89 path
= path
.AppendASCII(version_str
);
91 return path
.Append(installer::kDelegateExecuteExe
);
96 namespace upgrade_util
{
98 const char kRelaunchModeMetro
[] = "relaunch.mode.metro";
99 const char kRelaunchModeDesktop
[] = "relaunch.mode.desktop";
100 const char kRelaunchModeDefault
[] = "relaunch.mode.default";
102 // TODO(shrikant): Have a map/array to quickly map enum to strings.
103 std::string
RelaunchModeEnumToString(const RelaunchMode relaunch_mode
) {
104 if (relaunch_mode
== RELAUNCH_MODE_METRO
)
105 return kRelaunchModeMetro
;
107 if (relaunch_mode
== RELAUNCH_MODE_DESKTOP
)
108 return kRelaunchModeDesktop
;
110 // For the purpose of code flow, even in case of wrong value we will
111 // return default re-launch mode.
112 return kRelaunchModeDefault
;
115 RelaunchMode
RelaunchModeStringToEnum(const std::string
& relaunch_mode
) {
116 if (relaunch_mode
== kRelaunchModeMetro
)
117 return RELAUNCH_MODE_METRO
;
119 if (relaunch_mode
== kRelaunchModeDesktop
)
120 return RELAUNCH_MODE_DESKTOP
;
122 // On Windows 7 if the current browser is in Chrome OS mode, then restart
123 // into Chrome OS mode.
124 if ((base::win::GetVersion() == base::win::VERSION_WIN7
) &&
125 base::CommandLine::ForCurrentProcess()->HasSwitch(
126 switches::kViewerConnect
) &&
127 !g_browser_process
->local_state()->HasPrefPath(prefs::kRelaunchMode
)) {
129 // On Windows 8, the delegate execute process looks up the previously
130 // launched mode from the registry and relaunches into that mode. We need
131 // something similar on Windows 7. For now, set the pref to ensure that
132 // we get relaunched into Chrome OS mode.
133 g_browser_process
->local_state()->SetString(
134 prefs::kRelaunchMode
, upgrade_util::kRelaunchModeMetro
);
135 return RELAUNCH_MODE_METRO
;
138 return RELAUNCH_MODE_DEFAULT
;
141 bool RelaunchChromeHelper(const base::CommandLine
& command_line
,
142 const RelaunchMode
& relaunch_mode
) {
143 scoped_ptr
<base::Environment
> env(base::Environment::Create());
144 std::string version_str
;
146 // Get the version variable and remove it from the environment.
147 if (env
->GetVar(chrome::kChromeVersionEnvVar
, &version_str
))
148 env
->UnSetVar(chrome::kChromeVersionEnvVar
);
152 base::FilePath chrome_exe
;
153 if (!PathService::Get(base::FILE_EXE
, &chrome_exe
)) {
158 // Explicitly make sure to relaunch chrome.exe rather than old_chrome.exe.
159 // This can happen when old_chrome.exe is launched by a user.
160 base::CommandLine chrome_exe_command_line
= command_line
;
161 chrome_exe_command_line
.SetProgram(
162 chrome_exe
.DirName().Append(installer::kChromeExe
));
164 if (base::win::GetVersion() < base::win::VERSION_WIN8
&&
165 relaunch_mode
!= RELAUNCH_MODE_METRO
&&
166 relaunch_mode
!= RELAUNCH_MODE_DESKTOP
) {
167 return base::LaunchProcess(chrome_exe_command_line
,
168 base::LaunchOptions()).IsValid();
171 // On Windows 8 we always use the delegate_execute for re-launching chrome.
172 // On Windows 7 we use delegate_execute for re-launching chrome into Windows
175 // Pass this Chrome's Start Menu shortcut path to the relauncher so it can re-
176 // activate chrome via ShellExecute which will wait until we exit. Since
177 // ShellExecute does not support handle passing to the child process we create
178 // a uniquely named mutex that we aquire and never release. So when we exit,
179 // Windows marks our mutex as abandoned and the wait is satisfied. The format
180 // of the named mutex is important. See DelegateExecuteOperation for more
182 base::string16 mutex_name
=
183 base::StringPrintf(L
"chrome.relaunch.%d", ::GetCurrentProcessId());
184 HANDLE mutex
= ::CreateMutexW(NULL
, TRUE
, mutex_name
.c_str());
185 // The |mutex| handle needs to be leaked. See comment above.
190 if (::GetLastError() == ERROR_ALREADY_EXISTS
) {
191 NOTREACHED() << "Relaunch mutex already exists";
195 base::CommandLine
relaunch_cmd(base::CommandLine::NO_PROGRAM
);
196 relaunch_cmd
.AppendSwitchPath(switches::kRelaunchShortcut
,
197 ShellIntegration::GetStartMenuShortcut(chrome_exe
));
198 relaunch_cmd
.AppendSwitchNative(switches::kWaitForMutex
, mutex_name
);
200 if (relaunch_mode
!= RELAUNCH_MODE_DEFAULT
) {
201 relaunch_cmd
.AppendSwitch(relaunch_mode
== RELAUNCH_MODE_METRO
?
202 switches::kForceImmersive
: switches::kForceDesktop
);
205 base::string16
params(relaunch_cmd
.GetCommandLineString());
206 base::string16
path(GetMetroRelauncherPath(chrome_exe
, version_str
).value());
208 SHELLEXECUTEINFO sei
= { sizeof(sei
) };
209 sei
.fMask
= SEE_MASK_FLAG_LOG_USAGE
| SEE_MASK_NOCLOSEPROCESS
;
210 sei
.nShow
= SW_SHOWNORMAL
;
211 sei
.lpFile
= path
.c_str();
212 sei
.lpParameters
= params
.c_str();
214 if (!::ShellExecuteExW(&sei
)) {
215 NOTREACHED() << "ShellExecute failed with " << GetLastError();
218 DWORD pid
= ::GetProcessId(sei
.hProcess
);
219 CloseHandle(sei
.hProcess
);
222 // The next call appears to be needed if we are relaunching from desktop into
223 // metro mode. The observed effect if not done is that chrome starts in metro
224 // mode but it is not given focus and it gets killed by windows after a few
226 ::AllowSetForegroundWindow(pid
);
230 bool RelaunchChromeBrowser(const base::CommandLine
& command_line
) {
231 return RelaunchChromeHelper(command_line
, RELAUNCH_MODE_DEFAULT
);
234 bool RelaunchChromeWithMode(const base::CommandLine
& command_line
,
235 const RelaunchMode
& relaunch_mode
) {
236 return RelaunchChromeHelper(command_line
, relaunch_mode
);
239 bool IsUpdatePendingRestart() {
240 base::FilePath new_chrome_exe
;
241 if (!GetNewerChromeFile(&new_chrome_exe
))
243 return base::PathExists(new_chrome_exe
);
246 bool SwapNewChromeExeIfPresent() {
247 base::FilePath new_chrome_exe
;
248 if (!GetNewerChromeFile(&new_chrome_exe
))
250 if (!base::PathExists(new_chrome_exe
))
252 base::FilePath cur_chrome_exe
;
253 if (!PathService::Get(base::FILE_EXE
, &cur_chrome_exe
))
256 // Open up the registry key containing current version and rename information.
257 bool user_install
= InstallUtil::IsPerUserInstall(cur_chrome_exe
);
258 HKEY reg_root
= user_install
? HKEY_CURRENT_USER
: HKEY_LOCAL_MACHINE
;
259 BrowserDistribution
*dist
= BrowserDistribution::GetDistribution();
260 base::win::RegKey key
;
261 if (key
.Open(reg_root
, dist
->GetVersionKey().c_str(),
262 KEY_QUERY_VALUE
) == ERROR_SUCCESS
) {
263 // First try to rename exe by launching rename command ourselves.
264 std::wstring rename_cmd
;
265 if (key
.ReadValue(google_update::kRegRenameCmdField
,
266 &rename_cmd
) == ERROR_SUCCESS
) {
267 base::LaunchOptions options
;
269 options
.start_hidden
= true;
270 base::Process process
= base::LaunchProcess(rename_cmd
, options
);
271 if (process
.IsValid()) {
273 ::GetExitCodeProcess(process
.Handle(), &exit_code
);
274 if (exit_code
== installer::RENAME_SUCCESSFUL
)
280 // Rename didn't work so try to rename by calling Google Update
281 return InvokeGoogleUpdateForRename();
284 bool IsRunningOldChrome() {
285 // This figures out the actual file name that the section containing the
286 // mapped exe refers to. This is used instead of GetModuleFileName because the
287 // .exe may have been renamed out from under us while we've been running which
288 // GetModuleFileName won't notice.
289 wchar_t mapped_file_name
[MAX_PATH
* 2] = {};
291 if (!::GetMappedFileName(::GetCurrentProcess(),
292 reinterpret_cast<void*>(::GetModuleHandle(NULL
)),
294 arraysize(mapped_file_name
))) {
298 base::FilePath
file_name(base::FilePath(mapped_file_name
).BaseName());
299 return base::FilePath::CompareEqualIgnoreCase(file_name
.value(),
300 installer::kChromeOldExe
);
303 bool DoUpgradeTasks(const base::CommandLine
& command_line
) {
304 // The DelegateExecute verb handler finalizes pending in-use updates for
305 // metro mode launches, as Chrome cannot be gracefully relaunched when
306 // running in this mode.
307 if (base::win::IsMetroProcess())
309 if (!SwapNewChromeExeIfPresent() && !IsRunningOldChrome())
311 // At this point the chrome.exe has been swapped with the new one.
312 if (!RelaunchChromeBrowser(command_line
)) {
313 // The re-launch fails. Feel free to panic now.
319 } // namespace upgrade_util