1// Copyright (c) 2012 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/download_item_model.h" 6 7#include "base/i18n/number_formatting.h" 8#include "base/i18n/rtl.h" 9#include "base/metrics/field_trial.h" 10#include "base/strings/string16.h" 11#include "base/strings/sys_string_conversions.h" 12#include "base/strings/utf_string_conversions.h" 13#include "base/supports_user_data.h" 14#include "base/time/time.h" 15#include "chrome/browser/download/chrome_download_manager_delegate.h" 16#include "chrome/browser/download/download_crx_util.h" 17#include "chrome/browser/download/download_service.h" 18#include "chrome/browser/download/download_service_factory.h" 19#include "chrome/browser/download/download_stats.h" 20#include "chrome/browser/safe_browsing/download_feedback_service.h" 21#include "content/public/browser/download_danger_type.h" 22#include "content/public/browser/download_interrupt_reasons.h" 23#include "content/public/browser/download_item.h" 24#include "grit/chromium_strings.h" 25#include "grit/generated_resources.h" 26#include "ui/base/l10n/l10n_util.h" 27#include "ui/base/l10n/time_format.h" 28#include "ui/base/text/bytes_formatting.h" 29#include "ui/gfx/text_elider.h" 30 31using base::TimeDelta; 32using content::DownloadItem; 33 34namespace { 35 36// Per DownloadItem data used by DownloadItemModel. The model doesn't keep any 37// state since there could be multiple models associated with a single 38// DownloadItem, and the lifetime of the model is shorter than the DownloadItem. 39class DownloadItemModelData : public base::SupportsUserData::Data { 40 public: 41 // Get the DownloadItemModelData object for |download|. Returns NULL if 42 // there's no model data. 43 static const DownloadItemModelData* Get(const DownloadItem* download); 44 45 // Get the DownloadItemModelData object for |download|. Creates a model data 46 // object if not found. Always returns a non-NULL pointer, unless OOM. 47 static DownloadItemModelData* GetOrCreate(DownloadItem* download); 48 49 bool should_show_in_shelf() const { return should_show_in_shelf_; } 50 void set_should_show_in_shelf(bool should_show_in_shelf) { 51 should_show_in_shelf_ = should_show_in_shelf; 52 } 53 54 bool should_notify_ui() const { return should_notify_ui_; } 55 void set_should_notify_ui(bool should_notify_ui) { 56 should_notify_ui_ = should_notify_ui; 57 } 58 59 bool should_prefer_opening_in_browser() const { 60 return should_prefer_opening_in_browser_; 61 } 62 void set_should_prefer_opening_in_browser(bool preference) { 63 should_prefer_opening_in_browser_ = preference; 64 } 65 66 private: 67 DownloadItemModelData(); 68 virtual ~DownloadItemModelData() {} 69 70 static const char kKey[]; 71 72 // Whether the download should be displayed in the download shelf. True by 73 // default. 74 bool should_show_in_shelf_; 75 76 // Whether the UI should be notified when the download is ready to be 77 // presented. 78 bool should_notify_ui_; 79 80 // Whether the download should be opened in the browser vs. the system handler 81 // for the file type. 82 bool should_prefer_opening_in_browser_; 83}; 84 85// static 86const char DownloadItemModelData::kKey[] = "DownloadItemModelData key"; 87 88// static 89const DownloadItemModelData* DownloadItemModelData::Get( 90 const DownloadItem* download) { 91 return static_cast<const DownloadItemModelData*>(download->GetUserData(kKey)); 92} 93 94// static 95DownloadItemModelData* DownloadItemModelData::GetOrCreate( 96 DownloadItem* download) { 97 DownloadItemModelData* data = 98 static_cast<DownloadItemModelData*>(download->GetUserData(kKey)); 99 if (data == NULL) { 100 data = new DownloadItemModelData(); 101 download->SetUserData(kKey, data); 102 } 103 return data; 104} 105 106DownloadItemModelData::DownloadItemModelData() 107 : should_show_in_shelf_(true), 108 should_notify_ui_(false), 109 should_prefer_opening_in_browser_(false) { 110} 111 112base::string16 InterruptReasonStatusMessage(int reason) { 113 int string_id = 0; 114 115 switch (reason) { 116 case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED: 117 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_ACCESS_DENIED; 118 break; 119 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE: 120 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_DISK_FULL; 121 break; 122 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG: 123 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_PATH_TOO_LONG; 124 break; 125 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE: 126 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_FILE_TOO_LARGE; 127 break; 128 case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED: 129 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_VIRUS; 130 break; 131 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR: 132 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_TEMPORARY_PROBLEM; 133 break; 134 case content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED: 135 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_BLOCKED; 136 break; 137 case content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED: 138 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SECURITY_CHECK_FAILED; 139 break; 140 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT: 141 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_FILE_TOO_SHORT; 142 break; 143 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED: 144 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_ERROR; 145 break; 146 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT: 147 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_TIMEOUT; 148 break; 149 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED: 150 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_DISCONNECTED; 151 break; 152 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN: 153 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_DOWN; 154 break; 155 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED: 156 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_PROBLEM; 157 break; 158 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT: 159 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NO_FILE; 160 break; 161 case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED: 162 string_id = IDS_DOWNLOAD_STATUS_CANCELLED; 163 break; 164 case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN: 165 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SHUTDOWN; 166 break; 167 case content::DOWNLOAD_INTERRUPT_REASON_CRASH: 168 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_CRASH; 169 break; 170 default: 171 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS; 172 break; 173 } 174 175 return l10n_util::GetStringUTF16(string_id); 176} 177 178base::string16 InterruptReasonMessage(int reason) { 179 int string_id = 0; 180 base::string16 status_text; 181 182 switch (reason) { 183 case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED: 184 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_ACCESS_DENIED; 185 break; 186 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE: 187 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_DISK_FULL; 188 break; 189 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG: 190 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_PATH_TOO_LONG; 191 break; 192 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE: 193 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_LARGE; 194 break; 195 case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED: 196 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_VIRUS; 197 break; 198 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR: 199 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_TEMPORARY_PROBLEM; 200 break; 201 case content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED: 202 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_BLOCKED; 203 break; 204 case content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED: 205 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SECURITY_CHECK_FAILED; 206 break; 207 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT: 208 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_SHORT; 209 break; 210 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED: 211 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_ERROR; 212 break; 213 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT: 214 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_TIMEOUT; 215 break; 216 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED: 217 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_DISCONNECTED; 218 break; 219 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN: 220 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_DOWN; 221 break; 222 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED: 223 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_PROBLEM; 224 break; 225 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT: 226 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NO_FILE; 227 break; 228 case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED: 229 string_id = IDS_DOWNLOAD_STATUS_CANCELLED; 230 break; 231 case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN: 232 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SHUTDOWN; 233 break; 234 case content::DOWNLOAD_INTERRUPT_REASON_CRASH: 235 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_CRASH; 236 break; 237 default: 238 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS; 239 break; 240 } 241 242 status_text = l10n_util::GetStringUTF16(string_id); 243 244 return status_text; 245} 246 247} // namespace 248 249// ----------------------------------------------------------------------------- 250// DownloadItemModel 251 252DownloadItemModel::DownloadItemModel(DownloadItem* download) 253 : download_(download) {} 254 255DownloadItemModel::~DownloadItemModel() {} 256 257base::string16 DownloadItemModel::GetInterruptReasonText() const { 258 if (download_->GetState() != DownloadItem::INTERRUPTED || 259 download_->GetLastReason() == 260 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) { 261 return base::string16(); 262 } 263 return InterruptReasonMessage(download_->GetLastReason()); 264} 265 266base::string16 DownloadItemModel::GetStatusText() const { 267 base::string16 status_text; 268 switch (download_->GetState()) { 269 case DownloadItem::IN_PROGRESS: 270 status_text = GetInProgressStatusString(); 271 break; 272 case DownloadItem::COMPLETE: 273 if (download_->GetFileExternallyRemoved()) { 274 status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_REMOVED); 275 } else { 276 status_text.clear(); 277 } 278 break; 279 case DownloadItem::CANCELLED: 280 status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED); 281 break; 282 case DownloadItem::INTERRUPTED: { 283 content::DownloadInterruptReason reason = download_->GetLastReason(); 284 if (reason != content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) { 285 base::string16 interrupt_reason = InterruptReasonStatusMessage(reason); 286 status_text = l10n_util::GetStringFUTF16( 287 IDS_DOWNLOAD_STATUS_INTERRUPTED, interrupt_reason); 288 } else { 289 // Same as DownloadItem::CANCELLED. 290 status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED); 291 } 292 break; 293 } 294 default: 295 NOTREACHED(); 296 } 297 298 return status_text; 299} 300 301base::string16 DownloadItemModel::GetTabProgressStatusText() const { 302 int64 total = GetTotalBytes(); 303 int64 size = download_->GetReceivedBytes(); 304 base::string16 received_size = ui::FormatBytes(size); 305 base::string16 amount = received_size; 306 307 // Adjust both strings for the locale direction since we don't yet know which 308 // string we'll end up using for constructing the final progress string. 309 base::i18n::AdjustStringForLocaleDirection(&amount); 310 311 if (total) { 312 base::string16 total_text = ui::FormatBytes(total); 313 base::i18n::AdjustStringForLocaleDirection(&total_text); 314 315 base::i18n::AdjustStringForLocaleDirection(&received_size); 316 amount = l10n_util::GetStringFUTF16( 317 IDS_DOWNLOAD_TAB_PROGRESS_SIZE, received_size, total_text); 318 } else { 319 amount.assign(received_size); 320 } 321 int64 current_speed = download_->CurrentSpeed(); 322 base::string16 speed_text = ui::FormatSpeed(current_speed); 323 base::i18n::AdjustStringForLocaleDirection(&speed_text); 324 325 base::TimeDelta remaining; 326 base::string16 time_remaining; 327 if (download_->IsPaused()) 328 time_remaining = l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED); 329 else if (download_->TimeRemaining(&remaining)) 330 time_remaining = ui::TimeFormat::TimeRemaining(remaining); 331 332 if (time_remaining.empty()) { 333 base::i18n::AdjustStringForLocaleDirection(&amount); 334 return l10n_util::GetStringFUTF16( 335 IDS_DOWNLOAD_TAB_PROGRESS_STATUS_TIME_UNKNOWN, speed_text, amount); 336 } 337 return l10n_util::GetStringFUTF16( 338 IDS_DOWNLOAD_TAB_PROGRESS_STATUS, speed_text, amount, time_remaining); 339} 340 341base::string16 DownloadItemModel::GetTooltipText(const gfx::FontList& font_list, 342 int max_width) const { 343 base::string16 tooltip = gfx::ElideFilename( 344 download_->GetFileNameToReportUser(), font_list, max_width); 345 content::DownloadInterruptReason reason = download_->GetLastReason(); 346 if (download_->GetState() == DownloadItem::INTERRUPTED && 347 reason != content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) { 348 tooltip += ASCIIToUTF16("\n"); 349 tooltip += gfx::ElideText(InterruptReasonStatusMessage(reason), 350 font_list, max_width, gfx::ELIDE_AT_END); 351 } 352 return tooltip; 353} 354 355base::string16 DownloadItemModel::GetWarningText(const gfx::FontList& font_list, 356 int base_width) const { 357 // Should only be called if IsDangerous(). 358 DCHECK(IsDangerous()); 359 base::string16 elided_filename = 360 gfx::ElideFilename(download_->GetFileNameToReportUser(), font_list, 361 base_width); 362 switch (download_->GetDangerType()) { 363 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: { 364 return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL); 365 } 366 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: { 367 if (download_crx_util::IsExtensionDownload(*download_)) { 368 return l10n_util::GetStringUTF16( 369 IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION); 370 } else { 371 return l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD, 372 elided_filename); 373 } 374 } 375 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: 376 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: { 377 return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT, 378 elided_filename); 379 } 380 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: { 381 return l10n_util::GetStringFUTF16(IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT, 382 elided_filename); 383 } 384 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: { 385 return l10n_util::GetStringFUTF16( 386 IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS, elided_filename); 387 } 388 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: 389 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: 390 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: 391 case content::DOWNLOAD_DANGER_TYPE_MAX: { 392 break; 393 } 394 } 395 NOTREACHED(); 396 return base::string16(); 397} 398 399base::string16 DownloadItemModel::GetWarningConfirmButtonText() const { 400 // Should only be called if IsDangerous() 401 DCHECK(IsDangerous()); 402 if (download_->GetDangerType() == 403 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE && 404 download_crx_util::IsExtensionDownload(*download_)) { 405 return l10n_util::GetStringUTF16(IDS_CONTINUE_EXTENSION_DOWNLOAD); 406 } else { 407 return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD); 408 } 409} 410 411int64 DownloadItemModel::GetCompletedBytes() const { 412 return download_->GetReceivedBytes(); 413} 414 415int64 DownloadItemModel::GetTotalBytes() const { 416 return download_->AllDataSaved() ? download_->GetReceivedBytes() : 417 download_->GetTotalBytes(); 418} 419 420// TODO(asanka,rdsmith): Once 'open' moves exclusively to the 421// ChromeDownloadManagerDelegate, we should calculate the percentage here 422// instead of calling into the DownloadItem. 423int DownloadItemModel::PercentComplete() const { 424 return download_->PercentComplete(); 425} 426 427bool DownloadItemModel::IsDangerous() const { 428 return download_->IsDangerous(); 429} 430 431bool DownloadItemModel::MightBeMalicious() const { 432 if (!IsDangerous()) 433 return false; 434 switch (download_->GetDangerType()) { 435 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: 436 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: 437 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: 438 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: 439 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: 440 return true; 441 442 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: 443 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: 444 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: 445 case content::DOWNLOAD_DANGER_TYPE_MAX: 446 // We shouldn't get any of these due to the IsDangerous() test above. 447 NOTREACHED(); 448 // Fallthrough. 449 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: 450 return false; 451 } 452 NOTREACHED(); 453 return false; 454} 455 456// If you change this definition of malicious, also update 457// DownloadManagerImpl::NonMaliciousInProgressCount. 458bool DownloadItemModel::IsMalicious() const { 459 if (!MightBeMalicious()) 460 return false; 461 switch (download_->GetDangerType()) { 462 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: 463 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: 464 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: 465 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: 466 return true; 467 468 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: 469 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: 470 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: 471 case content::DOWNLOAD_DANGER_TYPE_MAX: 472 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: 473 // We shouldn't get any of these due to the MightBeMalicious() test above. 474 NOTREACHED(); 475 // Fallthrough. 476 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: 477 return false; 478 } 479 NOTREACHED(); 480 return false; 481} 482 483bool DownloadItemModel::ShouldAllowDownloadFeedback() const { 484#if defined(FULL_SAFE_BROWSING) 485 if (!IsDangerous()) 486 return false; 487 return safe_browsing::DownloadFeedbackService::IsEnabledForDownload( 488 *download_); 489#else 490 return false; 491#endif 492} 493 494bool DownloadItemModel::ShouldRemoveFromShelfWhenComplete() const { 495 switch (download_->GetState()) { 496 case DownloadItem::IN_PROGRESS: 497 // If the download is dangerous or malicious, we should display a warning 498 // on the shelf until the user accepts the download. 499 if (IsDangerous()) 500 return false; 501 502 // If the download is an extension, temporary, or will be opened 503 // automatically, then it should be removed from the shelf on completion. 504 // TODO(asanka): The logic for deciding opening behavior should be in a 505 // central location. http://crbug.com/167702 506 return (download_crx_util::IsExtensionDownload(*download_) || 507 download_->IsTemporary() || 508 download_->GetOpenWhenComplete() || 509 download_->ShouldOpenFileBasedOnExtension()); 510 511 case DownloadItem::COMPLETE: 512 // If the download completed, then rely on GetAutoOpened() to check for 513 // opening behavior. This should accurately reflect whether the download 514 // was successfully opened. Extensions, for example, may fail to open. 515 return download_->GetAutoOpened() || download_->IsTemporary(); 516 517 case DownloadItem::CANCELLED: 518 case DownloadItem::INTERRUPTED: 519 // Interrupted or cancelled downloads should remain on the shelf. 520 return false; 521 522 case DownloadItem::MAX_DOWNLOAD_STATE: 523 NOTREACHED(); 524 } 525 526 NOTREACHED(); 527 return false; 528} 529 530bool DownloadItemModel::ShouldShowDownloadStartedAnimation() const { 531 return !download_->IsSavePackageDownload() && 532 !download_crx_util::IsExtensionDownload(*download_); 533} 534 535bool DownloadItemModel::ShouldShowInShelf() const { 536 const DownloadItemModelData* data = DownloadItemModelData::Get(download_); 537 return !data || data->should_show_in_shelf(); 538} 539 540void DownloadItemModel::SetShouldShowInShelf(bool should_show) { 541 DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_); 542 data->set_should_show_in_shelf(should_show); 543} 544 545bool DownloadItemModel::ShouldNotifyUI() const { 546 const DownloadItemModelData* data = DownloadItemModelData::Get(download_); 547 return data && data->should_notify_ui(); 548} 549 550void DownloadItemModel::SetShouldNotifyUI(bool should_notify) { 551 DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_); 552 data->set_should_notify_ui(should_notify); 553} 554 555bool DownloadItemModel::ShouldPreferOpeningInBrowser() const { 556 const DownloadItemModelData* data = DownloadItemModelData::Get(download_); 557 return data && data->should_prefer_opening_in_browser(); 558} 559 560void DownloadItemModel::SetShouldPreferOpeningInBrowser(bool preference) { 561 DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_); 562 data->set_should_prefer_opening_in_browser(preference); 563} 564 565base::string16 DownloadItemModel::GetProgressSizesString() const { 566 base::string16 size_ratio; 567 int64 size = GetCompletedBytes(); 568 int64 total = GetTotalBytes(); 569 if (total > 0) { 570 ui::DataUnits amount_units = ui::GetByteDisplayUnits(total); 571 base::string16 simple_size = ui::FormatBytesWithUnits(size, amount_units, false); 572 573 // In RTL locales, we render the text "size/total" in an RTL context. This 574 // is problematic since a string such as "123/456 MB" is displayed 575 // as "MB 123/456" because it ends with an LTR run. In order to solve this, 576 // we mark the total string as an LTR string if the UI layout is 577 // right-to-left so that the string "456 MB" is treated as an LTR run. 578 base::string16 simple_total = base::i18n::GetDisplayStringInLTRDirectionality( 579 ui::FormatBytesWithUnits(total, amount_units, true)); 580 size_ratio = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_SIZES, 581 simple_size, simple_total); 582 } else { 583 size_ratio = ui::FormatBytes(size); 584 } 585 return size_ratio; 586} 587 588base::string16 DownloadItemModel::GetInProgressStatusString() const { 589 DCHECK_EQ(DownloadItem::IN_PROGRESS, download_->GetState()); 590 591 TimeDelta time_remaining; 592 // time_remaining is only known if the download isn't paused. 593 bool time_remaining_known = (!download_->IsPaused() && 594 download_->TimeRemaining(&time_remaining)); 595 596 // Indication of progress. (E.g.:"100/200 MB" or "100MB") 597 base::string16 size_ratio = GetProgressSizesString(); 598 599 // The download is a CRX (app, extension, theme, ...) and it is being unpacked 600 // and validated. 601 if (download_->AllDataSaved() && 602 download_crx_util::IsExtensionDownload(*download_)) { 603 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING); 604 } 605 606 // A paused download: "100/120 MB, Paused" 607 if (download_->IsPaused()) { 608 return l10n_util::GetStringFUTF16( 609 IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio, 610 l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED)); 611 } 612 613 // A download scheduled to be opened when complete: "Opening in 10 secs" 614 if (download_->GetOpenWhenComplete()) { 615 if (!time_remaining_known) 616 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE); 617 618 return l10n_util::GetStringFUTF16( 619 IDS_DOWNLOAD_STATUS_OPEN_IN, 620 ui::TimeFormat::TimeRemainingShort(time_remaining)); 621 } 622 623 // In progress download with known time left: "100/120 MB, 10 secs left" 624 if (time_remaining_known) { 625 return l10n_util::GetStringFUTF16( 626 IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio, 627 ui::TimeFormat::TimeRemaining(time_remaining)); 628 } 629 630 // In progress download with no known time left and non-zero completed bytes: 631 // "100/120 MB" or "100 MB" 632 if (GetCompletedBytes() > 0) 633 return size_ratio; 634 635 // Instead of displaying "0 B" we say "Starting..." 636 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING); 637} 638 639void DownloadItemModel::OpenUsingPlatformHandler() { 640 DownloadService* download_service = 641 DownloadServiceFactory::GetForBrowserContext( 642 download_->GetBrowserContext()); 643 if (!download_service) 644 return; 645 646 ChromeDownloadManagerDelegate* delegate = 647 download_service->GetDownloadManagerDelegate(); 648 if (!delegate) 649 return; 650 delegate->OpenDownloadUsingPlatformHandler(download_); 651 RecordDownloadOpenMethod(DOWNLOAD_OPEN_METHOD_USER_PLATFORM); 652} 653