blob: a9e9f195ff9ef19cb143c95f496b9789d94fe86c [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>
8#include <shellapi.h>
9
[email protected]bc38c252011-04-12 21:46:5710#include <algorithm>
11#include <string>
12
13#include "base/base_paths.h"
14#include "base/command_line.h"
15#include "base/environment.h"
16#include "base/file_path.h"
17#include "base/file_util.h"
18#include "base/logging.h"
19#include "base/path_service.h"
20#include "base/process_util.h"
[email protected]80274b92011-07-15 17:20:3821#include "base/string_util.h"
[email protected]770c6d82012-09-06 22:21:3222#include "base/stringprintf.h"
[email protected]3ea1b182013-02-08 22:38:4123#include "base/strings/string_number_conversions.h"
[email protected]72cafc12012-06-13 14:36:3624#include "base/win/metro.h"
[email protected]bc38c252011-04-12 21:46:5725#include "base/win/registry.h"
26#include "base/win/scoped_comptr.h"
[email protected]770c6d82012-09-06 22:21:3227#include "base/win/windows_version.h"
[email protected]3e087992011-04-14 22:28:1228#include "chrome/browser/first_run/upgrade_util_win.h"
[email protected]3f69d6e612012-08-03 18:52:2729#include "chrome/browser/shell_integration.h"
[email protected]bc38c252011-04-12 21:46:5730#include "chrome/common/chrome_constants.h"
[email protected]80274b92011-07-15 17:20:3831#include "chrome/common/chrome_switches.h"
[email protected]bc38c252011-04-12 21:46:5732#include "chrome/installer/util/browser_distribution.h"
33#include "chrome/installer/util/google_update_constants.h"
34#include "chrome/installer/util/install_util.h"
35#include "chrome/installer/util/shell_util.h"
36#include "chrome/installer/util/util_constants.h"
[email protected]cec99842012-02-10 03:24:2337#include "google_update/google_update_idl.h"
[email protected]bc38c252011-04-12 21:46:5738
39namespace {
40
[email protected]650b2d52013-02-10 03:41:4541bool GetNewerChromeFile(base::FilePath* path) {
[email protected]bc38c252011-04-12 21:46:5742 if (!PathService::Get(base::DIR_EXE, path))
43 return false;
44 *path = path->Append(installer::kChromeNewExe);
45 return true;
46}
47
48bool InvokeGoogleUpdateForRename() {
49 base::win::ScopedComPtr<IProcessLauncher> ipl;
50 if (!FAILED(ipl.CreateInstance(__uuidof(ProcessLauncherClass)))) {
51 ULONG_PTR phandle = NULL;
52 DWORD id = GetCurrentProcessId();
53 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
54 if (!FAILED(ipl->LaunchCmdElevated(dist->GetAppGuid().c_str(),
55 google_update::kRegRenameCmdField,
56 id,
57 &phandle))) {
58 HANDLE handle = HANDLE(phandle);
59 WaitForSingleObject(handle, INFINITE);
60 DWORD exit_code;
61 ::GetExitCodeProcess(handle, &exit_code);
62 ::CloseHandle(handle);
63 if (exit_code == installer::RENAME_SUCCESSFUL)
64 return true;
65 }
66 }
67 return false;
68}
69
[email protected]650b2d52013-02-10 03:41:4570base::FilePath GetMetroRelauncherPath(const base::FilePath& chrome_exe,
71 const std::string& version_str) {
72 base::FilePath path(chrome_exe.DirName());
[email protected]3f69d6e612012-08-03 18:52:2773
74 // The relauncher is ordinarily in the version directory. When running in a
75 // build tree however (where CHROME_VERSION is not set in the environment)
76 // look for it in Chrome's directory.
77 if (!version_str.empty())
78 path = path.AppendASCII(version_str);
79
80 return path.Append(installer::kDelegateExecuteExe);
81}
82
[email protected]bc38c252011-04-12 21:46:5783} // namespace
84
[email protected]815856722011-04-13 17:19:1985namespace upgrade_util {
86
[email protected]9baee952012-10-04 03:44:1787bool RelaunchChromeHelper(const CommandLine& command_line, bool mode_switch) {
[email protected]815856722011-04-13 17:19:1988 scoped_ptr<base::Environment> env(base::Environment::Create());
[email protected]3f69d6e612012-08-03 18:52:2789 std::string version_str;
90
91 // Get the version variable and remove it from the environment.
92 if (env->GetVar(chrome::kChromeVersionEnvVar, &version_str))
93 env->UnSetVar(chrome::kChromeVersionEnvVar);
94 else
95 version_str.clear();
96
[email protected]770c6d82012-09-06 22:21:3297 if (base::win::GetVersion() < base::win::VERSION_WIN8)
[email protected]3f69d6e612012-08-03 18:52:2798 return base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
99
[email protected]770c6d82012-09-06 22:21:32100 // On Windows 8 we always use the delegate_execute for re-launching chrome.
101 //
102 // Pass this Chrome's Start Menu shortcut path to the relauncher so it can
103 // re-activate chrome via ShellExecute.
[email protected]650b2d52013-02-10 03:41:45104 base::FilePath chrome_exe;
[email protected]3f69d6e612012-08-03 18:52:27105 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
106 NOTREACHED();
107 return false;
108 }
109
[email protected]770c6d82012-09-06 22:21:32110 // We need to use ShellExecute to launch the relauncher, which will wait until
111 // we exit. But ShellExecute does not support handle passing to the child
112 // process so we create a uniquely named mutex that we aquire and never
113 // release. So when we exit, Windows marks our mutex as abandoned and the
114 // wait is satisfied.
115 // The format of the named mutex is important. See DelegateExecuteOperation
116 // for more details.
117 string16 mutex_name =
118 base::StringPrintf(L"chrome.relaunch.%d", ::GetCurrentProcessId());
119 HANDLE mutex = ::CreateMutexW(NULL, TRUE, mutex_name.c_str());
120 // The |mutex| handle needs to be leaked. See comment above.
121 if (!mutex) {
122 NOTREACHED();
123 return false;
124 }
125 if (::GetLastError() == ERROR_ALREADY_EXISTS) {
126 NOTREACHED() << "Relaunch mutex already exists";
[email protected]3f69d6e612012-08-03 18:52:27127 return false;
128 }
129
[email protected]770c6d82012-09-06 22:21:32130 CommandLine relaunch_cmd(CommandLine::NO_PROGRAM);
[email protected]3f69d6e612012-08-03 18:52:27131 relaunch_cmd.AppendSwitchPath(switches::kRelaunchShortcut,
132 ShellIntegration::GetStartMenuShortcut(chrome_exe));
[email protected]770c6d82012-09-06 22:21:32133 relaunch_cmd.AppendSwitchNative(switches::kWaitForMutex, mutex_name);
[email protected]3f69d6e612012-08-03 18:52:27134
[email protected]9baee952012-10-04 03:44:17135 if (mode_switch) {
136 relaunch_cmd.AppendSwitch(base::win::IsMetroProcess() ?
137 switches::kForceDesktop : switches::kForceImmersive);
138 }
[email protected]770c6d82012-09-06 22:21:32139
140 string16 params(relaunch_cmd.GetCommandLineString());
141 string16 path(GetMetroRelauncherPath(chrome_exe, version_str).value());
142
143 SHELLEXECUTEINFO sei = { sizeof(sei) };
144 sei.fMask = SEE_MASK_FLAG_LOG_USAGE | SEE_MASK_NOCLOSEPROCESS;
145 sei.nShow = SW_SHOWNORMAL;
146 sei.lpFile = path.c_str();
147 sei.lpParameters = params.c_str();
148
149 if (!::ShellExecuteExW(&sei)) {
150 NOTREACHED() << "ShellExecute failed with " << GetLastError();
[email protected]3f69d6e612012-08-03 18:52:27151 return false;
152 }
[email protected]770c6d82012-09-06 22:21:32153 DWORD pid = ::GetProcessId(sei.hProcess);
154 CloseHandle(sei.hProcess);
155 if (!pid)
156 return false;
157 // The next call appears to be needed if we are relaunching from desktop into
158 // metro mode. The observed effect if not done is that chrome starts in metro
159 // mode but it is not given focus and it gets killed by windows after a few
160 // seconds.
161 ::AllowSetForegroundWindow(pid);
[email protected]3f69d6e612012-08-03 18:52:27162 return true;
[email protected]815856722011-04-13 17:19:19163}
164
[email protected]770c6d82012-09-06 22:21:32165bool RelaunchChromeBrowser(const CommandLine& command_line) {
[email protected]9baee952012-10-04 03:44:17166 return RelaunchChromeHelper(command_line, false);
[email protected]770c6d82012-09-06 22:21:32167}
168
169bool RelaunchChromeWithModeSwitch(const CommandLine& command_line) {
[email protected]9baee952012-10-04 03:44:17170 return RelaunchChromeHelper(command_line, true);
[email protected]770c6d82012-09-06 22:21:32171}
172
[email protected]815856722011-04-13 17:19:19173bool IsUpdatePendingRestart() {
[email protected]650b2d52013-02-10 03:41:45174 base::FilePath new_chrome_exe;
[email protected]815856722011-04-13 17:19:19175 if (!GetNewerChromeFile(&new_chrome_exe))
176 return false;
177 return file_util::PathExists(new_chrome_exe);
178}
179
[email protected]815856722011-04-13 17:19:19180bool SwapNewChromeExeIfPresent() {
[email protected]650b2d52013-02-10 03:41:45181 base::FilePath new_chrome_exe;
[email protected]bc38c252011-04-12 21:46:57182 if (!GetNewerChromeFile(&new_chrome_exe))
183 return false;
184 if (!file_util::PathExists(new_chrome_exe))
185 return false;
[email protected]650b2d52013-02-10 03:41:45186 base::FilePath cur_chrome_exe;
[email protected]bc38c252011-04-12 21:46:57187 if (!PathService::Get(base::FILE_EXE, &cur_chrome_exe))
188 return false;
189
[email protected]80274b92011-07-15 17:20:38190 // Open up the registry key containing current version and rename information.
[email protected]bc38c252011-04-12 21:46:57191 bool user_install =
192 InstallUtil::IsPerUserInstall(cur_chrome_exe.value().c_str());
193 HKEY reg_root = user_install ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
194 BrowserDistribution *dist = BrowserDistribution::GetDistribution();
195 base::win::RegKey key;
[email protected]80274b92011-07-15 17:20:38196 if (key.Open(reg_root, dist->GetVersionKey().c_str(),
197 KEY_QUERY_VALUE) == ERROR_SUCCESS) {
198
199 // Having just ascertained that we can swap, now check that we should: if
200 // we are given an explicit --chrome-version flag, don't rename unless the
201 // specified version matches the "pv" value. In practice, this is used to
202 // defer Chrome Frame updates until the current version of the Chrome Frame
203 // DLL component is loaded.
204 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
205 if (cmd_line.HasSwitch(switches::kChromeVersion)) {
206 std::string version_string =
207 cmd_line.GetSwitchValueASCII(switches::kChromeVersion);
[email protected]12126d372012-07-11 18:40:53208 Version cmd_version(version_string);
[email protected]80274b92011-07-15 17:20:38209
210 std::wstring pv_value;
211 if (key.ReadValue(google_update::kRegVersionField,
212 &pv_value) == ERROR_SUCCESS) {
[email protected]12126d372012-07-11 18:40:53213 Version pv_version(WideToASCII(pv_value));
214 if (cmd_version.IsValid() && pv_version.IsValid() &&
215 !cmd_version.Equals(pv_version)) {
[email protected]80274b92011-07-15 17:20:38216 return false;
217 }
218 }
219 }
220
221 // First try to rename exe by launching rename command ourselves.
222 std::wstring rename_cmd;
223 if (key.ReadValue(google_update::kRegRenameCmdField,
224 &rename_cmd) == ERROR_SUCCESS) {
225 base::ProcessHandle handle;
[email protected]89a56162011-07-18 21:38:02226 base::LaunchOptions options;
227 options.wait = true;
228 options.start_hidden = true;
229 if (base::LaunchProcess(rename_cmd, options, &handle)) {
[email protected]80274b92011-07-15 17:20:38230 DWORD exit_code;
231 ::GetExitCodeProcess(handle, &exit_code);
232 ::CloseHandle(handle);
233 if (exit_code == installer::RENAME_SUCCESSFUL)
234 return true;
235 }
[email protected]bc38c252011-04-12 21:46:57236 }
237 }
238
239 // Rename didn't work so try to rename by calling Google Update
240 return InvokeGoogleUpdateForRename();
241}
242
[email protected]815856722011-04-13 17:19:19243bool DoUpgradeTasks(const CommandLine& command_line) {
[email protected]72cafc12012-06-13 14:36:36244 // The DelegateExecute verb handler finalizes pending in-use updates for
245 // metro mode launches, as Chrome cannot be gracefully relaunched when
246 // running in this mode.
[email protected]7bf385c2012-06-18 20:40:06247 if (base::win::IsMetroProcess())
[email protected]72cafc12012-06-13 14:36:36248 return false;
[email protected]815856722011-04-13 17:19:19249 if (!SwapNewChromeExeIfPresent())
[email protected]bc38c252011-04-12 21:46:57250 return false;
251 // At this point the chrome.exe has been swapped with the new one.
[email protected]815856722011-04-13 17:19:19252 if (!RelaunchChromeBrowser(command_line)) {
[email protected]bc38c252011-04-12 21:46:57253 // The re-launch fails. Feel free to panic now.
254 NOTREACHED();
255 }
256 return true;
257}
258
[email protected]815856722011-04-13 17:19:19259} // namespace upgrade_util