Chromium Code Reviews| Index: chrome/browser/download/notification/download_notification_item.cc |
| diff --git a/chrome/browser/download/notification/download_notification_item.cc b/chrome/browser/download/notification/download_notification_item.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..670da591e0bca5237407c5bf889e4b41234c5a2a |
| --- /dev/null |
| +++ b/chrome/browser/download/notification/download_notification_item.cc |
| @@ -0,0 +1,377 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/download/notification/download_notification_item.h" |
| + |
| +#include "base/strings/string_number_conversions.h" |
| +#include "chrome/browser/download/download_crx_util.h" |
| +#include "chrome/browser/download/download_item_model.h" |
| +#include "chrome/grit/chromium_strings.h" |
| +#include "chrome/grit/generated_resources.h" |
| +#include "content/public/browser/browser_context.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/download_item.h" |
| +#include "content/public/browser/web_contents.h" |
| +#include "grit/theme_resources.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| +#include "ui/message_center/message_center.h" |
| +#include "ui/message_center/notification.h" |
| +#include "ui/message_center/notification_delegate.h" |
| + |
| +using message_center::Notification; |
| + |
| +namespace { |
| + |
| +const char kDownloadNotificationNotifierId[] = |
| + "chrome://settings/display/notification/id-notifier"; |
| +const char kDownloadNotificationIdBase[] = |
| + "chrome://settings/display/notification/id-"; |
| + |
| +} // anonymous namespace |
| + |
| +DownloadNotificationItem::DownloadNotificationItem( |
| + content::DownloadItem* item, |
| + Delegate* delegate) : |
| + openable_(false), |
| + downloading_(false), |
| + image_resource_id_(0), |
| + item_(item), |
| + delegate_(delegate) { |
| + item->AddObserver(this); |
| + |
| + message_center_ = message_center::MessageCenter::Get(); |
| + |
| + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| + |
| + const base::string16 timeout_message = |
| + l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING); |
| + const base::string16 message = |
| + l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL); |
| + |
| + std::string id(kDownloadNotificationIdBase); |
| + id += base::UintToString(item->GetId()); |
|
asanka
2015/02/10 02:25:30
Is the generated ID only expected to be valid for
yoshiki
2015/02/10 17:49:17
Download IDs are unique among profiles and notific
|
| + |
| + message_center::RichNotificationData data; |
| + notification_.reset(new Notification( |
| + message_center::NOTIFICATION_TYPE_PROGRESS, |
| + id, |
| + message, |
| + timeout_message, |
| + bundle.GetImageNamed(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING), |
| + base::string16() /* display_source */, |
| + message_center::NotifierId( |
| + message_center::NotifierId::SYSTEM_COMPONENT, |
| + kDownloadNotificationNotifierId), |
| + data, |
| + this)); |
| + |
| + notification_->set_progress(0); |
| + notification_->set_never_timeout(false); |
| + |
| + SetNotificationData(); |
| + |
| + scoped_ptr<Notification> notification(new Notification(*notification_)); |
| + message_center_->AddNotification(notification.Pass()); |
| +} |
| + |
| +DownloadNotificationItem::~DownloadNotificationItem() { |
| +} |
| + |
| +void DownloadNotificationItem::Close(bool by_user) { |
| + bool is_popup = false; |
| + |
| + const std::string id = notification_->id(); |
| + message_center::NotificationList::PopupNotifications popups = |
| + message_center_->GetPopupNotifications(); |
| + for (auto it = popups.begin(); it != popups.end(); it++) { |
| + if ((*it)->id() == id) { |
| + is_popup = true; |
| + } |
| + } |
| + LOG(ERROR) << "CLOSE: " << is_popup; |
| + |
| + if (is_popup) { |
| + notification_->set_is_read(true); |
| + ShowNotificationAgainSoon(); |
| + } else { |
| + item_->RemoveObserver(this); |
| + item_->Cancel(by_user); |
| + delegate_->OnDownloadNotificationItemDestroying(this); |
| + } |
| +} |
| + |
| +void DownloadNotificationItem::ShowNotificationAgainSoon() { |
| + content::BrowserThread::PostTask( |
| + content::BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&DownloadNotificationItem::ShowNotificationAgain, this)); |
| +} |
| + |
| +void DownloadNotificationItem::ShowNotificationAgain() { |
| + scoped_ptr<Notification> notification(new Notification(*notification_)); |
| + message_center_->AddNotification(notification.Pass()); |
| + message_center_->MarkSinglePopupAsShown( |
| + notification_->id(), notification_->IsRead()); |
| +} |
| + |
| +void DownloadNotificationItem::Click() { |
| + if (openable_) |
| + item_->OpenDownload(); |
| + if (item_->IsDone()) { |
| + message_center_->RemoveNotification(notification_->id(), true); |
| + } |
| +} |
| + |
| +bool DownloadNotificationItem::HasClickedListener() { |
| + return true; |
| +} |
| + |
| +void DownloadNotificationItem::ButtonClick(int button_index) { |
| + if (button_index < 0 || |
| + static_cast<size_t>(button_index) >= button_actions_->size()) { |
| + // Out of boundary. Do nothing. |
| + return; |
| + } |
| + |
| + DownloadCommand::Commands command = button_actions_->at(button_index); |
| + DownloadCommand(item_).ExecuteCommand(command, item_->GetWebContents()); |
| +} |
| + |
| +// DownloadItem::Observer methods |
| +void DownloadNotificationItem::OnDownloadUpdated(content::DownloadItem* item) { |
| + DCHECK_EQ(item, item_); |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + |
| + SetNotificationData(); |
| + |
| + // Updates notification. |
| + scoped_ptr<Notification> notification(new Notification(*notification_)); |
| + message_center_->UpdateNotification( |
| + notification->id(), |
| + notification.Pass()); |
| +} |
| + |
| +void DownloadNotificationItem::SetNotificationData() { |
| + DownloadItemModel* model = new DownloadItemModel(item_); |
| + DownloadCommand command(item_); |
| + |
| + if (!downloading_) { |
| + if (item_->GetState() == content::DownloadItem::IN_PROGRESS) |
| + delegate_->OnDownloadStarted(this); |
| + } else { |
| + if (item_->GetState() != content::DownloadItem::IN_PROGRESS) |
| + delegate_->OnDownloadStopped(this); |
| + } |
| + |
| + if (item_->IsDangerous()) { |
| + notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE); |
| + notification_->set_title(GetTitle()); |
| + notification_->set_message(GetWarningTextLong()); |
| + |
| + // Show icon. |
| + |
| + switch (item_->GetDangerType()) { |
| + case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: |
| + case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: |
| + break; |
| + default: |
| + SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS); |
| + break; |
| + } |
| + } else { |
| + notification_->set_title(GetTitle()); |
| + notification_->set_message(model->GetStatusText()); |
| + |
| + bool is_off_the_record = item_->GetBrowserContext()->IsOffTheRecord(); |
| + |
| + switch (item_->GetState()) { |
| + case content::DownloadItem::IN_PROGRESS: |
| + notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS); |
| + notification_->set_progress(item_->PercentComplete()); |
| + if (is_off_the_record) { |
| + // TODO(yoshiki): Replace the tentative image. |
| + SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO); |
| + } else { |
| + SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING); |
| + } |
| + break; |
| + case content::DownloadItem::COMPLETE: |
| + notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE); |
| + if (is_off_the_record) { |
| + // TODO(yoshiki): Replace the tentative image. |
| + SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO); |
| + } else { |
| + SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING); |
| + } |
| + |
| + // TODO(yoshiki): Popup a notification again. |
| + break; |
| + case content::DownloadItem::CANCELLED: |
| + notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE); |
| + SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_WARNING); |
| + break; |
| + case content::DownloadItem::INTERRUPTED: |
| + notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE); |
| + SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_WARNING); |
| + |
| + // TODO(yoshiki): Popup a notification again. |
| + break; |
| + case content::DownloadItem::MAX_DOWNLOAD_STATE: // sentinel |
| + NOTREACHED(); |
| + } |
| + } |
| + |
| + std::vector<message_center::ButtonInfo> notification_actions; |
| + scoped_ptr<std::vector<DownloadCommand::Commands> > |
| + actions(GetPossibleActions().Pass()); |
| + |
| + openable_ = false; |
| + button_actions_.reset(new std::vector<DownloadCommand::Commands>); |
| + ResourceBundle& bundle = ResourceBundle::GetSharedInstance(); |
| + for (auto it = actions->begin(); it != actions->end(); it++) { |
| + if (*it == DownloadCommand::OPEN_WHEN_COMPLETE) { |
| + openable_ = true; |
| + } else { |
| + button_actions_->push_back(*it); |
| + message_center::ButtonInfo button_info = message_center::ButtonInfo( |
| + l10n_util::GetStringUTF16(command.GetCommandStringId(*it))); |
| + button_info.icon = bundle.GetImageNamed(command.GetCommandIconId(*it)); |
| + notification_actions.push_back(button_info); |
| + } |
| + } |
| + notification_->set_buttons(notification_actions); |
| + |
| + if (item_->IsDone()) { |
| + // TODO(yoshiki): If the downloaded file is an image, show the thumbnail. |
| + } |
| +} |
| + |
| +void DownloadNotificationItem::OnDownloadOpened(content::DownloadItem* item) { |
| + DCHECK_EQ(item, item_); |
| + // Do nothing. |
| +} |
| + |
| +void DownloadNotificationItem::OnDownloadDestroyed( |
| + content::DownloadItem* item) { |
| + DCHECK_EQ(item, item_); |
| + |
| + // TODO(yoshiki): close the notification if necessary. |
| + |
| + delegate_->OnDownloadNotificationItemDestroying(this); |
| +} |
| + |
| +void DownloadNotificationItem::SetImageToNotification(int resource_id) { |
| + if (image_resource_id_ == resource_id) |
| + return; |
| + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| + image_resource_id_ = resource_id; |
| + notification_->set_icon(bundle.GetImageNamed(image_resource_id_)); |
| +} |
| + |
| +scoped_ptr<std::vector<DownloadCommand::Commands> > |
| +DownloadNotificationItem::GetPossibleActions() const { |
| + scoped_ptr<std::vector<DownloadCommand::Commands> > |
| + actions(new std::vector<DownloadCommand::Commands>()); |
| + |
| + if (item_->IsDangerous()) { |
| + actions->push_back(DownloadCommand::DISCARD); |
| + actions->push_back(DownloadCommand::SHOW_IN_FOLDER); |
| + return actions.Pass(); |
| + } |
| + |
| + switch (item_->GetState()) { |
| + case content::DownloadItem::IN_PROGRESS: |
| + actions->push_back(DownloadCommand::OPEN_WHEN_COMPLETE); |
| + if (!item_->IsPaused()) |
| + actions->push_back(DownloadCommand::PAUSE); |
| + else |
| + actions->push_back(DownloadCommand::RESUME); |
| + break; |
| + case content::DownloadItem::CANCELLED: |
| + case content::DownloadItem::INTERRUPTED: |
| + actions->push_back(DownloadCommand::RETRY); |
| + break; |
| + case content::DownloadItem::COMPLETE: |
| + actions->push_back(DownloadCommand::OPEN_WHEN_COMPLETE); |
| + actions->push_back(DownloadCommand::SHOW_IN_FOLDER); |
| + break; |
| + case content::DownloadItem::MAX_DOWNLOAD_STATE: |
| + NOTREACHED(); |
| + } |
| + return actions.Pass(); |
| +} |
| + |
| +base::string16 DownloadNotificationItem::GetTitle() const { |
| + base::string16 title_text; |
| + base::string16 file_name = |
| + item_->GetFileNameToReportUser().LossyDisplayName(); |
| + switch (item_->GetState()) { |
| + case content::DownloadItem::IN_PROGRESS: |
| + title_text = l10n_util::GetStringFUTF16( |
| + IDS_DOWNLOAD_STATUS_IN_PROGRESS_TITLE, |
| + file_name); |
| + break; |
| + case content::DownloadItem::COMPLETE: |
| + title_text = l10n_util::GetStringFUTF16( |
| + IDS_DOWNLOAD_STATUS_DOWNLOADED_TITLE, |
| + file_name); |
| + case content::DownloadItem::INTERRUPTED: |
| + title_text = l10n_util::GetStringFUTF16( |
| + IDS_DOWNLOAD_STATUS_DOWNLOADED_TITLE, |
| + file_name); |
| + break; |
| + case content::DownloadItem::CANCELLED: |
| + title_text = l10n_util::GetStringFUTF16( |
| + IDS_DOWNLOAD_STATUS_DOWNLOAD_FAILED_TITLE, |
| + file_name); |
| + break; |
| + case content::DownloadItem::MAX_DOWNLOAD_STATE: |
| + NOTREACHED(); |
| + } |
| + return title_text; |
| +} |
| + |
| +base::string16 DownloadNotificationItem::GetWarningTextLong() const { |
| + // Should only be called if IsDangerous(). |
| + DCHECK(item_->IsDangerous()); |
| + base::string16 elided_filename = |
| + item_->GetFileNameToReportUser().LossyDisplayName(); |
| + switch (item_->GetDangerType()) { |
| + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: { |
| + return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL); |
| + } |
| + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: { |
| + if (download_crx_util::IsExtensionDownload(*item_)) { |
| + return l10n_util::GetStringUTF16( |
| + IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION); |
| + } else { |
| + return l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD, |
| + elided_filename); |
| + } |
| + } |
| + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: |
| + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: { |
| + return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT, |
| + elided_filename); |
| + } |
| + case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: { |
| + return l10n_util::GetStringFUTF16(IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT, |
| + elided_filename); |
| + } |
| + case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: { |
| + return l10n_util::GetStringFUTF16( |
| + IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS, elided_filename); |
| + } |
| + case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: |
| + case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: |
| + case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: |
| + case content::DOWNLOAD_DANGER_TYPE_MAX: { |
| + break; |
| + } |
| + } |
| + NOTREACHED(); |
| + return base::string16(); |
| +} |
| + |