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

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

Powered by Google App Engine
This is Rietveld 408576698