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, |
| 36 Delegate* delegate) : |
| 37 openable_(false), |
| 38 downloading_(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 |
| 46 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| 47 |
| 48 const base::string16 timeout_message = |
| 49 l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING); |
| 50 const base::string16 message = |
| 51 l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL); |
| 52 |
| 53 std::string id(kDownloadNotificationIdBase); |
| 54 id += base::UintToString(item_->GetId()); |
| 55 |
| 56 message_center::RichNotificationData data; |
| 57 notification_.reset(new Notification( |
| 58 message_center::NOTIFICATION_TYPE_PROGRESS, |
| 59 id, |
| 60 message, |
| 61 timeout_message, |
| 62 bundle.GetImageNamed(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING), |
| 63 base::string16() /* display_source */, |
| 64 message_center::NotifierId( |
| 65 message_center::NotifierId::SYSTEM_COMPONENT, |
| 66 kDownloadNotificationNotifierId), |
| 67 data, |
| 68 this)); |
| 69 |
| 70 notification_->set_progress(0); |
| 71 notification_->set_never_timeout(false); |
| 72 |
| 73 SetNotificationData(); |
| 74 |
| 75 scoped_ptr<Notification> notification(new Notification(*notification_)); |
| 76 message_center_->AddNotification(notification.Pass()); |
| 77 } |
| 78 |
| 79 DownloadNotificationItem::~DownloadNotificationItem() { |
| 80 } |
| 81 |
| 82 void DownloadNotificationItem::Close(bool by_user) { |
| 83 bool is_popup = false; |
| 84 |
| 85 const std::string id = notification_->id(); |
| 86 message_center::NotificationList::PopupNotifications popups = |
| 87 message_center_->GetPopupNotifications(); |
| 88 for (auto it = popups.begin(); it != popups.end(); it++) { |
| 89 if ((*it)->id() == id) { |
| 90 is_popup = true; |
| 91 } |
| 92 } |
| 93 |
| 94 if (is_popup) { |
| 95 notification_->set_is_read(true); |
| 96 ShowNotificationAgainSoon(); |
| 97 } else { |
| 98 item_->RemoveObserver(this); |
| 99 item_->Cancel(by_user); |
| 100 delegate_->OnDownloadNotificationItemDestroying(this); |
| 101 } |
| 102 } |
| 103 |
| 104 void DownloadNotificationItem::ShowNotificationAgainSoon() { |
| 105 content::BrowserThread::PostTask( |
| 106 content::BrowserThread::UI, |
| 107 FROM_HERE, |
| 108 base::Bind(&DownloadNotificationItem::ShowNotificationAgain, this)); |
| 109 } |
| 110 |
| 111 void DownloadNotificationItem::ShowNotificationAgain() { |
| 112 scoped_ptr<Notification> notification(new Notification(*notification_)); |
| 113 message_center_->AddNotification(notification.Pass()); |
| 114 message_center_->MarkSinglePopupAsShown( |
| 115 notification_->id(), notification_->IsRead()); |
| 116 } |
| 117 |
| 118 void DownloadNotificationItem::Click() { |
| 119 if (openable_) |
| 120 item_->OpenDownload(); |
| 121 if (item_->IsDone()) { |
| 122 message_center_->RemoveNotification(notification_->id(), true); |
| 123 } |
| 124 } |
| 125 |
| 126 bool DownloadNotificationItem::HasClickedListener() { |
| 127 return true; |
| 128 } |
| 129 |
| 130 void DownloadNotificationItem::ButtonClick(int button_index) { |
| 131 if (button_index < 0 || |
| 132 static_cast<size_t>(button_index) >= button_actions_->size()) { |
| 133 // Out of boundary. Do nothing. |
| 134 return; |
| 135 } |
| 136 |
| 137 DownloadCommands::Command command = button_actions_->at(button_index); |
| 138 DownloadCommands(item_).ExecuteCommand(command, item_->GetWebContents()); |
| 139 } |
| 140 |
| 141 // DownloadItem::Observer methods |
| 142 void DownloadNotificationItem::OnDownloadUpdated(content::DownloadItem* item) { |
| 143 DCHECK_EQ(item, item_); |
| 144 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 145 |
| 146 SetNotificationData(); |
| 147 |
| 148 // Updates notification. |
| 149 scoped_ptr<Notification> notification(new Notification(*notification_)); |
| 150 message_center_->UpdateNotification( |
| 151 notification->id(), |
| 152 notification.Pass()); |
| 153 } |
| 154 |
| 155 void DownloadNotificationItem::SetNotificationData() { |
| 156 DownloadItemModel* model = new DownloadItemModel(item_); |
| 157 DownloadCommands command(item_); |
| 158 |
| 159 if (!downloading_) { |
| 160 if (item_->GetState() == content::DownloadItem::IN_PROGRESS) |
| 161 delegate_->OnDownloadStarted(this); |
| 162 } else { |
| 163 if (item_->GetState() != content::DownloadItem::IN_PROGRESS) |
| 164 delegate_->OnDownloadStopped(this); |
| 165 } |
| 166 |
| 167 if (item_->IsDangerous()) { |
| 168 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE); |
| 169 notification_->set_title(GetTitle()); |
| 170 notification_->set_message(GetWarningTextLong()); |
| 171 |
| 172 // Show icon. |
| 173 |
| 174 switch (item_->GetDangerType()) { |
| 175 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: |
| 176 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: |
| 177 break; |
| 178 default: |
| 179 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS); |
| 180 break; |
| 181 } |
| 182 } else { |
| 183 notification_->set_title(GetTitle()); |
| 184 notification_->set_message(model->GetStatusText()); |
| 185 |
| 186 bool is_off_the_record = item_->GetBrowserContext()->IsOffTheRecord(); |
| 187 |
| 188 switch (item_->GetState()) { |
| 189 case content::DownloadItem::IN_PROGRESS: |
| 190 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS); |
| 191 notification_->set_progress(item_->PercentComplete()); |
| 192 if (is_off_the_record) { |
| 193 // TODO(yoshiki): Replace the tentative image. |
| 194 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO); |
| 195 } else { |
| 196 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING); |
| 197 } |
| 198 break; |
| 199 case content::DownloadItem::COMPLETE: |
| 200 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE); |
| 201 if (is_off_the_record) { |
| 202 // TODO(yoshiki): Replace the tentative image. |
| 203 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO); |
| 204 } else { |
| 205 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING); |
| 206 } |
| 207 |
| 208 // TODO(yoshiki): Popup a notification again. |
| 209 break; |
| 210 case content::DownloadItem::CANCELLED: |
| 211 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE); |
| 212 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_WARNING); |
| 213 break; |
| 214 case content::DownloadItem::INTERRUPTED: |
| 215 notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE); |
| 216 SetImageToNotification(IDR_DOWNLOAD_NOTIFICATION_WARNING); |
| 217 |
| 218 // TODO(yoshiki): Popup a notification again. |
| 219 break; |
| 220 case content::DownloadItem::MAX_DOWNLOAD_STATE: // sentinel |
| 221 NOTREACHED(); |
| 222 } |
| 223 } |
| 224 |
| 225 std::vector<message_center::ButtonInfo> notification_actions; |
| 226 scoped_ptr<std::vector<DownloadCommands::Command> > |
| 227 actions(GetPossibleActions().Pass()); |
| 228 |
| 229 openable_ = false; |
| 230 button_actions_.reset(new std::vector<DownloadCommands::Command>); |
| 231 for (auto it = actions->begin(); it != actions->end(); it++) { |
| 232 if (*it == DownloadCommands::OPEN_WHEN_COMPLETE) { |
| 233 openable_ = true; |
| 234 } else { |
| 235 button_actions_->push_back(*it); |
| 236 message_center::ButtonInfo button_info = |
| 237 message_center::ButtonInfo(command.GetCommandString(*it)); |
| 238 button_info.icon = command.GetCommandIcon(*it); |
| 239 notification_actions.push_back(button_info); |
| 240 } |
| 241 } |
| 242 notification_->set_buttons(notification_actions); |
| 243 |
| 244 if (item_->IsDone()) { |
| 245 // TODO(yoshiki): If the downloaded file is an image, show the thumbnail. |
| 246 } |
| 247 } |
| 248 |
| 249 void DownloadNotificationItem::OnDownloadOpened(content::DownloadItem* item) { |
| 250 DCHECK_EQ(item, item_); |
| 251 // Do nothing. |
| 252 } |
| 253 |
| 254 void DownloadNotificationItem::OnDownloadDestroyed( |
| 255 content::DownloadItem* item) { |
| 256 DCHECK_EQ(item, item_); |
| 257 |
| 258 // TODO(yoshiki): close the notification if necessary. |
| 259 |
| 260 delegate_->OnDownloadNotificationItemDestroying(this); |
| 261 } |
| 262 |
| 263 void DownloadNotificationItem::SetImageToNotification(int resource_id) { |
| 264 if (image_resource_id_ == resource_id) |
| 265 return; |
| 266 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| 267 image_resource_id_ = resource_id; |
| 268 notification_->set_icon(bundle.GetImageNamed(image_resource_id_)); |
| 269 } |
| 270 |
| 271 scoped_ptr<std::vector<DownloadCommands::Command> > |
| 272 DownloadNotificationItem::GetPossibleActions() const { |
| 273 scoped_ptr<std::vector<DownloadCommands::Command> > |
| 274 actions(new std::vector<DownloadCommands::Command>()); |
| 275 |
| 276 if (item_->IsDangerous()) { |
| 277 actions->push_back(DownloadCommands::DISCARD); |
| 278 actions->push_back(DownloadCommands::SHOW_IN_FOLDER); |
| 279 return actions.Pass(); |
| 280 } |
| 281 |
| 282 switch (item_->GetState()) { |
| 283 case content::DownloadItem::IN_PROGRESS: |
| 284 actions->push_back(DownloadCommands::OPEN_WHEN_COMPLETE); |
| 285 if (!item_->IsPaused()) |
| 286 actions->push_back(DownloadCommands::PAUSE); |
| 287 else |
| 288 actions->push_back(DownloadCommands::RESUME); |
| 289 break; |
| 290 case content::DownloadItem::CANCELLED: |
| 291 case content::DownloadItem::INTERRUPTED: |
| 292 actions->push_back(DownloadCommands::RETRY); |
| 293 break; |
| 294 case content::DownloadItem::COMPLETE: |
| 295 actions->push_back(DownloadCommands::OPEN_WHEN_COMPLETE); |
| 296 actions->push_back(DownloadCommands::SHOW_IN_FOLDER); |
| 297 break; |
| 298 case content::DownloadItem::MAX_DOWNLOAD_STATE: |
| 299 NOTREACHED(); |
| 300 } |
| 301 return actions.Pass(); |
| 302 } |
| 303 |
| 304 base::string16 DownloadNotificationItem::GetTitle() const { |
| 305 base::string16 title_text; |
| 306 base::string16 file_name = |
| 307 item_->GetFileNameToReportUser().LossyDisplayName(); |
| 308 switch (item_->GetState()) { |
| 309 case content::DownloadItem::IN_PROGRESS: |
| 310 title_text = l10n_util::GetStringFUTF16( |
| 311 IDS_DOWNLOAD_STATUS_IN_PROGRESS_TITLE, |
| 312 file_name); |
| 313 break; |
| 314 case content::DownloadItem::COMPLETE: |
| 315 title_text = l10n_util::GetStringFUTF16( |
| 316 IDS_DOWNLOAD_STATUS_DOWNLOADED_TITLE, |
| 317 file_name); |
| 318 case content::DownloadItem::INTERRUPTED: |
| 319 title_text = l10n_util::GetStringFUTF16( |
| 320 IDS_DOWNLOAD_STATUS_DOWNLOADED_TITLE, |
| 321 file_name); |
| 322 break; |
| 323 case content::DownloadItem::CANCELLED: |
| 324 title_text = l10n_util::GetStringFUTF16( |
| 325 IDS_DOWNLOAD_STATUS_DOWNLOAD_FAILED_TITLE, |
| 326 file_name); |
| 327 break; |
| 328 case content::DownloadItem::MAX_DOWNLOAD_STATE: |
| 329 NOTREACHED(); |
| 330 } |
| 331 return title_text; |
| 332 } |
| 333 |
| 334 base::string16 DownloadNotificationItem::GetWarningTextLong() const { |
| 335 // Should only be called if IsDangerous(). |
| 336 DCHECK(item_->IsDangerous()); |
| 337 base::string16 elided_filename = |
| 338 item_->GetFileNameToReportUser().LossyDisplayName(); |
| 339 switch (item_->GetDangerType()) { |
| 340 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: { |
| 341 return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL); |
| 342 } |
| 343 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: { |
| 344 if (download_crx_util::IsExtensionDownload(*item_)) { |
| 345 return l10n_util::GetStringUTF16( |
| 346 IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION); |
| 347 } else { |
| 348 return l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD, |
| 349 elided_filename); |
| 350 } |
| 351 } |
| 352 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: |
| 353 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: { |
| 354 return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT, |
| 355 elided_filename); |
| 356 } |
| 357 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: { |
| 358 return l10n_util::GetStringFUTF16(IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT, |
| 359 elided_filename); |
| 360 } |
| 361 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: { |
| 362 return l10n_util::GetStringFUTF16( |
| 363 IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS, elided_filename); |
| 364 } |
| 365 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: |
| 366 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: |
| 367 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: |
| 368 case content::DOWNLOAD_DANGER_TYPE_MAX: { |
| 369 break; |
| 370 } |
| 371 } |
| 372 NOTREACHED(); |
| 373 return base::string16(); |
| 374 } |
| 375 |
OLD | NEW |