blob: e5933c78dd33ba63dcc631bb6e4b343a50eda164 [file] [log] [blame]
[email protected]cec99842012-02-10 03:24:231// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]bc38c252011-04-12 21:46:572// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]815856722011-04-13 17:19:195#include "chrome/browser/first_run/upgrade_util.h"
[email protected]bc38c252011-04-12 21:46:576
[email protected]770c6d82012-09-06 22:21:327#include <windows.h>
[email protected]fdbea98d2014-05-16 19:29:208#include <psapi.h>
[email protected]770c6d82012-09-06 22:21:329#include <shellapi.h>
10
[email protected]bc38c252011-04-12 21:46:5711#include <algorithm>
12#include <string>
13
14#include "base/base_paths.h"
15#include "base/command_line.h"
16#include "base/environment.h"
[email protected]57999812013-02-24 05:40:5217#include "base/files/file_path.h"
thestig18dfb7a52014-08-26 10:44:0418#include "base/files/file_util.h"
[email protected]bc38c252011-04-12 21:46:5719#include "base/logging.h"
20#include "base/path_service.h"
ananta069fc882014-09-13 01:22:1221#include "base/prefs/pref_service.h"
[email protected]d09a4ce1c2013-07-24 17:37:0222#include "base/process/launch.h"
23#include "base/process/process_handle.h"
[email protected]3ea1b182013-02-08 22:38:4124#include "base/strings/string_number_conversions.h"
[email protected]d8830562013-06-10 22:01:5425#include "base/strings/string_util.h"
26#include "base/strings/stringprintf.h"
[email protected]72cafc12012-06-13 14:36:3627#include "base/win/metro.h"
[email protected]bc38c252011-04-12 21:46:5728#include "base/win/registry.h"
29#include "base/win/scoped_comptr.h"
[email protected]770c6d82012-09-06 22:21:3230#include "base/win/windows_version.h"
ananta069fc882014-09-13 01:22:1231#include "chrome/browser/browser_process.h"
[email protected]3e087992011-04-14 22:28:1232#include "chrome/browser/first_run/upgrade_util_win.h"
[email protected]3f69d6e612012-08-03 18:52:2733#include "chrome/browser/shell_integration.h"
[email protected]bc38c252011-04-12 21:46:5734#include "chrome/common/chrome_constants.h"
[email protected]80274b92011-07-15 17:20:3835#include "chrome/common/chrome_switches.h"
ananta069fc882014-09-13 01:22:1236#include "chrome/common/pref_names.h"
[email protected]bc38c252011-04-12 21:46:5737#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"
ananta069fc882014-09-13 01:22:1242#include "ui/base/ui_base_switches.h"
[email protected]bc38c252011-04-12 21:46:5743
grt235b3f092015-05-27 21:42:4844#if defined(GOOGLE_CHROME_BUILD)
45#include "google_update/google_update_idl.h"
46#endif
47
[email protected]bc38c252011-04-12 21:46:5748namespace {
49
[email protected]650b2d52013-02-10 03:41:4550bool GetNewerChromeFile(base::FilePath* path) {
[email protected]bc38c252011-04-12 21:46:5751 if (!PathService::Get(base::DIR_EXE, path))
52 return false;
53 *path = path->Append(installer::kChromeNewExe);
54 return true;
55}
56
57bool InvokeGoogleUpdateForRename() {
grt235b3f092015-05-27 21:42:4858#if defined(GOOGLE_CHROME_BUILD)
[email protected]bc38c252011-04-12 21:46:5759 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,
66 id,
67 &phandle))) {
68 HANDLE handle = HANDLE(phandle);
69 WaitForSingleObject(handle, INFINITE);
70 DWORD exit_code;
71 ::GetExitCodeProcess(handle, &exit_code);
72 ::CloseHandle(handle);
73 if (exit_code == installer::RENAME_SUCCESSFUL)
74 return true;
75 }
76 }
grt235b3f092015-05-27 21:42:4877#endif // GOOGLE_CHROME_BUILD
[email protected]bc38c252011-04-12 21:46:5778 return false;
79}
80
[email protected]650b2d52013-02-10 03:41:4581base::FilePath GetMetroRelauncherPath(const base::FilePath& chrome_exe,
82 const std::string& version_str) {
83 base::FilePath path(chrome_exe.DirName());
[email protected]3f69d6e612012-08-03 18:52:2784
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);
90
91 return path.Append(installer::kDelegateExecuteExe);
92}
93
[email protected]bc38c252011-04-12 21:46:5794} // namespace
95
[email protected]815856722011-04-13 17:19:1996namespace upgrade_util {
97
[email protected]d51373d2013-10-31 15:22:0098const char kRelaunchModeMetro[] = "relaunch.mode.metro";
99const char kRelaunchModeDesktop[] = "relaunch.mode.desktop";
100const char kRelaunchModeDefault[] = "relaunch.mode.default";
101
102// TODO(shrikant): Have a map/array to quickly map enum to strings.
103std::string RelaunchModeEnumToString(const RelaunchMode relaunch_mode) {
104 if (relaunch_mode == RELAUNCH_MODE_METRO)
105 return kRelaunchModeMetro;
106
107 if (relaunch_mode == RELAUNCH_MODE_DESKTOP)
108 return kRelaunchModeDesktop;
109
110 // For the purpose of code flow, even in case of wrong value we will
111 // return default re-launch mode.
112 return kRelaunchModeDefault;
113}
114
115RelaunchMode RelaunchModeStringToEnum(const std::string& relaunch_mode) {
116 if (relaunch_mode == kRelaunchModeMetro)
117 return RELAUNCH_MODE_METRO;
118
119 if (relaunch_mode == kRelaunchModeDesktop)
120 return RELAUNCH_MODE_DESKTOP;
121
ananta069fc882014-09-13 01:22:12122 // 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) &&
avi556c05022014-12-22 23:31:43125 base::CommandLine::ForCurrentProcess()->HasSwitch(
126 switches::kViewerConnect) &&
127 !g_browser_process->local_state()->HasPrefPath(prefs::kRelaunchMode)) {
ananta069fc882014-09-13 01:22:12128 // TODO(ananta)
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;
136 }
137
[email protected]d51373d2013-10-31 15:22:00138 return RELAUNCH_MODE_DEFAULT;
139}
140
avi556c05022014-12-22 23:31:43141bool RelaunchChromeHelper(const base::CommandLine& command_line,
[email protected]d51373d2013-10-31 15:22:00142 const RelaunchMode& relaunch_mode) {
[email protected]815856722011-04-13 17:19:19143 scoped_ptr<base::Environment> env(base::Environment::Create());
[email protected]3f69d6e612012-08-03 18:52:27144 std::string version_str;
145
146 // Get the version variable and remove it from the environment.
147 if (env->GetVar(chrome::kChromeVersionEnvVar, &version_str))
148 env->UnSetVar(chrome::kChromeVersionEnvVar);
149 else
150 version_str.clear();
151
[email protected]650b2d52013-02-10 03:41:45152 base::FilePath chrome_exe;
[email protected]3f69d6e612012-08-03 18:52:27153 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
154 NOTREACHED();
155 return false;
156 }
157
[email protected]fdbea98d2014-05-16 19:29:20158 // 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.
avi556c05022014-12-22 23:31:43160 base::CommandLine chrome_exe_command_line = command_line;
[email protected]fdbea98d2014-05-16 19:29:20161 chrome_exe_command_line.SetProgram(
162 chrome_exe.DirName().Append(installer::kChromeExe));
163
ananta196db192014-08-28 21:37:55164 if (base::win::GetVersion() < base::win::VERSION_WIN8 &&
165 relaunch_mode != RELAUNCH_MODE_METRO &&
rvargas66628bd22014-12-30 19:27:00166 relaunch_mode != RELAUNCH_MODE_DESKTOP) {
[email protected]fdbea98d2014-05-16 19:29:20167 return base::LaunchProcess(chrome_exe_command_line,
rvargas2d12a4622014-12-15 17:57:13168 base::LaunchOptions()).IsValid();
rvargas66628bd22014-12-30 19:27:00169 }
[email protected]fdbea98d2014-05-16 19:29:20170
171 // On Windows 8 we always use the delegate_execute for re-launching chrome.
ananta196db192014-08-28 21:37:55172 // On Windows 7 we use delegate_execute for re-launching chrome into Windows
173 // ASH.
[email protected]fdbea98d2014-05-16 19:29:20174 //
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
181 // details.
[email protected]0085863a2013-12-06 21:19:03182 base::string16 mutex_name =
[email protected]770c6d82012-09-06 22:21:32183 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.
186 if (!mutex) {
187 NOTREACHED();
188 return false;
189 }
190 if (::GetLastError() == ERROR_ALREADY_EXISTS) {
191 NOTREACHED() << "Relaunch mutex already exists";
[email protected]3f69d6e612012-08-03 18:52:27192 return false;
193 }
194
avi556c05022014-12-22 23:31:43195 base::CommandLine relaunch_cmd(base::CommandLine::NO_PROGRAM);
[email protected]3f69d6e612012-08-03 18:52:27196 relaunch_cmd.AppendSwitchPath(switches::kRelaunchShortcut,
197 ShellIntegration::GetStartMenuShortcut(chrome_exe));
[email protected]770c6d82012-09-06 22:21:32198 relaunch_cmd.AppendSwitchNative(switches::kWaitForMutex, mutex_name);
[email protected]3f69d6e612012-08-03 18:52:27199
[email protected]d51373d2013-10-31 15:22:00200 if (relaunch_mode != RELAUNCH_MODE_DEFAULT) {
201 relaunch_cmd.AppendSwitch(relaunch_mode == RELAUNCH_MODE_METRO?
202 switches::kForceImmersive : switches::kForceDesktop);
[email protected]9baee952012-10-04 03:44:17203 }
[email protected]770c6d82012-09-06 22:21:32204
[email protected]0085863a2013-12-06 21:19:03205 base::string16 params(relaunch_cmd.GetCommandLineString());
206 base::string16 path(GetMetroRelauncherPath(chrome_exe, version_str).value());
[email protected]770c6d82012-09-06 22:21:32207
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();
213
214 if (!::ShellExecuteExW(&sei)) {
215 NOTREACHED() << "ShellExecute failed with " << GetLastError();
[email protected]3f69d6e612012-08-03 18:52:27216 return false;
217 }
[email protected]770c6d82012-09-06 22:21:32218 DWORD pid = ::GetProcessId(sei.hProcess);
219 CloseHandle(sei.hProcess);
220 if (!pid)
221 return false;
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
225 // seconds.
226 ::AllowSetForegroundWindow(pid);
[email protected]3f69d6e612012-08-03 18:52:27227 return true;
[email protected]815856722011-04-13 17:19:19228}
229
avi556c05022014-12-22 23:31:43230bool RelaunchChromeBrowser(const base::CommandLine& command_line) {
[email protected]d51373d2013-10-31 15:22:00231 return RelaunchChromeHelper(command_line, RELAUNCH_MODE_DEFAULT);
[email protected]770c6d82012-09-06 22:21:32232}
233
avi556c05022014-12-22 23:31:43234bool RelaunchChromeWithMode(const base::CommandLine& command_line,
[email protected]d51373d2013-10-31 15:22:00235 const RelaunchMode& relaunch_mode) {
236 return RelaunchChromeHelper(command_line, relaunch_mode);
[email protected]770c6d82012-09-06 22:21:32237}
238
[email protected]815856722011-04-13 17:19:19239bool IsUpdatePendingRestart() {
[email protected]650b2d52013-02-10 03:41:45240 base::FilePath new_chrome_exe;
[email protected]815856722011-04-13 17:19:19241 if (!GetNewerChromeFile(&new_chrome_exe))
242 return false;
[email protected]7567484142013-07-11 17:36:07243 return base::PathExists(new_chrome_exe);
[email protected]815856722011-04-13 17:19:19244}
245
[email protected]815856722011-04-13 17:19:19246bool SwapNewChromeExeIfPresent() {
[email protected]650b2d52013-02-10 03:41:45247 base::FilePath new_chrome_exe;
[email protected]bc38c252011-04-12 21:46:57248 if (!GetNewerChromeFile(&new_chrome_exe))
249 return false;
[email protected]7567484142013-07-11 17:36:07250 if (!base::PathExists(new_chrome_exe))
[email protected]bc38c252011-04-12 21:46:57251 return false;
[email protected]650b2d52013-02-10 03:41:45252 base::FilePath cur_chrome_exe;
[email protected]bc38c252011-04-12 21:46:57253 if (!PathService::Get(base::FILE_EXE, &cur_chrome_exe))
254 return false;
255
[email protected]80274b92011-07-15 17:20:38256 // Open up the registry key containing current version and rename information.
grte76ca2852014-12-05 16:42:10257 bool user_install = InstallUtil::IsPerUserInstall(cur_chrome_exe);
[email protected]bc38c252011-04-12 21:46:57258 HKEY reg_root = user_install ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
259 BrowserDistribution *dist = BrowserDistribution::GetDistribution();
260 base::win::RegKey key;
[email protected]80274b92011-07-15 17:20:38261 if (key.Open(reg_root, dist->GetVersionKey().c_str(),
262 KEY_QUERY_VALUE) == ERROR_SUCCESS) {
[email protected]80274b92011-07-15 17:20:38263 // 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) {
[email protected]89a56162011-07-18 21:38:02267 base::LaunchOptions options;
268 options.wait = true;
269 options.start_hidden = true;
rvargas61812772014-12-05 03:14:54270 base::Process process = base::LaunchProcess(rename_cmd, options);
271 if (process.IsValid()) {
[email protected]80274b92011-07-15 17:20:38272 DWORD exit_code;
rvargas61812772014-12-05 03:14:54273 ::GetExitCodeProcess(process.Handle(), &exit_code);
[email protected]80274b92011-07-15 17:20:38274 if (exit_code == installer::RENAME_SUCCESSFUL)
275 return true;
276 }
[email protected]bc38c252011-04-12 21:46:57277 }
278 }
279
280 // Rename didn't work so try to rename by calling Google Update
281 return InvokeGoogleUpdateForRename();
282}
283
[email protected]fdbea98d2014-05-16 19:29:20284bool 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] = {};
290
291 if (!::GetMappedFileName(::GetCurrentProcess(),
292 reinterpret_cast<void*>(::GetModuleHandle(NULL)),
293 mapped_file_name,
294 arraysize(mapped_file_name))) {
295 return false;
296 }
297
298 base::FilePath file_name(base::FilePath(mapped_file_name).BaseName());
299 return base::FilePath::CompareEqualIgnoreCase(file_name.value(),
300 installer::kChromeOldExe);
301}
302
avi556c05022014-12-22 23:31:43303bool DoUpgradeTasks(const base::CommandLine& command_line) {
[email protected]72cafc12012-06-13 14:36:36304 // 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.
[email protected]7bf385c2012-06-18 20:40:06307 if (base::win::IsMetroProcess())
[email protected]72cafc12012-06-13 14:36:36308 return false;
[email protected]fdbea98d2014-05-16 19:29:20309 if (!SwapNewChromeExeIfPresent() && !IsRunningOldChrome())
[email protected]bc38c252011-04-12 21:46:57310 return false;
311 // At this point the chrome.exe has been swapped with the new one.
[email protected]815856722011-04-13 17:19:19312 if (!RelaunchChromeBrowser(command_line)) {
[email protected]bc38c252011-04-12 21:46:57313 // The re-launch fails. Feel free to panic now.
314 NOTREACHED();
315 }
316 return true;
317}
318
[email protected]815856722011-04-13 17:19:19319} // namespace upgrade_util