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.
5 #include "chrome/browser/download/download_item_model.h"
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"
33 using base::TimeDelta
;
34 using content::DownloadItem
;
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
{
43 // Get the DownloadItemModelData object for |download|. Returns NULL if
44 // there's no model data.
45 static const DownloadItemModelData
* Get(const DownloadItem
* download
);
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
);
51 // Whether the download should be displayed in the download shelf. True by
53 bool should_show_in_shelf_
;
55 // Whether the UI has been notified about this download.
56 bool was_ui_notified_
;
58 // Whether the download should be opened in the browser vs. the system handler
60 bool should_prefer_opening_in_browser_
;
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_
;
66 // Whether the download is currently being revived.
67 bool is_being_revived_
;
70 DownloadItemModelData();
71 ~DownloadItemModelData() override
{}
73 static const char kKey
[];
77 const char DownloadItemModelData::kKey
[] = "DownloadItemModelData key";
80 const DownloadItemModelData
* DownloadItemModelData::Get(
81 const DownloadItem
* download
) {
82 return static_cast<const DownloadItemModelData
*>(download
->GetUserData(kKey
));
86 DownloadItemModelData
* DownloadItemModelData::GetOrCreate(
87 DownloadItem
* download
) {
88 DownloadItemModelData
* data
=
89 static_cast<DownloadItemModelData
*>(download
->GetUserData(kKey
));
91 data
= new DownloadItemModelData();
92 download
->SetUserData(kKey
, data
);
97 DownloadItemModelData::DownloadItemModelData()
98 : should_show_in_shelf_(true),
99 was_ui_notified_(false),
100 should_prefer_opening_in_browser_(false),
101 is_dangerous_file_based_on_type_(false),
102 is_being_revived_(false) {
105 base::string16
InterruptReasonStatusMessage(int reason
) {
106 int string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS
;
108 switch (static_cast<content::DownloadInterruptReason
>(reason
)) {
109 case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
:
110 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_ACCESS_DENIED
;
112 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE
:
113 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_DISK_FULL
;
115 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG
:
116 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_PATH_TOO_LONG
;
118 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE
:
119 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_FILE_TOO_LARGE
;
121 case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED
:
122 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_VIRUS
;
124 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR
:
125 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_TEMPORARY_PROBLEM
;
127 case content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED
:
128 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_BLOCKED
;
130 case content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED
:
131 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_SECURITY_CHECK_FAILED
;
133 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT
:
134 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_FILE_TOO_SHORT
;
136 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST
:
137 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED
:
138 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_ERROR
;
140 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT
:
141 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_TIMEOUT
;
143 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
:
144 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_DISCONNECTED
;
146 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN
:
147 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_DOWN
;
149 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED
:
150 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_PROBLEM
;
152 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT
:
153 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_NO_FILE
;
155 case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
:
156 string_id
= IDS_DOWNLOAD_STATUS_CANCELLED
;
158 case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN
:
159 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_SHUTDOWN
;
161 case content::DOWNLOAD_INTERRUPT_REASON_CRASH
:
162 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_CRASH
;
164 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED
:
165 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_UNAUTHORIZED
;
167 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM
:
168 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_CERT_PROBLEM
;
170 case content::DOWNLOAD_INTERRUPT_REASON_NONE
:
173 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE
:
174 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION
:
175 case content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED
:
176 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS
;
179 return l10n_util::GetStringUTF16(string_id
);
182 base::string16
InterruptReasonMessage(int reason
) {
183 int string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS
;
184 base::string16 status_text
;
186 switch (static_cast<content::DownloadInterruptReason
>(reason
)) {
187 case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
:
188 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_ACCESS_DENIED
;
190 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE
:
191 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_DISK_FULL
;
193 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG
:
194 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_PATH_TOO_LONG
;
196 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE
:
197 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_LARGE
;
199 case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED
:
200 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_VIRUS
;
202 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR
:
203 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_TEMPORARY_PROBLEM
;
205 case content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED
:
206 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_BLOCKED
;
208 case content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED
:
209 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SECURITY_CHECK_FAILED
;
211 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT
:
212 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_SHORT
;
214 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST
:
215 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED
:
216 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_ERROR
;
218 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT
:
219 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_TIMEOUT
;
221 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
:
222 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_DISCONNECTED
;
224 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN
:
225 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_DOWN
;
227 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED
:
228 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_PROBLEM
;
230 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT
:
231 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NO_FILE
;
233 case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
:
234 string_id
= IDS_DOWNLOAD_STATUS_CANCELLED
;
236 case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN
:
237 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SHUTDOWN
;
239 case content::DOWNLOAD_INTERRUPT_REASON_CRASH
:
240 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_CRASH
;
242 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED
:
243 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_UNAUTHORIZED
;
245 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM
:
246 string_id
= IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_CERT_PROBLEM
;
248 case content::DOWNLOAD_INTERRUPT_REASON_NONE
:
251 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE
:
252 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION
:
253 case content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED
:
254 string_id
= IDS_DOWNLOAD_INTERRUPTED_STATUS
;
257 status_text
= l10n_util::GetStringUTF16(string_id
);
264 // -----------------------------------------------------------------------------
267 DownloadItemModel::DownloadItemModel(DownloadItem
* download
)
268 : download_(download
) {}
270 DownloadItemModel::~DownloadItemModel() {}
272 base::string16
DownloadItemModel::GetInterruptReasonText() const {
273 if (download_
->GetState() != DownloadItem::INTERRUPTED
||
274 download_
->GetLastReason() ==
275 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
) {
276 return base::string16();
278 return InterruptReasonMessage(download_
->GetLastReason());
281 base::string16
DownloadItemModel::GetStatusText() const {
282 base::string16 status_text
;
283 switch (download_
->GetState()) {
284 case DownloadItem::IN_PROGRESS
:
285 status_text
= GetInProgressStatusString();
287 case DownloadItem::COMPLETE
:
288 if (download_
->GetFileExternallyRemoved()) {
289 status_text
= l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_REMOVED
);
294 case DownloadItem::CANCELLED
:
295 status_text
= l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED
);
297 case DownloadItem::INTERRUPTED
: {
298 content::DownloadInterruptReason reason
= download_
->GetLastReason();
299 if (reason
!= content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
) {
300 base::string16 interrupt_reason
= InterruptReasonStatusMessage(reason
);
301 status_text
= l10n_util::GetStringFUTF16(
302 IDS_DOWNLOAD_STATUS_INTERRUPTED
, interrupt_reason
);
304 // Same as DownloadItem::CANCELLED.
305 status_text
= l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED
);
316 base::string16
DownloadItemModel::GetTabProgressStatusText() const {
317 int64 total
= GetTotalBytes();
318 int64 size
= download_
->GetReceivedBytes();
319 base::string16 received_size
= ui::FormatBytes(size
);
320 base::string16 amount
= received_size
;
322 // Adjust both strings for the locale direction since we don't yet know which
323 // string we'll end up using for constructing the final progress string.
324 base::i18n::AdjustStringForLocaleDirection(&amount
);
327 base::string16 total_text
= ui::FormatBytes(total
);
328 base::i18n::AdjustStringForLocaleDirection(&total_text
);
330 base::i18n::AdjustStringForLocaleDirection(&received_size
);
331 amount
= l10n_util::GetStringFUTF16(
332 IDS_DOWNLOAD_TAB_PROGRESS_SIZE
, received_size
, total_text
);
334 amount
.assign(received_size
);
336 int64 current_speed
= download_
->CurrentSpeed();
337 base::string16 speed_text
= ui::FormatSpeed(current_speed
);
338 base::i18n::AdjustStringForLocaleDirection(&speed_text
);
340 base::TimeDelta remaining
;
341 base::string16 time_remaining
;
342 if (download_
->IsPaused()) {
343 time_remaining
= l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED
);
344 } else if (download_
->TimeRemaining(&remaining
)) {
345 time_remaining
= ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING
,
346 ui::TimeFormat::LENGTH_SHORT
,
350 if (time_remaining
.empty()) {
351 base::i18n::AdjustStringForLocaleDirection(&amount
);
352 return l10n_util::GetStringFUTF16(
353 IDS_DOWNLOAD_TAB_PROGRESS_STATUS_TIME_UNKNOWN
, speed_text
, amount
);
355 return l10n_util::GetStringFUTF16(
356 IDS_DOWNLOAD_TAB_PROGRESS_STATUS
, speed_text
, amount
, time_remaining
);
359 base::string16
DownloadItemModel::GetTooltipText(const gfx::FontList
& font_list
,
360 int max_width
) const {
361 base::string16 tooltip
= gfx::ElideFilename(
362 download_
->GetFileNameToReportUser(), font_list
, max_width
);
363 content::DownloadInterruptReason reason
= download_
->GetLastReason();
364 if (download_
->GetState() == DownloadItem::INTERRUPTED
&&
365 reason
!= content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
) {
366 tooltip
+= base::ASCIIToUTF16("\n");
367 tooltip
+= gfx::ElideText(InterruptReasonStatusMessage(reason
),
368 font_list
, max_width
, gfx::ELIDE_TAIL
);
373 base::string16
DownloadItemModel::GetWarningText(const gfx::FontList
& font_list
,
374 int base_width
) const {
375 // Should only be called if IsDangerous().
376 DCHECK(IsDangerous());
377 base::string16 elided_filename
=
378 gfx::ElideFilename(download_
->GetFileNameToReportUser(), font_list
,
380 switch (download_
->GetDangerType()) {
381 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
: {
382 return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL
);
384 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE
: {
385 if (download_crx_util::IsExtensionDownload(*download_
)) {
386 return l10n_util::GetStringUTF16(
387 IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION
);
389 return l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD
,
393 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT
:
394 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST
: {
395 return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT
,
398 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
: {
399 return l10n_util::GetStringFUTF16(IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT
,
402 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED
: {
403 return l10n_util::GetStringFUTF16(
404 IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS
, elided_filename
);
406 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
:
407 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT
:
408 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED
:
409 case content::DOWNLOAD_DANGER_TYPE_MAX
: {
414 return base::string16();
417 base::string16
DownloadItemModel::GetWarningConfirmButtonText() const {
418 // Should only be called if IsDangerous()
419 DCHECK(IsDangerous());
420 if (download_
->GetDangerType() ==
421 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE
&&
422 download_crx_util::IsExtensionDownload(*download_
)) {
423 return l10n_util::GetStringUTF16(IDS_CONTINUE_EXTENSION_DOWNLOAD
);
425 return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD
);
429 int64
DownloadItemModel::GetCompletedBytes() const {
430 return download_
->GetReceivedBytes();
433 int64
DownloadItemModel::GetTotalBytes() const {
434 return download_
->AllDataSaved() ? download_
->GetReceivedBytes() :
435 download_
->GetTotalBytes();
438 // TODO(asanka,rdsmith): Once 'open' moves exclusively to the
439 // ChromeDownloadManagerDelegate, we should calculate the percentage here
440 // instead of calling into the DownloadItem.
441 int DownloadItemModel::PercentComplete() const {
442 return download_
->PercentComplete();
445 bool DownloadItemModel::IsDangerous() const {
446 return download_
->IsDangerous();
449 bool DownloadItemModel::MightBeMalicious() const {
452 switch (download_
->GetDangerType()) {
453 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
:
454 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT
:
455 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
:
456 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST
:
457 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED
:
460 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
:
461 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT
:
462 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED
:
463 case content::DOWNLOAD_DANGER_TYPE_MAX
:
464 // We shouldn't get any of these due to the IsDangerous() test above.
467 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE
:
474 // If you change this definition of malicious, also update
475 // DownloadManagerImpl::NonMaliciousInProgressCount.
476 bool DownloadItemModel::IsMalicious() const {
477 if (!MightBeMalicious())
479 switch (download_
->GetDangerType()) {
480 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
:
481 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT
:
482 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST
:
483 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED
:
486 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
:
487 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT
:
488 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED
:
489 case content::DOWNLOAD_DANGER_TYPE_MAX
:
490 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE
:
491 // We shouldn't get any of these due to the MightBeMalicious() test above.
494 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
:
501 bool DownloadItemModel::ShouldAllowDownloadFeedback() const {
502 #if defined(FULL_SAFE_BROWSING)
505 return safe_browsing::DownloadFeedbackService::IsEnabledForDownload(
512 bool DownloadItemModel::ShouldRemoveFromShelfWhenComplete() const {
513 switch (download_
->GetState()) {
514 case DownloadItem::IN_PROGRESS
:
515 // If the download is dangerous or malicious, we should display a warning
516 // on the shelf until the user accepts the download.
520 // If the download is an extension, temporary, or will be opened
521 // automatically, then it should be removed from the shelf on completion.
522 // TODO(asanka): The logic for deciding opening behavior should be in a
523 // central location. http://crbug.com/167702
524 return (download_crx_util::IsExtensionDownload(*download_
) ||
525 download_
->IsTemporary() ||
526 download_
->GetOpenWhenComplete() ||
527 download_
->ShouldOpenFileBasedOnExtension());
529 case DownloadItem::COMPLETE
:
530 // If the download completed, then rely on GetAutoOpened() to check for
531 // opening behavior. This should accurately reflect whether the download
532 // was successfully opened. Extensions, for example, may fail to open.
533 return download_
->GetAutoOpened() || download_
->IsTemporary();
535 case DownloadItem::CANCELLED
:
536 case DownloadItem::INTERRUPTED
:
537 // Interrupted or cancelled downloads should remain on the shelf.
540 case DownloadItem::MAX_DOWNLOAD_STATE
:
548 bool DownloadItemModel::ShouldShowDownloadStartedAnimation() const {
549 return !download_
->IsSavePackageDownload() &&
550 !download_crx_util::IsExtensionDownload(*download_
);
553 bool DownloadItemModel::ShouldShowInShelf() const {
554 const DownloadItemModelData
* data
= DownloadItemModelData::Get(download_
);
555 return !data
|| data
->should_show_in_shelf_
;
558 void DownloadItemModel::SetShouldShowInShelf(bool should_show
) {
559 DownloadItemModelData
* data
= DownloadItemModelData::GetOrCreate(download_
);
560 data
->should_show_in_shelf_
= should_show
;
563 bool DownloadItemModel::ShouldNotifyUI() const {
565 Profile::FromBrowserContext(download_
->GetBrowserContext());
566 DownloadService
* download_service
=
567 DownloadServiceFactory::GetForBrowserContext(profile
);
568 DownloadHistory
* download_history
=
569 (download_service
? download_service
->GetDownloadHistory() : NULL
);
571 // The browser is only interested in new downloads. Ones that were restored
572 // from history are not displayed on the shelf. The downloads page
573 // independently listens for new downloads when it is active. Note that the UI
574 // will be notified of downloads even if they are not meant to be displayed on
575 // the shelf (i.e. ShouldShowInShelf() returns false). This is because:
576 // * The shelf isn't the only UI. E.g. on Android, the UI is the system
578 // * There are other UI activities that need to be performed. E.g. if the
579 // download was initiated from a new tab, then that tab should be closed.
581 // TODO(asanka): If an interrupted download is restored from history and is
582 // resumed, then ideally the UI should be notified.
583 return !download_history
||
584 !download_history
->WasRestoredFromHistory(download_
);
587 bool DownloadItemModel::WasUINotified() const {
588 const DownloadItemModelData
* data
= DownloadItemModelData::Get(download_
);
589 return data
&& data
->was_ui_notified_
;
592 void DownloadItemModel::SetWasUINotified(bool was_ui_notified
) {
593 DownloadItemModelData
* data
= DownloadItemModelData::GetOrCreate(download_
);
594 data
->was_ui_notified_
= was_ui_notified
;
597 bool DownloadItemModel::ShouldPreferOpeningInBrowser() const {
598 const DownloadItemModelData
* data
= DownloadItemModelData::Get(download_
);
599 return data
&& data
->should_prefer_opening_in_browser_
;
602 void DownloadItemModel::SetShouldPreferOpeningInBrowser(bool preference
) {
603 DownloadItemModelData
* data
= DownloadItemModelData::GetOrCreate(download_
);
604 data
->should_prefer_opening_in_browser_
= preference
;
607 bool DownloadItemModel::IsDangerousFileBasedOnType() const {
608 const DownloadItemModelData
* data
= DownloadItemModelData::Get(download_
);
609 return data
&& data
->is_dangerous_file_based_on_type_
;
612 void DownloadItemModel::SetIsDangerousFileBasedOnType(bool dangerous
) {
613 DownloadItemModelData
* data
= DownloadItemModelData::GetOrCreate(download_
);
614 data
->is_dangerous_file_based_on_type_
= dangerous
;
617 bool DownloadItemModel::IsBeingRevived() const {
618 const DownloadItemModelData
* data
= DownloadItemModelData::Get(download_
);
619 return data
&& data
->is_being_revived_
;
622 void DownloadItemModel::SetIsBeingRevived(bool is_being_revived
) {
623 DownloadItemModelData
* data
= DownloadItemModelData::GetOrCreate(download_
);
624 data
->is_being_revived_
= is_being_revived
;
627 base::string16
DownloadItemModel::GetProgressSizesString() const {
628 base::string16 size_ratio
;
629 int64 size
= GetCompletedBytes();
630 int64 total
= GetTotalBytes();
632 ui::DataUnits amount_units
= ui::GetByteDisplayUnits(total
);
633 base::string16 simple_size
= ui::FormatBytesWithUnits(size
, amount_units
, false);
635 // In RTL locales, we render the text "size/total" in an RTL context. This
636 // is problematic since a string such as "123/456 MB" is displayed
637 // as "MB 123/456" because it ends with an LTR run. In order to solve this,
638 // we mark the total string as an LTR string if the UI layout is
639 // right-to-left so that the string "456 MB" is treated as an LTR run.
640 base::string16 simple_total
= base::i18n::GetDisplayStringInLTRDirectionality(
641 ui::FormatBytesWithUnits(total
, amount_units
, true));
642 size_ratio
= l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_SIZES
,
643 simple_size
, simple_total
);
645 size_ratio
= ui::FormatBytes(size
);
650 base::string16
DownloadItemModel::GetInProgressStatusString() const {
651 DCHECK_EQ(DownloadItem::IN_PROGRESS
, download_
->GetState());
653 TimeDelta time_remaining
;
654 // time_remaining is only known if the download isn't paused.
655 bool time_remaining_known
= (!download_
->IsPaused() &&
656 download_
->TimeRemaining(&time_remaining
));
658 // Indication of progress. (E.g.:"100/200 MB" or "100MB")
659 base::string16 size_ratio
= GetProgressSizesString();
661 // The download is a CRX (app, extension, theme, ...) and it is being unpacked
663 if (download_
->AllDataSaved() &&
664 download_crx_util::IsExtensionDownload(*download_
)) {
665 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING
);
668 // A paused download: "100/120 MB, Paused"
669 if (download_
->IsPaused()) {
670 return l10n_util::GetStringFUTF16(
671 IDS_DOWNLOAD_STATUS_IN_PROGRESS
, size_ratio
,
672 l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED
));
675 // A download scheduled to be opened when complete: "Opening in 10 secs"
676 if (download_
->GetOpenWhenComplete()) {
677 if (!time_remaining_known
)
678 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE
);
680 return l10n_util::GetStringFUTF16(
681 IDS_DOWNLOAD_STATUS_OPEN_IN
,
682 ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION
,
683 ui::TimeFormat::LENGTH_SHORT
, time_remaining
));
686 // In progress download with known time left: "100/120 MB, 10 secs left"
687 if (time_remaining_known
) {
688 return l10n_util::GetStringFUTF16(
689 IDS_DOWNLOAD_STATUS_IN_PROGRESS
, size_ratio
,
690 ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING
,
691 ui::TimeFormat::LENGTH_SHORT
, time_remaining
));
694 // In progress download with no known time left and non-zero completed bytes:
695 // "100/120 MB" or "100 MB"
696 if (GetCompletedBytes() > 0)
699 // Instead of displaying "0 B" we say "Starting..."
700 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING
);
703 void DownloadItemModel::OpenUsingPlatformHandler() {
704 DownloadService
* download_service
=
705 DownloadServiceFactory::GetForBrowserContext(
706 download_
->GetBrowserContext());
707 if (!download_service
)
710 ChromeDownloadManagerDelegate
* delegate
=
711 download_service
->GetDownloadManagerDelegate();
714 delegate
->OpenDownloadUsingPlatformHandler(download_
);
715 RecordDownloadOpenMethod(DOWNLOAD_OPEN_METHOD_USER_PLATFORM
);