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

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: Fixed build & test errors on non-ChromeOS platform. Created 5 years, 9 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::NotificationWatcher::NotificationWatcher(
35 DownloadNotificationItem* item)
36 : item_(item) {
37 }
38
39 DownloadNotificationItem::NotificationWatcher::~NotificationWatcher() {
40 }
41
42 void DownloadNotificationItem::NotificationWatcher::Close(bool by_user) {
43 item_->OnNotificationClose(by_user);
44 }
45
46 void DownloadNotificationItem::NotificationWatcher::Click() {
47 item_->OnNotificationClick();
48 }
49
50 bool DownloadNotificationItem::NotificationWatcher::HasClickedListener() {
51 return true;
52 }
53
54 void DownloadNotificationItem::NotificationWatcher::ButtonClick(
55 int button_index) {
56 item_->OnNotificationButtonClick(button_index);
57 }
58
59 void DownloadNotificationItem::NotificationWatcher::OnNotificationRemoved(
60 const std::string& id,
61 bool by_user) {
62 if (id != item_->notification_->id())
63 return;
64 item_->OnNotificationRemoved(by_user);
65 }
66
67 DownloadNotificationItem::DownloadNotificationItem(content::DownloadItem* item,
68 Delegate* delegate)
69 : openable_(false),
70 downloading_(false),
71 reshow_after_remove_(false),
72 image_resource_id_(0),
73 watcher_(new NotificationWatcher(this)),
74 item_(item),
75 delegate_(delegate) {
76 item->AddObserver(this);
77
78 message_center_ = message_center::MessageCenter::Get();
79 message_center_->AddObserver(watcher_.get());
80
81 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
82
83 const base::string16 timeout_message =
84 l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING);
85 const base::string16 message =
86 l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL);
87
88 std::string id(kDownloadNotificationIdBase);
89 id += base::UintToString(item_->GetId());
90
91 message_center::RichNotificationData data;
92 notification_.reset(new Notification(
93 message_center::NOTIFICATION_TYPE_PROGRESS, id, message, timeout_message,
94 bundle.GetImageNamed(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING),
95 base::string16() /* display_source */,
96 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
97 kDownloadNotificationNotifierId),
98 data, watcher_.get()));
99
100 notification_->set_progress(0);
101 notification_->set_never_timeout(false);
102
103 UpdateNotificationData();
104
105 scoped_ptr<Notification> notification(new Notification(*notification_));
106 message_center_->AddNotification(notification.Pass());
107 }
108
109 DownloadNotificationItem::~DownloadNotificationItem() {
110 if (item_)
111 item_->RemoveObserver(this);
112 message_center_->RemoveObserver(watcher_.get());
113 }
114
115 void DownloadNotificationItem::OnNotificationClose(bool by_user) {
116 if (item_->GetState() != content::DownloadItem::IN_PROGRESS) {
117 reshow_after_remove_ = false;
118 } else {
119 bool popup = false;
120
121 const std::string id = notification_->id();
122 message_center::NotificationList::PopupNotifications popups =
123 message_center_->GetPopupNotifications();
124 for (auto it = popups.begin(); it != popups.end(); it++) {
125 if ((*it)->id() == id) {
126 popup = true;
127 break;
128 }
129 }
130
131 // Reshows the notification in the notification center, if the download is
132 // in progress and the notifitation being closed is a popup.
133 reshow_after_remove_ = popup;
134 }
135
136 // OnNotificationRemoved() will be called soon, just after the notification
137 // is removed.
138 }
139
140 void DownloadNotificationItem::OnNotificationRemoved(bool by_user) {
141 if (reshow_after_remove_) {
142 // Sets the notification as read.
143 notification_->set_is_read(true);
144
145 // Reshows the notification.
146 scoped_ptr<Notification> notification(new Notification(*notification_));
147 message_center_->AddNotification(notification.Pass());
148 // Show the reshown notification as a non-popup.
149 message_center_->MarkSinglePopupAsShown(notification_->id(), true);
150
151 reshow_after_remove_ = false;
152 } else {
153 // Cancels the download.
154 item_->Cancel(by_user);
155 delegate_->OnDownloadRemoved(this);
156 }
157 }
158
159 void DownloadNotificationItem::OnNotificationClick() {
160 if (openable_) {
161 if (item_->IsDone())
162 item_->OpenDownload();
163 else
164 item_->SetOpenWhenComplete(!item_->GetOpenWhenComplete()); // Toggle
165 }
166
167 if (item_->IsDone())
168 message_center_->RemoveNotification(notification_->id(), true);
169 }
170
171 void DownloadNotificationItem::OnNotificationButtonClick(int button_index) {
172 if (button_index < 0 ||
173 static_cast<size_t>(button_index) >= button_actions_->size()) {
174 // Out of boundary.
175 NOTREACHED();
176 return;
177 }
178
179 DownloadCommands::Command command = button_actions_->at(button_index);
180 DownloadCommands(item_).ExecuteCommand(command);
181 }
182
183 // DownloadItem::Observer methods
184 void DownloadNotificationItem::OnDownloadUpdated(content::DownloadItem* item) {
185 DCHECK_EQ(item, item_);
186
187 UpdateNotificationData();
188
189 // Updates notification.
190 scoped_ptr<Notification> notification(new Notification(*notification_));
191 std::string id = notification->id();
192 message_center_->UpdateNotification(id, notification.Pass());
193 }
194
195 void DownloadNotificationItem::UpdateNotificationData() {
196 DownloadItemModel model(item_);
197 DownloadCommands command(item_);
198
199 if (!downloading_) {
200 if (item_->GetState() == content::DownloadItem::IN_PROGRESS) {
201 delegate_->OnDownloadStarted(this);
202 downloading_ = true;
203 }
204 } else {
205 if (item_->GetState() != content::DownloadItem::IN_PROGRESS) {
206 delegate_->OnDownloadStopped(this);
207 downloading_ = false;
208 }
209 }
210
211 if (item_->IsDangerous()) {
212 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE);
213 notification_->set_title(GetTitle());
214 notification_->set_message(GetWarningText());
215
216 // Show icon.
217 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS);
218 } else {
219 notification_->set_title(GetTitle());
220 notification_->set_message(model.GetStatusText());
221
222 bool is_off_the_record = item_->GetBrowserContext() &&
223 item_->GetBrowserContext()->IsOffTheRecord();
224
225 switch (item_->GetState()) {
226 case content::DownloadItem::IN_PROGRESS:
227 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
228 notification_->set_progress(item_->PercentComplete());
229 if (is_off_the_record) {
230 // TODO(yoshiki): Replace the tentative image.
231 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
232 } else {
233 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
234 }
235 break;
236 case content::DownloadItem::COMPLETE:
237 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE);
238 if (is_off_the_record) {
239 // TODO(yoshiki): Replace the tentative image.
240 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
241 } else {
242 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
243 }
244
245 // TODO(yoshiki): Popup a notification again.
246 break;
247 case content::DownloadItem::CANCELLED:
248 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE);
249 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_WARNING);
250 break;
251 case content::DownloadItem::INTERRUPTED:
252 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE);
253 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_WARNING);
254
255 // TODO(yoshiki): Popup a notification again.
256 break;
257 case content::DownloadItem::MAX_DOWNLOAD_STATE: // sentinel
258 NOTREACHED();
259 }
260 }
261
262 std::vector<message_center::ButtonInfo> notification_actions;
263 scoped_ptr<std::vector<DownloadCommands::Command>> actions(
264 GetPossibleActions().Pass());
265
266 openable_ = false;
267 button_actions_.reset(new std::vector<DownloadCommands::Command>);
268 for (auto it = actions->begin(); it != actions->end(); it++) {
269 if (*it == DownloadCommands::OPEN_WHEN_COMPLETE) {
270 openable_ = true;
271 } else {
272 button_actions_->push_back(*it);
273 message_center::ButtonInfo button_info =
274 message_center::ButtonInfo(GetCommandLabel(*it));
275 button_info.icon = command.GetCommandIcon(*it);
276 notification_actions.push_back(button_info);
277 }
278 }
279 notification_->set_buttons(notification_actions);
280
281 if (item_->IsDone()) {
282 // TODO(yoshiki): If the downloaded file is an image, show the thumbnail.
283 }
284 }
285
286 void DownloadNotificationItem::OnDownloadOpened(content::DownloadItem* item) {
287 DCHECK_EQ(item, item_);
288 // Do nothing.
289 }
290
291 void DownloadNotificationItem::OnDownloadRemoved(content::DownloadItem* item) {
292 DCHECK_EQ(item, item_);
293
294 // Removing the notification causes calling both |OnNotificationClose()| and
295 // |OnNotificationRemoved()|.
296 message_center_->RemoveNotification(notification_->id(), false);
297 }
298
299 void DownloadNotificationItem::OnDownloadDestroyed(
300 content::DownloadItem* item) {
301 DCHECK_EQ(item, item_);
302
303 item_ = nullptr;
304 }
305
306 void DownloadNotificationItem::SetNotificationImage(int resource_id) {
307 if (image_resource_id_ == resource_id)
308 return;
309 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
310 image_resource_id_ = resource_id;
311 notification_->set_icon(bundle.GetImageNamed(image_resource_id_));
312 }
313
314 scoped_ptr<std::vector<DownloadCommands::Command>>
315 DownloadNotificationItem::GetPossibleActions() const {
316 scoped_ptr<std::vector<DownloadCommands::Command>> actions(
317 new std::vector<DownloadCommands::Command>());
318
319 if (item_->IsDangerous()) {
320 actions->push_back(DownloadCommands::DISCARD);
321 actions->push_back(DownloadCommands::KEEP);
322 return actions.Pass();
323 }
324
325 switch (item_->GetState()) {
326 case content::DownloadItem::IN_PROGRESS:
327 actions->push_back(DownloadCommands::OPEN_WHEN_COMPLETE);
328 if (!item_->IsPaused())
329 actions->push_back(DownloadCommands::PAUSE);
330 else
331 actions->push_back(DownloadCommands::RESUME);
332 break;
333 case content::DownloadItem::CANCELLED:
334 case content::DownloadItem::INTERRUPTED:
335 actions->push_back(DownloadCommands::RETRY);
336 break;
337 case content::DownloadItem::COMPLETE:
338 actions->push_back(DownloadCommands::OPEN_WHEN_COMPLETE);
339 actions->push_back(DownloadCommands::SHOW_IN_FOLDER);
340 break;
341 case content::DownloadItem::MAX_DOWNLOAD_STATE:
342 NOTREACHED();
343 }
344 return actions.Pass();
345 }
346
347 base::string16 DownloadNotificationItem::GetTitle() const {
348 base::string16 title_text;
349 base::string16 file_name =
350 item_->GetFileNameToReportUser().LossyDisplayName();
351 switch (item_->GetState()) {
352 case content::DownloadItem::IN_PROGRESS:
353 title_text = l10n_util::GetStringFUTF16(
354 IDS_DOWNLOAD_STATUS_IN_PROGRESS_TITLE, file_name);
355 break;
356 case content::DownloadItem::COMPLETE:
357 title_text = l10n_util::GetStringFUTF16(
358 IDS_DOWNLOAD_STATUS_DOWNLOADED_TITLE, file_name);
359 case content::DownloadItem::INTERRUPTED:
360 title_text = l10n_util::GetStringFUTF16(
361 IDS_DOWNLOAD_STATUS_DOWNLOADED_TITLE, file_name);
362 break;
363 case content::DownloadItem::CANCELLED:
364 title_text = l10n_util::GetStringFUTF16(
365 IDS_DOWNLOAD_STATUS_DOWNLOAD_FAILED_TITLE, file_name);
366 break;
367 case content::DownloadItem::MAX_DOWNLOAD_STATE:
368 NOTREACHED();
369 }
370 return title_text;
371 }
372
373 base::string16 DownloadNotificationItem::GetCommandLabel(
374 DownloadCommands::Command command) const {
375 int id = -1;
376 switch (command) {
377 case DownloadCommands::OPEN_WHEN_COMPLETE:
378 if (item_ && !item_->IsDone())
379 id = IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE;
380 else
381 id = IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE;
382 break;
383 case DownloadCommands::PAUSE:
384 // Only for non menu.
385 id = IDS_DOWNLOAD_LINK_PAUSE;
386 break;
387 case DownloadCommands::RESUME:
388 // Only for non menu.
389 id = IDS_DOWNLOAD_LINK_RESUME;
390 break;
391 case DownloadCommands::SHOW_IN_FOLDER:
392 id = IDS_DOWNLOAD_LINK_SHOW;
393 break;
394 case DownloadCommands::RETRY:
395 // Only for non menu.
396 id = IDS_DOWNLOAD_LINK_RETRY;
397 break;
398 case DownloadCommands::DISCARD:
399 id = IDS_DISCARD_DOWNLOAD;
400 break;
401 case DownloadCommands::KEEP:
402 id = IDS_CONFIRM_DOWNLOAD;
403 break;
404 case DownloadCommands::ALWAYS_OPEN_TYPE:
405 case DownloadCommands::PLATFORM_OPEN:
406 case DownloadCommands::CANCEL:
407 case DownloadCommands::LEARN_MORE_SCANNING:
408 case DownloadCommands::LEARN_MORE_INTERRUPTED:
409 // Only for menu.
410 NOTREACHED();
411 return base::string16();
412 }
413 CHECK(id != -1);
414 return l10n_util::GetStringUTF16(id);
415 }
416
417 base::string16 DownloadNotificationItem::GetWarningText() const {
418 // Should only be called if IsDangerous().
419 DCHECK(item_->IsDangerous());
420 base::string16 elided_filename =
421 item_->GetFileNameToReportUser().LossyDisplayName();
422 switch (item_->GetDangerType()) {
423 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: {
424 return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL);
425 }
426 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: {
427 if (download_crx_util::IsExtensionDownload(*item_)) {
428 return l10n_util::GetStringUTF16(
429 IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION);
430 } else {
431 return l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD,
432 elided_filename);
433 }
434 }
435 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
436 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
437 return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT,
438 elided_filename);
439 }
440 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
441 return l10n_util::GetStringFUTF16(IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT,
442 elided_filename);
443 }
444 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: {
445 return l10n_util::GetStringFUTF16(IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS,
446 elided_filename);
447 }
448 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
449 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
450 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
451 case content::DOWNLOAD_DANGER_TYPE_MAX: {
452 break;
453 }
454 }
455 NOTREACHED();
456 return base::string16();
457 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698