blob: e1fd45f207ea8cfa9271811c0eb7242efa45c617 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// 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_ui_model.h"
#include <utility>
#include "base/feature_list.h"
#include "base/i18n/rtl.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/bubble/download_bubble_prefs.h"
#include "chrome/browser/download/download_commands.h"
#include "chrome/browser/download/download_item_warning_data.h"
#include "chrome/browser/download/download_ui_safe_browsing_util.h"
#include "chrome/browser/download/offline_item_utils.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/enterprise/common/proto/connectors.pb.h"
#include "components/google/core/common/google_util.h"
#include "components/safe_browsing/buildflags.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/safe_browsing/core/common/safebrowsing_referral_methods.h"
#include "components/vector_icons/vector_icons.h"
#include "net/base/mime_util.h"
#include "third_party/blink/public/common/mime_util/mime_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h"
#include "ui/base/text/bytes_formatting.h"
#include "ui/base/ui_base_features.h"
#include "ui/color/color_id.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/browser.h"
#include "components/url_formatter/elide_url.h"
#include "ui/gfx/text_elider.h"
#include "ui/views/vector_icons.h"
#endif
using download::DownloadItem;
using offline_items_collection::FailState;
using safe_browsing::DownloadFileType;
namespace {
// TODO(qinmin): Migrate this description generator to OfflineItemUtils once
// that component gets used to build desktop UI.
std::u16string FailStateDescription(FailState fail_state) {
int string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS;
std::u16string status_text;
switch (fail_state) {
case FailState::FILE_ACCESS_DENIED:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_ACCESS_DENIED;
break;
case FailState::FILE_NO_SPACE:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_DISK_FULL;
break;
case FailState::FILE_NAME_TOO_LONG:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_PATH_TOO_LONG;
break;
case FailState::FILE_TOO_LARGE:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_LARGE;
break;
case FailState::FILE_VIRUS_INFECTED:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_VIRUS;
break;
case FailState::FILE_TRANSIENT_ERROR:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_TEMPORARY_PROBLEM;
break;
case FailState::FILE_BLOCKED:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_BLOCKED;
break;
case FailState::FILE_SECURITY_CHECK_FAILED:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SECURITY_CHECK_FAILED;
break;
case FailState::FILE_TOO_SHORT:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_SHORT;
break;
case FailState::FILE_SAME_AS_SOURCE:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_SAME_AS_SOURCE;
break;
case FailState::NETWORK_INVALID_REQUEST:
case FailState::NETWORK_FAILED:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_ERROR;
break;
case FailState::NETWORK_TIMEOUT:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_TIMEOUT;
break;
case FailState::NETWORK_DISCONNECTED:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_DISCONNECTED;
break;
case FailState::NETWORK_SERVER_DOWN:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_DOWN;
break;
case FailState::SERVER_FAILED:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_PROBLEM;
break;
case FailState::SERVER_BAD_CONTENT:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NO_FILE;
break;
case FailState::USER_CANCELED:
string_id = IDS_DOWNLOAD_STATUS_CANCELLED;
break;
case FailState::USER_SHUTDOWN:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SHUTDOWN;
break;
case FailState::CRASH:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_CRASH;
break;
case FailState::SERVER_UNAUTHORIZED:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_UNAUTHORIZED;
break;
case FailState::SERVER_CERT_PROBLEM:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_CERT_PROBLEM;
break;
case FailState::SERVER_FORBIDDEN:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FORBIDDEN;
break;
case FailState::SERVER_UNREACHABLE:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_UNREACHABLE;
break;
case FailState::SERVER_CONTENT_LENGTH_MISMATCH:
string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_CONTENT_LENGTH_MISMATCH;
break;
case FailState::NO_FAILURE:
NOTREACHED();
[[fallthrough]];
// fallthrough
case FailState::CANNOT_DOWNLOAD:
case FailState::NETWORK_INSTABILITY:
case FailState::SERVER_NO_RANGE:
case FailState::SERVER_CROSS_ORIGIN_REDIRECT:
case FailState::FILE_FAILED:
case FailState::FILE_HASH_MISMATCH:
string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS;
}
status_text = l10n_util::GetStringUTF16(string_id);
return status_text;
}
} // namespace
DownloadUIModel::DownloadUIModel()
: DownloadUIModel::DownloadUIModel(std::make_unique<StatusTextBuilder>()) {}
DownloadUIModel::DownloadUIModel(
std::unique_ptr<StatusTextBuilderBase> status_text_builder)
: status_text_builder_(std::move(status_text_builder)) {
status_text_builder_->SetModel(this);
}
DownloadUIModel::~DownloadUIModel() = default;
void DownloadUIModel::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
}
base::WeakPtr<DownloadUIModel> DownloadUIModel::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
bool DownloadUIModel::HasSupportedImageMimeType() const {
if (blink::IsSupportedImageMimeType(GetMimeType()))
return true;
std::string mime;
base::FilePath::StringType extension_with_dot =
GetTargetFilePath().FinalExtension();
if (!extension_with_dot.empty() &&
net::GetWellKnownMimeTypeFromExtension(extension_with_dot.substr(1),
&mime) &&
blink::IsSupportedImageMimeType(mime)) {
return true;
}
return false;
}
std::u16string DownloadUIModel::GetProgressSizesString() const {
return status_text_builder_->GetProgressSizesString();
}
std::u16string DownloadUIModel::StatusTextBuilder::GetProgressSizesString()
const {
std::u16string size_ratio;
int64_t size = model_->GetCompletedBytes();
int64_t total = model_->GetTotalBytes();
if (total > 0) {
ui::DataUnits amount_units = ui::GetByteDisplayUnits(total);
std::u16string 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.
std::u16string simple_total =
base::i18n::GetDisplayStringInLTRDirectionality(
ui::FormatBytesWithUnits(total, amount_units, true));
size_ratio = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_SIZES,
simple_size, simple_total);
} else {
size_ratio = ui::FormatBytes(size);
}
return size_ratio;
}
std::u16string
DownloadUIModel::BubbleStatusTextBuilder::GetProgressSizesString() const {
std::u16string size_ratio;
int64_t size = model_->GetCompletedBytes();
int64_t total = model_->GetTotalBytes();
if (total > 0) {
ui::DataUnits amount_units = ui::GetByteDisplayUnits(total);
std::u16string simple_size =
ui::FormatBytesWithUnits(size, amount_units, false);
std::u16string simple_total =
ui::FormatBytesWithUnits(total, amount_units, true);
// Linux prepends an RLM (right-to-left mark) in the FormatBytesWithUnits
// call when showing units if the string has strong RTL characters. This is
// problematic for this fraction use case because it ends up moving it
// around so that the numerator is in the wrong place. Therefore, we remove
// that extra marker before proceeding.
base::i18n::UnadjustStringForLocaleDirection(&simple_total);
size_ratio = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_SIZES,
simple_size, simple_total);
} else {
size_ratio = ui::FormatBytes(size);
}
return size_ratio;
}
std::u16string DownloadUIModel::GetStatusText() const {
return status_text_builder_->GetStatusText(GetState());
}
#if !BUILDFLAG(IS_ANDROID)
std::u16string DownloadUIModel::GetStatusTextForLabel(
const gfx::FontList& font_list,
float available_pixel_width) const {
if (!ShouldPromoteOrigin()) {
return GetStatusText();
}
if (const GURL url = GetOriginalURL(); url.is_valid()) {
std::u16string url_string = url_formatter::FormatUrlForSecurityDisplay(url);
// available_pixel_width can be 0 before the view is inflated.
return available_pixel_width <= 0
? url_string
: gfx::ElideText(url_string, font_list, available_pixel_width,
gfx::ELIDE_TAIL);
}
return GetStatusText();
}
#endif
std::u16string DownloadUIModel::StatusTextBuilderBase::GetStatusText(
download::DownloadItem::DownloadState state) const {
DCHECK(model_);
switch (state) {
case DownloadItem::IN_PROGRESS:
return GetInProgressStatusText();
case DownloadItem::COMPLETE:
return GetCompletedStatusText();
case DownloadItem::INTERRUPTED: {
const FailState fail_state = model_->GetLastFailState();
if (fail_state != FailState::USER_CANCELED) {
return GetInterruptedStatusText(fail_state);
}
}
[[fallthrough]];
case DownloadItem::CANCELLED:
return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED);
case DownloadItem::MAX_DOWNLOAD_STATE:
NOTREACHED();
return std::u16string();
}
}
std::u16string DownloadUIModel::GetTooltipText() const {
std::u16string tooltip = GetFileNameToReportUser().LossyDisplayName();
if (GetState() == DownloadItem::INTERRUPTED &&
GetLastFailState() != FailState::USER_CANCELED) {
tooltip +=
u"\n" + status_text_builder_->GetFailStateMessage(GetLastFailState());
}
return tooltip;
}
std::u16string DownloadUIModel::GetWarningText(const std::u16string& filename,
size_t* offset) const {
*offset = std::string::npos;
switch (GetDangerType()) {
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL);
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
return IsExtensionDownload()
? l10n_util::GetStringUTF16(
IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION)
: l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD,
filename, offset);
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE:
return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT,
filename, offset);
case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
bool request_ap_verdicts = false;
#if BUILDFLAG(FULL_SAFE_BROWSING)
request_ap_verdicts =
safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
profile())
->IsUnderAdvancedProtection();
#endif
return l10n_util::GetStringFUTF16(
request_ap_verdicts
? IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT_IN_ADVANCED_PROTECTION
: IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT,
filename, offset);
}
case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
return l10n_util::GetStringFUTF16(IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS,
filename, offset);
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE:
return l10n_util::GetStringFUTF16(IDS_PROMPT_DOWNLOAD_BLOCKED_TOO_LARGE,
filename, offset);
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED:
return l10n_util::GetStringFUTF16(
IDS_PROMPT_DOWNLOAD_BLOCKED_PASSWORD_PROTECTED, filename, offset);
case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
return l10n_util::GetStringUTF16(
IDS_PROMPT_DOWNLOAD_SENSITIVE_CONTENT_WARNING);
case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK:
return l10n_util::GetStringUTF16(
IDS_PROMPT_DOWNLOAD_SENSITIVE_CONTENT_BLOCKED);
case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING:
return l10n_util::GetStringFUTF16(IDS_PROMPT_DEEP_SCANNING, filename,
offset);
case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING:
// TODO(crbug.com/1491184): Implement UX for this danger type.
return u"";
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED:
// TODO(b/327392327): Implement UX for this danger type.
NOTREACHED();
break;
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED:
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY:
case download::DOWNLOAD_DANGER_TYPE_MAX:
break;
}
switch (GetInsecureDownloadStatus()) {
case download::DownloadItem::InsecureDownloadStatus::BLOCK:
return l10n_util::GetStringFUTF16(IDS_PROMPT_DOWNLOAD_INSECURE_BLOCKED,
filename, offset);
case download::DownloadItem::InsecureDownloadStatus::WARN:
return l10n_util::GetStringFUTF16(IDS_PROMPT_DOWNLOAD_INSECURE_WARNING,
filename, offset);
case download::DownloadItem::InsecureDownloadStatus::UNKNOWN:
case download::DownloadItem::InsecureDownloadStatus::SAFE:
case download::DownloadItem::InsecureDownloadStatus::VALIDATED:
case download::DownloadItem::InsecureDownloadStatus::SILENT_BLOCK:
break;
}
return std::u16string();
}
std::u16string DownloadUIModel::GetWarningConfirmButtonText() const {
const auto kDangerousFile = download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
return l10n_util::GetStringUTF16(
(GetDangerType() == kDangerousFile && IsExtensionDownload())
? IDS_CONTINUE_EXTENSION_DOWNLOAD
: IDS_CONFIRM_DOWNLOAD);
}
std::u16string DownloadUIModel::GetShowInFolderText() const {
return l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_SHOW);
}
ContentId DownloadUIModel::GetContentId() const {
NOTREACHED();
return ContentId();
}
Profile* DownloadUIModel::profile() const {
NOTREACHED();
return nullptr;
}
std::u16string DownloadUIModel::GetTabProgressStatusText() const {
return std::u16string();
}
int64_t DownloadUIModel::GetCompletedBytes() const {
return 0;
}
int64_t DownloadUIModel::GetTotalBytes() const {
return 0;
}
int DownloadUIModel::PercentComplete() const {
return -1;
}
bool DownloadUIModel::IsDangerous() const {
return false;
}
bool DownloadUIModel::MightBeMalicious() const {
return false;
}
bool DownloadUIModel::IsMalicious() const {
return false;
}
bool DownloadUIModel::IsInsecure() const {
return false;
}
bool DownloadUIModel::ShouldRemoveFromShelfWhenComplete() const {
return false;
}
bool DownloadUIModel::ShouldShowDownloadStartedAnimation() const {
return true;
}
bool DownloadUIModel::ShouldShowInShelf() const {
return true;
}
void DownloadUIModel::SetShouldShowInShelf(bool should_show) {}
bool DownloadUIModel::ShouldNotifyUI() const {
return true;
}
bool DownloadUIModel::WasUINotified() const {
return false;
}
void DownloadUIModel::SetWasUINotified(bool should_notify) {}
bool DownloadUIModel::WasActionedOn() const {
return true;
}
void DownloadUIModel::SetActionedOn(bool actioned_on) {}
bool DownloadUIModel::WasUIWarningShown() const {
return false;
}
void DownloadUIModel::SetWasUIWarningShown(bool was_ui_warning_shown) {}
std::optional<base::Time> DownloadUIModel::GetEphemeralWarningUiShownTime()
const {
return std::optional<base::Time>();
}
void DownloadUIModel::SetEphemeralWarningUiShownTime(
std::optional<base::Time> time) {}
bool DownloadUIModel::ShouldPreferOpeningInBrowser() {
return true;
}
void DownloadUIModel::SetShouldPreferOpeningInBrowser(bool preference) {}
DownloadFileType::DangerLevel DownloadUIModel::GetDangerLevel() const {
return DownloadFileType::NOT_DANGEROUS;
}
void DownloadUIModel::SetDangerLevel(
DownloadFileType::DangerLevel danger_level) {}
download::DownloadItem::InsecureDownloadStatus
DownloadUIModel::GetInsecureDownloadStatus() const {
return download::DownloadItem::InsecureDownloadStatus::UNKNOWN;
}
void DownloadUIModel::OpenUsingPlatformHandler() {}
#if BUILDFLAG(IS_CHROMEOS_ASH)
std::optional<DownloadCommands::Command>
DownloadUIModel::MaybeGetMediaAppAction() const {
return std::nullopt;
}
void DownloadUIModel::OpenUsingMediaApp() {}
#endif
bool DownloadUIModel::IsBeingRevived() const {
return true;
}
void DownloadUIModel::SetIsBeingRevived(bool is_being_revived) {}
const DownloadItem* DownloadUIModel::GetDownloadItem() const {
return nullptr;
}
DownloadItem* DownloadUIModel::GetDownloadItem() {
return const_cast<DownloadItem*>(std::as_const(*this).GetDownloadItem());
}
base::FilePath DownloadUIModel::GetFileNameToReportUser() const {
return base::FilePath();
}
base::FilePath DownloadUIModel::GetTargetFilePath() const {
return base::FilePath();
}
void DownloadUIModel::OpenDownload() {
NOTREACHED();
}
download::DownloadItem::DownloadState DownloadUIModel::GetState() const {
return download::DownloadItem::IN_PROGRESS;
}
bool DownloadUIModel::IsPaused() const {
return false;
}
download::DownloadDangerType DownloadUIModel::GetDangerType() const {
return download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
}
bool DownloadUIModel::GetOpenWhenComplete() const {
return false;
}
bool DownloadUIModel::IsOpenWhenCompleteByPolicy() const {
return false;
}
bool DownloadUIModel::TimeRemaining(base::TimeDelta* remaining) const {
return false;
}
base::Time DownloadUIModel::GetStartTime() const {
return base::Time();
}
base::Time DownloadUIModel::GetEndTime() const {
return base::Time();
}
bool DownloadUIModel::GetOpened() const {
return false;
}
void DownloadUIModel::SetOpened(bool opened) {}
bool DownloadUIModel::IsDone() const {
return false;
}
void DownloadUIModel::Pause() {}
void DownloadUIModel::Resume() {}
void DownloadUIModel::Cancel(bool user_cancel) {}
void DownloadUIModel::Remove() {}
void DownloadUIModel::SetOpenWhenComplete(bool open) {}
FailState DownloadUIModel::GetLastFailState() const {
return FailState::NO_FAILURE;
}
base::FilePath DownloadUIModel::GetFullPath() const {
return base::FilePath();
}
bool DownloadUIModel::CanResume() const {
return false;
}
bool DownloadUIModel::AllDataSaved() const {
return false;
}
bool DownloadUIModel::GetFileExternallyRemoved() const {
return false;
}
GURL DownloadUIModel::GetURL() const {
return GURL();
}
bool DownloadUIModel::HasUserGesture() const {
return false;
}
GURL DownloadUIModel::GetOriginalURL() const {
return GURL();
}
bool DownloadUIModel::ShouldPromoteOrigin() const {
return false;
}
#if !BUILDFLAG(IS_ANDROID)
bool DownloadUIModel::IsCommandEnabled(
const DownloadCommands* download_commands,
DownloadCommands::Command command) const {
switch (command) {
case DownloadCommands::SHOW_IN_FOLDER:
case DownloadCommands::OPEN_WHEN_COMPLETE:
case DownloadCommands::PLATFORM_OPEN:
case DownloadCommands::ALWAYS_OPEN_TYPE:
case DownloadCommands::OPEN_WITH_MEDIA_APP:
case DownloadCommands::EDIT_WITH_MEDIA_APP:
NOTREACHED();
return false;
case DownloadCommands::CANCEL:
return !IsDone();
case DownloadCommands::PAUSE:
return !IsDone() && !IsPaused() &&
GetState() == download::DownloadItem::IN_PROGRESS;
case DownloadCommands::RESUME:
return CanResume() &&
(IsPaused() || GetState() != download::DownloadItem::IN_PROGRESS);
case DownloadCommands::COPY_TO_CLIPBOARD:
return download_commands->CanBeCopiedToClipboard();
case DownloadCommands::DISCARD:
case DownloadCommands::KEEP:
case DownloadCommands::LEARN_MORE_SCANNING:
case DownloadCommands::LEARN_MORE_INTERRUPTED:
case DownloadCommands::LEARN_MORE_INSECURE_DOWNLOAD:
case DownloadCommands::LEARN_MORE_DOWNLOAD_BLOCKED:
case DownloadCommands::DEEP_SCAN:
case DownloadCommands::BYPASS_DEEP_SCANNING:
case DownloadCommands::BYPASS_DEEP_SCANNING_AND_OPEN:
case DownloadCommands::REVIEW:
case DownloadCommands::RETRY:
case DownloadCommands::CANCEL_DEEP_SCAN:
return true;
case DownloadCommands::OPEN_SAFE_BROWSING_SETTING:
return CanUserTurnOnSafeBrowsing(profile());
}
NOTREACHED();
return false;
}
bool DownloadUIModel::IsCommandChecked(
const DownloadCommands* download_commands,
DownloadCommands::Command command) const {
switch (command) {
case DownloadCommands::OPEN_WHEN_COMPLETE:
case DownloadCommands::ALWAYS_OPEN_TYPE:
NOTREACHED();
return false;
case DownloadCommands::PAUSE:
case DownloadCommands::RESUME:
return IsPaused();
case DownloadCommands::SHOW_IN_FOLDER:
case DownloadCommands::PLATFORM_OPEN:
case DownloadCommands::CANCEL:
case DownloadCommands::DISCARD:
case DownloadCommands::KEEP:
case DownloadCommands::LEARN_MORE_SCANNING:
case DownloadCommands::LEARN_MORE_INTERRUPTED:
case DownloadCommands::LEARN_MORE_INSECURE_DOWNLOAD:
case DownloadCommands::LEARN_MORE_DOWNLOAD_BLOCKED:
case DownloadCommands::OPEN_SAFE_BROWSING_SETTING:
case DownloadCommands::COPY_TO_CLIPBOARD:
case DownloadCommands::DEEP_SCAN:
case DownloadCommands::BYPASS_DEEP_SCANNING:
case DownloadCommands::BYPASS_DEEP_SCANNING_AND_OPEN:
case DownloadCommands::REVIEW:
case DownloadCommands::RETRY:
case DownloadCommands::CANCEL_DEEP_SCAN:
case DownloadCommands::OPEN_WITH_MEDIA_APP:
case DownloadCommands::EDIT_WITH_MEDIA_APP:
return false;
}
return false;
}
void DownloadUIModel::ExecuteCommand(DownloadCommands* download_commands,
DownloadCommands::Command command) {
switch (command) {
case DownloadCommands::SHOW_IN_FOLDER:
case DownloadCommands::OPEN_WHEN_COMPLETE:
case DownloadCommands::ALWAYS_OPEN_TYPE:
NOTREACHED();
break;
case DownloadCommands::PLATFORM_OPEN:
OpenUsingPlatformHandler();
break;
case DownloadCommands::CANCEL:
Cancel(true /* Cancelled by user */);
break;
case DownloadCommands::DISCARD:
Remove();
break;
case DownloadCommands::KEEP:
case DownloadCommands::LEARN_MORE_SCANNING:
NOTREACHED();
break;
case DownloadCommands::LEARN_MORE_INTERRUPTED:
download_commands->GetBrowser()->OpenURL(
content::OpenURLParams(
download_commands->GetLearnMoreURLForInterruptedDownload(),
content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false),
/*navigation_handle_callback=*/{});
break;
case DownloadCommands::LEARN_MORE_INSECURE_DOWNLOAD:
download_commands->GetBrowser()->OpenURL(
content::OpenURLParams(
GURL(chrome::kInsecureDownloadBlockingLearnMoreUrl),
content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false),
/*navigation_handle_callback=*/{});
break;
case DownloadCommands::LEARN_MORE_DOWNLOAD_BLOCKED:
download_commands->GetBrowser()->OpenURL(
content::OpenURLParams(google_util::AppendGoogleLocaleParam(
GURL(chrome::kDownloadBlockedLearnMoreURL),
g_browser_process->GetApplicationLocale()),
content::Referrer(),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false),
/*navigation_handle_callback=*/{});
break;
case DownloadCommands::OPEN_SAFE_BROWSING_SETTING:
chrome::ShowSafeBrowsingEnhancedProtectionWithIph(
download_commands->GetBrowser(),
safe_browsing::SafeBrowsingSettingReferralMethod::
kDownloadBubbleSubpage);
break;
case DownloadCommands::PAUSE:
Pause();
break;
case DownloadCommands::RESUME:
Resume();
break;
case DownloadCommands::COPY_TO_CLIPBOARD:
download_commands->CopyFileAsImageToClipboard();
break;
case DownloadCommands::DEEP_SCAN:
break;
case DownloadCommands::BYPASS_DEEP_SCANNING:
case DownloadCommands::BYPASS_DEEP_SCANNING_AND_OPEN:
case DownloadCommands::REVIEW:
case DownloadCommands::RETRY:
case DownloadCommands::CANCEL_DEEP_SCAN:
break;
case DownloadCommands::OPEN_WITH_MEDIA_APP:
case DownloadCommands::EDIT_WITH_MEDIA_APP:
#if BUILDFLAG(IS_CHROMEOS_ASH)
OpenUsingMediaApp();
#else
NOTREACHED();
#endif
break;
}
}
DownloadUIModel::TailoredWarningType DownloadUIModel::GetTailoredWarningType()
const {
return TailoredWarningType::kNoTailoredWarning;
}
DownloadUIModel::DangerUiPattern DownloadUIModel::GetDangerUiPattern() const {
return DangerUiPattern::kNormal;
}
bool DownloadUIModel::ShouldShowInBubble() const {
return ShouldShowInShelf();
}
bool DownloadUIModel::IsEphemeralWarning() const {
return false;
}
#endif // !BUILDFLAG(IS_ANDROID)
std::string DownloadUIModel::GetMimeType() const {
return "text/html";
}
bool DownloadUIModel::IsExtensionDownload() const {
return false;
}
std::u16string DownloadUIModel::StatusTextBuilder::GetInProgressStatusText()
const {
DCHECK_EQ(DownloadItem::IN_PROGRESS, model_->GetState());
base::TimeDelta time_remaining;
// time_remaining is only known if the download isn't paused, and it isn't
// going to be rerouted to a web drive.
bool time_remaining_known =
(!model_->IsPaused() && model_->TimeRemaining(&time_remaining));
// Indication of progress. (E.g.:"100/200 MB" or "100MB")
std::u16string size_ratio = GetProgressSizesString();
// The download is a CRX (app, extension, theme, ...) and it is being unpacked
// and validated.
if (model_->AllDataSaved() && model_->IsExtensionDownload()) {
return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING);
}
// A paused download: "100/120 MB, Paused"
if (model_->IsPaused()) {
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio,
l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED));
}
// A download scheduled to be opened when complete: "Opening in 10 secs"
if (model_->GetOpenWhenComplete()) {
if (!time_remaining_known)
return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE);
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_STATUS_OPEN_IN,
ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
ui::TimeFormat::LENGTH_SHORT, time_remaining));
}
// In progress download with known time left: "100/120 MB, 10 secs left"
if (time_remaining_known) {
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio,
ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
ui::TimeFormat::LENGTH_SHORT, time_remaining));
}
const auto completed_bytes = model_->GetCompletedBytes();
const auto total_bytes = model_->GetTotalBytes();
if (completed_bytes == 0) {
// Instead of displaying "0 B" we say "Starting..."
return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING);
} else if (completed_bytes < total_bytes || total_bytes == 0) {
// In progress download with no known time left and non-zero completed
// bytes: "100/120 MB • Resuming..." or "100 MB • Resuming...", or "100/120
// MB" or "100 MB"
return size_ratio;
} else {
return std::u16string();
}
}
// static
std::u16string
DownloadUIModel::BubbleStatusTextBuilder::GetBubbleStatusMessageWithBytes(
const std::u16string& bytes_substring,
const std::u16string& detail_message,
bool is_active) {
// For some RTL languages (e.g. Hebrew), the translated form of 123/456 MB
// still uses the English characters "MB" rather than RTL characters. We
// specifically mark this as LTR because it should be displayed as "123/456
// MB" (not "MB 123/456"). Conversely, some other RTL languages (e.g. Arabic)
// do translate "MB" to RTL characters. For these, we do nothing, that way the
// phrase is correctly displayed as RTL, with the translated "MB" to the left
// of the fraction.
std::u16string final_bytes_substring =
base::i18n::GetStringDirection(bytes_substring) ==
base::i18n::TextDirection::LEFT_TO_RIGHT
? base::i18n::GetDisplayStringInLTRDirectionality(bytes_substring)
: bytes_substring;
std::u16string download_progress =
is_active ? l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_WITH_SYMBOL,
final_bytes_substring)
: final_bytes_substring;
std::u16string text = l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR,
download_progress, detail_message);
// Some RTL languages like Hebrew still display "MB" in English
// characters, which are the first strongly directional characters in
// the full string. We mark the full string as RTL to ensure it doesn't get
// displayed as LTR in spite of the first characters ("MB") being LTR.
base::i18n::AdjustStringForLocaleDirection(&text);
return text;
}
std::u16string
DownloadUIModel::BubbleStatusTextBuilder::GetBubbleWarningStatusText() const {
// If the detail message is "Malware", then this returns "Blocked • Malware"
auto get_blocked_warning = [](int detail_message_id) {
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR,
l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_BLOCKED),
l10n_util::GetStringUTF16(detail_message_id));
};
switch (model_->GetInsecureDownloadStatus()) {
case download::DownloadItem::InsecureDownloadStatus::BLOCK:
case download::DownloadItem::InsecureDownloadStatus::WARN:
// "Insecure download blocked"
return l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_WARNING_INSECURE);
case download::DownloadItem::InsecureDownloadStatus::UNKNOWN:
case download::DownloadItem::InsecureDownloadStatus::SAFE:
case download::DownloadItem::InsecureDownloadStatus::VALIDATED:
case download::DownloadItem::InsecureDownloadStatus::SILENT_BLOCK:
break;
}
switch (model_->GetDangerType()) {
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
if (model_->IsExtensionDownload()) {
// "Blocked • Unknown source"
return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_STATUS_UNKNOWN_SOURCE);
}
if (WasSafeBrowsingVerdictObtained(model_->GetDownloadItem())) {
// "Suspicious download blocked"
return l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_WARNING_SUSPICIOUS);
}
// "Unverified download blocked"
return l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_WARNING_UNVERIFIED);
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE:
case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
// "Dangerous download blocked"
return l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_WARNING_DANGEROUS);
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED:
// "Blocked • Encrypted"
return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_STATUS_ENCRYPTED);
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE:
// "Blocked • Too big"
return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_STATUS_TOO_BIG);
case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
bool request_ap_verdicts = false;
#if BUILDFLAG(FULL_SAFE_BROWSING)
request_ap_verdicts =
safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
model_->profile())
->IsUnderAdvancedProtection();
#endif
// "Blocked by Advanced Protection" or "Suspicious download blocked"
return request_ap_verdicts
? l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_ADVANCED_PROTECTION)
: l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_WARNING_SUSPICIOUS);
}
case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
// "Sensitive content"
return l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_SENSITIVE_CONTENT);
case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK:
// "Blocked by your organization"
return l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_BLOCKED_ORGANIZATION);
case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING:
// "Scan for malware • Suspicious"
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR,
l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_DEEP_SCANNING_PROMPT_UPDATED),
l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_SUSPICIOUS));
case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING:
// "Suspicious file blocked • Password needed"
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR,
l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_LOCAL_DECRYPTION_STATUS),
l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_PASSWORD_NEEDED));
case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
#if BUILDFLAG(IS_ANDROID)
// "Scanning..."
return l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_ASYNC_SCANNING);
#else
// Either "Checking with your organization's security policies..." or
// "Scanning..."
if (download::DoesDownloadConnectorBlock(
model_->profile(), model_->GetDownloadItem()->GetURL())) {
return l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_ASYNC_SCANNING_ENTERPRISE);
} else {
return l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_ASYNC_SCANNING);
}
#endif
case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING:
// "Checking for malware..."
return l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_LOCAL_DECRYPTING);
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED:
// "Scan failed • Suspicious"
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR,
l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_DEEP_SCANNED_FAILED_UPDATED),
l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_SUSPICIOUS));
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED:
// TODO(b/327392327): Implement message for this danger type.
NOTREACHED();
break;
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY:
case download::DOWNLOAD_DANGER_TYPE_MAX:
break;
}
return std::u16string();
}
std::u16string
DownloadUIModel::BubbleStatusTextBuilder::GetInProgressStatusText() const {
DCHECK_EQ(DownloadItem::IN_PROGRESS, model_->GetState());
std::u16string warning_status_text = GetBubbleWarningStatusText();
if (!warning_status_text.empty())
return warning_status_text;
base::TimeDelta time_remaining;
// time_remaining is only known if the download isn't paused, and it isn't
// going to be rerouted to a web drive.
bool time_remaining_known =
(!model_->IsPaused() && model_->TimeRemaining(&time_remaining));
// Indication of progress. (E.g.:"100/200 MB" or "100MB")
std::u16string size_ratio = GetProgressSizesString();
// If the detail message is "Paused" and the size_ratio is "100/120 MB", then
// this returns "100/120 MB • Paused".
auto get_size_ratio_string = [size_ratio](std::u16string detail_message) {
return GetBubbleStatusMessageWithBytes(size_ratio, detail_message,
/*is_active=*/false);
};
// If the detail message is "Opening in 10 seconds..." and the size_ratio is
// "100/120 MB", then this returns "↓ 100/120 MB • Opening in 10 seconds...".
auto get_active_download_size_ratio_string =
[size_ratio](std::u16string detail_message) {
return GetBubbleStatusMessageWithBytes(size_ratio, detail_message,
/*is_active=*/true);
};
const auto completed_bytes = model_->GetCompletedBytes();
const auto total_bytes = model_->GetTotalBytes();
// If the detail message is "Done" and the total_bytes is "120 MB", then
// this returns "120 MB • Done".
auto get_total_string = [total_bytes](std::u16string detail_message) {
return GetBubbleStatusMessageWithBytes(ui::FormatBytes(total_bytes),
detail_message, /*is_active=*/false);
};
// The download is a CRX (app, extension, theme, ...) and it is being unpacked
// and validated.
if (model_->AllDataSaved() && model_->IsExtensionDownload()) {
// "120 MB • Adding to Chrome..."
return get_total_string(
l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING));
}
// A paused download: "100/120 MB • Paused"
if (model_->IsPaused()) {
return get_size_ratio_string(
l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED));
}
// A download scheduled to be opened when complete: "↓ 100/120 MB • Opening in
// 10 seconds"
if (model_->GetOpenWhenComplete()) {
if (!time_remaining_known)
// "100/120 MB • Opening when complete"
return get_size_ratio_string(
l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE));
// "↓ 100/120 MB • Opening in 10 seconds..."
return get_active_download_size_ratio_string(l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_STATUS_OPEN_IN,
ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
ui::TimeFormat::LENGTH_LONG, time_remaining)));
}
// In progress download with known time left: "↓ 100/120 MB • 10 seconds left"
if (time_remaining_known) {
return get_active_download_size_ratio_string(
ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
ui::TimeFormat::LENGTH_LONG, time_remaining));
}
if (completed_bytes == 0) {
// "0/120 MB • Starting..."
return get_size_ratio_string(
l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING));
} else if (completed_bytes < total_bytes || total_bytes == 0) {
// In progress download with no known time left and non-zero completed
// bytes: "100/120 MB • Resuming..." or "100 MB • Resuming..."
return get_size_ratio_string(
l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_RESUMING));
} else {
// "120 MB • Done"
return get_total_string(
l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_DONE));
}
}
std::u16string
DownloadUIModel::StatusTextBuilderBase::GetCompletedRemovedOrSavedStatusText()
const {
if (model_->GetFileExternallyRemoved()) {
return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_REMOVED);
}
return std::u16string();
}
std::u16string DownloadUIModel::StatusTextBuilder::GetCompletedStatusText()
const {
return GetCompletedRemovedOrSavedStatusText();
}
std::u16string
DownloadUIModel::BubbleStatusTextBuilder::GetCompletedStatusText() const {
std::u16string warning_status_text = GetBubbleWarningStatusText();
if (!warning_status_text.empty())
return warning_status_text;
std::u16string status_text = GetCompletedRemovedOrSavedStatusText();
if (!status_text.empty())
return status_text;
if (model_->GetEndTime().is_null()) {
// Offline items have these null.
return l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_DONE);
} else {
std::u16string delta_str;
if (model_->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE) {
// "2 B • Scan is done"
delta_str = l10n_util::GetStringUTF16(
IDS_DOWNLOAD_BUBBLE_STATUS_DEEP_SCANNING_DONE_UPDATED);
} else {
base::TimeDelta time_elapsed = base::Time::Now() - model_->GetEndTime();
// If less than 1 minute has passed since download completed: "2 B • Done"
// Otherwise: e.g. "2 B • 3 minutes ago"
delta_str =
time_elapsed.InMinutes() == 0
? l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_DONE)
: ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_ELAPSED,
ui::TimeFormat::LENGTH_LONG,
time_elapsed);
}
return GetBubbleStatusMessageWithBytes(
ui::FormatBytes(model_->GetTotalBytes()), delta_str,
/*is_active=*/false);
}
}
// To clarify variable / method names in methods below that help form failure
// status messages:
// long & descriptive / short & concise
// "Failed - <STATE_DESCRIPTION / STATE_MESSAGE>"
// "Fail to save to <WEB_DRIVE> - <STATE_DESCRIPTION / STATE_MESSAGE>"
// < DESCRIPTION/STATUS_TEXT >
std::u16string DownloadUIModel::StatusTextBuilderBase::GetFailStateMessage(
offline_items_collection::FailState fail_state) const {
std::u16string state_msg;
return OfflineItemUtils::GetFailStateMessage(fail_state);
}
void DownloadUIModel::set_clock_for_testing(base::Clock* clock) {
clock_ = clock;
}
void DownloadUIModel::set_status_text_builder_for_testing(bool for_bubble) {
if (for_bubble) {
status_text_builder_ =
std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>();
} else {
status_text_builder_ =
std::make_unique<DownloadUIModel::StatusTextBuilder>();
}
status_text_builder_->SetModel(this);
}
std::u16string DownloadUIModel::GetInterruptDescription() const {
const auto fail_state = GetLastFailState();
std::u16string state_description = FailStateDescription(fail_state);
return l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_INTERRUPTED,
state_description);
}
std::u16string DownloadUIModel::GetHistoryPageStatusText() const {
if (GetLastFailState() == FailState::SERVER_FAILED) {
// Display the full error description in case of server failure.
return GetInterruptDescription();
}
return GetStatusText();
}
void DownloadUIModel::StatusTextBuilderBase::SetModel(DownloadUIModel* model) {
model_ = model;
}
std::u16string DownloadUIModel::StatusTextBuilderBase::GetInterruptedStatusText(
FailState fail_state) const {
auto state_msg = GetFailStateMessage(fail_state);
return l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_INTERRUPTED, state_msg);
}
std::u16string
DownloadUIModel::BubbleStatusTextBuilder::GetInterruptedStatusText(
FailState fail_state) const {
int string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_WRONG;
switch (fail_state) {
case FailState::FILE_ACCESS_DENIED:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_NEEDS_PERMISSION;
break;
case FailState::FILE_NO_SPACE:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_DISK_FULL;
break;
case FailState::FILE_NAME_TOO_LONG:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_PATH_TOO_LONG;
break;
case FailState::FILE_TOO_LARGE:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_FILE_TOO_LARGE;
break;
case FailState::FILE_VIRUS_INFECTED:
string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_VIRUS;
break;
case FailState::FILE_BLOCKED:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_BLOCKED_ORGANIZATION;
break;
case FailState::FILE_SECURITY_CHECK_FAILED:
string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SECURITY_CHECK_FAILED;
break;
case FailState::FILE_TOO_SHORT:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_WRONG;
break;
case FailState::FILE_SAME_AS_SOURCE:
string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_FILE_SAME_AS_SOURCE;
break;
case FailState::NETWORK_INVALID_REQUEST:
case FailState::NETWORK_FAILED:
case FailState::NETWORK_INSTABILITY:
case FailState::NETWORK_TIMEOUT:
case FailState::NETWORK_DISCONNECTED:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_NETWORK_ERROR;
break;
case FailState::NETWORK_SERVER_DOWN:
case FailState::SERVER_FAILED:
case FailState::SERVER_CERT_PROBLEM:
case FailState::SERVER_UNREACHABLE:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_SITE_UNAVAILABLE;
break;
case FailState::SERVER_UNAUTHORIZED:
case FailState::SERVER_FORBIDDEN:
case FailState::SERVER_BAD_CONTENT:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_FILE_UNAVAILABLE;
break;
case FailState::USER_CANCELED:
string_id = IDS_DOWNLOAD_STATUS_CANCELLED;
break;
case FailState::FILE_TRANSIENT_ERROR:
case FailState::USER_SHUTDOWN:
case FailState::CRASH:
case FailState::SERVER_CONTENT_LENGTH_MISMATCH:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_FILE_UNFINISHED;
break;
case FailState::CANNOT_DOWNLOAD:
case FailState::SERVER_NO_RANGE:
case FailState::SERVER_CROSS_ORIGIN_REDIRECT:
case FailState::FILE_FAILED:
case FailState::FILE_HASH_MISMATCH:
string_id = IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_WRONG;
break;
case FailState::NO_FAILURE:
NOTREACHED();
}
return l10n_util::GetStringUTF16(string_id);
}
#if BUILDFLAG(FULL_SAFE_BROWSING)
void DownloadUIModel::CompleteSafeBrowsingScan() {}
void DownloadUIModel::ReviewScanningVerdict(
content::WebContents* web_contents) {}
#endif
bool DownloadUIModel::ShouldShowDropdown() const {
return true;
}
void DownloadUIModel::DetermineAndSetShouldPreferOpeningInBrowser(
const base::FilePath& target_path,
bool is_filetype_handled_safely) {}
std::u16string DownloadUIModel::GetInProgressAccessibleAlertText() const {
// Prefer to announce the time remaining, if known.
base::TimeDelta remaining;
if (TimeRemaining(&remaining)) {
// If complete, skip this round: a completion status update is coming soon.
if (remaining.is_zero())
return std::u16string();
const std::u16string remaining_string =
ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
ui::TimeFormat::LENGTH_SHORT, remaining);
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_STATUS_TIME_REMAINING_ACCESSIBLE_ALERT,
GetFileNameToReportUser().LossyDisplayName(), remaining_string);
}
// Time remaining is unknown, try to announce percent remaining.
if (PercentComplete() > 0) {
DCHECK_LE(PercentComplete(), 100);
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_STATUS_PERCENT_COMPLETE_ACCESSIBLE_ALERT,
GetFileNameToReportUser().LossyDisplayName(),
base::FormatNumber(100 - PercentComplete()));
}
// Percent remaining is also unknown, announce bytes to download.
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_STATUS_IN_PROGRESS_ACCESSIBLE_ALERT,
ui::FormatBytes(GetTotalBytes()),
GetFileNameToReportUser().LossyDisplayName());
}
bool DownloadUIModel::IsEncryptedArchive() const {
return false;
}