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