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