blob: 9043518047843dcad92d224441f5dad4685f9f76 [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
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"
Lei Zhang1f46798c2021-05-26 01:35:1821#include "base/cxx17_backports.h"
[email protected]57999812013-02-24 05:40:5222#include "base/files/file_path.h"
thestig18dfb7a52014-08-26 10:44:0423#include "base/files/file_util.h"
[email protected]bc38c252011-04-12 21:46:5724#include "base/logging.h"
Gabriel Charette12b58ac2021-09-22 18:08:5525#include "base/metrics/histogram_macros.h"
[email protected]bc38c252011-04-12 21:46:5726#include "base/path_service.h"
[email protected]d09a4ce1c2013-07-24 17:37:0227#include "base/process/launch.h"
28#include "base/process/process_handle.h"
[email protected]3ea1b182013-02-08 22:38:4129#include "base/strings/string_number_conversions.h"
[email protected]d8830562013-06-10 22:01:5430#include "base/strings/string_util.h"
Gabriel Charette12b58ac2021-09-22 18:08:5531#include "base/time/time.h"
32#include "base/trace_event/trace_event.h"
[email protected]bc38c252011-04-12 21:46:5733#include "base/win/registry.h"
[email protected]770c6d82012-09-06 22:21:3234#include "base/win/windows_version.h"
Nico Webereaa08412019-08-14 01:24:3735#include "build/branding_buildflags.h"
ananta069fc882014-09-13 01:22:1236#include "chrome/browser/browser_process.h"
[email protected]3e087992011-04-14 22:28:1237#include "chrome/browser/first_run/upgrade_util_win.h"
[email protected]3f69d6e612012-08-03 18:52:2738#include "chrome/browser/shell_integration.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"
[email protected]bc38c252011-04-12 21:46:5742#include "chrome/installer/util/google_update_constants.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
80 ULONG_PTR process_handle;
Gabriel Charettef7e85712021-12-10 20:46:2281 {
82 TRACE_EVENT0("startup", "InvokeGoogleUpdateForRename LaunchCmdElevated");
83 hr = ipl->LaunchCmdElevated(install_static::GetAppGuid(),
84 google_update::kRegRenameCmdField,
85 ::GetCurrentProcessId(), &process_handle);
86 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
113 return true;
Nico Webereaa08412019-08-14 01:24:37114#else // BUILDFLAG(GOOGLE_CHROME_BRANDING)
[email protected]bc38c252011-04-12 21:46:57115 return false;
Nico Webereaa08412019-08-14 01:24:37116#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
[email protected]bc38c252011-04-12 21:46:57117}
118
119} // namespace
120
[email protected]815856722011-04-13 17:19:19121namespace upgrade_util {
122
Greg Thompson87ee38f2019-08-09 06:00:23123bool RelaunchChromeBrowserImpl(const base::CommandLine& command_line) {
Gabriel Charette12b58ac2021-09-22 18:08:55124 TRACE_EVENT0("startup", "upgrade_util::RelaunchChromeBrowserImpl");
125
[email protected]650b2d52013-02-10 03:41:45126 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52127 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
[email protected]3f69d6e612012-08-03 18:52:27128 NOTREACHED();
129 return false;
130 }
131
[email protected]fdbea98d2014-05-16 19:29:20132 // Explicitly make sure to relaunch chrome.exe rather than old_chrome.exe.
133 // This can happen when old_chrome.exe is launched by a user.
avi556c05022014-12-22 23:31:43134 base::CommandLine chrome_exe_command_line = command_line;
[email protected]fdbea98d2014-05-16 19:29:20135 chrome_exe_command_line.SetProgram(
136 chrome_exe.DirName().Append(installer::kChromeExe));
137
scottmg20920cb2016-04-07 16:23:59138 // Set the working directory to the exe's directory. This avoids a handle to
139 // the version directory being kept open in the relaunched child process.
140 base::LaunchOptions launch_options;
141 launch_options.current_directory = chrome_exe.DirName();
Greg Thompson47faf202018-05-18 20:59:03142 // Give the new process the right to bring its windows to the foreground.
143 launch_options.grant_foreground_privilege = true;
scottmg20920cb2016-04-07 16:23:59144 return base::LaunchProcess(chrome_exe_command_line, launch_options).IsValid();
[email protected]815856722011-04-13 17:19:19145}
146
147bool IsUpdatePendingRestart() {
Gabriel Charette12b58ac2021-09-22 18:08:55148 TRACE_EVENT0("startup", "upgrade_util::IsUpdatePendingRestart");
[email protected]650b2d52013-02-10 03:41:45149 base::FilePath new_chrome_exe;
[email protected]815856722011-04-13 17:19:19150 if (!GetNewerChromeFile(&new_chrome_exe))
151 return false;
[email protected]7567484142013-07-11 17:36:07152 return base::PathExists(new_chrome_exe);
[email protected]815856722011-04-13 17:19:19153}
154
[email protected]815856722011-04-13 17:19:19155bool SwapNewChromeExeIfPresent() {
grtb35a90b2016-08-30 05:34:21156 if (!IsUpdatePendingRestart())
[email protected]bc38c252011-04-12 21:46:57157 return false;
grtb35a90b2016-08-30 05:34:21158
Gabriel Charette12b58ac2021-09-22 18:08:55159 TRACE_EVENT0("startup", "upgrade_util::SwapNewChromeExeIfPresent");
160
fdoraye9c97a02016-10-04 12:05:16161 // If this is a system-level install, ask Google Update to launch an elevated
162 // process to rename Chrome executables.
Greg Thompsond2efb1f2018-08-29 06:12:54163 if (install_static::IsSystemInstall())
fdoraye9c97a02016-10-04 12:05:16164 return InvokeGoogleUpdateForRename();
[email protected]bc38c252011-04-12 21:46:57165
fdoraye9c97a02016-10-04 12:05:16166 // If this is a user-level install, directly launch a process to rename Chrome
167 // executables. Obtain the command to launch the process from the registry.
[email protected]bc38c252011-04-12 21:46:57168 base::win::RegKey key;
Greg Thompsona1f95122019-01-31 22:46:45169 auto result =
170 key.Open(HKEY_CURRENT_USER, install_static::GetClientsKeyPath().c_str(),
171 KEY_QUERY_VALUE | KEY_WOW64_32KEY);
172 if (result != ERROR_SUCCESS) {
173 ::SetLastError(result);
174 PLOG(ERROR) << "Open Clients key failed";
175 return false;
[email protected]bc38c252011-04-12 21:46:57176 }
177
Greg Thompsona1f95122019-01-31 22:46:45178 std::wstring rename_cmd;
179 result = key.ReadValue(google_update::kRegRenameCmdField, &rename_cmd);
180 if (result != ERROR_SUCCESS) {
181 ::SetLastError(result);
182 PLOG(ERROR) << "Read rename command failed";
183 return false;
184 }
185
186 base::LaunchOptions options;
187 options.wait = true;
188 options.start_hidden = true;
189 ::SetLastError(ERROR_SUCCESS);
190 base::Process process = base::LaunchProcess(rename_cmd, options);
191 if (!process.IsValid()) {
192 PLOG(ERROR) << "Launch rename process failed";
193 return false;
194 }
195
196 DWORD exit_code;
197 if (!::GetExitCodeProcess(process.Handle(), &exit_code)) {
198 PLOG(ERROR) << "GetExitCodeProcess of rename process failed";
199 return false;
200 }
201
202 if (exit_code != installer::RENAME_SUCCESSFUL) {
203 LOG(ERROR) << "Rename process failed with exit code " << exit_code;
204 return false;
205 }
206
207 return true;
[email protected]bc38c252011-04-12 21:46:57208}
209
[email protected]fdbea98d2014-05-16 19:29:20210bool IsRunningOldChrome() {
Gabriel Charette12b58ac2021-09-22 18:08:55211 TRACE_EVENT0("startup", "upgrade_util::IsRunningOldChrome");
[email protected]fdbea98d2014-05-16 19:29:20212 // This figures out the actual file name that the section containing the
213 // mapped exe refers to. This is used instead of GetModuleFileName because the
214 // .exe may have been renamed out from under us while we've been running which
215 // GetModuleFileName won't notice.
216 wchar_t mapped_file_name[MAX_PATH * 2] = {};
217
218 if (!::GetMappedFileName(::GetCurrentProcess(),
219 reinterpret_cast<void*>(::GetModuleHandle(NULL)),
Avi Drissman5f0fb8c2018-12-25 23:20:49220 mapped_file_name, base::size(mapped_file_name))) {
[email protected]fdbea98d2014-05-16 19:29:20221 return false;
222 }
223
224 base::FilePath file_name(base::FilePath(mapped_file_name).BaseName());
225 return base::FilePath::CompareEqualIgnoreCase(file_name.value(),
226 installer::kChromeOldExe);
227}
228
avi556c05022014-12-22 23:31:43229bool DoUpgradeTasks(const base::CommandLine& command_line) {
Gabriel Charette12b58ac2021-09-22 18:08:55230 TRACE_EVENT0("startup", "upgrade_util::DoUpgradeTasks");
231 const auto begin_time = base::TimeTicks::Now();
232 if (!SwapNewChromeExeIfPresent() && !IsRunningOldChrome()) {
233 UMA_HISTOGRAM_MEDIUM_TIMES("Startup.DoUpgradeTasks.NoRelaunch",
234 base::TimeTicks::Now() - begin_time);
[email protected]bc38c252011-04-12 21:46:57235 return false;
Gabriel Charette12b58ac2021-09-22 18:08:55236 }
[email protected]bc38c252011-04-12 21:46:57237 // At this point the chrome.exe has been swapped with the new one.
Gabriel Charette12b58ac2021-09-22 18:08:55238 if (RelaunchChromeBrowser(command_line)) {
239 UMA_HISTOGRAM_MEDIUM_TIMES("Startup.DoUpgradeTasks.RelaunchSucceeded",
240 base::TimeTicks::Now() - begin_time);
241 } else {
242 // The re-launch failed. Feel free to panic now.
[email protected]bc38c252011-04-12 21:46:57243 NOTREACHED();
Gabriel Charette12b58ac2021-09-22 18:08:55244 // Log a metric anyways to see if this is at fault in crbug.com/1252004
245 UMA_HISTOGRAM_MEDIUM_TIMES("Startup.DoUpgradeTasks.RelaunchFailed",
246 base::TimeTicks::Now() - begin_time);
[email protected]bc38c252011-04-12 21:46:57247 }
248 return true;
249}
250
[email protected]815856722011-04-13 17:19:19251} // namespace upgrade_util