Home | History | Annotate | Download | only in download
      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 
     31 using base::TimeDelta;
     32 using content::DownloadItem;
     33 
     34 namespace {
     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.
     39 class 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
     86 const char DownloadItemModelData::kKey[] = "DownloadItemModelData key";
     87 
     88 // static
     89 const DownloadItemModelData* DownloadItemModelData::Get(
     90     const DownloadItem* download) {
     91   return static_cast<const DownloadItemModelData*>(download->GetUserData(kKey));
     92 }
     93 
     94 // static
     95 DownloadItemModelData* 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 
    106 DownloadItemModelData::DownloadItemModelData()
    107     : should_show_in_shelf_(true),
    108       should_notify_ui_(false),
    109       should_prefer_opening_in_browser_(false) {
    110 }
    111 
    112 base::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 
    178 base::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 
    252 DownloadItemModel::DownloadItemModel(DownloadItem* download)
    253     : download_(download) {}
    254 
    255 DownloadItemModel::~DownloadItemModel() {}
    256 
    257 base::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 
    266 base::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 
    301 base::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 
    341 base::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 
    355 base::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 
    399 base::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 
    411 int64 DownloadItemModel::GetCompletedBytes() const {
    412   return download_->GetReceivedBytes();
    413 }
    414 
    415 int64 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.
    423 int DownloadItemModel::PercentComplete() const {
    424   return download_->PercentComplete();
    425 }
    426 
    427 bool DownloadItemModel::IsDangerous() const {
    428   return download_->IsDangerous();
    429 }
    430 
    431 bool 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.
    458 bool 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 
    483 bool 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 
    494 bool 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 
    530 bool DownloadItemModel::ShouldShowDownloadStartedAnimation() const {
    531   return !download_->IsSavePackageDownload() &&
    532       !download_crx_util::IsExtensionDownload(*download_);
    533 }
    534 
    535 bool DownloadItemModel::ShouldShowInShelf() const {
    536   const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
    537   return !data || data->should_show_in_shelf();
    538 }
    539 
    540 void DownloadItemModel::SetShouldShowInShelf(bool should_show) {
    541   DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
    542   data->set_should_show_in_shelf(should_show);
    543 }
    544 
    545 bool DownloadItemModel::ShouldNotifyUI() const {
    546   const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
    547   return data && data->should_notify_ui();
    548 }
    549 
    550 void DownloadItemModel::SetShouldNotifyUI(bool should_notify) {
    551   DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
    552   data->set_should_notify_ui(should_notify);
    553 }
    554 
    555 bool DownloadItemModel::ShouldPreferOpeningInBrowser() const {
    556   const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
    557   return data && data->should_prefer_opening_in_browser();
    558 }
    559 
    560 void DownloadItemModel::SetShouldPreferOpeningInBrowser(bool preference) {
    561   DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
    562   data->set_should_prefer_opening_in_browser(preference);
    563 }
    564 
    565 base::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 
    588 base::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 
    639 void 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