Chromium Code Reviews| 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 |