Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(165)

Side by Side Diff: chrome/browser/download/notification/download_notification_item.cc

Issue 852043002: Initial Implementation of Download Notification (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed comments & added tests. Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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/notification/download_notification_item.h"
6
7 #include "base/strings/string_number_conversions.h"
8 #include "chrome/browser/download/download_crx_util.h"
9 #include "chrome/browser/download/download_item_model.h"
10 #include "chrome/grit/chromium_strings.h"
11 #include "chrome/grit/generated_resources.h"
12 #include "content/public/browser/browser_context.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/download_item.h"
15 #include "content/public/browser/web_contents.h"
16 #include "grit/theme_resources.h"
17 #include "ui/base/l10n/l10n_util.h"
18 #include "ui/base/resource/resource_bundle.h"
19 #include "ui/message_center/message_center.h"
20 #include "ui/message_center/notification.h"
21 #include "ui/message_center/notification_delegate.h"
22
23 using message_center::Notification;
24
25 namespace {
26
27 const char kDownloadNotificationNotifierId[] =
28 "chrome://settings/display/notification/id-notifier";
29 const char kDownloadNotificationIdBase[] =
30 "chrome://settings/display/notification/id-";
31
32 } // anonymous namespace
33
34 DownloadNotificationItem::DownloadNotificationItem(
35 content::DownloadItem* item, Delegate* delegate)
36 : openable_(false),
37 downloading_(false),
38 popup_closing_(false),
39 image_resource_id_(0),
40 item_(item),
41 delegate_(delegate) {
42 item->AddObserver(this);
43
44 message_center_ = message_center::MessageCenter::Get();
45 message_center_->AddObserver(this);
46
47 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
48
49 const base::string16 timeout_message =
50 l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING);
51 const base::string16 message =
52 l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL);
53
54 std::string id(kDownloadNotificationIdBase);
55 id += base::UintToString(item_->GetId());
56
57 message_center::RichNotificationData data;
58 notification_.reset(new Notification(
59 message_center::NOTIFICATION_TYPE_PROGRESS,
60 id,
61 message,
62 timeout_message,
63 bundle.GetImageNamed(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING),
64 base::string16() /* display_source */,
65 message_center::NotifierId(
66 message_center::NotifierId::SYSTEM_COMPONENT,
67 kDownloadNotificationNotifierId),
68 data,
69 this));
70
71 notification_->set_progress(0);
72 notification_->set_never_timeout(false);
73
74 SetNotificationData();
75
76 scoped_ptr<Notification> notification(new Notification(*notification_));
77 message_center_->AddNotification(notification.Pass());
78 }
79
80 DownloadNotificationItem::~DownloadNotificationItem() {
81 message_center_->RemoveObserver(this);
82 }
83
84 void DownloadNotificationItem::Close(bool by_user) {
85 popup_closing_ = false;
86
87 const std::string id = notification_->id();
88 message_center::NotificationList::PopupNotifications popups =
89 message_center_->GetPopupNotifications();
90 for (auto it = popups.begin(); it != popups.end(); it++) {
91 if ((*it)->id() == id) {
92 popup_closing_ = true;
93 return;
94 }
95 }
96
97 // OnNotificationRemoved() will be called soon, just after the notification
98 // is removed.
99 }
100
101 void DownloadNotificationItem::OnNotificationRemoved(
102 const std::string& id, bool by_user) {
103 if (popup_closing_) {
104 notification_->set_is_read(true);
105 notification_->set_shown_as_popup(false);
106
107 // Reshows the notification.
asanka 2015/02/20 23:34:39 Can you explain what this logic does?
yoshiki 2015/02/24 21:30:57 I added some comments. Does it make sense?
108 scoped_ptr<Notification> notification(new Notification(*notification_));
109 message_center_->AddNotification(notification.Pass());
110 message_center_->MarkSinglePopupAsShown(
111 notification_->id(), true);
112
113 popup_closing_ = false;
114 } else {
115 item_->RemoveObserver(this);
116 item_->Cancel(by_user);
117 delegate_->OnDownloadRemoved(this);
asanka 2015/02/20 23:34:40 Is the OnDownloadRemoved call going to destroy |th
yoshiki 2015/02/24 21:30:57 Yes. It removes |this|.
118 }
119 }
120
121 void DownloadNotificationItem::Click() {
122 if (openable_) {
123 if (item_->IsDone())
124 item_->OpenDownload();
125 else
126 item_->SetOpenWhenComplete(!item_->GetOpenWhenComplete()); // Toggle
127 }
128
129 if (item_->IsDone())
130 message_center_->RemoveNotification(notification_->id(), true);
131 }
132
133 bool DownloadNotificationItem::HasClickedListener() {
134 return true;
135 }
136
137 void DownloadNotificationItem::ButtonClick(int button_index) {
138 if (button_index < 0 ||
139 static_cast<size_t>(button_index) >= button_actions_->size()) {
140 // Out of boundary.
141 NOTREACHED();
142 return;
143 }
144
145 DownloadCommands::Command command = button_actions_->at(button_index);
146 DownloadCommands(item_).ExecuteCommand(command, item_->GetWebContents());
asanka 2015/02/20 23:34:39 GetWebContents() can (and often will) return NULL
yoshiki 2015/02/24 21:30:57 I changed it to pass a |Profile| instead of a |Web
147 }
148
149 // DownloadItem::Observer methods
150 void DownloadNotificationItem::OnDownloadUpdated(content::DownloadItem* item) {
151 DCHECK_EQ(item, item_);
152
153 SetNotificationData();
154
155 // Updates notification.
156 scoped_ptr<Notification> notification(new Notification(*notification_));
157 std::string id = notification->id();
158 message_center_->UpdateNotification(
159 id,
160 notification.Pass());
161 }
162
163 void DownloadNotificationItem::SetNotificationData() {
164 DownloadItemModel* model = new DownloadItemModel(item_);
asanka 2015/02/20 23:34:40 This leaks. In general, DownloadItemModel is a th
yoshiki 2015/02/24 21:30:57 Done.
165 DownloadCommands command(item_);
166
167 if (!downloading_) {
168 if (item_->GetState() == content::DownloadItem::IN_PROGRESS) {
169 delegate_->OnDownloadStarted(this);
170 downloading_ = true;
171 }
172 } else {
173 if (item_->GetState() != content::DownloadItem::IN_PROGRESS) {
174 delegate_->OnDownloadStopped(this);
175 downloading_ = false;
176 }
177 }
178
179 if (item_->IsDangerous()) {
180 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE);
181 notification_->set_title(GetTitle());
182 notification_->set_message(GetWarningText());
183
184 // Show icon.
185 switch (item_->GetDangerType()) {
186 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
187 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
188 // TODO(yoshiki): Shows an appreciate icon.
asanka 2015/02/20 23:34:39 appreciate?
yoshiki 2015/02/24 21:30:57 I changed the code around here and the comment has
189 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS);
190 break;
191 default:
asanka 2015/02/20 23:34:39 Don't use a 'default' label when using an enumerat
yoshiki 2015/02/24 21:30:57 I changed the code around here and it has been rem
192 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS);
193 break;
194 }
195 } else {
196 notification_->set_title(GetTitle());
197 notification_->set_message(model->GetStatusText());
198
199 bool is_off_the_record = item_->GetBrowserContext() &&
200 item_->GetBrowserContext()->IsOffTheRecord();
201
202 switch (item_->GetState()) {
203 case content::DownloadItem::IN_PROGRESS:
204 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
205 notification_->set_progress(item_->PercentComplete());
206 if (is_off_the_record) {
207 // TODO(yoshiki): Replace the tentative image.
208 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
209 } else {
210 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
211 }
212 break;
213 case content::DownloadItem::COMPLETE:
214 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE);
215 if (is_off_the_record) {
216 // TODO(yoshiki): Replace the tentative image.
217 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
218 } else {
219 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
220 }
221
222 // TODO(yoshiki): Popup a notification again.
223 break;
224 case content::DownloadItem::CANCELLED:
225 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE);
226 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_WARNING);
227 break;
228 case content::DownloadItem::INTERRUPTED:
229 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE);
230 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_WARNING);
231
232 // TODO(yoshiki): Popup a notification again.
233 break;
234 case content::DownloadItem::MAX_DOWNLOAD_STATE: // sentinel
235 NOTREACHED();
236 }
237 }
238
239 std::vector<message_center::ButtonInfo> notification_actions;
240 scoped_ptr<std::vector<DownloadCommands::Command> >
241 actions(GetPossibleActions().Pass());
242
243 openable_ = false;
244 button_actions_.reset(new std::vector<DownloadCommands::Command>);
245 for (auto it = actions->begin(); it != actions->end(); it++) {
246 if (*it == DownloadCommands::OPEN_WHEN_COMPLETE) {
247 openable_ = true;
248 } else {
249 button_actions_->push_back(*it);
250 message_center::ButtonInfo button_info =
251 message_center::ButtonInfo(GetCommandLabel(*it));
252 button_info.icon = command.GetCommandIcon(*it);
253 notification_actions.push_back(button_info);
254 }
255 }
256 notification_->set_buttons(notification_actions);
257
258 if (item_->IsDone()) {
259 // TODO(yoshiki): If the downloaded file is an image, show the thumbnail.
260 }
261 }
262
263 void DownloadNotificationItem::OnDownloadOpened(content::DownloadItem* item) {
264 DCHECK_EQ(item, item_);
265 // Do nothing.
266 }
267
268 void DownloadNotificationItem::OnDownloadRemoved(content::DownloadItem* item) {
269 DCHECK_EQ(item, item_);
270
271 // TODO(yoshiki): close the notification if necessary.
272
273 delegate_->OnDownloadRemoved(this);
asanka 2015/02/20 23:34:39 DownloadNOtificationItem being a RefCounted class
yoshiki 2015/02/24 21:30:57 Done.
274 }
275
276 void DownloadNotificationItem::SetImageToNotification(int resource_id) {
277 if (image_resource_id_ == resource_id)
278 return;
279 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
280 image_resource_id_ = resource_id;
281 notification_->set_icon(bundle.GetImageNamed(image_resource_id_));
282 }
283
284 scoped_ptr<std::vector<DownloadCommands::Command> >
285 DownloadNotificationItem::GetPossibleActions() const {
286 scoped_ptr<std::vector<DownloadCommands::Command> >
287 actions(new std::vector<DownloadCommands::Command>());
288
289 if (item_->IsDangerous()) {
290 actions->push_back(DownloadCommands::DISCARD);
291 actions->push_back(DownloadCommands::SHOW_IN_FOLDER);
asanka 2015/02/20 23:34:40 YOu don't want to SHOW_IN_FOLDER if it's dangerous
yoshiki 2015/02/24 21:30:57 Done.
292 return actions.Pass();
293 }
294
295 switch (item_->GetState()) {
296 case content::DownloadItem::IN_PROGRESS:
297 actions->push_back(DownloadCommands::OPEN_WHEN_COMPLETE);
298 if (!item_->IsPaused())
299 actions->push_back(DownloadCommands::PAUSE);
300 else
301 actions->push_back(DownloadCommands::RESUME);
302 break;
303 case content::DownloadItem::CANCELLED:
304 case content::DownloadItem::INTERRUPTED:
305 actions->push_back(DownloadCommands::RETRY);
306 break;
307 case content::DownloadItem::COMPLETE:
308 actions->push_back(DownloadCommands::OPEN_WHEN_COMPLETE);
309 actions->push_back(DownloadCommands::SHOW_IN_FOLDER);
310 break;
311 case content::DownloadItem::MAX_DOWNLOAD_STATE:
312 NOTREACHED();
313 }
314 return actions.Pass();
315 }
316
317 base::string16 DownloadNotificationItem::GetTitle() const {
318 base::string16 title_text;
319 base::string16 file_name =
320 item_->GetFileNameToReportUser().LossyDisplayName();
321 switch (item_->GetState()) {
322 case content::DownloadItem::IN_PROGRESS:
323 title_text = l10n_util::GetStringFUTF16(
324 IDS_DOWNLOAD_STATUS_IN_PROGRESS_TITLE,
325 file_name);
326 break;
327 case content::DownloadItem::COMPLETE:
328 title_text = l10n_util::GetStringFUTF16(
329 IDS_DOWNLOAD_STATUS_DOWNLOADED_TITLE,
330 file_name);
331 case content::DownloadItem::INTERRUPTED:
332 title_text = l10n_util::GetStringFUTF16(
333 IDS_DOWNLOAD_STATUS_DOWNLOADED_TITLE,
334 file_name);
335 break;
336 case content::DownloadItem::CANCELLED:
337 title_text = l10n_util::GetStringFUTF16(
338 IDS_DOWNLOAD_STATUS_DOWNLOAD_FAILED_TITLE,
339 file_name);
340 break;
341 case content::DownloadItem::MAX_DOWNLOAD_STATE:
342 NOTREACHED();
343 }
344 return title_text;
345 }
346
347 base::string16 DownloadNotificationItem::GetCommandLabel(
348 DownloadCommands::Command command) const {
349 int id = -1;
350 switch (command) {
351 case DownloadCommands::OPEN_WHEN_COMPLETE:
352 if (item_ && !item_->IsDone())
353 id = IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE;
354 else
355 id = IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE;
356 break;
357 case DownloadCommands::PAUSE:
358 // Only for non menu.
359 id = IDS_DOWNLOAD_LINK_PAUSE;
360 break;
361 case DownloadCommands::RESUME:
362 // Only for non menu.
363 id = IDS_DOWNLOAD_LINK_RESUME;
364 break;
365 case DownloadCommands::SHOW_IN_FOLDER:
366 id = IDS_DOWNLOAD_LINK_SHOW;
367 break;
368 case DownloadCommands::RETRY:
369 // Only for non menu.
370 id = IDS_DOWNLOAD_LINK_RETRY;
371 break;
372 case DownloadCommands::DISCARD:
373 id = IDS_DISCARD_DOWNLOAD;
374 break;
375 case DownloadCommands::KEEP:
376 case DownloadCommands::ALWAYS_OPEN_TYPE:
377 case DownloadCommands::PLATFORM_OPEN:
378 case DownloadCommands::CANCEL:
379 case DownloadCommands::LEARN_MORE_SCANNING:
380 case DownloadCommands::LEARN_MORE_INTERRUPTED:
381 // Only for menu.
382 NOTREACHED();
383 return base::string16();
384 }
385 CHECK(id != -1);
386 return l10n_util::GetStringUTF16(id);
387 }
388
389 base::string16 DownloadNotificationItem::GetWarningText() const {
390 // Should only be called if IsDangerous().
391 DCHECK(item_->IsDangerous());
392 base::string16 elided_filename =
393 item_->GetFileNameToReportUser().LossyDisplayName();
394 switch (item_->GetDangerType()) {
395 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: {
396 return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL);
397 }
398 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: {
399 if (download_crx_util::IsExtensionDownload(*item_)) {
400 return l10n_util::GetStringUTF16(
401 IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION);
402 } else {
403 return l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD,
404 elided_filename);
405 }
406 }
407 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
408 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
409 return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT,
410 elided_filename);
411 }
412 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
413 return l10n_util::GetStringFUTF16(IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT,
414 elided_filename);
415 }
416 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: {
417 return l10n_util::GetStringFUTF16(
418 IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS, elided_filename);
419 }
420 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
421 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
422 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
423 case content::DOWNLOAD_DANGER_TYPE_MAX: {
424 break;
425 }
426 }
427 NOTREACHED();
428 return base::string16();
429 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698