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