7536a2a728161551bc5a9229c3482879fafd709e
[platform/framework/web/crosswalk.git] / src / chrome / browser / download / download_item_model.cc
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_SERVER_UNAUTHORIZED:
173       string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_UNAUTHORIZED;
174       break;
175     case content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM:
176       string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_CERT_PROBLEM;
177       break;
178     case content::DOWNLOAD_INTERRUPT_REASON_NONE:
179       NOTREACHED();
180       // fallthrough
181     case content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE:
182     case content::DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION:
183     case content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED:
184       string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS;
185   }
186
187   return l10n_util::GetStringUTF16(string_id);
188 }
189
190 base::string16 InterruptReasonMessage(int reason) {
191   int string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS;
192   base::string16 status_text;
193
194   switch (static_cast<content::DownloadInterruptReason>(reason)) {
195     case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED:
196       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_ACCESS_DENIED;
197       break;
198     case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE:
199       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_DISK_FULL;
200       break;
201     case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG:
202       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_PATH_TOO_LONG;
203       break;
204     case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE:
205       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_LARGE;
206       break;
207     case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED:
208       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_VIRUS;
209       break;
210     case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR:
211       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_TEMPORARY_PROBLEM;
212       break;
213     case content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED:
214       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_BLOCKED;
215       break;
216     case content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED:
217       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SECURITY_CHECK_FAILED;
218       break;
219     case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT:
220       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_SHORT;
221       break;
222     case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST:
223     case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED:
224       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_ERROR;
225       break;
226     case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT:
227       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_TIMEOUT;
228       break;
229     case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED:
230       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_DISCONNECTED;
231       break;
232     case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN:
233       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_DOWN;
234       break;
235     case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED:
236       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_PROBLEM;
237       break;
238     case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT:
239       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NO_FILE;
240       break;
241     case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED:
242       string_id = IDS_DOWNLOAD_STATUS_CANCELLED;
243       break;
244     case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN:
245       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SHUTDOWN;
246       break;
247     case content::DOWNLOAD_INTERRUPT_REASON_CRASH:
248       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_CRASH;
249       break;
250     case content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED:
251       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_UNAUTHORIZED;
252       break;
253     case content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM:
254       string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_CERT_PROBLEM;
255       break;
256     case content::DOWNLOAD_INTERRUPT_REASON_NONE:
257       NOTREACHED();
258       // fallthrough
259     case content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE:
260     case content::DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION:
261     case content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED:
262       string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS;
263   }
264
265   status_text = l10n_util::GetStringUTF16(string_id);
266
267   return status_text;
268 }
269
270 } // namespace
271
272 // -----------------------------------------------------------------------------
273 // DownloadItemModel
274
275 DownloadItemModel::DownloadItemModel(DownloadItem* download)
276     : download_(download) {}
277
278 DownloadItemModel::~DownloadItemModel() {}
279
280 base::string16 DownloadItemModel::GetInterruptReasonText() const {
281   if (download_->GetState() != DownloadItem::INTERRUPTED ||
282       download_->GetLastReason() ==
283       content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) {
284     return base::string16();
285   }
286   return InterruptReasonMessage(download_->GetLastReason());
287 }
288
289 base::string16 DownloadItemModel::GetStatusText() const {
290   base::string16 status_text;
291   switch (download_->GetState()) {
292     case DownloadItem::IN_PROGRESS:
293       status_text = GetInProgressStatusString();
294       break;
295     case DownloadItem::COMPLETE:
296       if (download_->GetFileExternallyRemoved()) {
297         status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_REMOVED);
298       } else {
299         status_text.clear();
300       }
301       break;
302     case DownloadItem::CANCELLED:
303       status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED);
304       break;
305     case DownloadItem::INTERRUPTED: {
306       content::DownloadInterruptReason reason = download_->GetLastReason();
307       if (reason != content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) {
308         base::string16 interrupt_reason = InterruptReasonStatusMessage(reason);
309         status_text = l10n_util::GetStringFUTF16(
310             IDS_DOWNLOAD_STATUS_INTERRUPTED, interrupt_reason);
311       } else {
312         // Same as DownloadItem::CANCELLED.
313         status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED);
314       }
315       break;
316     }
317     default:
318       NOTREACHED();
319   }
320
321   return status_text;
322 }
323
324 base::string16 DownloadItemModel::GetTabProgressStatusText() const {
325   int64 total = GetTotalBytes();
326   int64 size = download_->GetReceivedBytes();
327   base::string16 received_size = ui::FormatBytes(size);
328   base::string16 amount = received_size;
329
330   // Adjust both strings for the locale direction since we don't yet know which
331   // string we'll end up using for constructing the final progress string.
332   base::i18n::AdjustStringForLocaleDirection(&amount);
333
334   if (total) {
335     base::string16 total_text = ui::FormatBytes(total);
336     base::i18n::AdjustStringForLocaleDirection(&total_text);
337
338     base::i18n::AdjustStringForLocaleDirection(&received_size);
339     amount = l10n_util::GetStringFUTF16(
340         IDS_DOWNLOAD_TAB_PROGRESS_SIZE, received_size, total_text);
341   } else {
342     amount.assign(received_size);
343   }
344   int64 current_speed = download_->CurrentSpeed();
345   base::string16 speed_text = ui::FormatSpeed(current_speed);
346   base::i18n::AdjustStringForLocaleDirection(&speed_text);
347
348   base::TimeDelta remaining;
349   base::string16 time_remaining;
350   if (download_->IsPaused()) {
351     time_remaining = l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED);
352   } else if (download_->TimeRemaining(&remaining)) {
353     time_remaining = ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
354                                             ui::TimeFormat::LENGTH_SHORT,
355                                             remaining);
356   }
357
358   if (time_remaining.empty()) {
359     base::i18n::AdjustStringForLocaleDirection(&amount);
360     return l10n_util::GetStringFUTF16(
361         IDS_DOWNLOAD_TAB_PROGRESS_STATUS_TIME_UNKNOWN, speed_text, amount);
362   }
363   return l10n_util::GetStringFUTF16(
364       IDS_DOWNLOAD_TAB_PROGRESS_STATUS, speed_text, amount, time_remaining);
365 }
366
367 base::string16 DownloadItemModel::GetTooltipText(const gfx::FontList& font_list,
368                                                  int max_width) const {
369   base::string16 tooltip = gfx::ElideFilename(
370       download_->GetFileNameToReportUser(), font_list, max_width);
371   content::DownloadInterruptReason reason = download_->GetLastReason();
372   if (download_->GetState() == DownloadItem::INTERRUPTED &&
373       reason != content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) {
374     tooltip += base::ASCIIToUTF16("\n");
375     tooltip += gfx::ElideText(InterruptReasonStatusMessage(reason),
376                              font_list, max_width, gfx::ELIDE_TAIL);
377   }
378   return tooltip;
379 }
380
381 base::string16 DownloadItemModel::GetWarningText(const gfx::FontList& font_list,
382                                                  int base_width) const {
383   // Should only be called if IsDangerous().
384   DCHECK(IsDangerous());
385   base::string16 elided_filename =
386       gfx::ElideFilename(download_->GetFileNameToReportUser(), font_list,
387                         base_width);
388   switch (download_->GetDangerType()) {
389     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: {
390       return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL);
391     }
392     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: {
393       if (download_crx_util::IsExtensionDownload(*download_)) {
394         return l10n_util::GetStringUTF16(
395             IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION);
396       } else {
397         return l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD,
398                                           elided_filename);
399       }
400     }
401     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
402     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
403       return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT,
404                                         elided_filename);
405     }
406     case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
407       return l10n_util::GetStringFUTF16(IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT,
408                                         elided_filename);
409     }
410     case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: {
411       return l10n_util::GetStringFUTF16(
412           IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS, elided_filename);
413     }
414     case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
415     case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
416     case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
417     case content::DOWNLOAD_DANGER_TYPE_MAX: {
418       break;
419     }
420   }
421   NOTREACHED();
422   return base::string16();
423 }
424
425 base::string16 DownloadItemModel::GetWarningConfirmButtonText() const {
426   // Should only be called if IsDangerous()
427   DCHECK(IsDangerous());
428   if (download_->GetDangerType() ==
429           content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE &&
430       download_crx_util::IsExtensionDownload(*download_)) {
431     return l10n_util::GetStringUTF16(IDS_CONTINUE_EXTENSION_DOWNLOAD);
432   } else {
433     return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD);
434   }
435 }
436
437 int64 DownloadItemModel::GetCompletedBytes() const {
438   return download_->GetReceivedBytes();
439 }
440
441 int64 DownloadItemModel::GetTotalBytes() const {
442   return download_->AllDataSaved() ? download_->GetReceivedBytes() :
443                                      download_->GetTotalBytes();
444 }
445
446 // TODO(asanka,rdsmith): Once 'open' moves exclusively to the
447 //     ChromeDownloadManagerDelegate, we should calculate the percentage here
448 //     instead of calling into the DownloadItem.
449 int DownloadItemModel::PercentComplete() const {
450   return download_->PercentComplete();
451 }
452
453 bool DownloadItemModel::IsDangerous() const {
454   return download_->IsDangerous();
455 }
456
457 bool DownloadItemModel::MightBeMalicious() const {
458   if (!IsDangerous())
459     return false;
460   switch (download_->GetDangerType()) {
461     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
462     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
463     case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
464     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
465     case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
466       return true;
467
468     case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
469     case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
470     case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
471     case content::DOWNLOAD_DANGER_TYPE_MAX:
472       // We shouldn't get any of these due to the IsDangerous() test above.
473       NOTREACHED();
474       // Fallthrough.
475     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
476       return false;
477   }
478   NOTREACHED();
479   return false;
480 }
481
482 // If you change this definition of malicious, also update
483 // DownloadManagerImpl::NonMaliciousInProgressCount.
484 bool DownloadItemModel::IsMalicious() const {
485   if (!MightBeMalicious())
486     return false;
487   switch (download_->GetDangerType()) {
488     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
489     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
490     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
491     case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
492       return true;
493
494     case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
495     case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
496     case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
497     case content::DOWNLOAD_DANGER_TYPE_MAX:
498     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
499       // We shouldn't get any of these due to the MightBeMalicious() test above.
500       NOTREACHED();
501       // Fallthrough.
502     case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
503       return false;
504   }
505   NOTREACHED();
506   return false;
507 }
508
509 bool DownloadItemModel::ShouldAllowDownloadFeedback() const {
510 #if defined(FULL_SAFE_BROWSING)
511   if (!IsDangerous())
512     return false;
513   return safe_browsing::DownloadFeedbackService::IsEnabledForDownload(
514       *download_);
515 #else
516   return false;
517 #endif
518 }
519
520 bool DownloadItemModel::ShouldRemoveFromShelfWhenComplete() const {
521   switch (download_->GetState()) {
522     case DownloadItem::IN_PROGRESS:
523       // If the download is dangerous or malicious, we should display a warning
524       // on the shelf until the user accepts the download.
525       if (IsDangerous())
526         return false;
527
528       // If the download is an extension, temporary, or will be opened
529       // automatically, then it should be removed from the shelf on completion.
530       // TODO(asanka): The logic for deciding opening behavior should be in a
531       //               central location. http://crbug.com/167702
532       return (download_crx_util::IsExtensionDownload(*download_) ||
533               download_->IsTemporary() ||
534               download_->GetOpenWhenComplete() ||
535               download_->ShouldOpenFileBasedOnExtension());
536
537     case DownloadItem::COMPLETE:
538       // If the download completed, then rely on GetAutoOpened() to check for
539       // opening behavior. This should accurately reflect whether the download
540       // was successfully opened.  Extensions, for example, may fail to open.
541       return download_->GetAutoOpened() || download_->IsTemporary();
542
543     case DownloadItem::CANCELLED:
544     case DownloadItem::INTERRUPTED:
545       // Interrupted or cancelled downloads should remain on the shelf.
546       return false;
547
548     case DownloadItem::MAX_DOWNLOAD_STATE:
549       NOTREACHED();
550   }
551
552   NOTREACHED();
553   return false;
554 }
555
556 bool DownloadItemModel::ShouldShowDownloadStartedAnimation() const {
557   return !download_->IsSavePackageDownload() &&
558       !download_crx_util::IsExtensionDownload(*download_);
559 }
560
561 bool DownloadItemModel::ShouldShowInShelf() const {
562   const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
563   return !data || data->should_show_in_shelf();
564 }
565
566 void DownloadItemModel::SetShouldShowInShelf(bool should_show) {
567   DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
568   data->set_should_show_in_shelf(should_show);
569 }
570
571 bool DownloadItemModel::ShouldNotifyUI() const {
572   Profile* profile =
573       Profile::FromBrowserContext(download_->GetBrowserContext());
574   DownloadService* download_service =
575       DownloadServiceFactory::GetForBrowserContext(profile);
576   DownloadHistory* download_history =
577       (download_service ? download_service->GetDownloadHistory() : NULL);
578
579   // The browser is only interested in new downloads. Ones that were restored
580   // from history are not displayed on the shelf. The downloads page
581   // independently listens for new downloads when it is active. Note that the UI
582   // will be notified of downloads even if they are not meant to be displayed on
583   // the shelf (i.e. ShouldShowInShelf() returns false). This is because:
584   // *  The shelf isn't the only UI. E.g. on Android, the UI is the system
585   //    DownloadManager.
586   // *  There are other UI activities that need to be performed. E.g. if the
587   //    download was initiated from a new tab, then that tab should be closed.
588   //
589   // TODO(asanka): If an interrupted download is restored from history and is
590   // resumed, then ideally the UI should be notified.
591   return !download_history ||
592          !download_history->WasRestoredFromHistory(download_);
593 }
594
595 bool DownloadItemModel::WasUINotified() const {
596   const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
597   return data && data->was_ui_notified();
598 }
599
600 void DownloadItemModel::SetWasUINotified(bool was_ui_notified) {
601   DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
602   data->set_was_ui_notified(was_ui_notified);
603 }
604
605 bool DownloadItemModel::ShouldPreferOpeningInBrowser() const {
606   const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
607   return data && data->should_prefer_opening_in_browser();
608 }
609
610 void DownloadItemModel::SetShouldPreferOpeningInBrowser(bool preference) {
611   DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
612   data->set_should_prefer_opening_in_browser(preference);
613 }
614
615 base::string16 DownloadItemModel::GetProgressSizesString() const {
616   base::string16 size_ratio;
617   int64 size = GetCompletedBytes();
618   int64 total = GetTotalBytes();
619   if (total > 0) {
620     ui::DataUnits amount_units = ui::GetByteDisplayUnits(total);
621     base::string16 simple_size = ui::FormatBytesWithUnits(size, amount_units, false);
622
623     // In RTL locales, we render the text "size/total" in an RTL context. This
624     // is problematic since a string such as "123/456 MB" is displayed
625     // as "MB 123/456" because it ends with an LTR run. In order to solve this,
626     // we mark the total string as an LTR string if the UI layout is
627     // right-to-left so that the string "456 MB" is treated as an LTR run.
628     base::string16 simple_total = base::i18n::GetDisplayStringInLTRDirectionality(
629         ui::FormatBytesWithUnits(total, amount_units, true));
630     size_ratio = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_SIZES,
631                                             simple_size, simple_total);
632   } else {
633     size_ratio = ui::FormatBytes(size);
634   }
635   return size_ratio;
636 }
637
638 base::string16 DownloadItemModel::GetInProgressStatusString() const {
639   DCHECK_EQ(DownloadItem::IN_PROGRESS, download_->GetState());
640
641   TimeDelta time_remaining;
642   // time_remaining is only known if the download isn't paused.
643   bool time_remaining_known = (!download_->IsPaused() &&
644                                download_->TimeRemaining(&time_remaining));
645
646   // Indication of progress. (E.g.:"100/200 MB" or "100MB")
647   base::string16 size_ratio = GetProgressSizesString();
648
649   // The download is a CRX (app, extension, theme, ...) and it is being unpacked
650   // and validated.
651   if (download_->AllDataSaved() &&
652       download_crx_util::IsExtensionDownload(*download_)) {
653     return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING);
654   }
655
656   // A paused download: "100/120 MB, Paused"
657   if (download_->IsPaused()) {
658     return l10n_util::GetStringFUTF16(
659         IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio,
660         l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED));
661   }
662
663   // A download scheduled to be opened when complete: "Opening in 10 secs"
664   if (download_->GetOpenWhenComplete()) {
665     if (!time_remaining_known)
666       return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE);
667
668     return l10n_util::GetStringFUTF16(
669         IDS_DOWNLOAD_STATUS_OPEN_IN,
670         ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
671                                ui::TimeFormat::LENGTH_SHORT, time_remaining));
672   }
673
674   // In progress download with known time left: "100/120 MB, 10 secs left"
675   if (time_remaining_known) {
676     return l10n_util::GetStringFUTF16(
677         IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio,
678         ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
679                                ui::TimeFormat::LENGTH_SHORT, time_remaining));
680   }
681
682   // In progress download with no known time left and non-zero completed bytes:
683   // "100/120 MB" or "100 MB"
684   if (GetCompletedBytes() > 0)
685     return size_ratio;
686
687   // Instead of displaying "0 B" we say "Starting..."
688   return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING);
689 }
690
691 void DownloadItemModel::OpenUsingPlatformHandler() {
692   DownloadService* download_service =
693       DownloadServiceFactory::GetForBrowserContext(
694           download_->GetBrowserContext());
695   if (!download_service)
696     return;
697
698   ChromeDownloadManagerDelegate* delegate =
699       download_service->GetDownloadManagerDelegate();
700   if (!delegate)
701     return;
702   delegate->OpenDownloadUsingPlatformHandler(download_);
703   RecordDownloadOpenMethod(DOWNLOAD_OPEN_METHOD_USER_PLATFORM);
704 }