blob: a07a08868eba4ec2a250c2a279b251605ee68750 [file] [log] [blame]
Foromo Daniel Soromou67860ff2025-05-05 15:38:331// Copyright 2025 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/win/installer_downloader/installer_downloader_controller.h"
6
7#include <memory>
8#include <optional>
9#include <utility>
10
Foromo Daniel Soromou3646aa82025-05-22 17:07:2711#include "base/base_paths.h"
Foromo Daniel Soromou615461c2025-05-16 14:05:3112#include "base/check_deref.h"
Foromo Daniel Soromou67860ff2025-05-05 15:38:3313#include "base/files/file_path.h"
Foromo Daniel Soromoubadfb802025-05-05 16:22:0414#include "base/functional/bind.h"
15#include "base/functional/callback.h"
Foromo Daniel Soromou3646aa82025-05-22 17:07:2716#include "base/path_service.h"
Foromo Daniel Soromouce65be02025-05-22 14:14:0617#include "base/strings/string_util.h"
18#include "base/uuid.h"
Foromo Daniel Soromoued9d5882025-05-22 14:23:5219#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
20#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
21#include "chrome/browser/profiles/profile.h"
Foromo Daniel Soromou615461c2025-05-16 14:05:3122#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
23#include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
Foromo Daniel Soromouce65be02025-05-22 14:14:0624#include "chrome/browser/win/installer_downloader/installer_downloader_feature.h"
Foromo Daniel Soromou9c116012025-05-23 16:02:0925#include "chrome/browser/win/installer_downloader/installer_downloader_infobar_window_active_tab_tracker.h"
Foromo Daniel Soromou67860ff2025-05-05 15:38:3326#include "chrome/browser/win/installer_downloader/installer_downloader_model.h"
27#include "chrome/browser/win/installer_downloader/installer_downloader_model_impl.h"
Foromo Daniel Soromouf0d01d52025-05-08 15:25:4128#include "chrome/browser/win/installer_downloader/system_info_provider_impl.h"
Foromo Daniel Soromou9c116012025-05-23 16:02:0929#include "components/infobars/content/content_infobar_manager.h"
30#include "components/infobars/core/confirm_infobar_delegate.h"
31#include "components/infobars/core/infobar.h"
Foromo Daniel Soromou615461c2025-05-16 14:05:3132#include "components/tabs/public/tab_interface.h"
Foromo Daniel Soromouce65be02025-05-22 14:14:0633#include "content/public/browser/browser_context.h"
34#include "content/public/browser/download_manager.h"
Foromo Daniel Soromou615461c2025-05-16 14:05:3135#include "content/public/browser/web_contents.h"
Foromo Daniel Soromouce65be02025-05-22 14:14:0636#include "url/gurl.h"
Foromo Daniel Soromou67860ff2025-05-05 15:38:3337
38namespace installer_downloader {
39
Foromo Daniel Soromou615461c2025-05-16 14:05:3140namespace {
41
42content::WebContents* GetActiveWebContents() {
43 for (BrowserWindowInterface* browser : GetAllBrowserWindowInterfaces()) {
44 if (!browser->IsActive() ||
45 browser->GetType() != BrowserWindowInterface::Type::TYPE_NORMAL) {
46 continue;
47 }
48
49 return CHECK_DEREF(browser->GetActiveTabInterface()).GetContents();
50 }
51
52 return nullptr;
Foromo Daniel Soromou67860ff2025-05-05 15:38:3353}
54
Foromo Daniel Soromouce65be02025-05-22 14:14:0655std::optional<GURL> BuildInstallerDownloadUrl(bool is_metrics_enabled) {
56 std::string installer_url_template = kInstallerUrlTemplateParam.Get();
57
58 base::ReplaceFirstSubstringAfterOffset(
59 &installer_url_template, /*start_offset=*/0, "IIDGUID",
60 base::Uuid::GenerateRandomV4().AsLowercaseString());
61
62 base::ReplaceFirstSubstringAfterOffset(&installer_url_template,
63 /*start_offset=*/0, "STATS",
64 is_metrics_enabled ? "1" : "0");
65
66 GURL installer_url(installer_url_template);
67
68 return installer_url.is_valid()
69 ? std::optional<GURL>(std::move(installer_url))
70 : std::nullopt;
71}
72
Foromo Daniel Soromou615461c2025-05-16 14:05:3173} // namespace
74
75InstallerDownloaderController::InstallerDownloaderController(
Foromo Daniel Soromouce65be02025-05-22 14:14:0676 ShowInfobarCallback show_infobar_callback,
77 base::RepeatingCallback<bool()> is_metrics_enabled_callback)
78 : is_metrics_enabled_callback_(std::move(is_metrics_enabled_callback)),
79 show_infobar_callback_(std::move(show_infobar_callback)),
Foromo Daniel Soromou615461c2025-05-16 14:05:3180 model_(std::make_unique<InstallerDownloaderModelImpl>(
81 std::make_unique<SystemInfoProviderImpl>())),
82 get_active_web_contents_callback_(
Foromo Daniel Soromou9c116012025-05-23 16:02:0983 base::BindRepeating(&GetActiveWebContents)) {
84 RegisterBrowserWindowEvents();
85}
Foromo Daniel Soromou615461c2025-05-16 14:05:3186
87InstallerDownloaderController::InstallerDownloaderController(
88 ShowInfobarCallback show_infobar_callback,
Foromo Daniel Soromouce65be02025-05-22 14:14:0689 base::RepeatingCallback<bool()> is_metrics_enabled_callback,
Foromo Daniel Soromou615461c2025-05-16 14:05:3190 std::unique_ptr<InstallerDownloaderModel> model)
Foromo Daniel Soromouce65be02025-05-22 14:14:0691 : is_metrics_enabled_callback_(std::move(is_metrics_enabled_callback)),
92 show_infobar_callback_(std::move(show_infobar_callback)),
Foromo Daniel Soromou615461c2025-05-16 14:05:3193 model_(std::move(model)),
94 get_active_web_contents_callback_(
Foromo Daniel Soromou9c116012025-05-23 16:02:0995 base::BindRepeating(&GetActiveWebContents)) {
96 RegisterBrowserWindowEvents();
97}
98
99void InstallerDownloaderController::RegisterBrowserWindowEvents() {
100 active_window_subscription_ =
101 window_tracker_.RegisterActiveWindowChangedCallback(base::BindRepeating(
102 &InstallerDownloaderController::OnActiveBrowserWindowChanged,
103 base::Unretained(this)));
104
105 removed_window_subscription_ =
106 window_tracker_.RegisterRemovedWindowCallback(base::BindRepeating(
107 &InstallerDownloaderController::OnRemovedBrowserWindow,
108 base::Unretained(this)));
109}
Foromo Daniel Soromou615461c2025-05-16 14:05:31110
Foromo Daniel Soromou67860ff2025-05-05 15:38:33111InstallerDownloaderController::~InstallerDownloaderController() = default;
112
Foromo Daniel Soromou9c116012025-05-23 16:02:09113void InstallerDownloaderController::OnActiveBrowserWindowChanged(
114 BrowserWindowInterface* bwi) {
115 // This can be null during the startup or when the last window is closed.
116 if (!bwi) {
117 return;
118 }
Foromo Daniel Soromoubadfb802025-05-05 16:22:04119
Foromo Daniel Soromou9c116012025-05-23 16:02:09120 if (bwi_and_active_tab_tracker_map_.contains(bwi)) {
121 return;
122 }
123
124 bwi_and_active_tab_tracker_map_[bwi] =
125 std::make_unique<InstallerDownloaderInfobarWindowActiveTabTracker>(
126 bwi,
127 base::BindRepeating(&InstallerDownloaderController::MaybeShowInfoBar,
128 base::Unretained(this)));
129}
130
131void InstallerDownloaderController::OnRemovedBrowserWindow(
132 BrowserWindowInterface* bwi) {
133 if (!bwi_and_active_tab_tracker_map_.contains(bwi)) {
134 return;
135 }
136
137 bwi_and_active_tab_tracker_map_.erase(bwi);
138}
139
140void InstallerDownloaderController::MaybeShowInfoBar() {
Foromo Daniel Soromoubadfb802025-05-05 16:22:04141 // The max show count of the infobar have been reached. Eligibility check is
142 // no longer needed.
143 if (model_->IsMaxShowCountReached()) {
144 return;
145 }
146
147 model_->CheckEligibility(
148 base::BindOnce(&InstallerDownloaderController::OnEligibilityReady,
149 base::Unretained(this)));
Foromo Daniel Soromou67860ff2025-05-05 15:38:33150}
151
152void InstallerDownloaderController::OnEligibilityReady(
Foromo Daniel Soromou3646aa82025-05-22 17:07:27153 std::optional<base::FilePath> destination) {
Foromo Daniel Soromou9c116012025-05-23 16:02:09154 if (infobar_closed_) {
155 return;
156 }
157
158 auto* contents = get_active_web_contents_callback_.Run();
159
160 if (!contents) {
161 return;
162 }
163
164 if (visible_infobars_web_contents_.contains(contents)) {
165 return;
166 }
167
Foromo Daniel Soromou3646aa82025-05-22 17:07:27168 // Early return when we have no destination and bypass is not allowed.
169 if (!destination.has_value() && !model_->ShouldByPassEligibilityCheck()) {
Foromo Daniel Soromou615461c2025-05-16 14:05:31170 return;
171 }
172
Foromo Daniel Soromou3646aa82025-05-22 17:07:27173 // Compute a fallback destination (user’s Desktop) when bypassing eligibility.
174 if (!destination) {
175 base::FilePath desktop_path;
176 if (!base::PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) {
177 return;
178 }
179
180 destination = std::move(desktop_path);
181 }
182
Foromo Daniel Soromou9c116012025-05-23 16:02:09183 infobars::ContentInfoBarManager* infobar_manager =
184 infobars::ContentInfoBarManager::FromWebContents(contents);
Foromo Daniel Soromou615461c2025-05-16 14:05:31185
186 // Installer Downloader is a global feature, therefore it's guaranteed that
187 // InstallerDownloaderController will be alive at any point during the browser
188 // runtime.
Foromo Daniel Soromou9c116012025-05-23 16:02:09189 infobars::InfoBar* infobar = show_infobar_callback_.Run(
190 infobar_manager,
Muhammad Salmaandadc22982025-05-20 20:31:26191 base::BindOnce(&InstallerDownloaderController::OnDownloadRequestAccepted,
Foromo Daniel Soromouce65be02025-05-22 14:14:06192 base::Unretained(this), destination.value()),
Muhammad Salmaandadc22982025-05-20 20:31:26193 base::BindOnce(&InstallerDownloaderController::OnInfoBarDismissed,
194 base::Unretained(this)));
Foromo Daniel Soromou9c116012025-05-23 16:02:09195
196 if (!infobar) {
197 return;
198 }
199
200 infobar_manager->AddObserver(this);
Foromo Daniel Soromou89f11bf2025-05-22 18:34:07201 model_->IncrementShowCount();
Foromo Daniel Soromou9c116012025-05-23 16:02:09202 visible_infobars_web_contents_[contents] = infobar;
203}
204
205void InstallerDownloaderController::OnInfoBarRemoved(infobars::InfoBar* infobar,
206 bool animate) {
207 auto it = std::find_if(
208 visible_infobars_web_contents_.begin(),
209 visible_infobars_web_contents_.end(),
210 [infobar](const auto& entry) { return entry.second == infobar; });
211
212 if (it == visible_infobars_web_contents_.end()) {
213 return;
214 }
215
216 it->second->owner()->RemoveObserver(this);
217 visible_infobars_web_contents_.erase(it);
218
219 if (!user_initiated_info_bar_close_pending_) {
220 return;
221 }
222
223 for (auto [contents, infobar_instance] : visible_infobars_web_contents_) {
224 infobar_instance->owner()->RemoveObserver(this);
225 infobar_instance->RemoveSelf();
226 }
227
228 visible_infobars_web_contents_.clear();
229 infobar_closed_ = true;
Foromo Daniel Soromou67860ff2025-05-05 15:38:33230}
231
Foromo Daniel Soromouce65be02025-05-22 14:14:06232void InstallerDownloaderController::OnDownloadRequestAccepted(
233 const base::FilePath& destination) {
Foromo Daniel Soromou9c116012025-05-23 16:02:09234 user_initiated_info_bar_close_pending_ = true;
Foromo Daniel Soromou67860ff2025-05-05 15:38:33235 // User have explicitly gave download consent. Therefore, a background
236 // download should be issued.
Foromo Daniel Soromouce65be02025-05-22 14:14:06237 auto* contents = get_active_web_contents_callback_.Run();
238
239 if (!contents) {
240 return;
241 }
242
243 std::optional<GURL> installer_url =
244 BuildInstallerDownloadUrl(is_metrics_enabled_callback_.Run());
245
246 if (!installer_url.has_value()) {
247 return;
248 }
249
Foromo Daniel Soromoued9d5882025-05-22 14:23:52250 // Keep the profile alive until the download completes.
251 auto* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
252 auto keep_alive = std::make_unique<ScopedProfileKeepAlive>(
253 profile, ProfileKeepAliveOrigin::kDownloadInProgress);
254
Foromo Daniel Soromouce65be02025-05-22 14:14:06255 model_->StartDownload(
256 installer_url.value(), destination,
Foromo Daniel Soromoued9d5882025-05-22 14:23:52257 CHECK_DEREF(profile->GetDownloadManager()),
Foromo Daniel Soromouce65be02025-05-22 14:14:06258 base::BindOnce(&InstallerDownloaderController::OnDownloadCompleted,
Foromo Daniel Soromoued9d5882025-05-22 14:23:52259 base::Unretained(this), std::move(keep_alive)));
Foromo Daniel Soromou67860ff2025-05-05 15:38:33260}
261
Foromo Daniel Soromoued9d5882025-05-22 14:23:52262void InstallerDownloaderController::OnDownloadCompleted(
263 std::unique_ptr<ScopedProfileKeepAlive> keep_alive,
264 bool success) {
Foromo Daniel Soromou67860ff2025-05-05 15:38:33265 // Update local state to indicated that downloaded have been successfully
266 // completed.
267}
268
Foromo Daniel Soromou615461c2025-05-16 14:05:31269void InstallerDownloaderController::SetActiveWebContentsCallbackForTesting(
270 GetActiveWebContentsCallback callback) {
271 get_active_web_contents_callback_ = std::move(callback);
272}
273
Muhammad Salmaandadc22982025-05-20 20:31:26274void InstallerDownloaderController::OnInfoBarDismissed() {
Foromo Daniel Soromou9c116012025-05-23 16:02:09275 user_initiated_info_bar_close_pending_ = true;
Muhammad Salmaandadc22982025-05-20 20:31:26276}
277
Foromo Daniel Soromou67860ff2025-05-05 15:38:33278} // namespace installer_downloader