blob: 6b23bf07009fb819574c57e6e2ebc5792a661259 [file] [log] [blame]
Nigel Taoc5d6bcb2018-07-25 04:57:421// Copyright 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Song Fangzhen08553c782021-08-05 05:00:115#include "chrome/browser/web_applications/externally_managed_app_manager.h"
Nigel Taoc5d6bcb2018-07-25 04:57:426
Christopher Lama45ea6d2019-07-08 07:22:057#include <map>
Giovanni Ortuño Urquidib4839fb2018-08-24 10:36:578#include <memory>
9#include <utility>
Dibyajyoti Pald251e0e2022-06-13 15:44:3210#include <vector>
Giovanni Ortuño Urquidib4839fb2018-08-24 10:36:5711
danakjdb9ae7942020-11-11 16:01:3512#include "base/callback_helpers.h"
Jan Wilken Dörriebf97d442020-12-09 19:01:5813#include "base/containers/contains.h"
Peter Kasting4703b5b2022-08-31 08:15:5814#include "base/ranges/algorithm.h"
Lei Zhange11edbe02021-06-25 18:30:2215#include "base/stl_util.h"
Christopher Lam8b4142d2019-04-08 06:54:4216#include "base/threading/thread_task_runner_handle.h"
Dibyajyoti Pald251e0e2022-06-13 15:44:3217#include "build/chromeos_buildflags.h"
Daniel Murphy19a34bf2022-04-07 15:29:3618#include "chrome/browser/web_applications/web_app_install_utils.h"
Song Fangzhened83c142021-07-13 03:21:4419#include "chrome/browser/web_applications/web_app_registrar.h"
Alexander Bolodurin48a8d4e2022-02-16 03:06:1820#include "components/webapps/browser/install_result_code.h"
Nigel Tao02f334fe2018-09-20 01:28:2921
Dibyajyoti Pald251e0e2022-06-13 15:44:3222#if BUILDFLAG(IS_CHROMEOS)
23#include "base/containers/cxx20_erase.h"
24#endif
25
Nigel Taoc5d6bcb2018-07-25 04:57:4226namespace web_app {
27
Alan Cutterf29ca2732021-12-06 06:07:2528ExternallyManagedAppManager::InstallResult::InstallResult() = default;
29
30ExternallyManagedAppManager::InstallResult::InstallResult(
Alexander Bolodurin48a8d4e2022-02-16 03:06:1831 webapps::InstallResultCode code,
Alan Cutterf29ca2732021-12-06 06:07:2532 absl::optional<AppId> app_id,
33 bool did_uninstall_and_replace)
34 : code(code),
35 app_id(std::move(app_id)),
36 did_uninstall_and_replace(did_uninstall_and_replace) {}
37
38ExternallyManagedAppManager::InstallResult::InstallResult(
39 const InstallResult&) = default;
40
41ExternallyManagedAppManager::InstallResult::~InstallResult() = default;
42
Daniel Murphy6294817d2021-04-13 22:28:1343bool ExternallyManagedAppManager::InstallResult::operator==(
Alan Cutter2ce91fc2020-12-02 03:56:4144 const InstallResult& other) const {
Alan Cutterf29ca2732021-12-06 06:07:2545 return std::tie(code, app_id, did_uninstall_and_replace) ==
46 std::tie(other.code, other.app_id, other.did_uninstall_and_replace);
Alan Cutter2ce91fc2020-12-02 03:56:4147}
48
Daniel Murphy6294817d2021-04-13 22:28:1349ExternallyManagedAppManager::SynchronizeRequest::SynchronizeRequest(
Christopher Lam8b4142d2019-04-08 06:54:4250 SynchronizeCallback callback,
Daniel Murphy527765a52021-09-13 19:09:2151 std::vector<ExternalInstallOptions> pending_installs,
52 int remaining_uninstall_requests)
53 : callback(std::move(callback)),
54 remaining_install_requests(pending_installs.size()),
55 pending_installs(std::move(pending_installs)),
56 remaining_uninstall_requests(remaining_uninstall_requests) {}
Christopher Lam8b4142d2019-04-08 06:54:4257
Daniel Murphy6294817d2021-04-13 22:28:1358ExternallyManagedAppManager::SynchronizeRequest::~SynchronizeRequest() =
59 default;
Christopher Lam8b4142d2019-04-08 06:54:4260
Daniel Murphy6294817d2021-04-13 22:28:1361ExternallyManagedAppManager::SynchronizeRequest&
62ExternallyManagedAppManager::SynchronizeRequest::operator=(
63 ExternallyManagedAppManager::SynchronizeRequest&&) = default;
Christopher Lam8b4142d2019-04-08 06:54:4264
Daniel Murphy6294817d2021-04-13 22:28:1365ExternallyManagedAppManager::SynchronizeRequest::SynchronizeRequest(
Christopher Lam8b4142d2019-04-08 06:54:4266 SynchronizeRequest&& other) = default;
67
Daniel Murphy6294817d2021-04-13 22:28:1368ExternallyManagedAppManager::ExternallyManagedAppManager() = default;
Nigel Taoc5d6bcb2018-07-25 04:57:4269
Daniel Murphy6294817d2021-04-13 22:28:1370ExternallyManagedAppManager::~ExternallyManagedAppManager() {
Eric Willigers31c682b22019-08-27 03:57:4071 DCHECK(!registration_callback_);
72}
Nigel Taoc5d6bcb2018-07-25 04:57:4273
Daniel Murphy6294817d2021-04-13 22:28:1374void ExternallyManagedAppManager::SetSubsystems(
Song Fangzhened83c142021-07-13 03:21:4475 WebAppRegistrar* registrar,
phillis526a61d2020-07-20 21:10:5776 WebAppUiManager* ui_manager,
Song Fangzhenb60b44f42021-08-11 08:49:2377 WebAppInstallFinalizer* finalizer,
Phillis Tangb1cb31f2022-05-11 08:06:1078 WebAppCommandManager* command_manager,
Mustapha Jaber651c2042022-02-25 15:27:4979 WebAppSyncBridge* sync_bridge) {
Christopher Lama45ea6d2019-07-08 07:22:0580 registrar_ = registrar;
Alan Cutter31b206b2019-07-17 16:57:3581 ui_manager_ = ui_manager;
Christopher Lama45ea6d2019-07-08 07:22:0582 finalizer_ = finalizer;
Phillis Tangb1cb31f2022-05-11 08:06:1083 command_manager_ = command_manager;
Mustapha Jaber651c2042022-02-25 15:27:4984 sync_bridge_ = sync_bridge;
Christopher Lama45ea6d2019-07-08 07:22:0585}
86
Daniel Murphy6294817d2021-04-13 22:28:1387void ExternallyManagedAppManager::SynchronizeInstalledApps(
Alexey Baskakov0b9a2ee2019-07-22 01:21:2488 std::vector<ExternalInstallOptions> desired_apps_install_options,
Alexey Baskakov031895272019-07-18 08:19:3389 ExternalInstallSource install_source,
Christopher Lam8b4142d2019-04-08 06:54:4290 SynchronizeCallback callback) {
Christopher Lam44ee1a82019-06-21 06:50:5691 DCHECK(registrar_);
Peter Kasting4703b5b2022-08-31 08:15:5892 DCHECK(base::ranges::all_of(
93 desired_apps_install_options,
Alexey Baskakov0b9a2ee2019-07-22 01:21:2494 [&install_source](const ExternalInstallOptions& install_options) {
95 return install_options.install_source == install_source;
96 }));
Alexey Baskakov031895272019-07-18 08:19:3397 // Only one concurrent SynchronizeInstalledApps() expected per
98 // ExternalInstallSource.
Jan Wilken Dörriea8cb56302019-06-06 18:59:3699 DCHECK(!base::Contains(synchronize_requests_, install_source));
Nigel Tao02f334fe2018-09-20 01:28:29100
Christopher Lam44ee1a82019-06-21 06:50:56101 std::vector<GURL> installed_urls;
Eric Willigersfd787482022-03-02 03:26:54102 for (const auto& apps_it :
103 registrar_->GetExternallyInstalledApps(install_source)) {
Dibyajyoti Palf7430b3e2022-06-28 14:32:10104 // TODO(crbug.com/1339965): Remove this check once we cleanup
Dibyajyoti Pald251e0e2022-06-13 15:44:32105 // ExternallyInstalledWebAppPrefs on external app uninstall.
Mustapha Jaber651c2042022-02-25 15:27:49106 bool has_same_external_source =
107 registrar_->GetAppById(apps_it.first)
108 ->GetSources()
Daniel Murphy19a34bf2022-04-07 15:29:36109 .test(ConvertExternalInstallSourceToSource(install_source));
Dibyajyoti Pal2cb22f92022-06-08 20:22:39110 if (has_same_external_source) {
111 for (const GURL& url : apps_it.second) {
112 installed_urls.push_back(url);
113 }
114 }
Mustapha Jaber651c2042022-02-25 15:27:49115 }
Christopher Lam44ee1a82019-06-21 06:50:56116
117 std::sort(installed_urls.begin(), installed_urls.end());
Nigel Tao02f334fe2018-09-20 01:28:29118
119 std::vector<GURL> desired_urls;
Eric Willigersfd787482022-03-02 03:26:54120 desired_urls.reserve(desired_apps_install_options.size());
Christopher Lam44ee1a82019-06-21 06:50:56121 for (const auto& info : desired_apps_install_options)
Alan Cutter0cc83732020-09-02 02:26:36122 desired_urls.push_back(info.install_url);
Christopher Lam44ee1a82019-06-21 06:50:56123
Nigel Tao02f334fe2018-09-20 01:28:29124 std::sort(desired_urls.begin(), desired_urls.end());
125
Dibyajyoti Pald251e0e2022-06-13 15:44:32126 std::vector<GURL> urls_to_remove =
Christopher Lam44ee1a82019-06-21 06:50:56127 base::STLSetDifference<std::vector<GURL>>(installed_urls, desired_urls);
Christopher Lam8b4142d2019-04-08 06:54:42128
Dibyajyoti Pald251e0e2022-06-13 15:44:32129#if BUILDFLAG(IS_CHROMEOS)
130 // This check ensures that on Chrome OS, the messages app is not uninstalled
131 // automatically when SynchronizeInstalledApps() is called for preinstalled
132 // apps.
133 // TODO(crbug.com/1239801): Once Messages has been migrated to be a
134 // preinstalled app, this logic can be removed because the
135 // PreInstalledWebAppManager will take care of this.
136 if (!urls_to_remove.empty() &&
137 ConvertExternalInstallSourceToSource(install_source) ==
138 WebAppManagement::kDefault) {
139 base::EraseIf(urls_to_remove, [&](const GURL& url) {
140 return url.spec() ==
141 "https://messages-web.sandbox.google.com/web/authentication" ||
142 url.spec() == "https://messages.google.com/web/authentication";
143 });
144 }
145#endif
146
Christopher Lam8b4142d2019-04-08 06:54:42147 // Run callback immediately if there's no work to be done.
148 if (urls_to_remove.empty() && desired_apps_install_options.empty()) {
149 base::ThreadTaskRunnerHandle::Get()->PostTask(
150 FROM_HERE,
Alan Cutter2ce91fc2020-12-02 03:56:41151 base::BindOnce(std::move(callback), std::map<GURL, InstallResult>(),
Christopher Lam9513eb2f2019-06-27 07:57:57152 std::map<GURL, bool>()));
Christopher Lam8b4142d2019-04-08 06:54:42153 return;
154 }
155
Christopher Lam8b4142d2019-04-08 06:54:42156 // Add the callback to a map and call once all installs/uninstalls finish.
157 synchronize_requests_.insert_or_assign(
158 install_source,
159 SynchronizeRequest(std::move(callback),
Daniel Murphy527765a52021-09-13 19:09:21160 std::move(desired_apps_install_options),
161 urls_to_remove.size()));
Christopher Lam8b4142d2019-04-08 06:54:42162
Daniel Murphy527765a52021-09-13 19:09:21163 if (urls_to_remove.empty()) {
164 // If there are no uninstalls, this will trigger the installs.
165 ContinueOrCompleteSynchronization(install_source);
166 } else {
167 UninstallApps(
168 urls_to_remove, install_source,
169 base::BindRepeating(
170 &ExternallyManagedAppManager::UninstallForSynchronizeCallback,
171 weak_ptr_factory_.GetWeakPtr(), install_source));
172 }
Christopher Lam8b4142d2019-04-08 06:54:42173}
174
Daniel Murphy6294817d2021-04-13 22:28:13175void ExternallyManagedAppManager::SetRegistrationCallbackForTesting(
Eric Willigers31c682b22019-08-27 03:57:40176 RegistrationCallback callback) {
Eric Willigersfd787482022-03-02 03:26:54177 registration_callback_ = std::move(callback);
Eric Willigers17390d12019-08-26 08:16:27178}
179
Daniel Murphy6294817d2021-04-13 22:28:13180void ExternallyManagedAppManager::ClearRegistrationCallbackForTesting() {
Eric Willigers31c682b22019-08-27 03:57:40181 registration_callback_ = RegistrationCallback();
Eric Willigers17390d12019-08-26 08:16:27182}
183
Daniel Murphy6294817d2021-04-13 22:28:13184void ExternallyManagedAppManager::SetRegistrationsCompleteCallbackForTesting(
Alan Cutter464b32c62020-09-16 06:41:37185 base::OnceClosure callback) {
186 registrations_complete_callback_ = std::move(callback);
187}
188
Daniel Murphy6294817d2021-04-13 22:28:13189void ExternallyManagedAppManager::OnRegistrationFinished(
190 const GURL& install_url,
191 RegistrationResultCode result) {
Eric Willigers31c682b22019-08-27 03:57:40192 if (registration_callback_)
Alan Cutter096084e2020-09-02 08:54:02193 registration_callback_.Run(install_url, result);
Eric Willigers17390d12019-08-26 08:16:27194}
195
Daniel Murphy6294817d2021-04-13 22:28:13196void ExternallyManagedAppManager::InstallForSynchronizeCallback(
Alexey Baskakov031895272019-07-18 08:19:33197 ExternalInstallSource source,
Alan Cutteraca3563b2022-07-19 08:48:27198 const GURL& install_url,
Daniel Murphy6294817d2021-04-13 22:28:13199 ExternallyManagedAppManager::InstallResult result) {
Alan Cutter2ce91fc2020-12-02 03:56:41200 if (!IsSuccess(result.code)) {
Alan Cutteraca3563b2022-07-19 08:48:27201 LOG(ERROR) << install_url << " from install source "
202 << static_cast<int>(source) << " failed to install with reason "
Alan Cutter2ce91fc2020-12-02 03:56:41203 << static_cast<int>(result.code);
Christopher Lam9513eb2f2019-06-27 07:57:57204 }
205
206 auto source_and_request = synchronize_requests_.find(source);
207 DCHECK(source_and_request != synchronize_requests_.end());
208 SynchronizeRequest& request = source_and_request->second;
Alan Cutteraca3563b2022-07-19 08:48:27209 request.install_results[install_url] = std::move(result);
Daniel Murphy527765a52021-09-13 19:09:21210 --request.remaining_install_requests;
211 DCHECK_GE(request.remaining_install_requests, 0);
Christopher Lam9513eb2f2019-06-27 07:57:57212
Daniel Murphy527765a52021-09-13 19:09:21213 ContinueOrCompleteSynchronization(source);
Christopher Lam8b4142d2019-04-08 06:54:42214}
215
Daniel Murphy6294817d2021-04-13 22:28:13216void ExternallyManagedAppManager::UninstallForSynchronizeCallback(
Alexey Baskakov031895272019-07-18 08:19:33217 ExternalInstallSource source,
Alan Cutteraca3563b2022-07-19 08:48:27218 const GURL& install_url,
Alexey Baskakov031895272019-07-18 08:19:33219 bool succeeded) {
Christopher Lam9513eb2f2019-06-27 07:57:57220 auto source_and_request = synchronize_requests_.find(source);
221 DCHECK(source_and_request != synchronize_requests_.end());
222 SynchronizeRequest& request = source_and_request->second;
Alan Cutteraca3563b2022-07-19 08:48:27223 request.uninstall_results[install_url] = succeeded;
Daniel Murphy527765a52021-09-13 19:09:21224 --request.remaining_uninstall_requests;
225 DCHECK_GE(request.remaining_uninstall_requests, 0);
Christopher Lam9513eb2f2019-06-27 07:57:57226
Daniel Murphy527765a52021-09-13 19:09:21227 ContinueOrCompleteSynchronization(source);
Christopher Lam8b4142d2019-04-08 06:54:42228}
229
Daniel Murphy527765a52021-09-13 19:09:21230void ExternallyManagedAppManager::ContinueOrCompleteSynchronization(
231 ExternalInstallSource source) {
Christopher Lam9513eb2f2019-06-27 07:57:57232 auto source_and_request = synchronize_requests_.find(source);
233 DCHECK(source_and_request != synchronize_requests_.end());
Christopher Lam8b4142d2019-04-08 06:54:42234
Christopher Lam9513eb2f2019-06-27 07:57:57235 SynchronizeRequest& request = source_and_request->second;
Christopher Lam9513eb2f2019-06-27 07:57:57236
Daniel Murphy527765a52021-09-13 19:09:21237 if (request.remaining_uninstall_requests > 0)
238 return;
239
240 // Installs only take place after all uninstalls.
241 if (!request.pending_installs.empty()) {
242 DCHECK_GT(request.remaining_install_requests, 0);
243 // Note: It is intentional that std::move(request.pending_installs) clears
244 // the vector in `request`, preventing this branch from triggering again.
245 InstallApps(std::move(request.pending_installs),
246 base::BindRepeating(
247 &ExternallyManagedAppManager::InstallForSynchronizeCallback,
248 weak_ptr_factory_.GetWeakPtr(), source));
249 return;
Christopher Lam8b4142d2019-04-08 06:54:42250 }
Daniel Murphy527765a52021-09-13 19:09:21251
252 if (request.remaining_install_requests > 0)
253 return;
254
255 base::ThreadTaskRunnerHandle::Get()->PostTask(
256 FROM_HERE, base::BindOnce(std::move(request.callback),
257 std::move(request.install_results),
258 std::move(request.uninstall_results)));
259 synchronize_requests_.erase(source);
Giovanni Ortuño Urquidib4839fb2018-08-24 10:36:57260}
Daniel Murphy6294817d2021-04-13 22:28:13261void ExternallyManagedAppManager::ClearSynchronizeRequestsForTesting() {
Dominic Schulzdd4d8662020-07-27 17:49:09262 synchronize_requests_.erase(synchronize_requests_.begin(),
263 synchronize_requests_.end());
264}
Giovanni Ortuño Urquidib4839fb2018-08-24 10:36:57265
Nigel Taoc5d6bcb2018-07-25 04:57:42266} // namespace web_app