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 "chrome/grit/chromium_strings.h"
24#include "chrome/grit/generated_resources.h"
25#include "content/public/browser/download_danger_type.h"
26#include "content/public/browser/download_interrupt_reasons.h"
27#include "content/public/browser/download_item.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
33using base::TimeDelta;
34using content::DownloadItem;
35
36namespace {
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.
41class 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  // Whether the download should be displayed in the download shelf. True by
52  // default.
53  bool should_show_in_shelf_;
54
55  // Whether the UI has been notified about this download.
56  bool was_ui_notified_;
57
58  // Whether the download should be opened in the browser vs. the system handler
59  // for the file type.
60  bool should_prefer_opening_in_browser_;
61
62  // Whether the download should be considered dangerous if SafeBrowsing doesn't
63  // come up with a verdict.
64  bool is_dangerous_file_based_on_type_;
65
66 private:
67  DownloadItemModelData();
68  virtual ~DownloadItemModelData() {}
69
70  static const char kKey[];
71};
72
73// static
74const char DownloadItemModelData::kKey[] = "DownloadItemModelData key";
75
76// static
77const DownloadItemModelData* DownloadItemModelData::Get(
78    const DownloadItem* download) {
79  return static_cast<const DownloadItemModelData*>(download->GetUserData(kKey));
80}
81
82// static
83DownloadItemModelData* DownloadItemModelData::GetOrCreate(
84    DownloadItem* download) {
85  DownloadItemModelData* data =
86      static_cast<DownloadItemModelData*>(download->GetUserData(kKey));
87  if (data == NULL) {
88    data = new DownloadItemModelData();
89    download->SetUserData(kKey, data);
90  }
91  return data;
92}
93
94DownloadItemModelData::DownloadItemModelData()
95    : should_show_in_shelf_(true),
96      was_ui_notified_(false),
97      should_prefer_opening_in_browser_(false),
98      is_dangerous_file_based_on_type_(false) {
99}
100
101base::string16 InterruptReasonStatusMessage(int reason) {
102  int string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS;
103
104  switch (static_cast<content::DownloadInterruptReason>(reason)) {
105    case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED:
106      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_ACCESS_DENIED;
107      break;
108    case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE:
109      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_DISK_FULL;
110      break;
111    case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG:
112      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_PATH_TOO_LONG;
113      break;
114    case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE:
115      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_FILE_TOO_LARGE;
116      break;
117    case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED:
118      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_VIRUS;
119      break;
120    case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR:
121      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_TEMPORARY_PROBLEM;
122      break;
123    case content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED:
124      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_BLOCKED;
125      break;
126    case content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED:
127      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SECURITY_CHECK_FAILED;
128      break;
129    case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT:
130      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_FILE_TOO_SHORT;
131      break;
132    case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST:
133    case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED:
134      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_ERROR;
135      break;
136    case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT:
137      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_TIMEOUT;
138      break;
139    case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED:
140      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_DISCONNECTED;
141      break;
142    case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN:
143      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_DOWN;
144      break;
145    case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED:
146      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_PROBLEM;
147      break;
148    case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT:
149      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NO_FILE;
150      break;
151    case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED:
152      string_id = IDS_DOWNLOAD_STATUS_CANCELLED;
153      break;
154    case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN:
155      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SHUTDOWN;
156      break;
157    case content::DOWNLOAD_INTERRUPT_REASON_CRASH:
158      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_CRASH;
159      break;
160    case content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED:
161      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_UNAUTHORIZED;
162      break;
163    case content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM:
164      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_CERT_PROBLEM;
165      break;
166    case content::DOWNLOAD_INTERRUPT_REASON_NONE:
167      NOTREACHED();
168      // fallthrough
169    case content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE:
170    case content::DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION:
171    case content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED:
172      string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS;
173  }
174
175  return l10n_util::GetStringUTF16(string_id);
176}
177
178base::string16 InterruptReasonMessage(int reason) {
179  int string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS;
180  base::string16 status_text;
181
182  switch (static_cast<content::DownloadInterruptReason>(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_INVALID_REQUEST:
211    case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED:
212      string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_ERROR;
213      break;
214    case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT:
215      string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_TIMEOUT;
216      break;
217    case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED:
218      string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_DISCONNECTED;
219      break;
220    case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN:
221      string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_DOWN;
222      break;
223    case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED:
224      string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_PROBLEM;
225      break;
226    case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT:
227      string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NO_FILE;
228      break;
229    case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED:
230      string_id = IDS_DOWNLOAD_STATUS_CANCELLED;
231      break;
232    case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN:
233      string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SHUTDOWN;
234      break;
235    case content::DOWNLOAD_INTERRUPT_REASON_CRASH:
236      string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_CRASH;
237      break;
238    case content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED:
239      string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_UNAUTHORIZED;
240      break;
241    case content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM:
242      string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_CERT_PROBLEM;
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
263DownloadItemModel::DownloadItemModel(DownloadItem* download)
264    : download_(download) {}
265
266DownloadItemModel::~DownloadItemModel() {}
267
268base::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
277base::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
312base::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
355base::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
369base::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
413base::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
425int64 DownloadItemModel::GetCompletedBytes() const {
426  return download_->GetReceivedBytes();
427}
428
429int64 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.
437int DownloadItemModel::PercentComplete() const {
438  return download_->PercentComplete();
439}
440
441bool DownloadItemModel::IsDangerous() const {
442  return download_->IsDangerous();
443}
444
445bool 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.
472bool 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
497bool 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
508bool 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
544bool DownloadItemModel::ShouldShowDownloadStartedAnimation() const {
545  return !download_->IsSavePackageDownload() &&
546      !download_crx_util::IsExtensionDownload(*download_);
547}
548
549bool DownloadItemModel::ShouldShowInShelf() const {
550  const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
551  return !data || data->should_show_in_shelf_;
552}
553
554void DownloadItemModel::SetShouldShowInShelf(bool should_show) {
555  DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
556  data->should_show_in_shelf_ = should_show;
557}
558
559bool 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
583bool DownloadItemModel::WasUINotified() const {
584  const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
585  return data && data->was_ui_notified_;
586}
587
588void DownloadItemModel::SetWasUINotified(bool was_ui_notified) {
589  DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
590  data->was_ui_notified_ = was_ui_notified;
591}
592
593bool DownloadItemModel::ShouldPreferOpeningInBrowser() const {
594  const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
595  return data && data->should_prefer_opening_in_browser_;
596}
597
598void DownloadItemModel::SetShouldPreferOpeningInBrowser(bool preference) {
599  DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
600  data->should_prefer_opening_in_browser_ = preference;
601}
602
603bool DownloadItemModel::IsDangerousFileBasedOnType() const {
604  const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
605  return data && data->is_dangerous_file_based_on_type_;
606}
607
608void DownloadItemModel::SetIsDangerousFileBasedOnType(bool dangerous) {
609  DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
610  data->is_dangerous_file_based_on_type_ = dangerous;
611}
612
613base::string16 DownloadItemModel::GetProgressSizesString() const {
614  base::string16 size_ratio;
615  int64 size = GetCompletedBytes();
616  int64 total = GetTotalBytes();
617  if (total > 0) {
618    ui::DataUnits amount_units = ui::GetByteDisplayUnits(total);
619    base::string16 simple_size = ui::FormatBytesWithUnits(size, amount_units, false);
620
621    // In RTL locales, we render the text "size/total" in an RTL context. This
622    // is problematic since a string such as "123/456 MB" is displayed
623    // as "MB 123/456" because it ends with an LTR run. In order to solve this,
624    // we mark the total string as an LTR string if the UI layout is
625    // right-to-left so that the string "456 MB" is treated as an LTR run.
626    base::string16 simple_total = base::i18n::GetDisplayStringInLTRDirectionality(
627        ui::FormatBytesWithUnits(total, amount_units, true));
628    size_ratio = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_SIZES,
629                                            simple_size, simple_total);
630  } else {
631    size_ratio = ui::FormatBytes(size);
632  }
633  return size_ratio;
634}
635
636base::string16 DownloadItemModel::GetInProgressStatusString() const {
637  DCHECK_EQ(DownloadItem::IN_PROGRESS, download_->GetState());
638
639  TimeDelta time_remaining;
640  // time_remaining is only known if the download isn't paused.
641  bool time_remaining_known = (!download_->IsPaused() &&
642                               download_->TimeRemaining(&time_remaining));
643
644  // Indication of progress. (E.g.:"100/200 MB" or "100MB")
645  base::string16 size_ratio = GetProgressSizesString();
646
647  // The download is a CRX (app, extension, theme, ...) and it is being unpacked
648  // and validated.
649  if (download_->AllDataSaved() &&
650      download_crx_util::IsExtensionDownload(*download_)) {
651    return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING);
652  }
653
654  // A paused download: "100/120 MB, Paused"
655  if (download_->IsPaused()) {
656    return l10n_util::GetStringFUTF16(
657        IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio,
658        l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED));
659  }
660
661  // A download scheduled to be opened when complete: "Opening in 10 secs"
662  if (download_->GetOpenWhenComplete()) {
663    if (!time_remaining_known)
664      return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE);
665
666    return l10n_util::GetStringFUTF16(
667        IDS_DOWNLOAD_STATUS_OPEN_IN,
668        ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
669                               ui::TimeFormat::LENGTH_SHORT, time_remaining));
670  }
671
672  // In progress download with known time left: "100/120 MB, 10 secs left"
673  if (time_remaining_known) {
674    return l10n_util::GetStringFUTF16(
675        IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio,
676        ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
677                               ui::TimeFormat::LENGTH_SHORT, time_remaining));
678  }
679
680  // In progress download with no known time left and non-zero completed bytes:
681  // "100/120 MB" or "100 MB"
682  if (GetCompletedBytes() > 0)
683    return size_ratio;
684
685  // Instead of displaying "0 B" we say "Starting..."
686  return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING);
687}
688
689void DownloadItemModel::OpenUsingPlatformHandler() {
690  DownloadService* download_service =
691      DownloadServiceFactory::GetForBrowserContext(
692          download_->GetBrowserContext());
693  if (!download_service)
694    return;
695
696  ChromeDownloadManagerDelegate* delegate =
697      download_service->GetDownloadManagerDelegate();
698  if (!delegate)
699    return;
700  delegate->OpenDownloadUsingPlatformHandler(download_);
701  RecordDownloadOpenMethod(DOWNLOAD_OPEN_METHOD_USER_PLATFORM);
702}
703