[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 1 | // Copyright 2013 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 | |
| 5 | #include "chrome/browser/download/download_ui_controller.h" |
| 6 | |
Peter Boström | 924f803 | 2021-04-02 20:36:02 | [diff] [blame] | 7 | #include <memory> |
dcheng | e73d8520c | 2015-12-27 01:19:09 | [diff] [blame] | 8 | #include <utility> |
| 9 | |
[email protected] | 9dd188f | 2014-05-15 18:35:20 | [diff] [blame] | 10 | #include "base/callback.h" |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 11 | #include "base/memory/raw_ptr.h" |
Christopher Thompson | caf69fe | 2018-03-06 18:33:01 | [diff] [blame] | 12 | #include "base/metrics/histogram_macros.h" |
avi | e4d7b6f | 2015-12-26 00:59:18 | [diff] [blame] | 13 | #include "build/build_config.h" |
James Cook | 27fdaae | 2020-11-13 23:33:31 | [diff] [blame] | 14 | #include "build/chromeos_buildflags.h" |
pfeldman | 0348594 | 2016-03-29 19:51:04 | [diff] [blame] | 15 | #include "chrome/browser/devtools/devtools_window.h" |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 16 | #include "chrome/browser/download/download_item_model.h" |
yoshiki | 09481b7 | 2015-07-17 03:12:38 | [diff] [blame] | 17 | #include "chrome/browser/download/download_shelf.h" |
Side Yilmaz | cdf366dc | 2021-02-26 09:39:56 | [diff] [blame] | 18 | #include "chrome/browser/download/download_stats.h" |
Christopher Thompson | caf69fe | 2018-03-06 18:33:01 | [diff] [blame] | 19 | #include "chrome/browser/ssl/security_state_tab_helper.h" |
Min Qin | a9f48787 | 2018-02-09 20:43:23 | [diff] [blame] | 20 | #include "components/download/public/common/download_item.h" |
Christopher Thompson | caf69fe | 2018-03-06 18:33:01 | [diff] [blame] | 21 | #include "components/security_state/core/security_state.h" |
Min Qin | a9f48787 | 2018-02-09 20:43:23 | [diff] [blame] | 22 | #include "content/public/browser/download_item_utils.h" |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 23 | #include "content/public/browser/web_contents.h" |
| 24 | #include "content/public/browser/web_contents_delegate.h" |
| 25 | |
Xiaohan Wang | ab5fb91 | 2022-01-12 19:21:48 | [diff] [blame] | 26 | #if BUILDFLAG(IS_ANDROID) |
Min Qin | 0b80a037 | 2019-12-18 03:20:50 | [diff] [blame] | 27 | #include "chrome/browser/download/android/download_controller.h" |
Min Qin | 86987406 | 2019-10-15 19:27:56 | [diff] [blame] | 28 | #include "chrome/browser/download/android/download_controller_base.h" |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 29 | #else |
| 30 | #include "chrome/browser/profiles/profile.h" |
dbeam | 2faad5a | 2015-03-02 22:49:25 | [diff] [blame] | 31 | #include "chrome/browser/ui/browser_finder.h" |
Collin Baker | 8a21755 | 2019-05-29 19:47:51 | [diff] [blame] | 32 | #include "chrome/browser/ui/browser_tabstrip.h" |
| 33 | #include "chrome/browser/ui/browser_window.h" |
| 34 | #include "chrome/browser/ui/tabs/tab_strip_model.h" |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 35 | #endif |
| 36 | |
Xiaohan Wang | ab5fb91 | 2022-01-12 19:21:48 | [diff] [blame] | 37 | #if BUILDFLAG(IS_CHROMEOS) |
estade | 7fc53a3 | 2015-09-29 19:44:19 | [diff] [blame] | 38 | #include "chrome/browser/download/notification/download_notification_manager.h" |
Xiaohan Wang | ab5fb91 | 2022-01-12 19:21:48 | [diff] [blame] | 39 | #endif // BUILDFLAG(IS_CHROMEOS) |
estade | 7fc53a3 | 2015-09-29 19:44:19 | [diff] [blame] | 40 | |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 41 | namespace { |
| 42 | |
yoshiki | f42d8fc | 2015-03-04 20:07:50 | [diff] [blame] | 43 | // DownloadShelfUIControllerDelegate{Android,} is used when a |
| 44 | // DownloadUIController is |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 45 | // constructed without specifying an explicit Delegate. |
Xiaohan Wang | ab5fb91 | 2022-01-12 19:21:48 | [diff] [blame] | 46 | #if BUILDFLAG(IS_ANDROID) |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 47 | |
yoshiki | f42d8fc | 2015-03-04 20:07:50 | [diff] [blame] | 48 | class AndroidUIControllerDelegate : public DownloadUIController::Delegate { |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 49 | public: |
yoshiki | f42d8fc | 2015-03-04 20:07:50 | [diff] [blame] | 50 | AndroidUIControllerDelegate() {} |
| 51 | ~AndroidUIControllerDelegate() override {} |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 52 | |
| 53 | private: |
| 54 | // DownloadUIController::Delegate |
Min Qin | a9f48787 | 2018-02-09 20:43:23 | [diff] [blame] | 55 | void OnNewDownloadReady(download::DownloadItem* item) override; |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 56 | }; |
| 57 | |
yoshiki | f42d8fc | 2015-03-04 20:07:50 | [diff] [blame] | 58 | void AndroidUIControllerDelegate::OnNewDownloadReady( |
Min Qin | a9f48787 | 2018-02-09 20:43:23 | [diff] [blame] | 59 | download::DownloadItem* item) { |
jinsukkim | 3da95ee0 | 2016-06-21 22:34:46 | [diff] [blame] | 60 | DownloadControllerBase::Get()->OnDownloadStarted(item); |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 61 | } |
| 62 | |
Xiaohan Wang | 41179376 | 2022-01-17 20:23:15 | [diff] [blame] | 63 | #else // BUILDFLAG(IS_ANDROID) |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 64 | |
yoshiki | f42d8fc | 2015-03-04 20:07:50 | [diff] [blame] | 65 | class DownloadShelfUIControllerDelegate |
| 66 | : public DownloadUIController::Delegate { |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 67 | public: |
yoshiki | f42d8fc | 2015-03-04 20:07:50 | [diff] [blame] | 68 | // |profile| is required to outlive DownloadShelfUIControllerDelegate. |
| 69 | explicit DownloadShelfUIControllerDelegate(Profile* profile) |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 70 | : profile_(profile) {} |
yoshiki | f42d8fc | 2015-03-04 20:07:50 | [diff] [blame] | 71 | ~DownloadShelfUIControllerDelegate() override {} |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 72 | |
| 73 | private: |
| 74 | // DownloadUIController::Delegate |
Min Qin | a9f48787 | 2018-02-09 20:43:23 | [diff] [blame] | 75 | void OnNewDownloadReady(download::DownloadItem* item) override; |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 76 | |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 77 | raw_ptr<Profile> profile_; |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 78 | }; |
| 79 | |
yoshiki | f42d8fc | 2015-03-04 20:07:50 | [diff] [blame] | 80 | void DownloadShelfUIControllerDelegate::OnNewDownloadReady( |
Min Qin | a9f48787 | 2018-02-09 20:43:23 | [diff] [blame] | 81 | download::DownloadItem* item) { |
| 82 | content::WebContents* web_contents = |
| 83 | content::DownloadItemUtils::GetWebContents(item); |
pfeldman | 0348594 | 2016-03-29 19:51:04 | [diff] [blame] | 84 | // For the case of DevTools web contents, we'd like to use target browser |
| 85 | // shelf although saving from the DevTools web contents. |
| 86 | if (web_contents && DevToolsWindow::IsDevToolsWindow(web_contents)) { |
| 87 | DevToolsWindow* devtools_window = |
| 88 | DevToolsWindow::AsDevToolsWindow(web_contents); |
| 89 | content::WebContents* inspected = |
| 90 | devtools_window->GetInspectedWebContents(); |
| 91 | // Do not overwrite web contents for the case of remote debugging. |
| 92 | if (inspected) |
| 93 | web_contents = inspected; |
| 94 | } |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 95 | Browser* browser = |
| 96 | web_contents ? chrome::FindBrowserWithWebContents(web_contents) : NULL; |
| 97 | |
| 98 | // As a last resort, use the last active browser for this profile. Not ideal, |
| 99 | // but better than not showing the download at all. |
scottmg | 5c03fe02 | 2016-02-03 01:27:24 | [diff] [blame] | 100 | if (browser == nullptr) |
| 101 | browser = chrome::FindLastActiveWithProfile(profile_); |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 102 | |
yoshiki | 09481b7 | 2015-07-17 03:12:38 | [diff] [blame] | 103 | if (browser && browser->window() && |
| 104 | DownloadItemModel(item).ShouldShowInShelf()) { |
Shakti Sahu | ceb08fb | 2018-09-27 23:56:28 | [diff] [blame] | 105 | DownloadUIModel::DownloadUIModelPtr model = DownloadItemModel::Wrap(item); |
| 106 | |
yoshiki | 09481b7 | 2015-07-17 03:12:38 | [diff] [blame] | 107 | // GetDownloadShelf creates the download shelf if it was not yet created. |
Shakti Sahu | ceb08fb | 2018-09-27 23:56:28 | [diff] [blame] | 108 | browser->window()->GetDownloadShelf()->AddDownload(std::move(model)); |
yoshiki | 09481b7 | 2015-07-17 03:12:38 | [diff] [blame] | 109 | } |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 110 | } |
| 111 | |
Xiaohan Wang | 41179376 | 2022-01-17 20:23:15 | [diff] [blame] | 112 | #endif // BUILDFLAG(IS_ANDROID) |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 113 | |
| 114 | } // namespace |
| 115 | |
| 116 | DownloadUIController::Delegate::~Delegate() { |
| 117 | } |
| 118 | |
Shakti Sahu | ff9ee2b6 | 2019-09-16 23:28:59 | [diff] [blame] | 119 | DownloadUIController::DownloadUIController(content::DownloadManager* manager, |
| 120 | std::unique_ptr<Delegate> delegate) |
| 121 | : download_notifier_(manager, this), delegate_(std::move(delegate)) { |
Xiaohan Wang | ab5fb91 | 2022-01-12 19:21:48 | [diff] [blame] | 122 | #if BUILDFLAG(IS_ANDROID) |
estade | 7fc53a3 | 2015-09-29 19:44:19 | [diff] [blame] | 123 | if (!delegate_) |
Peter Boström | 08e7ed8 | 2021-04-19 17:49:59 | [diff] [blame] | 124 | delegate_ = std::make_unique<AndroidUIControllerDelegate>(); |
Xiaohan Wang | ab5fb91 | 2022-01-12 19:21:48 | [diff] [blame] | 125 | #elif BUILDFLAG(IS_CHROMEOS) |
estade | 889fbece | 2016-08-08 23:20:22 | [diff] [blame] | 126 | if (!delegate_) { |
estade | 7fc53a3 | 2015-09-29 19:44:19 | [diff] [blame] | 127 | // The Profile is guaranteed to be valid since DownloadUIController is owned |
| 128 | // by DownloadService, which in turn is a profile keyed service. |
Peter Boström | 6b70182 | 2021-04-15 03:53:08 | [diff] [blame] | 129 | delegate_ = std::make_unique<DownloadNotificationManager>( |
| 130 | Profile::FromBrowserContext(manager->GetBrowserContext())); |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 131 | } |
Xiaohan Wang | ab5fb91 | 2022-01-12 19:21:48 | [diff] [blame] | 132 | #else // BUILDFLAG(IS_CHROMEOS) |
estade | 7fc53a3 | 2015-09-29 19:44:19 | [diff] [blame] | 133 | if (!delegate_) { |
Peter Boström | 924f803 | 2021-04-02 20:36:02 | [diff] [blame] | 134 | delegate_ = std::make_unique<DownloadShelfUIControllerDelegate>( |
| 135 | Profile::FromBrowserContext(manager->GetBrowserContext())); |
estade | 7fc53a3 | 2015-09-29 19:44:19 | [diff] [blame] | 136 | } |
Xiaohan Wang | ab5fb91 | 2022-01-12 19:21:48 | [diff] [blame] | 137 | #endif // BUILDFLAG(IS_ANDROID) |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | DownloadUIController::~DownloadUIController() { |
| 141 | } |
| 142 | |
| 143 | void DownloadUIController::OnDownloadCreated(content::DownloadManager* manager, |
Min Qin | a9f48787 | 2018-02-09 20:43:23 | [diff] [blame] | 144 | download::DownloadItem* item) { |
Christopher Thompson | caf69fe | 2018-03-06 18:33:01 | [diff] [blame] | 145 | // Record the security level of the page triggering the download. Only record |
| 146 | // when the download occurs in the WebContents that initiated the download |
| 147 | // (e.g., not downloads in new tabs or windows, which have a different |
| 148 | // WebContents). |
| 149 | content::WebContents* web_contents = |
| 150 | content::DownloadItemUtils::GetWebContents(item); |
| 151 | if (web_contents && (item->IsSavePackageDownload() || |
Jochen Eisinger | 7678c8ac | 2018-05-07 15:47:34 | [diff] [blame] | 152 | (web_contents->GetURL() != item->GetOriginalUrl() && |
Christopher Thompson | caf69fe | 2018-03-06 18:33:01 | [diff] [blame] | 153 | web_contents->GetURL() != item->GetURL()))) { |
| 154 | auto* security_state_tab_helper = |
| 155 | SecurityStateTabHelper::FromWebContents(web_contents); |
| 156 | if (security_state_tab_helper) { |
Christopher Thompson | caf69fe | 2018-03-06 18:33:01 | [diff] [blame] | 157 | UMA_HISTOGRAM_ENUMERATION("Security.SecurityLevel.DownloadStarted", |
Emily Stark | 0e6b93ad | 2019-03-11 17:27:30 | [diff] [blame] | 158 | security_state_tab_helper->GetSecurityLevel(), |
Christopher Thompson | caf69fe | 2018-03-06 18:33:01 | [diff] [blame] | 159 | security_state::SECURITY_LEVEL_COUNT); |
Emily Stark | ca844c0 | 2019-08-28 04:28:44 | [diff] [blame] | 160 | UMA_HISTOGRAM_ENUMERATION( |
| 161 | "Security.SafetyTips.DownloadStarted", |
| 162 | security_state_tab_helper->GetVisibleSecurityState() |
meacer | b66107d | 2019-10-17 20:00:19 | [diff] [blame] | 163 | ->safety_tip_info.status); |
Christopher Thompson | caf69fe | 2018-03-06 18:33:01 | [diff] [blame] | 164 | } |
| 165 | } |
| 166 | |
Side Yilmaz | cdf366dc | 2021-02-26 09:39:56 | [diff] [blame] | 167 | if (web_contents) { |
| 168 | // TODO(crbug.com/1179196): Add test for this metric. |
| 169 | RecordDownloadStartPerProfileType( |
| 170 | Profile::FromBrowserContext(web_contents->GetBrowserContext())); |
| 171 | } |
| 172 | |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 173 | // SavePackage downloads are created in a state where they can be shown in the |
| 174 | // browser. Call OnDownloadUpdated() once to notify the UI immediately. |
| 175 | OnDownloadUpdated(manager, item); |
| 176 | } |
| 177 | |
| 178 | void DownloadUIController::OnDownloadUpdated(content::DownloadManager* manager, |
Min Qin | a9f48787 | 2018-02-09 20:43:23 | [diff] [blame] | 179 | download::DownloadItem* item) { |
[email protected] | 9dd188f | 2014-05-15 18:35:20 | [diff] [blame] | 180 | DownloadItemModel item_model(item); |
| 181 | |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 182 | // Ignore if we've already notified the UI about |item| or if it isn't a new |
| 183 | // download. |
[email protected] | 9dd188f | 2014-05-15 18:35:20 | [diff] [blame] | 184 | if (item_model.WasUINotified() || !item_model.ShouldNotifyUI()) |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 185 | return; |
| 186 | |
Owen Min | f98b57e5 | 2021-09-30 23:14:15 | [diff] [blame] | 187 | // Downloads blocked by local policies should be notified, otherwise users |
| 188 | // won't get any feedback that the download has failed. |
| 189 | bool should_notify = |
| 190 | item->GetLastReason() == |
| 191 | download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED && |
| 192 | item->GetMixedContentStatus() != |
| 193 | download::DownloadItem::MixedContentStatus::SILENT_BLOCK; |
| 194 | |
hichris123 | 3032fbd | 2015-10-12 20:37:28 | [diff] [blame] | 195 | // Wait until the target path is determined or the download is canceled. |
| 196 | if (item->GetTargetFilePath().empty() && |
Owen Min | f98b57e5 | 2021-09-30 23:14:15 | [diff] [blame] | 197 | item->GetState() != download::DownloadItem::CANCELLED && !should_notify) { |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 198 | return; |
Owen Min | f98b57e5 | 2021-09-30 23:14:15 | [diff] [blame] | 199 | } |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 200 | |
Min Qin | a9f48787 | 2018-02-09 20:43:23 | [diff] [blame] | 201 | content::WebContents* web_contents = |
| 202 | content::DownloadItemUtils::GetWebContents(item); |
yoshiki | 09481b7 | 2015-07-17 03:12:38 | [diff] [blame] | 203 | if (web_contents) { |
Xiaohan Wang | ab5fb91 | 2022-01-12 19:21:48 | [diff] [blame] | 204 | #if BUILDFLAG(IS_ANDROID) |
Min Qin | c55efe5 | 2021-04-15 05:26:54 | [diff] [blame] | 205 | DownloadController::CloseTabIfEmpty(web_contents, item); |
Min Qin | 0b80a037 | 2019-12-18 03:20:50 | [diff] [blame] | 206 | #else |
yoshiki | 09481b7 | 2015-07-17 03:12:38 | [diff] [blame] | 207 | Browser* browser = chrome::FindBrowserWithWebContents(web_contents); |
| 208 | // If the download occurs in a new tab, and it's not a save page |
| 209 | // download (started before initial navigation completed) close it. |
| 210 | // Avoid calling CloseContents if the tab is not in this browser's tab strip |
| 211 | // model; this can happen if the download was initiated by something |
| 212 | // internal to Chrome, such as by the app list. |
| 213 | if (browser && web_contents->GetController().IsInitialNavigation() && |
| 214 | browser->tab_strip_model()->count() > 1 && |
| 215 | browser->tab_strip_model()->GetIndexOfWebContents(web_contents) != |
| 216 | TabStripModel::kNoTab && |
| 217 | !item->IsSavePackageDownload()) { |
| 218 | web_contents->Close(); |
| 219 | } |
Xiaohan Wang | ab5fb91 | 2022-01-12 19:21:48 | [diff] [blame] | 220 | #endif // BUILDFLAG(IS_ANDROID) |
yoshiki | 09481b7 | 2015-07-17 03:12:38 | [diff] [blame] | 221 | } |
yoshiki | 09481b7 | 2015-07-17 03:12:38 | [diff] [blame] | 222 | |
Min Qin | a9f48787 | 2018-02-09 20:43:23 | [diff] [blame] | 223 | if (item->GetState() == download::DownloadItem::CANCELLED) |
hichris123 | 3032fbd | 2015-10-12 20:37:28 | [diff] [blame] | 224 | return; |
| 225 | |
| 226 | DownloadItemModel(item).SetWasUINotified(true); |
[email protected] | 9dd188f | 2014-05-15 18:35:20 | [diff] [blame] | 227 | delegate_->OnNewDownloadReady(item); |
[email protected] | b375601 | 2013-03-06 17:43:02 | [diff] [blame] | 228 | } |