blob: 563d5e13d148cda826d245c4b21fb8041ef9ff66 [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
Daniel Cheng7d9e3d52022-02-26 09:03:245#include "chrome/browser/first_run/upgrade_util_win.h"
[email protected]bc38c252011-04-12 21:46:576
Nico Webereaa08412019-08-14 01:24:377// Must be first.
[email protected]770c6d82012-09-06 22:21:328#include <windows.h>
Nico Webereaa08412019-08-14 01:24:379
robliaoa872e992017-05-18 06:36:1910#include <objbase.h>
[email protected]fdbea98d2014-05-16 19:29:2011#include <psapi.h>
[email protected]770c6d82012-09-06 22:21:3212#include <shellapi.h>
Robert Liaob2bc703d2017-10-17 20:52:3513#include <wrl/client.h>
[email protected]770c6d82012-09-06 22:21:3214
[email protected]bc38c252011-04-12 21:46:5715#include <algorithm>
Greg Thompsona1f95122019-01-31 22:46:4516#include <ios>
[email protected]bc38c252011-04-12 21:46:5717#include <string>
18
19#include "base/base_paths.h"
20#include "base/command_line.h"
[email protected]57999812013-02-24 05:40:5221#include "base/files/file_path.h"
thestig18dfb7a52014-08-26 10:44:0422#include "base/files/file_util.h"
[email protected]bc38c252011-04-12 21:46:5723#include "base/logging.h"
Gabriel Charette12b58ac2021-09-22 18:08:5524#include "base/metrics/histogram_macros.h"
[email protected]bc38c252011-04-12 21:46:5725#include "base/path_service.h"
[email protected]d09a4ce1c2013-07-24 17:37:0226#include "base/process/launch.h"
27#include "base/process/process_handle.h"
[email protected]3ea1b182013-02-08 22:38:4128#include "base/strings/string_number_conversions.h"
[email protected]d8830562013-06-10 22:01:5429#include "base/strings/string_util.h"
Gabriel Charette12b58ac2021-09-22 18:08:5530#include "base/time/time.h"
31#include "base/trace_event/trace_event.h"
[email protected]bc38c252011-04-12 21:46:5732#include "base/win/registry.h"
[email protected]770c6d82012-09-06 22:21:3233#include "base/win/windows_version.h"
Nico Webereaa08412019-08-14 01:24:3734#include "build/branding_buildflags.h"
ananta069fc882014-09-13 01:22:1235#include "chrome/browser/browser_process.h"
Daniel Cheng7d9e3d52022-02-26 09:03:2436#include "chrome/browser/first_run/upgrade_util.h"
[email protected]3f69d6e612012-08-03 18:52:2737#include "chrome/browser/shell_integration.h"
[email protected]80274b92011-07-15 17:20:3838#include "chrome/common/chrome_switches.h"
ananta069fc882014-09-13 01:22:1239#include "chrome/common/pref_names.h"
grt4474dad2017-02-27 21:00:4640#include "chrome/install_static/install_util.h"
[email protected]bc38c252011-04-12 21:46:5741#include "chrome/installer/util/google_update_constants.h"
[email protected]bc38c252011-04-12 21:46:5742#include "chrome/installer/util/util_constants.h"
brettwb1fc1b82016-02-02 00:19:0843#include "components/prefs/pref_service.h"
ananta069fc882014-09-13 01:22:1244#include "ui/base/ui_base_switches.h"
[email protected]bc38c252011-04-12 21:46:5745
Nico Webereaa08412019-08-14 01:24:3746#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
grt235b3f092015-05-27 21:42:4847#include "google_update/google_update_idl.h"
48#endif
49
[email protected]bc38c252011-04-12 21:46:5750namespace {
51
[email protected]650b2d52013-02-10 03:41:4552bool GetNewerChromeFile(base::FilePath* path) {
Avi Drissman9098f9002018-05-04 00:11:5253 if (!base::PathService::Get(base::DIR_EXE, path))
[email protected]bc38c252011-04-12 21:46:5754 return false;
55 *path = path->Append(installer::kChromeNewExe);
56 return true;
57}
58
59bool InvokeGoogleUpdateForRename() {
Nico Webereaa08412019-08-14 01:24:3760#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
Gabriel Charettef7e85712021-12-10 20:46:2261 // This has been identified as very slow on some startups. Detailed trace
62 // events below try to shine a light on each steps. crbug.com/1252004
Gabriel Charette12b58ac2021-09-22 18:08:5563 TRACE_EVENT0("startup", "upgrade_util::InvokeGoogleUpdateForRename");
64
Robert Liaob2bc703d2017-10-17 20:52:3565 Microsoft::WRL::ComPtr<IProcessLauncher> ipl;
Gabriel Charettef7e85712021-12-10 20:46:2266 {
67 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename CoCreateInstance");
68 HRESULT hr = ::CoCreateInstance(__uuidof(ProcessLauncherClass), nullptr,
69 CLSCTX_ALL, IID_PPV_ARGS(&ipl));
70 if (FAILED(hr)) {
71 TRACE_EVENT0("startup",
72 "InvokeGoogleUpdateForRename CoCreateInstance failed");
73 LOG(ERROR) << "CoCreate ProcessLauncherClass failed; hr = " << std::hex
74 << hr;
75 return false;
76 }
[email protected]bc38c252011-04-12 21:46:5777 }
Greg Thompsona1f95122019-01-31 22:46:4578
79 ULONG_PTR process_handle;
Gabriel Charettef7e85712021-12-10 20:46:2280 {
81 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename LaunchCmdElevated");
Dominique Fauteux-Chapleau269cabf2021-12-10 22:10:3182 HRESULT hr = ipl->LaunchCmdElevated(
83 install_static::GetAppGuid(),
84 google_update::kRegRenameCmdField,
85 ::GetCurrentProcessId(), &process_handle);
Gabriel Charettef7e85712021-12-10 20:46:2286 if (FAILED(hr)) {
87 TRACE_EVENT0("startup",
88 "InvokeGoogleUpdateForRename LaunchCmdElevated failed");
89 LOG(ERROR) << "IProcessLauncher::LaunchCmdElevated failed; hr = "
90 << std::hex << hr;
91 return false;
92 }
Greg Thompsona1f95122019-01-31 22:46:4593 }
94
95 base::Process rename_process(
96 reinterpret_cast<base::ProcessHandle>(process_handle));
97 int exit_code;
Gabriel Charettef7e85712021-12-10 20:46:2298 {
99 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename WaitForExit");
100 if (!rename_process.WaitForExit(&exit_code)) {
101 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename WaitForExit failed");
102 PLOG(ERROR) << "WaitForExit of rename process failed";
103 return false;
104 }
Greg Thompsona1f95122019-01-31 22:46:45105 }
106
107 if (exit_code != installer::RENAME_SUCCESSFUL) {
Gabriel Charettef7e85712021-12-10 20:46:22108 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename !RENAME_SUCCESSFUL");
Greg Thompsona1f95122019-01-31 22:46:45109 LOG(ERROR) << "Rename process failed with exit code " << exit_code;
110 return false;
111 }
112
Gabriel Charette334775a2021-12-13 18:57:26113 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename RENAME_SUCCESSFUL");
114
Greg Thompsona1f95122019-01-31 22:46:45115 return true;
Nico Webereaa08412019-08-14 01:24:37116#else // BUILDFLAG(GOOGLE_CHROME_BRANDING)
[email protected]bc38c252011-04-12 21:46:57117 return false;
Nico Webereaa08412019-08-14 01:24:37118#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
[email protected]bc38c252011-04-12 21:46:57119}
120
121} // namespace
122
[email protected]815856722011-04-13 17:19:19123namespace upgrade_util {
124
Greg Thompson87ee38f2019-08-09 06:00:23125bool RelaunchChromeBrowserImpl(const base::CommandLine& command_line) {
Gabriel Charette12b58ac2021-09-22 18:08:55126 TRACE_EVENT0("startup", "upgrade_util::RelaunchChromeBrowserImpl");
127
[email protected]650b2d52013-02-10 03:41:45128 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52129 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
[email protected]3f69d6e612012-08-03 18:52:27130 NOTREACHED();
131 return false;
132 }
133
[email protected]fdbea98d2014-05-16 19:29:20134 // Explicitly make sure to relaunch chrome.exe rather than old_chrome.exe.
135 // This can happen when old_chrome.exe is launched by a user.
avi556c05022014-12-22 23:31:43136 base::CommandLine chrome_exe_command_line = command_line;
[email protected]fdbea98d2014-05-16 19:29:20137 chrome_exe_command_line.SetProgram(
138 chrome_exe.DirName().Append(installer::kChromeExe));
139
scottmg20920cb2016-04-07 16:23:59140 // Set the working directory to the exe's directory. This avoids a handle to
141 // the version directory being kept open in the relaunched child process.
142 base::LaunchOptions launch_options;
143 launch_options.current_directory = chrome_exe.DirName();
Greg Thompson47faf202018-05-18 20:59:03144 // Give the new process the right to bring its windows to the foreground.
145 launch_options.grant_foreground_privilege = true;
scottmg20920cb2016-04-07 16:23:59146 return base::LaunchProcess(chrome_exe_command_line, launch_options).IsValid();
[email protected]815856722011-04-13 17:19:19147}
148
149bool IsUpdatePendingRestart() {
Gabriel Charette12b58ac2021-09-22 18:08:55150 TRACE_EVENT0("startup", "upgrade_util::IsUpdatePendingRestart");
[email protected]650b2d52013-02-10 03:41:45151 base::FilePath new_chrome_exe;
[email protected]815856722011-04-13 17:19:19152 if (!GetNewerChromeFile(&new_chrome_exe))
153 return false;
[email protected]7567484142013-07-11 17:36:07154 return base::PathExists(new_chrome_exe);
[email protected]815856722011-04-13 17:19:19155}
156
[email protected]815856722011-04-13 17:19:19157bool SwapNewChromeExeIfPresent() {
grtb35a90b2016-08-30 05:34:21158 if (!IsUpdatePendingRestart())
[email protected]bc38c252011-04-12 21:46:57159 return false;
grtb35a90b2016-08-30 05:34:21160
Gabriel Charette12b58ac2021-09-22 18:08:55161 TRACE_EVENT0("startup", "upgrade_util::SwapNewChromeExeIfPresent");
162
fdoraye9c97a02016-10-04 12:05:16163 // If this is a system-level install, ask Google Update to launch an elevated
164 // process to rename Chrome executables.
Greg Thompsond2efb1f2018-08-29 06:12:54165 if (install_static::IsSystemInstall())
fdoraye9c97a02016-10-04 12:05:16166 return InvokeGoogleUpdateForRename();
[email protected]bc38c252011-04-12 21:46:57167
fdoraye9c97a02016-10-04 12:05:16168 // If this is a user-level install, directly launch a process to rename Chrome
169 // executables. Obtain the command to launch the process from the registry.
[email protected]bc38c252011-04-12 21:46:57170 base::win::RegKey key;
Greg Thompsona1f95122019-01-31 22:46:45171 auto result =
172 key.Open(HKEY_CURRENT_USER, install_static::GetClientsKeyPath().c_str(),
173 KEY_QUERY_VALUE | KEY_WOW64_32KEY);
174 if (result != ERROR_SUCCESS) {
175 ::SetLastError(result);
176 PLOG(ERROR) << "Open Clients key failed";
177 return false;
[email protected]bc38c252011-04-12 21:46:57178 }
179
Greg Thompsona1f95122019-01-31 22:46:45180 std::wstring rename_cmd;
181 result = key.ReadValue(google_update::kRegRenameCmdField, &rename_cmd);
182 if (result != ERROR_SUCCESS) {
183 ::SetLastError(result);
184 PLOG(ERROR) << "Read rename command failed";
185 return false;
186 }
187
188 base::LaunchOptions options;
189 options.wait = true;
190 options.start_hidden = true;
191 ::SetLastError(ERROR_SUCCESS);
192 base::Process process = base::LaunchProcess(rename_cmd, options);
193 if (!process.IsValid()) {
194 PLOG(ERROR) << "Launch rename process failed";
195 return false;
196 }
197
198 DWORD exit_code;
199 if (!::GetExitCodeProcess(process.Handle(), &exit_code)) {
200 PLOG(ERROR) << "GetExitCodeProcess of rename process failed";
201 return false;
202 }
203
204 if (exit_code != installer::RENAME_SUCCESSFUL) {
205 LOG(ERROR) << "Rename process failed with exit code " << exit_code;
206 return false;
207 }
208
209 return true;
[email protected]bc38c252011-04-12 21:46:57210}
211
[email protected]fdbea98d2014-05-16 19:29:20212bool IsRunningOldChrome() {
Gabriel Charette12b58ac2021-09-22 18:08:55213 TRACE_EVENT0("startup", "upgrade_util::IsRunningOldChrome");
[email protected]fdbea98d2014-05-16 19:29:20214 // This figures out the actual file name that the section containing the
215 // mapped exe refers to. This is used instead of GetModuleFileName because the
216 // .exe may have been renamed out from under us while we've been running which
217 // GetModuleFileName won't notice.
218 wchar_t mapped_file_name[MAX_PATH * 2] = {};
219
220 if (!::GetMappedFileName(::GetCurrentProcess(),
221 reinterpret_cast<void*>(::GetModuleHandle(NULL)),
Daniel Cheng7d9e3d52022-02-26 09:03:24222 mapped_file_name, std::size(mapped_file_name))) {
[email protected]fdbea98d2014-05-16 19:29:20223 return false;
224 }
225
226 base::FilePath file_name(base::FilePath(mapped_file_name).BaseName());
227 return base::FilePath::CompareEqualIgnoreCase(file_name.value(),
228 installer::kChromeOldExe);
229}
230
avi556c05022014-12-22 23:31:43231bool DoUpgradeTasks(const base::CommandLine& command_line) {
Gabriel Charette12b58ac2021-09-22 18:08:55232 TRACE_EVENT0("startup", "upgrade_util::DoUpgradeTasks");
233 const auto begin_time = base::TimeTicks::Now();
234 if (!SwapNewChromeExeIfPresent() && !IsRunningOldChrome()) {
235 UMA_HISTOGRAM_MEDIUM_TIMES("Startup.DoUpgradeTasks.NoRelaunch",
236 base::TimeTicks::Now() - begin_time);
[email protected]bc38c252011-04-12 21:46:57237 return false;
Gabriel Charette12b58ac2021-09-22 18:08:55238 }
[email protected]bc38c252011-04-12 21:46:57239 // At this point the chrome.exe has been swapped with the new one.
Gabriel Charette12b58ac2021-09-22 18:08:55240 if (RelaunchChromeBrowser(command_line)) {
241 UMA_HISTOGRAM_MEDIUM_TIMES("Startup.DoUpgradeTasks.RelaunchSucceeded",
242 base::TimeTicks::Now() - begin_time);
243 } else {
244 // The re-launch failed. Feel free to panic now.
[email protected]bc38c252011-04-12 21:46:57245 NOTREACHED();
Gabriel Charette12b58ac2021-09-22 18:08:55246 // Log a metric anyways to see if this is at fault in crbug.com/1252004
247 UMA_HISTOGRAM_MEDIUM_TIMES("Startup.DoUpgradeTasks.RelaunchFailed",
248 base::TimeTicks::Now() - begin_time);
[email protected]bc38c252011-04-12 21:46:57249 }
250 return true;
251}
252
[email protected]815856722011-04-13 17:19:19253} // namespace upgrade_util