| // Copyright 2024 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/ui/download/download_bubble_security_view_info.h" |
| |
| #include "base/metrics/histogram_functions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/download/download_item_model.h" |
| #include "chrome/browser/download/download_item_warning_data.h" |
| #include "chrome/browser/download/download_ui_enterprise_util.h" |
| #include "chrome/browser/download/download_ui_safe_browsing_util.h" |
| #include "chrome/browser/download/offline_item_utils.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/color/chrome_color_id.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/buildflags/buildflags.h" |
| #include "components/offline_items_collection/core/fail_state.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| #if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) |
| #include "chrome/browser/enterprise/connectors/common.h" |
| #endif |
| |
| using download::DownloadItem; |
| using TailoredWarningType = DownloadUIModel::TailoredWarningType; |
| using offline_items_collection::FailState; |
| |
| DownloadBubbleSecurityViewInfoObserver:: |
| DownloadBubbleSecurityViewInfoObserver() = default; |
| DownloadBubbleSecurityViewInfoObserver:: |
| ~DownloadBubbleSecurityViewInfoObserver() = default; |
| |
| DownloadBubbleSecurityViewInfo::SubpageButton::SubpageButton( |
| DownloadCommands::Command command, |
| std::u16string label, |
| bool is_prominent, |
| std::optional<ui::ColorId> text_color) |
| : command(command), |
| label(label), |
| is_prominent(is_prominent), |
| text_color(text_color) {} |
| |
| DownloadBubbleSecurityViewInfo::DownloadBubbleSecurityViewInfo() = default; |
| DownloadBubbleSecurityViewInfo::~DownloadBubbleSecurityViewInfo() = default; |
| |
| void DownloadBubbleSecurityViewInfo::InitializeForDownload( |
| DownloadUIModel& model) { |
| if (model.GetContentId() != content_id_) { |
| Reset(); |
| download_item_observation_.Observe(model.GetDownloadItem()); |
| } |
| |
| OnDownloadUpdated(model.GetDownloadItem()); |
| } |
| |
| void DownloadBubbleSecurityViewInfo::SetSubpageButtonsForTesting( |
| std::vector<SubpageButton> buttons) { |
| subpage_buttons_ = std::move(buttons); |
| } |
| |
| bool DownloadBubbleSecurityViewInfo::HasSubpage() const { |
| return !warning_summary_.empty(); |
| } |
| |
| void DownloadBubbleSecurityViewInfo::OnDownloadUpdated( |
| download::DownloadItem* download) { |
| ContentId content_id = OfflineItemUtils::GetContentIdForDownload(download); |
| bool is_different_download = content_id != content_id_; |
| bool danger_type_changed = danger_type_ != download->GetDangerType(); |
| if (is_different_download) { |
| content_id_ = content_id; |
| title_text_ = download->GetFileNameToReportUser().LossyDisplayName(); |
| NotifyObservers( |
| &DownloadBubbleSecurityViewInfoObserver::OnContentIdChanged); |
| } |
| |
| if (is_different_download || danger_type_changed) { |
| danger_type_ = download->GetDangerType(); |
| PopulateForDownload(download); |
| NotifyObservers(&DownloadBubbleSecurityViewInfoObserver::OnInfoChanged); |
| } |
| } |
| |
| void DownloadBubbleSecurityViewInfo::OnDownloadRemoved( |
| download::DownloadItem* download) { |
| CHECK(content_id_ == OfflineItemUtils::GetContentIdForDownload(download)); |
| Reset(); |
| } |
| |
| void DownloadBubbleSecurityViewInfo::Reset() { |
| content_id_.reset(); |
| title_text_ = std::u16string(); |
| download_item_observation_.Reset(); |
| } |
| |
| void DownloadBubbleSecurityViewInfo::ClearForUpdate() { |
| // `title_text_` cannot change when a download is updated, so we do |
| // not need to clear it. |
| warning_summary_.clear(); |
| warning_secondary_text_.clear(); |
| warning_secondary_icon_ = nullptr; |
| learn_more_link_ = std::nullopt; |
| subpage_buttons_.clear(); |
| has_progress_bar_ = false; |
| is_progress_bar_looping_ = false; |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulateForDownload( |
| download::DownloadItem* download) { |
| ClearForUpdate(); |
| |
| DownloadItemModel model( |
| download, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()); |
| icon_and_color_ = IconAndColorForDownload(model); |
| |
| switch (model.GetState()) { |
| case download::DownloadItem::IN_PROGRESS: |
| case download::DownloadItem::COMPLETE: |
| PopulateForInProgressOrComplete(model); |
| break; |
| case download::DownloadItem::INTERRUPTED: |
| if (model.GetLastFailState() != |
| offline_items_collection::FailState::USER_CANCELED) { |
| PopulateForInterrupted(model); |
| } |
| break; |
| case download::DownloadItem::CANCELLED: |
| break; |
| case download::DownloadItem::MAX_DOWNLOAD_STATE: |
| NOTREACHED(); |
| } |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulateForDangerousUi( |
| const std::u16string& subpage_summary) { |
| warning_summary_ = subpage_summary; |
| PopulateLearnMoreLink( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_BLOCKED_LEARN_MORE_LINK), |
| DownloadCommands::Command::LEARN_MORE_DOWNLOAD_BLOCKED); |
| PopulatePrimarySubpageButton( |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DELETE_FROM_HISTORY), |
| DownloadCommands::Command::DISCARD); |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulateForSuspiciousUi( |
| const std::u16string& subpage_summary, |
| const std::u16string& secondary_subpage_button_label) { |
| warning_summary_ = subpage_summary; |
| PopulateLearnMoreLink( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_BLOCKED_LEARN_MORE_LINK), |
| DownloadCommands::Command::LEARN_MORE_DOWNLOAD_BLOCKED); |
| PopulatePrimarySubpageButton( |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DELETE_FROM_HISTORY), |
| DownloadCommands::Command::DISCARD); |
| PopulateSecondarySubpageButton(secondary_subpage_button_label, |
| DownloadCommands::Command::KEEP); |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulateForInterrupted( |
| const DownloadUIModel& model) { |
| // Only handle danger types that are terminated in the interrupted state in |
| // this function. The other danger types are handled in |
| // `PopulateForInProgressOrComplete`. |
| switch (model.GetDangerType()) { |
| case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED: |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ENCRYPTED); |
| return; |
| case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE: |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_TOO_BIG); |
| return; |
| case download::DOWNLOAD_DANGER_TYPE_FORCE_SAVE_TO_GDRIVE: |
| case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK: { |
| #if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) |
| if (enterprise_connectors::ShouldPromptReviewForDownload( |
| model.profile(), model.GetDownloadItem())) { |
| return; |
| } |
| #endif // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_SENSITIVE_CONTENT_BLOCK); |
| return; |
| } |
| case download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED: { |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_SCAN_FAILED); |
| return; |
| } |
| case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: |
| 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: |
| case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: |
| case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING: |
| case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING: |
| case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING: |
| case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING: |
| case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING: |
| case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED: |
| case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE: |
| case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_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_NOT_DANGEROUS: |
| case download::DOWNLOAD_DANGER_TYPE_MAX: |
| // Fall through to the failed UX |
| // TODO(drubery): Not all of these danger types can occur with a |
| // fail state. Investigate which can and cannot, and fall |
| // through less frequently. |
| break; |
| } |
| |
| switch (model.GetLastFailState()) { |
| case FailState::FILE_BLOCKED: |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_BLOCKED_ORGANIZATION); |
| return; |
| case FailState::FILE_NAME_TOO_LONG: |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_PATH_TOO_LONG); |
| return; |
| case FailState::FILE_NO_SPACE: |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_DISK_FULL); |
| return; |
| case FailState::SERVER_UNAUTHORIZED: |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_FILE_UNAVAILABLE); |
| return; |
| // No Retry in these cases. |
| case FailState::FILE_TOO_LARGE: |
| case FailState::FILE_VIRUS_INFECTED: |
| case FailState::FILE_SECURITY_CHECK_FAILED: |
| case FailState::FILE_ACCESS_DENIED: |
| case FailState::SERVER_FORBIDDEN: |
| case FailState::FILE_SAME_AS_SOURCE: |
| case FailState::SERVER_BAD_CONTENT: |
| // Try resume if possible or retry if not in these cases, and in the default |
| // case. |
| case FailState::NETWORK_INVALID_REQUEST: |
| case FailState::NETWORK_FAILED: |
| case FailState::NETWORK_TIMEOUT: |
| case FailState::NETWORK_DISCONNECTED: |
| case FailState::NETWORK_SERVER_DOWN: |
| case FailState::FILE_TRANSIENT_ERROR: |
| case FailState::USER_SHUTDOWN: |
| case FailState::CRASH: |
| case FailState::SERVER_CONTENT_LENGTH_MISMATCH: |
| case FailState::SERVER_NO_RANGE: |
| case FailState::SERVER_CROSS_ORIGIN_REDIRECT: |
| case FailState::FILE_FAILED: |
| case FailState::FILE_HASH_MISMATCH: |
| case FailState::SERVER_FAILED: |
| case FailState::SERVER_CERT_PROBLEM: |
| case FailState::SERVER_UNREACHABLE: |
| case FailState::FILE_TOO_SHORT: |
| return; |
| // Not possible because the USER_CANCELED fail state does not allow a call |
| // into this function |
| case FailState::USER_CANCELED: |
| // Deprecated |
| case FailState::NETWORK_INSTABILITY: |
| case FailState::CANNOT_DOWNLOAD: |
| NOTREACHED(); |
| case FailState::NO_FAILURE: |
| return; |
| } |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulateForInProgressOrComplete( |
| const DownloadUIModel& model) { |
| switch (model.GetInsecureDownloadStatus()) { |
| case download::DownloadItem::InsecureDownloadStatus::BLOCK: |
| case download::DownloadItem::InsecureDownloadStatus::WARN: |
| PopulateForSuspiciousUi( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_INSECURE), |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_CONTINUE_INSECURE_FILE)); |
| return; |
| case download::DownloadItem::InsecureDownloadStatus::UNKNOWN: |
| case download::DownloadItem::InsecureDownloadStatus::SAFE: |
| case download::DownloadItem::InsecureDownloadStatus::VALIDATED: |
| case download::DownloadItem::InsecureDownloadStatus::SILENT_BLOCK: |
| break; |
| } |
| |
| #if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) |
| if (enterprise_connectors::ShouldPromptReviewForDownload( |
| model.profile(), model.GetDownloadItem())) { |
| switch (model.GetDangerType()) { |
| case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: |
| case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: |
| case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING: |
| return; |
| default: |
| break; |
| } |
| } |
| #endif // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) |
| |
| if (TailoredWarningType type = model.GetTailoredWarningType(); |
| type != TailoredWarningType::kNoTailoredWarning) { |
| return PopulateForTailoredWarning(model); |
| } |
| |
| switch (model.GetDangerType()) { |
| case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: |
| if (model.IsExtensionDownload()) { |
| PopulateForSuspiciousUi( |
| l10n_util::GetStringFUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_UNKNOWN_SOURCE, |
| l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)), |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_CONTINUE_SUSPICIOUS_FILE)); |
| return; |
| } |
| if (WasSafeBrowsingVerdictObtained(model.GetDownloadItem())) { |
| PopulateForSuspiciousUi( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_DANGEROUS_FILE_TYPE), |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_CONTINUE_SUSPICIOUS_FILE)); |
| return; |
| } |
| if (ShouldShowWarningForNoSafeBrowsing(model.profile())) { |
| PopulateForFileTypeWarningNoSafeBrowsing(model); |
| return; |
| } |
| PopulateForSuspiciousUi( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_DANGEROUS_FILE_TYPE), |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_CONTINUE_UNVERIFIED_FILE)); |
| return; |
| |
| 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_DANGEROUS_URL: |
| PopulateForDangerousUi(l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_DANGEROUS)); |
| return; |
| |
| case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: |
| PopulateForDangerousUi(l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_DECEPTIVE)); |
| return; |
| |
| 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 |
| if (request_ap_verdicts) { |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ADVANCED_PROTECTION); |
| PopulatePrimarySubpageButton( |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DELETE), |
| DownloadCommands::Command::DISCARD); |
| PopulateSecondarySubpageButton( |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CONTINUE), |
| DownloadCommands::Command::KEEP, kColorDownloadItemTextWarning); |
| return; |
| } |
| PopulateForSuspiciousUi( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_UNCOMMON_FILE), |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_CONTINUE_SUSPICIOUS_FILE)); |
| return; |
| } |
| case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING: |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_SENSITIVE_CONTENT_WARNING); |
| PopulatePrimarySubpageButton( |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DELETE), |
| DownloadCommands::Command::DISCARD); |
| PopulateSecondarySubpageButton( |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CONTINUE), |
| DownloadCommands::Command::KEEP, kColorDownloadItemTextWarning); |
| return; |
| case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING: { |
| warning_summary_ = l10n_util::GetStringFUTF16( |
| model.IsTopLevelEncryptedArchive() |
| ? IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT_ENCRYPTED_ARCHIVE |
| : IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT_UPDATED, |
| u"\n\n"); |
| PopulatePrimarySubpageButton( |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_SCAN_UPDATED), |
| DownloadCommands::Command::DEEP_SCAN); |
| PopulateSecondarySubpageButton( |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_OPEN_UPDATED), |
| DownloadCommands::Command::BYPASS_DEEP_SCANNING); |
| PopulateLearnMoreLink(l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_DEEP_SCANNING_LINK), |
| DownloadCommands::LEARN_MORE_SCANNING); |
| return; |
| } |
| case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING: { |
| warning_summary_ = l10n_util::GetStringFUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT_LOCAL_DECRYPTION, |
| u"\n\n"); |
| PopulatePrimarySubpageButton( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_ACCEPT_LOCAL_DECRYPTION), |
| // These download commands are not propagated |
| // to the DownloadItem. Instead they are handled specially in |
| // DownloadBubbleSecurityView::ProcessButtonClick. That makes it |
| // okay that the we aren't really prompting for a deep scan. |
| // TODO(crbug.com/40931768): Remove this by creating a dedicated View |
| // for the local decryption prompt which directly handles the |
| // button presses. |
| DownloadCommands::Command::DEEP_SCAN); |
| PopulateSecondarySubpageButton( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_BYPASS_LOCAL_DECRYPTION), |
| DownloadCommands::Command::KEEP); |
| PopulateLearnMoreLink( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_BLOCKED_LEARN_MORE_LINK), |
| DownloadCommands::LEARN_MORE_SCANNING); |
| return; |
| } |
| case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING: |
| has_progress_bar_ = true; |
| is_progress_bar_looping_ = true; |
| if (DownloadItemWarningData::DownloadDeepScanTrigger( |
| model.GetDownloadItem()) == |
| DownloadItemWarningData::DeepScanTrigger:: |
| TRIGGER_IMMEDIATE_DEEP_SCAN) { |
| warning_summary_ = l10n_util::GetStringFUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_IMMEDIATE_DEEP_SCAN_IN_PROGRESS, |
| u"\n\n"); |
| PopulatePrimarySubpageButton( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_IMMEDIATE_DEEP_SCAN_CANCEL), |
| DownloadCommands::Command::DISCARD); |
| PopulateSecondarySubpageButton( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_IMMEDIATE_DEEP_SCAN_BYPASS), |
| DownloadCommands::Command::BYPASS_DEEP_SCANNING); |
| PopulateLearnMoreLink( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_DEEP_SCANNING_LINK), |
| DownloadCommands::LEARN_MORE_SCANNING); |
| } else { |
| warning_summary_ = l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING); |
| warning_secondary_icon_ = &vector_icons::kDocumentScannerIcon; |
| warning_secondary_text_ = |
| download::DoesDownloadConnectorBlock(model.profile(), |
| model.GetURL()) |
| ? l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING_ENTERPRISE_SECONDARY) |
| : l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING_SECONDARY); |
| PopulatePrimarySubpageButton( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING_DISCARD), |
| DownloadCommands::Command::DISCARD, |
| /*is_prominent=*/false); |
| if (!download::DoesDownloadConnectorBlock(model.profile(), |
| model.GetURL())) { |
| PopulateSecondarySubpageButton( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING_CANCEL), |
| DownloadCommands::Command::CANCEL_DEEP_SCAN); |
| } |
| } |
| return; |
| case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING: { |
| warning_summary_ = l10n_util::GetStringFUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_LOCAL_DECRYPTION_IN_PROGRESS, |
| u"\n\n"); |
| has_progress_bar_ = true; |
| is_progress_bar_looping_ = true; |
| // These download commands are not propagated |
| // to the DownloadItem. Instead they are handled specially |
| // in DownloadBubbleSecurityView::ProcessButtonClick. That |
| // means the semantics don't have to line up with the actual |
| // behavior of the download command. |
| // TODO(crbug.com/40931768): Remove this by creating a dedicated |
| // View for the local decryption prompt which directly |
| // handles the button presses. |
| PopulatePrimarySubpageButton( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_LOCAL_DECRYPTION_CANCEL), |
| DownloadCommands::Command::CANCEL); |
| PopulateSecondarySubpageButton( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_BYPASS_LOCAL_DECRYPTION), |
| DownloadCommands::Command::BYPASS_DEEP_SCANNING); |
| return; |
| } |
| case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED: |
| case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED: |
| case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE: |
| case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK: |
| 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_BLOCKED_SCAN_FAILED: |
| case download::DOWNLOAD_DANGER_TYPE_FORCE_SAVE_TO_GDRIVE: |
| case download::DOWNLOAD_DANGER_TYPE_MAX: |
| return; |
| } |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulateForTailoredWarning( |
| const DownloadUIModel& model) { |
| switch (model.GetTailoredWarningType()) { |
| case TailoredWarningType::kSuspiciousArchive: |
| PopulateForSuspiciousUi( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_ARCHIVE_MALWARE), |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_CONTINUE_SUSPICIOUS_FILE)); |
| return; |
| case TailoredWarningType::kCookieTheft: |
| PopulateForDangerousUi(l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_COOKIE_THEFT)); |
| return; |
| case TailoredWarningType::kNoTailoredWarning: { |
| NOTREACHED(); |
| } |
| } |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulateForFileTypeWarningNoSafeBrowsing( |
| const DownloadUIModel& model) { |
| PopulateForSuspiciousUi( |
| l10n_util::GetStringUTF16( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_NO_SAFE_BROWSING), |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CONTINUE_UNVERIFIED_FILE)); |
| // Clear the "Learn why Chrome..." link. If the user is not capable of turning |
| // on SB, do not show the default link and label. |
| learn_more_link_ = std::nullopt; |
| if (CanUserTurnOnSafeBrowsing(model.profile())) { |
| PopulateLearnMoreLink( |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_SAFE_BROWSING_SETTING_LABEL, |
| IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_SAFE_BROWSING_SETTING_LINK, |
| DownloadCommands::Command::OPEN_SAFE_BROWSING_SETTING); |
| } |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulateLearnMoreLink( |
| const std::u16string& link_text, |
| DownloadCommands::Command command) { |
| learn_more_link_ = LabelWithLink{ |
| link_text, LabelWithLink::LinkedRange{0u, link_text.length(), command}}; |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulateLearnMoreLink( |
| int label_text_id, |
| int link_text_id, |
| DownloadCommands::Command command) { |
| size_t link_start_offset = 0; |
| std::u16string link_text = l10n_util::GetStringUTF16(link_text_id); |
| std::u16string label_and_link_text = |
| l10n_util::GetStringFUTF16(label_text_id, link_text, &link_start_offset); |
| learn_more_link_ = LabelWithLink{ |
| label_and_link_text, LabelWithLink::LinkedRange{ |
| link_start_offset, link_text.length(), command}}; |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulatePrimarySubpageButton( |
| const std::u16string& label, |
| DownloadCommands::Command command, |
| bool is_prominent) { |
| CHECK(subpage_buttons_.empty()); |
| subpage_buttons_.emplace_back(command, label, is_prominent); |
| } |
| |
| void DownloadBubbleSecurityViewInfo::PopulateSecondarySubpageButton( |
| const std::u16string& label, |
| DownloadCommands::Command command, |
| std::optional<ui::ColorId> color) { |
| CHECK(subpage_buttons_.size() == 1); |
| subpage_buttons_.emplace_back(command, label, /*is_prominent=*/false, color); |
| } |