// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/download/download_item_model.h" #include "base/i18n/number_formatting.h" #include "base/i18n/rtl.h" #include "base/string16.h" #include "base/sys_string_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/download/chrome_download_manager_delegate.h" #include "chrome/common/time_format.h" #include "content/public/browser/download_danger_type.h" #include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_item.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/text/bytes_formatting.h" #include "ui/base/text/text_elider.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/gdata/gdata_download_observer.h" #endif using base::TimeDelta; using content::DownloadItem; // ----------------------------------------------------------------------------- // DownloadItemModel DownloadItemModel::DownloadItemModel(DownloadItem* download) : BaseDownloadItemModel(download) { } void DownloadItemModel::CancelTask() { download_->Cancel(true /* update history service */); } string16 DownloadItemModel::GetStatusText() { int64 size = download_->GetReceivedBytes(); int64 total = download_->AllDataSaved() ? size : download_->GetTotalBytes(); bool is_gdata = false; #if defined(OS_CHROMEOS) is_gdata = gdata::GDataDownloadObserver::IsGDataDownload(download_); // For GData downloads, the size is the count of bytes uploaded. if (is_gdata) size = gdata::GDataDownloadObserver::GetUploadedBytes(download_); #endif ui::DataUnits amount_units = ui::GetByteDisplayUnits(total); string16 simple_size = ui::FormatBytesWithUnits(size, amount_units, false); // In RTL locales, we render the text "size/total" in an RTL context. This // is problematic since a string such as "123/456 MB" is displayed // as "MB 123/456" because it ends with an LTR run. In order to solve this, // we mark the total string as an LTR string if the UI layout is // right-to-left so that the string "456 MB" is treated as an LTR run. string16 simple_total = base::i18n::GetDisplayStringInLTRDirectionality( ui::FormatBytesWithUnits(total, amount_units, true)); // TODO(asanka): Calculate a TimeRemaining() for GData uploads. TimeDelta remaining; string16 simple_time; if (download_->IsInProgress() && download_->IsPaused()) { simple_time = l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED); } else if (!is_gdata && download_->TimeRemaining(&remaining)) { simple_time = download_->GetOpenWhenComplete() ? TimeFormat::TimeRemainingShort(remaining) : TimeFormat::TimeRemaining(remaining); } string16 size_text; string16 status_text; content::DownloadInterruptReason reason; switch (download_->GetState()) { case DownloadItem::IN_PROGRESS: #if defined(OS_CHROMEOS) if (is_gdata && size == 0) { // We haven't started the upload yet. The download needs to progress // further before we will see any upload progress. Show "Downloading..." // until we start uploading. status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_WAITING); break; } #endif if (ChromeDownloadManagerDelegate::IsExtensionDownload(download_) && download_->AllDataSaved() && download_->GetState() == DownloadItem::IN_PROGRESS) { // The download is a CRX (app, extension, theme, ...) and it is // being unpacked and validated. status_text = l10n_util::GetStringUTF16( IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING); } else if (download_->GetOpenWhenComplete()) { if (simple_time.empty()) { status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE); } else { status_text = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_OPEN_IN, simple_time); } } else { if (!simple_time.empty()) { status_text = l10n_util::GetStringFUTF16( IDS_DOWNLOAD_STATUS_IN_PROGRESS, simple_size, simple_total, simple_time); } else if (total > 0 && size > 0) { status_text = l10n_util::GetStringFUTF16( IDS_DOWNLOAD_STATUS_IN_PROGRESS_SIZES_ONLY, simple_size, simple_total); } else { // Instead of displaying "0 B" we keep the "Starting..." string. status_text = (size == 0) ? l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING) : ui::FormatBytes(size); } } break; case DownloadItem::COMPLETE: if (download_->GetFileExternallyRemoved()) { status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_REMOVED); } else { status_text.clear(); } break; case DownloadItem::CANCELLED: status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELED); break; case DownloadItem::REMOVING: break; case DownloadItem::INTERRUPTED: reason = download_->GetLastReason(); status_text = InterruptReasonStatusMessage(reason); if (total <= 0) { size_text = ui::FormatBytes(size); } else { size_text = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_RECEIVED_SIZE, simple_size, simple_total); } size_text = size_text + ASCIIToUTF16(" "); if (reason != content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) status_text = size_text + status_text; break; default: NOTREACHED(); } return status_text; } string16 DownloadItemModel::GetTooltipText(const gfx::Font& font, int max_width) const { string16 tooltip = ui::ElideFilename( download_->GetFileNameToReportUser(), font, max_width); content::DownloadInterruptReason reason = download_->GetLastReason(); if (download_->GetState() == DownloadItem::INTERRUPTED && reason != content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) { tooltip += ASCIIToUTF16("\n"); tooltip += ui::ElideText(InterruptReasonStatusMessage(reason), font, max_width, ui::ELIDE_AT_END); } return tooltip; } int DownloadItemModel::PercentComplete() const { #if defined(OS_CHROMEOS) // For GData uploads, progress is based on the number of bytes // uploaded. Progress is unknown until the upload starts. if (gdata::GDataDownloadObserver::IsGDataDownload(download_)) return gdata::GDataDownloadObserver::PercentComplete(download_); #endif return download_->PercentComplete(); } string16 DownloadItemModel::GetWarningText(const gfx::Font& font, int base_width) { // Should only be called if IsDangerous(). DCHECK(IsDangerous()); switch (download_->GetDangerType()) { case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL); case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: if (ChromeDownloadManagerDelegate::IsExtensionDownload(download_)) { return l10n_util::GetStringUTF16( IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION); } else { return l10n_util::GetStringFUTF16( IDS_PROMPT_DANGEROUS_DOWNLOAD, ui::ElideFilename(download_->GetFileNameToReportUser(), font, base_width)); } case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: return l10n_util::GetStringFUTF16( IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT, ui::ElideFilename(download_->GetFileNameToReportUser(), font, base_width)); case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: return l10n_util::GetStringFUTF16( IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT, ui::ElideFilename(download_->GetFileNameToReportUser(), font, base_width)); case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: case content::DOWNLOAD_DANGER_TYPE_MAX: NOTREACHED(); } return string16(); } string16 DownloadItemModel::GetWarningConfirmButtonText() { // Should only be called if IsDangerous() DCHECK(IsDangerous()); if (download_->GetDangerType() == content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE && ChromeDownloadManagerDelegate::IsExtensionDownload(download_)) { return l10n_util::GetStringUTF16(IDS_CONTINUE_EXTENSION_DOWNLOAD); } else { return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD); } } bool DownloadItemModel::IsMalicious() { if (!IsDangerous()) return false; switch (download_->GetDangerType()) { case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: return true; case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: case content::DOWNLOAD_DANGER_TYPE_MAX: // We shouldn't get any of these due to the IsDangerous() test above. NOTREACHED(); // Fallthrough. case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: return false; } NOTREACHED(); return false; } bool DownloadItemModel::IsDangerous() { return download_->GetSafetyState() == DownloadItem::DANGEROUS; } // static string16 BaseDownloadItemModel::InterruptReasonStatusMessage(int reason) { int string_id = 0; switch (reason) { case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_ACCESS_DENIED; break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_DISK_FULL; break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_PATH_TOO_LONG; break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_FILE_TOO_LARGE; break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_VIRUS; break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_TEMPORARY_PROBLEM; break; case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_TIMEOUT; break; case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_DISCONNECTED; break; case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_DOWN; break; case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_ERROR; break; case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NO_FILE; break; case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_PROBLEM; break; case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN: case content::DOWNLOAD_INTERRUPT_REASON_CRASH: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SHUTDOWN; break; case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED: string_id = IDS_DOWNLOAD_STATUS_CANCELED; break; default: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS; break; } return l10n_util::GetStringUTF16(string_id); } // static string16 BaseDownloadItemModel::InterruptReasonMessage(int reason) { int string_id = 0; string16 status_text; switch (reason) { case content::DOWNLOAD_INTERRUPT_REASON_NONE: break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_ACCESS_DENIED; break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_DISK_FULL; break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_PATH_TOO_LONG; break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_LARGE; break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_VIRUS; break; case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_TEMPORARY_PROBLEM; break; case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_TIMEOUT; break; case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_DISCONNECTED; break; case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_DOWN; break; case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_ERROR; break; case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NO_FILE; break; case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_PROBLEM; break; case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN: case content::DOWNLOAD_INTERRUPT_REASON_CRASH: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SHUTDOWN; break; case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED: string_id = IDS_DOWNLOAD_STATUS_CANCELED; break; default: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS; break; } status_text = l10n_util::GetStringUTF16(string_id); return status_text; }