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