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
28using base::TimeDelta;
29using content::DownloadItem;
30
31namespace {
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.
36class 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
72const char DownloadItemModelData::kKey[] = "DownloadItemModelData key";
73
74// static
75const DownloadItemModelData* DownloadItemModelData::Get(
76    const DownloadItem* download) {
77  return static_cast<const DownloadItemModelData*>(download->GetUserData(kKey));
78}
79
80// static
81DownloadItemModelData* 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
92DownloadItemModelData::DownloadItemModelData()
93    : should_show_in_shelf_(true),
94      should_notify_ui_(false) {
95}
96
97string16 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
163string16 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
237DownloadItemModel::DownloadItemModel(DownloadItem* download)
238    : download_(download) {
239}
240
241DownloadItemModel::~DownloadItemModel() {
242}
243
244string16 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
253string16 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
288string16 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
302string16 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
356string16 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
368int64 DownloadItemModel::GetCompletedBytes() const {
369  return download_->GetReceivedBytes();
370}
371
372int64 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.
380int DownloadItemModel::PercentComplete() const {
381  return download_->PercentComplete();
382}
383
384bool DownloadItemModel::IsDangerous() const {
385  return download_->IsDangerous();
386}
387
388bool 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
413bool DownloadItemModel::ShouldAllowDownloadFeedback() const {
414  if (!IsDangerous())
415    return false;
416  return safe_browsing::DownloadFeedbackService::IsEnabledForDownload(
417      *download_);
418}
419
420bool 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
456bool DownloadItemModel::ShouldShowDownloadStartedAnimation() const {
457  return !download_->IsSavePackageDownload() &&
458      !download_crx_util::IsExtensionDownload(*download_);
459}
460
461bool DownloadItemModel::ShouldShowInShelf() const {
462  const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
463  return !data || data->should_show_in_shelf();
464}
465
466void DownloadItemModel::SetShouldShowInShelf(bool should_show) {
467  DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
468  data->set_should_show_in_shelf(should_show);
469}
470
471bool DownloadItemModel::ShouldNotifyUI() const {
472  const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
473  return data && data->should_notify_ui();
474}
475
476void DownloadItemModel::SetShouldNotifyUI(bool should_notify) {
477  DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
478  data->set_should_notify_ui(should_notify);
479}
480
481string16 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
504string16 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