OLD | NEW |
---|---|
(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 } | |
OLD | NEW |