blob: 1bac044e1ad84fc2e9617e8ed6ee3a662dd5af55 [file] [log] [blame]
Avi Drissman4a8573c2022-09-09 19:35:541// Copyright 2012 The Chromium Authors
[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"
Javier Flores Assad9c26f9b92022-05-21 01:20:4338#include "chrome/browser/win/browser_util.h"
[email protected]80274b92011-07-15 17:20:3839#include "chrome/common/chrome_switches.h"
ananta069fc882014-09-13 01:22:1240#include "chrome/common/pref_names.h"
grt4474dad2017-02-27 21:00:4641#include "chrome/install_static/install_util.h"
S. Ganeshf77e2a52022-11-22 18:40:1942#include "chrome/installer/util/app_command.h"
[email protected]bc38c252011-04-12 21:46:5743#include "chrome/installer/util/util_constants.h"
brettwb1fc1b82016-02-02 00:19:0844#include "components/prefs/pref_service.h"
ananta069fc882014-09-13 01:22:1245#include "ui/base/ui_base_switches.h"
[email protected]bc38c252011-04-12 21:46:5746
Nico Webereaa08412019-08-14 01:24:3747#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
grt235b3f092015-05-27 21:42:4848#include "google_update/google_update_idl.h"
49#endif
50
[email protected]bc38c252011-04-12 21:46:5751namespace {
52
[email protected]650b2d52013-02-10 03:41:4553bool GetNewerChromeFile(base::FilePath* path) {
Avi Drissman9098f9002018-05-04 00:11:5254 if (!base::PathService::Get(base::DIR_EXE, path))
[email protected]bc38c252011-04-12 21:46:5755 return false;
56 *path = path->Append(installer::kChromeNewExe);
57 return true;
58}
59
60bool InvokeGoogleUpdateForRename() {
Nico Webereaa08412019-08-14 01:24:3761#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
Gabriel Charettef7e85712021-12-10 20:46:2262 // This has been identified as very slow on some startups. Detailed trace
63 // events below try to shine a light on each steps. crbug.com/1252004
Gabriel Charette12b58ac2021-09-22 18:08:5564 TRACE_EVENT0("startup", "upgrade_util::InvokeGoogleUpdateForRename");
65
Robert Liaob2bc703d2017-10-17 20:52:3566 Microsoft::WRL::ComPtr<IProcessLauncher> ipl;
Gabriel Charettef7e85712021-12-10 20:46:2267 {
68 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename CoCreateInstance");
69 HRESULT hr = ::CoCreateInstance(__uuidof(ProcessLauncherClass), nullptr,
70 CLSCTX_ALL, IID_PPV_ARGS(&ipl));
71 if (FAILED(hr)) {
72 TRACE_EVENT0("startup",
73 "InvokeGoogleUpdateForRename CoCreateInstance failed");
74 LOG(ERROR) << "CoCreate ProcessLauncherClass failed; hr = " << std::hex
75 << hr;
76 return false;
77 }
[email protected]bc38c252011-04-12 21:46:5778 }
Greg Thompsona1f95122019-01-31 22:46:4579
S. Ganesh74432c12022-11-02 06:35:3480 ULONG_PTR process_handle = 0;
Gabriel Charettef7e85712021-12-10 20:46:2281 {
82 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename LaunchCmdElevated");
Dominique Fauteux-Chapleau269cabf2021-12-10 22:10:3183 HRESULT hr = ipl->LaunchCmdElevated(
S. Ganeshf77e2a52022-11-22 18:40:1984 install_static::GetAppGuid(), installer::kCmdRenameChromeExe,
Dominique Fauteux-Chapleau269cabf2021-12-10 22:10:3185 ::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.
S. Ganeshf77e2a52022-11-22 18:40:19170 installer::AppCommand rename_cmd(installer::kCmdRenameChromeExe, {});
171 if (!rename_cmd.Initialize(HKEY_CURRENT_USER))
Greg Thompsona1f95122019-01-31 22:46:45172 return false;
Greg Thompsona1f95122019-01-31 22:46:45173
174 base::LaunchOptions options;
175 options.wait = true;
176 options.start_hidden = true;
177 ::SetLastError(ERROR_SUCCESS);
S. Ganeshf77e2a52022-11-22 18:40:19178 base::Process process =
179 base::LaunchProcess(rename_cmd.command_line(), options);
Greg Thompsona1f95122019-01-31 22:46:45180 if (!process.IsValid()) {
181 PLOG(ERROR) << "Launch rename process failed";
182 return false;
183 }
184
185 DWORD exit_code;
186 if (!::GetExitCodeProcess(process.Handle(), &exit_code)) {
187 PLOG(ERROR) << "GetExitCodeProcess of rename process failed";
188 return false;
189 }
190
191 if (exit_code != installer::RENAME_SUCCESSFUL) {
192 LOG(ERROR) << "Rename process failed with exit code " << exit_code;
193 return false;
194 }
195
196 return true;
[email protected]bc38c252011-04-12 21:46:57197}
198
[email protected]fdbea98d2014-05-16 19:29:20199bool IsRunningOldChrome() {
Gabriel Charette12b58ac2021-09-22 18:08:55200 TRACE_EVENT0("startup", "upgrade_util::IsRunningOldChrome");
[email protected]fdbea98d2014-05-16 19:29:20201 // This figures out the actual file name that the section containing the
202 // mapped exe refers to. This is used instead of GetModuleFileName because the
203 // .exe may have been renamed out from under us while we've been running which
204 // GetModuleFileName won't notice.
205 wchar_t mapped_file_name[MAX_PATH * 2] = {};
206
207 if (!::GetMappedFileName(::GetCurrentProcess(),
208 reinterpret_cast<void*>(::GetModuleHandle(NULL)),
Daniel Cheng7d9e3d52022-02-26 09:03:24209 mapped_file_name, std::size(mapped_file_name))) {
[email protected]fdbea98d2014-05-16 19:29:20210 return false;
211 }
212
213 base::FilePath file_name(base::FilePath(mapped_file_name).BaseName());
214 return base::FilePath::CompareEqualIgnoreCase(file_name.value(),
215 installer::kChromeOldExe);
216}
217
avi556c05022014-12-22 23:31:43218bool DoUpgradeTasks(const base::CommandLine& command_line) {
Gabriel Charette12b58ac2021-09-22 18:08:55219 TRACE_EVENT0("startup", "upgrade_util::DoUpgradeTasks");
Javier Flores Assad9c26f9b92022-05-21 01:20:43220 // If there is no other instance already running then check if there is a
221 // pending update and complete it by performing the swap and then relaunch.
222 bool did_swap = false;
223 if (!browser_util::IsBrowserAlreadyRunning())
224 did_swap = SwapNewChromeExeIfPresent();
225
226 // We don't need to relaunch if we didn't swap and we aren't running stale
227 // binaries.
228 if (!did_swap && !IsRunningOldChrome()) {
[email protected]bc38c252011-04-12 21:46:57229 return false;
Gabriel Charette12b58ac2021-09-22 18:08:55230 }
Javier Flores Assad9c26f9b92022-05-21 01:20:43231
[email protected]bc38c252011-04-12 21:46:57232 // At this point the chrome.exe has been swapped with the new one.
Wissem Gamra8e67a95d2023-03-17 17:41:21233 if (!RelaunchChromeBrowser(command_line)) {
Javier Flores Assad9c26f9b92022-05-21 01:20:43234 // The relaunch failed. Feel free to panic now.
[email protected]bc38c252011-04-12 21:46:57235 NOTREACHED();
[email protected]bc38c252011-04-12 21:46:57236 }