blob: 4f4b1f0aa611cb7029a78dfade4ca2d9b899e08e [file] [log] [blame]
// Copyright 2012 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/chrome_download_manager_delegate.h"
#include <algorithm>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_runner.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_core_service.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_crx_util.h"
#include "chrome/browser/download/download_dialog_types.h"
#include "chrome/browser/download/download_file_picker.h"
#include "chrome/browser/download/download_history.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_request_limiter.h"
#include "chrome/browser/download/download_stats.h"
#include "chrome/browser/download/download_target_determiner.h"
#include "chrome/browser/download/download_ui_safe_browsing_util.h"
#include "chrome/browser/download/insecure_download_blocking.h"
#include "chrome/browser/download/save_package_file_picker.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tab_group_sync/tab_group_sync_tab_state.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/download/public/common/download_features.h"
#include "components/download/public/common/download_interrupt_reasons.h"
#include "components/download/public/common/download_item.h"
#include "components/download/public/common/download_item_rename_handler.h"
#include "components/download/public/common/download_stats.h"
#include "components/enterprise/connectors/core/reporting_utils.h"
#include "components/offline_pages/buildflags/buildflags.h"
#include "components/pdf/common/constants.h"
#include "components/pdf/common/pdf_util.h"
#include "components/policy/content/policy_blocklist_service.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/buildflags.h"
#include "components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.h"
#include "components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_search_api/safe_search_util.h"
#include "components/saved_tab_groups/public/features.h"
#include "components/services/quarantine/public/mojom/quarantine.mojom.h"
#include "components/services/quarantine/quarantine_impl.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/service_process_host.h"
#include "content/public/common/buildflags.h"
#include "content/public/common/origin_util.h"
#include "extensions/buildflags/buildflags.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/filename_util.h"
#include "net/base/mime_util.h"
#include "net/base/network_change_notifier.h"
#include "ui/base/l10n/l10n_util.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/content_uri_utils.h"
#include "base/android/path_utils.h"
#include "base/process/process_handle.h"
#include "chrome/browser/android/tab_android.h"
#include "chrome/browser/download/android/download_controller.h"
#include "chrome/browser/download/android/download_dialog_bridge.h"
#include "chrome/browser/download/android/download_manager_service.h"
#include "chrome/browser/download/android/download_message_bridge.h"
#include "chrome/browser/download/android/download_open_source.h"
#include "chrome/browser/download/android/download_utils.h"
#include "chrome/browser/download/android/duplicate_download_dialog_bridge_delegate.h"
#include "chrome/browser/download/android/insecure_download_dialog_bridge.h"
#include "chrome/browser/download/android/new_navigation_observer.h"
#include "chrome/browser/flags/android/chrome_feature_list.h"
#include "chrome/browser/ui/android/pdf/pdf_jni_headers/PdfUtils_jni.h"
#include "chrome/browser/ui/android/tab_model/tab_model.h"
#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
#include "components/download/public/common/download_task_runner.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "content/public/common/content_features.h"
#include "net/http/http_content_disposition.h"
#include "third_party/blink/public/common/mime_util/mime_util.h"
#include "ui/android/window_android.h"
#else
#include "chrome/browser/download/download_item_web_app_data.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "chrome/browser/extensions/api/downloads/downloads_api.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/webstore_installer.h"
#include "extensions/common/constants.h"
#include "extensions/common/user_script.h"
#endif
#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
#include "chrome/browser/offline_pages/offline_page_utils.h"
#include "components/offline_pages/core/client_namespace_constants.h"
#endif
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
#include "components/enterprise/obfuscation/core/download_obfuscator.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ash/policy/skyvault/skyvault_rename_handler.h"
#endif
#if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
#include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "components/safe_browsing/content/browser/download/download_stats.h"
#include "components/safe_browsing/content/common/file_type_policies.h"
#endif
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
#include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h"
#endif
#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
#include "chrome/browser/enterprise/connectors/reporting/reporting_event_router_factory.h"
#include "components/enterprise/connectors/core/reporting_constants.h"
#include "components/enterprise/connectors/core/reporting_event_router.h"
#endif // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
#endif // BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
#include "base/android/device_info.h"
using content::BrowserThread;
using content::DownloadManager;
using download::DownloadItem;
using download::DownloadPathReservationTracker;
using download::PathValidationResult;
using safe_browsing::DownloadFileType;
using ConnectionType = net::NetworkChangeNotifier::ConnectionType;
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
using safe_browsing::DownloadProtectionService;
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
using extensions::CrxInstaller;
using extensions::CrxInstallError;
#endif
namespace {
// How long an ephemeral warning lasts before being automatically canceled (if
// there is no user interaction).
constexpr base::TimeDelta kEphemeralWarningLifetimeBeforeCancel =
base::Hours(1);
bool IsEphemeralWarningCancellationEnabled() {
#if BUILDFLAG(IS_ANDROID)
return ShouldShowSafeBrowsingAndroidDownloadWarnings();
#elif BUILDFLAG(IS_CHROMEOS)
return false;
#else
return true;
#endif
}
#if BUILDFLAG(IS_ANDROID)
const char kPdfDirName[] = "pdfs";
#endif
// Used with GetPlatformDownloadPath() to indicate which platform path to
// return.
enum PlatformDownloadPathType {
// Return the platform specific target path.
PLATFORM_TARGET_PATH,
// Return the platform specific current path. If the download is in-progress
// and the download location is a local filesystem path, then
// GetPlatformDownloadPath will return the path to the intermediate file.
PLATFORM_CURRENT_PATH
};
// Returns a path in the form that that is expected by platform_util::OpenItem /
// platform_util::ShowItemInFolder / DownloadTargetDeterminer.
//
// How the platform path is determined is based on PlatformDownloadPathType.
base::FilePath GetPlatformDownloadPath(const DownloadItem* download,
PlatformDownloadPathType path_type) {
if (path_type == PLATFORM_TARGET_PATH)
return download->GetTargetFilePath();
return download->GetFullPath();
}
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
// Callback invoked by DownloadProtectionService::CheckClientDownload.
// |is_content_check_supported| is true if the SB service supports scanning the
// download for malicious content.
// |callback| is invoked with a danger type determined as follows:
//
// Danger type is (in order of preference):
// * DANGEROUS_URL, if the URL is a known malware site.
// * MAYBE_DANGEROUS_CONTENT, if the content will be scanned for
// malware. I.e. |is_content_check_supported| is true.
// * ALLOWLISTED_BY_POLICY, if the download matches enterprise whitelist.
// * NOT_DANGEROUS.
void CheckDownloadUrlDone(
DownloadTargetDeterminerDelegate::CheckDownloadUrlCallback callback,
const std::vector<GURL>& download_urls,
bool is_content_check_supported,
safe_browsing::DownloadCheckResult result) {
safe_browsing::WebUIInfoSingleton::GetInstance()->AddToDownloadUrlsChecked(
download_urls, result);
download::DownloadDangerType danger_type;
if (result == safe_browsing::DownloadCheckResult::SAFE ||
result == safe_browsing::DownloadCheckResult::UNKNOWN) {
// If this type of files is handled by the enhanced SafeBrowsing download
// protection, mark it as potentially dangerous content until we are done
// with scanning it.
if (is_content_check_supported)
danger_type = download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT;
else
danger_type = download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
} else if (result ==
safe_browsing::DownloadCheckResult::ALLOWLISTED_BY_POLICY) {
danger_type = download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY;
} else {
// If the URL is malicious, we'll use that as the danger type. The results
// of the content check, if one is performed, will be ignored.
danger_type = download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL;
}
std::move(callback).Run(danger_type);
}
#endif // SAFE_BROWSING_DOWNLOAD_PROTECTION
// Called asynchronously to determine the MIME type for |path|.
std::string GetMimeType(const base::FilePath& path) {
#if BUILDFLAG(IS_ANDROID)
if (path.IsContentUri()) {
return base::GetContentUriMimeType(path);
}
#endif
std::string mime_type;
net::GetMimeTypeFromFile(path, &mime_type);
return mime_type;
}
// On Android, Chrome wants to warn the user of file overwrites rather than
// uniquify.
#if BUILDFLAG(IS_ANDROID)
const DownloadPathReservationTracker::FilenameConflictAction
kDefaultPlatformConflictAction = DownloadPathReservationTracker::PROMPT;
#else
const DownloadPathReservationTracker::FilenameConflictAction
kDefaultPlatformConflictAction = DownloadPathReservationTracker::UNIQUIFY;
#endif
// Invoked when whether download can proceed is determined.
// Args: whether storage permission is granted and whether the download is
// allowed.
using CanDownloadCallback =
base::OnceCallback<void(bool /* storage permission granted */,
bool /*allow*/)>;
void CheckCanDownload(const content::WebContents::Getter& web_contents_getter,
const GURL& url,
const std::string& request_method,
std::optional<url::Origin> request_initiator,
bool from_download_cross_origin_redirect,
CanDownloadCallback can_download_cb) {
DownloadRequestLimiter* limiter =
g_browser_process->download_request_limiter();
if (limiter) {
limiter->CanDownload(web_contents_getter, url, request_method,
std::move(request_initiator),
from_download_cross_origin_redirect,
base::BindOnce(std::move(can_download_cb), true));
}
}
#if BUILDFLAG(IS_ANDROID)
// TODO(qinmin): reuse the similar function defined in
// DownloadResourceThrottle.
void OnDownloadAcquireFileAccessPermissionDone(
const content::WebContents::Getter& web_contents_getter,
const GURL& url,
const std::string& request_method,
std::optional<url::Origin> request_initiator,
CanDownloadCallback can_download_cb,
bool granted) {
if (granted) {
CheckCanDownload(web_contents_getter, url, request_method,
std::move(request_initiator),
false /* from_download_cross_origin_redirect */,
std::move(can_download_cb));
} else {
std::move(can_download_cb).Run(false, false);
}
}
// Overlays download location dialog result to target determiner.
void OnDownloadDialogClosed(
DownloadTargetDeterminerDelegate::ConfirmationCallback callback,
DownloadDialogResult result) {
switch (result.location_result) {
case DownloadLocationDialogResult::USER_CONFIRMED:
std::move(callback).Run(DownloadConfirmationResult::CONFIRMED_WITH_DIALOG,
ui::SelectedFileInfo(result.file_path));
break;
case DownloadLocationDialogResult::USER_CANCELED:
std::move(callback).Run(DownloadConfirmationResult::CANCELED,
ui::SelectedFileInfo());
break;
case DownloadLocationDialogResult::CONFIRMED_WITHOUT_USER_INPUT:
[[fallthrough]];
case DownloadLocationDialogResult::DUPLICATE_DIALOG:
// TODO(xingliu): Figure out the dialog behavior on multiple downloads.
// Currently we just let other downloads continue, which doesn't make
// sense.
std::move(callback).Run(
DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
ui::SelectedFileInfo(result.file_path));
break;
}
}
base::FilePath GetTempPdfDir() {
base::FilePath cache_dir;
base::android::GetCacheDirectory(&cache_dir);
return cache_dir.Append(kPdfDirName);
}
bool ShouldOpenPdfInlineInternal(bool incognito) {
JNIEnv* env = base::android::AttachCurrentThread();
return Java_PdfUtils_shouldOpenPdfInline(env, incognito);
}
void OnDetermineSavePackagePathDone(
content::SavePackagePathPickedCallback callback,
const base::FilePath& file_path,
const base::FilePath& display_name) {
content::SavePackagePathPickedParams param;
param.file_path = file_path;
param.save_type = content::SavePageType::SAVE_PAGE_TYPE_AS_MHTML;
param.display_name = display_name;
std::move(callback).Run(param, base::DoNothing());
}
#endif // BUILDFLAG(IS_ANDROID)
void OnCheckExistingDownloadPathDone(download::DownloadTargetInfo target_info,
download::DownloadTargetCallback callback,
bool file_exists) {
if (file_exists) {
target_info.interrupt_reason =
download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED;
}
std::move(callback).Run(std::move(target_info));
}
#if BUILDFLAG(IS_ANDROID)
// Callback used by Insecure Download infobar on Android. Unlike on Desktop,
// this infobar's entire life occurs prior to download start.
void HandleInsecureDownloadInfoBarResult(
download::DownloadItem* download_item,
download::DownloadTargetInfo target_info,
download::DownloadTargetCallback callback,
bool should_download) {
// If the download should be blocked, we can call the callback directly.
if (!should_download) {
target_info.danger_type = download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
target_info.interrupt_reason =
download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED;
target_info.insecure_download_status =
DownloadItem::InsecureDownloadStatus::SILENT_BLOCK;
std::move(callback).Run(std::move(target_info));
return;
}
target_info.insecure_download_status =
download::DownloadItem::InsecureDownloadStatus::VALIDATED;
// Otherwise, proceed as normal and check for a separate reservation with the
// same target path. If such a reservation exists, cancel this reservation.
const base::FilePath target_path = target_info.target_path;
DownloadPathReservationTracker::CheckDownloadPathForExistingDownload(
target_path, download_item,
base::BindOnce(&OnCheckExistingDownloadPathDone, std::move(target_info),
std::move(callback)));
}
#endif
void MaybeReportDangerousDownloadBlocked(
policy::DownloadRestriction download_restriction,
std::string danger_type,
std::string download_path,
download::DownloadItem* download) {
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
if (download_restriction !=
policy::DownloadRestriction::POTENTIALLY_DANGEROUS_FILES &&
download_restriction != policy::DownloadRestriction::DANGEROUS_FILES &&
download_restriction != policy::DownloadRestriction::MALICIOUS_FILES) {
return;
}
if (!download) {
return;
}
content::BrowserContext* browser_context =
content::DownloadItemUtils::GetBrowserContext(download);
Profile* profile = Profile::FromBrowserContext(browser_context);
if (!profile)
return;
// If |download| has a deep scanning malware verdict, then it means the
// dangerous file has already been reported.
auto* scan_result = static_cast<enterprise_connectors::ScanResult*>(
download->GetUserData(enterprise_connectors::ScanResult::kKey));
if (scan_result) {
for (const auto& metadata : scan_result->file_metadata) {
if (enterprise_connectors::ContainsMalwareVerdict(metadata.scan_response))
return;
}
}
#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
auto* router =
enterprise_connectors::ReportingEventRouterFactory::GetForBrowserContext(
browser_context);
if (router) {
std::string raw_digest_sha256;
if (download->GetState() == DownloadItem::DownloadState::COMPLETE) {
raw_digest_sha256 = download->GetHash();
}
google::protobuf::RepeatedPtrField<safe_browsing::ReferrerChainEntry>
referrer_chain;
if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedFieldsForSecOps)) {
referrer_chain =
safe_browsing::GetOrIdentifyReferrerChainForEnterprise(*download);
}
router->OnDangerousDownloadEvent(
download->GetURL(), download->GetTabUrl(), /*source*/ "",
/*destination=*/"", download_path, base::HexEncode(raw_digest_sha256),
danger_type, download->GetMimeType(),
enterprise_connectors::kFileDownloadDataTransferEventTrigger,
/*scan_id=*/"", /*content_transfer_method=*/"",
download->GetTotalBytes(), referrer_chain,
enterprise_connectors::EventResult::BLOCKED);
}
#endif // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
#endif // BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
}
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
download::DownloadDangerType SavePackageDangerType(
safe_browsing::DownloadCheckResult result) {
switch (result) {
case safe_browsing::DownloadCheckResult::ASYNC_SCANNING:
return download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING;
case safe_browsing::DownloadCheckResult::SENSITIVE_CONTENT_WARNING:
return download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING;
case safe_browsing::DownloadCheckResult::UNKNOWN:
// Failed scans with an unknown result should fail-open, so treat them as
// if they're not dangerous.
return download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
case safe_browsing::DownloadCheckResult::DEEP_SCANNED_SAFE:
return download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE;
case safe_browsing::DownloadCheckResult::BLOCKED_PASSWORD_PROTECTED:
return download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED;
case safe_browsing::DownloadCheckResult::BLOCKED_TOO_LARGE:
return download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE;
case safe_browsing::DownloadCheckResult::SENSITIVE_CONTENT_BLOCK:
return download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK;
case safe_browsing::DownloadCheckResult::BLOCKED_SCAN_FAILED:
return download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED;
default:
NOTREACHED();
}
}
#endif // BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
// Events related to ephemeral warning cancellation.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class CancelEphemeralWarningEvent {
// The delayed task is scheduled.
kCancellationScheduled = 0,
// The delayed task is invoked. The volume should be the sum of all buckets
// below.
kCancellationTriggered = 1,
// The cancellation failed because the download is not found.
kCancellationFailedDownloadNotFound = 2,
// The cancellation failed because the download is not an ephemeral warning.
kCancellationFailedDownloadNotEphemeral = 3,
// The cancellation succeeded.
kCancellationSucceeded = 4,
kMaxValue = kCancellationSucceeded
};
void LogCancelEphemeralWarningEvent(CancelEphemeralWarningEvent event) {
base::UmaHistogramEnumeration("SBClientDownload.CancelEphemeralWarning",
event);
}
void OnCheckDownloadAllowedFailed(
content::CheckDownloadAllowedCallback check_download_allowed_cb) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(check_download_allowed_cb), false));
}
} // namespace
ChromeDownloadManagerDelegate::ChromeDownloadManagerDelegate(Profile* profile)
: profile_(profile),
next_download_id_(download::DownloadItem::kInvalidId),
next_id_retrieved_(false),
download_prefs_(new DownloadPrefs(profile)),
is_file_picker_showing_(false) {
#if BUILDFLAG(IS_ANDROID)
download_dialog_bridge_ = std::make_unique<DownloadDialogBridge>();
download_message_bridge_ = std::make_unique<DownloadMessageBridge>();
#endif
}
ChromeDownloadManagerDelegate::~ChromeDownloadManagerDelegate() {
// If a DownloadManager was set for this, Shutdown() must be called.
DCHECK(!download_manager_);
}
void ChromeDownloadManagerDelegate::SetDownloadManager(DownloadManager* dm) {
if (download_manager_) {
download_manager_->RemoveObserver(this);
}
download_manager_ = dm;
// This is only for Incident Reporting, which does not report on downloads on
// Android.
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) && !BUILDFLAG(IS_ANDROID)
safe_browsing::SafeBrowsingService* sb_service =
g_browser_process->safe_browsing_service();
if (sb_service && !profile_->IsOffTheRecord()) {
// Include this download manager in the set monitored by safe browsing.
sb_service->AddDownloadManager(dm);
}
#endif
if (download_manager_) {
download_manager_->AddObserver(this);
}
}
#if BUILDFLAG(IS_ANDROID)
void ChromeDownloadManagerDelegate::ShowDownloadDialog(
gfx::NativeWindow native_window,
int64_t total_bytes,
DownloadLocationDialogType dialog_type,
const base::FilePath& suggested_path,
DownloadDialogBridge::DialogCallback callback) {
DCHECK(download_dialog_bridge_);
auto connection_type = net::NetworkChangeNotifier::GetConnectionType();
download_dialog_bridge_->ShowDialog(
native_window, total_bytes, connection_type, dialog_type, suggested_path,
profile_, std::move(callback));
}
void ChromeDownloadManagerDelegate::SetDownloadDialogBridgeForTesting(
DownloadDialogBridge* bridge) {
download_dialog_bridge_.reset(bridge);
}
void ChromeDownloadManagerDelegate::SetDownloadMessageBridgeForTesting(
DownloadMessageBridge* bridge) {
download_message_bridge_.reset(bridge);
}
#endif // BUILDFLAG(IS_ANDROID)
void ChromeDownloadManagerDelegate::Shutdown() {
download_prefs_.reset();
weak_ptr_factory_.InvalidateWeakPtrs();
if (download_manager_) {
download_manager_->RemoveObserver(this);
download_manager_ = nullptr;
}
}
void ChromeDownloadManagerDelegate::OnDownloadCanceledAtShutdown(
download::DownloadItem* item) {
// Be careful, limited objects are still alive at this point. This function is
// called at profile shutdown. Only keyed service, downloadItem and objects
// directly owned by the browser process are available.
MaybeSendDangerousDownloadCanceledReport(item, /*is_shutdown=*/true);
}
content::DownloadIdCallback
ChromeDownloadManagerDelegate::GetDownloadIdReceiverCallback() {
return base::BindOnce(&ChromeDownloadManagerDelegate::SetNextId,
weak_ptr_factory_.GetWeakPtr());
}
void ChromeDownloadManagerDelegate::SetNextId(uint32_t next_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!profile_->IsOffTheRecord());
// |download::DownloadItem::kInvalidId| will be returned only when history
// database failed to initialize.
bool history_db_available = (next_id != download::DownloadItem::kInvalidId);
RecordDatabaseAvailability(history_db_available);
if (history_db_available)
next_download_id_ = next_id;
next_id_retrieved_ = true;
IdCallbackVector callbacks;
id_callbacks_.swap(callbacks);
for (auto& callback : callbacks)
ReturnNextId(std::move(callback));
}
void ChromeDownloadManagerDelegate::GetNextId(
content::DownloadIdCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (profile_->IsOffTheRecord()) {
profile_->GetOriginalProfile()->GetDownloadManager()->GetNextId(
std::move(callback));
return;
}
if (!next_id_retrieved_) {
id_callbacks_.push_back(std::move(callback));
return;
}
ReturnNextId(std::move(callback));
}
void ChromeDownloadManagerDelegate::ReturnNextId(
content::DownloadIdCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!profile_->IsOffTheRecord());
// kInvalidId is returned to indicate the error.
std::move(callback).Run(next_download_id_);
if (next_download_id_ != download::DownloadItem::kInvalidId)
++next_download_id_;
}
bool ChromeDownloadManagerDelegate::DetermineDownloadTarget(
DownloadItem* download,
download::DownloadTargetCallback* callback) {
if (download->GetTargetFilePath().empty() &&
download->GetMimeType() == pdf::kPDFMimeType &&
!download->HasUserGesture()) {
ReportPDFLoadStatus(PDFLoadStatus::kTriggeredNoGestureDriveByDownload);
}
DownloadTargetDeterminer::CompletionCallback target_determined_callback =
base::BindOnce(&ChromeDownloadManagerDelegate::OnDownloadTargetDetermined,
weak_ptr_factory_.GetWeakPtr(), download->GetId(),
std::move(*callback));
base::FilePath download_path =
GetPlatformDownloadPath(download, PLATFORM_TARGET_PATH);
DownloadPathReservationTracker::FilenameConflictAction action =
kDefaultPlatformConflictAction;
#if BUILDFLAG(IS_ANDROID)
if (base::android::device_info::is_desktop()) {
action = DownloadPathReservationTracker::UNIQUIFY;
}
if (download->IsTransient()) {
if (download_path.empty() && download->GetMimeType() == pdf::kPDFMimeType &&
!download->IsMustDownload()) {
if (profile_->IsOffTheRecord() && download->GetDownloadFile() &&
download->GetDownloadFile()->IsMemoryFile()) {
download_path = download->GetDownloadFile()->FullPath();
action = DownloadPathReservationTracker::OVERWRITE;
} else {
base::FilePath generated_filename = net::GenerateFileName(
download->GetURL(), download->GetContentDisposition(),
profile_->GetPrefs()->GetString(prefs::kDefaultCharset),
download->GetSuggestedFilename(), download->GetMimeType(),
l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
download_path = GetTempPdfDir().Append(generated_filename);
action = DownloadPathReservationTracker::UNIQUIFY;
}
} else {
action = DownloadPathReservationTracker::OVERWRITE;
}
} else if (download_prefs_->download_restriction() ==
policy::DownloadRestriction::ALL_FILES) {
// If download will be blocked, no need to prompt the user.
action = DownloadPathReservationTracker::UNIQUIFY;
} else if (!download_path.empty()) {
// If this is a resumption attempt, don't prompt the user.
action = DownloadPathReservationTracker::UNIQUIFY;
}
#endif
DownloadTargetDeterminer::Start(download, download_path, action,
download_prefs_.get(), this,
std::move(target_determined_callback));
return true;
}
bool ChromeDownloadManagerDelegate::ShouldAutomaticallyOpenFile(
const GURL& url,
const base::FilePath& path) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (path.Extension().empty())
return false;
#if BUILDFLAG(ENABLE_EXTENSIONS)
// TODO(crbug.com/40129365): This determination is done based on |path|, while
// ShouldOpenDownload() detects extension downloads based on the
// characteristics of the download. Reconcile this.
if (path.MatchesExtension(extensions::kExtensionFileExtension))
return false;
#endif
bool should_open = download_prefs_->IsAutoOpenEnabled(url, path);
#if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
int64_t file_type_uma_value =
safe_browsing::FileTypePolicies::GetInstance()->UmaValueForFile(path);
if (should_open) {
base::UmaHistogramSparse("SBClientDownload.AutoOpenEnabledFileType",
file_type_uma_value);
} else {
base::UmaHistogramSparse("SBClientDownload.AutoOpenDisabledFileType",
file_type_uma_value);
}
#endif
return should_open;
}
bool ChromeDownloadManagerDelegate::ShouldAutomaticallyOpenFileByPolicy(
const GURL& url,
const base::FilePath& path) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (path.Extension().empty())
return false;
#if BUILDFLAG(ENABLE_EXTENSIONS)
// TODO(crbug.com/40129365): This determination is done based on |path|, while
// ShouldOpenDownload() detects extension downloads based on the
// characteristics of the download. Reconcile this.
if (path.MatchesExtension(extensions::kExtensionFileExtension))
return false;
#endif
return download_prefs_->IsAutoOpenByPolicy(url, path);
}
// static
void ChromeDownloadManagerDelegate::DisableSafeBrowsing(DownloadItem* item) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
SafeBrowsingState* state = static_cast<SafeBrowsingState*>(
item->GetUserData(&SafeBrowsingState::kSafeBrowsingUserDataKey));
if (!state) {
auto new_state = std::make_unique<SafeBrowsingState>();
state = new_state.get();
item->SetUserData(&SafeBrowsingState::kSafeBrowsingUserDataKey,
std::move(new_state));
}
state->CompleteDownload();
#endif
}
// static
bool ChromeDownloadManagerDelegate::IsDangerTypeBlocked(
download::DownloadDangerType danger_type) {
return danger_type == download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE ||
danger_type ==
download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED ||
danger_type ==
download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK ||
danger_type == download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED;
}
bool ChromeDownloadManagerDelegate::IsDownloadReadyForCompletion(
DownloadItem* item,
base::OnceClosure internal_complete_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
if (item->GetDangerType() == download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED) {
// For obfuscated files, deobfuscate after validation.
enterprise_obfuscation::DownloadObfuscationData* obfuscation_data =
static_cast<enterprise_obfuscation::DownloadObfuscationData*>(
item->GetUserData(
enterprise_obfuscation::DownloadObfuscationData::kUserDataKey));
if (obfuscation_data && obfuscation_data->is_obfuscated) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(&enterprise_obfuscation::DeobfuscateFileInPlace,
item->GetFullPath()),
base::BindOnce(
&ChromeDownloadManagerDelegate::OnDeobfuscationComplete,
weak_ptr_factory_.GetWeakPtr(),
std::move(internal_complete_callback)));
// Ensure that deobfuscation is ran only once.
// TODO(crbug.com/367259664): Move to `OnDeobfuscationComplete` after
// adding better error handling.
obfuscation_data->is_obfuscated = false;
return false;
}
}
#endif
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
// If this is a chrome triggered download, return true;
if (!item->RequireSafetyChecks()) {
return true;
}
if (!download_prefs_->safebrowsing_for_trusted_sources_enabled() &&
download_prefs_->IsFromTrustedSource(*item) &&
!safe_browsing::ShouldUploadBinaryForDeepScanning(item).has_value()) {
return true;
}
SafeBrowsingState* state = static_cast<SafeBrowsingState*>(
item->GetUserData(&SafeBrowsingState::kSafeBrowsingUserDataKey));
if (!state) {
// Begin the safe browsing download protection check.
state = new SafeBrowsingState();
state->set_callback(std::move(internal_complete_callback));
item->SetUserData(&SafeBrowsingState::kSafeBrowsingUserDataKey,
base::WrapUnique(state));
DownloadProtectionService* service = GetDownloadProtectionService();
if (service) {
DVLOG(2) << __func__ << "() Start SB download check for download = "
<< item->DebugString(false);
if (service->MaybeCheckClientDownload(
item, base::BindRepeating(
&ChromeDownloadManagerDelegate::CheckClientDownloadDone,
weak_ptr_factory_.GetWeakPtr(), item->GetId()))) {
return false;
}
}
// In case the service was disabled between the download starting and now,
// we need to restore the danger state.
download::DownloadDangerType danger_type = item->GetDangerType();
if (DownloadItemModel(item).GetDangerLevel() !=
DownloadFileType::NOT_DANGEROUS &&
(danger_type == download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS ||
danger_type ==
download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT)) {
DVLOG(2) << __func__
<< "() SB service disabled. Marking download as DANGEROUS FILE";
if (ShouldBlockFile(item,
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE)) {
MaybeReportDangerousDownloadBlocked(
download_prefs_->download_restriction(), "DANGEROUS_FILE_TYPE",
item->GetTargetFilePath().AsUTF8Unsafe(), item);
item->OnContentCheckCompleted(
// Specifying a dangerous type here would take precedence over the
// blocking of the file.
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED);
} else {
item->OnContentCheckCompleted(
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
download::DOWNLOAD_INTERRUPT_REASON_NONE);
}
state->CompleteDownload();
return false;
}
} else if (!state->is_complete() &&
item->GetDangerType() !=
download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED) {
// Don't complete the download until we have an answer.
state->set_callback(std::move(internal_complete_callback));
return false;
}
#endif // BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
return true;
}
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
void ChromeDownloadManagerDelegate::OnDeobfuscationComplete(
base::OnceClosure callback,
base::expected<void, enterprise_obfuscation::Error> deobfuscation_result) {
if (!deobfuscation_result.has_value()) {
// TODO(crbug.com/367259664): Add better error handling for deobfuscation.
DVLOG(1) << "Failed to deobfuscate download file.";
}
if (callback) {
std::move(callback).Run();
}
}
#endif
void ChromeDownloadManagerDelegate::ShouldCompleteDownloadInternal(
uint32_t download_id,
base::OnceClosure user_complete_callback) {
DownloadItem* item = download_manager_->GetDownload(download_id);
if (!item)
return;
auto [async_completion, sync_completion] =
base::SplitOnceCallback(std::move(user_complete_callback));
if (ShouldCompleteDownload(item, std::move(async_completion))) {
// If `ShouldCompleteDownload()` returns true, `async_completion` will never
// run.
std::move(sync_completion).Run();
}
}
bool ChromeDownloadManagerDelegate::ShouldCompleteDownload(
DownloadItem* item,
base::OnceClosure user_complete_callback) {
return IsDownloadReadyForCompletion(
item, base::BindOnce(
&ChromeDownloadManagerDelegate::ShouldCompleteDownloadInternal,
weak_ptr_factory_.GetWeakPtr(), item->GetId(),
std::move(user_complete_callback)));
}
bool ChromeDownloadManagerDelegate::ShouldOpenDownload(
DownloadItem* item,
content::DownloadOpenDelayedCallback callback) {
#if BUILDFLAG(ENABLE_EXTENSIONS)
if (download_crx_util::IsExtensionDownload(*item) &&
!extensions::WebstoreInstaller::GetAssociatedApproval(*item)) {
scoped_refptr<CrxInstaller> installer(
download_crx_util::CreateCrxInstaller(profile_, *item));
if (download_crx_util::OffStoreInstallAllowedByPrefs(profile_, *item)) {
installer->set_off_store_install_allow_reason(
CrxInstaller::OffStoreInstallAllowedBecausePref);
}
auto token = base::UnguessableToken::Create();
running_crx_installs_[token] = installer;
installer->AddInstallerCallback(base::BindOnce(
&ChromeDownloadManagerDelegate::OnInstallerDone,
weak_ptr_factory_.GetWeakPtr(), token, std::move(callback)));
if (extensions::UserScript::IsURLUserScript(item->GetURL(),
item->GetMimeType())) {
installer->InstallUserScript(item->GetFullPath(), item->GetURL());
} else {
installer->InstallCrx(item->GetFullPath());
}
// The status text and percent complete indicator will change now
// that we are installing a CRX. Update observers so that they pick
// up the change.
item->UpdateObservers();
return false;
}
#endif
return true;
}
bool ChromeDownloadManagerDelegate::ShouldObfuscateDownload(
download::DownloadItem* item) {
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
if (!base::FeatureList::IsEnabled(
enterprise_obfuscation::kEnterpriseFileObfuscation)) {
return false;
}
// Skip obfuscation for chrome-initiated, save package or parallel downloads.
if (!item || !item->RequireSafetyChecks() || item->IsSavePackageDownload() ||
item->IsParallelDownload()) {
return false;
}
// Skip obfuscation for large files if size is known.
if (static_cast<size_t>(item->GetTotalBytes()) >
safe_browsing::BinaryUploadService::kMaxUploadSizeBytes) {
return false;
}
// Skip obfuscation if there are no matching connector policies and for
// report-only scans.
Profile* profile = Profile::FromBrowserContext(
content::DownloadItemUtils::GetBrowserContext(item));
if (profile) {
auto settings = safe_browsing::ShouldUploadBinaryForDeepScanning(item);
if (settings.has_value() &&
settings.value().block_until_verdict ==
enterprise_connectors::BlockUntilVerdict::kBlock) {
item->SetUserData(
enterprise_obfuscation::DownloadObfuscationData::kUserDataKey,
std::make_unique<enterprise_obfuscation::DownloadObfuscationData>(
true));
return true;
}
}
#endif
return false;
}
bool ChromeDownloadManagerDelegate::InterceptDownloadIfApplicable(
const GURL& url,
const std::string& user_agent,
const std::string& content_disposition,
const std::string& mime_type,
const std::string& request_origin,
int64_t content_length,
bool is_transient,
bool is_content_initiated,
content::WebContents* web_contents) {
PolicyBlocklistService* service =
PolicyBlocklistFactory::GetForBrowserContext(profile_);
policy::URLBlocklist::URLBlocklistState blocklist_state =
service->GetURLBlocklistState(url);
if (blocklist_state ==
policy::URLBlocklist::URLBlocklistState::URL_IN_BLOCKLIST) {
LOG(WARNING) << "URL is blocked by a policy.";
return true;
}
#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// For background service downloads we don't want offline pages backend to
// intercept the download. |is_transient| flag is used to determine whether
// the download corresponds to background service. Additionally we don't want
// offline pages backend to intercept html files explicitly marked as
// attachments.
// Also, we only want to respond to browser actions, like saving pages from
// a context menu, not content initiated actions. See crbug.com/425492793.
if (!is_transient && !is_content_initiated &&
!net::HttpContentDisposition(content_disposition, std::string())
.is_attachment() &&
offline_pages::OfflinePageUtils::CanDownloadAsOfflinePage(url,
mime_type)) {
#if BUILDFLAG(IS_ANDROID)
if (profile_->IsOffTheRecord()) {
return false;
}
#endif // BUILDFLAG(IS_ANDROID)
offline_pages::OfflinePageUtils::ScheduleDownload(
web_contents, offline_pages::kDownloadNamespace, url,
offline_pages::OfflinePageUtils::DownloadUIActionFlags::ALL,
request_origin);
return true;
}
#endif
#if BUILDFLAG(IS_ANDROID)
if (base::android::device_info::is_automotive()) {
if (!blink::IsSupportedMimeType(mime_type) &&
!IsPdfAndSupported(mime_type, web_contents)) {
download_message_bridge_->ShowUnsupportedDownloadMessage(web_contents);
base::UmaHistogramEnumeration(
"Download.Blocked.ContentType.Automotive",
download::DownloadContentFromMimeType(mime_type, false));
return true;
}
}
if (ShouldOpenPdfInlineInternal(/*incognito=*/false) &&
mime_type == pdf::kPDFMimeType) {
// If this is already a file, there is no need to download.
if (url.SchemeIsFile() || url.SchemeIs("content")) {
return true;
}
}
#endif // BUILDFLAG(IS_ANDROID)
return false;
}
#if BUILDFLAG(IS_ANDROID)
bool ChromeDownloadManagerDelegate::IsPdfAndSupported(
const std::string& mime_type,
content::WebContents* web_contents) {
if (mime_type != pdf::kPDFMimeType) {
return false;
}
if (web_contents == nullptr || web_contents->GetBrowserContext() == nullptr) {
return false;
}
return ShouldOpenPdfInlineInternal(
web_contents->GetBrowserContext()->IsOffTheRecord());
}
#endif // BUILDFLAG(IS_ANDROID)
void ChromeDownloadManagerDelegate::GetSaveDir(
content::BrowserContext* browser_context,
base::FilePath* website_save_dir,
base::FilePath* download_save_dir) {
*website_save_dir = download_prefs_->SaveFilePath();
DCHECK(!website_save_dir->empty());
*download_save_dir = download_prefs_->DownloadPath();
}
void ChromeDownloadManagerDelegate::ChooseSavePath(
content::WebContents* web_contents,
const base::FilePath& suggested_path,
const base::FilePath::StringType& default_extension,
bool can_save_as_complete,
content::SavePackagePathPickedCallback callback) {
#if BUILDFLAG(IS_ANDROID)
if (!web_contents) {
return;
}
base::OnceCallback<void(bool)> confirm_callback =
base::BindOnce(&ChromeDownloadManagerDelegate::
RequestIncognitoSavePackageConfirmationDone,
weak_ptr_factory_.GetWeakPtr(), web_contents->GetURL(),
suggested_path, std::move(callback));
if (profile_->IsOffTheRecord()) {
RequestIncognitoWarningConfirmation(std::move(confirm_callback));
} else {
std::move(confirm_callback).Run(/*accepted=*/true);
}
#else
// Deletes itself.
new SavePackageFilePicker(web_contents, suggested_path, default_extension,
can_save_as_complete, download_prefs_.get(),
std::move(callback));
#endif
}
void ChromeDownloadManagerDelegate::SanitizeSavePackageResourceName(
base::FilePath* filename,
const GURL& source_url) {
#if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
safe_browsing::FileTypePolicies* file_type_policies =
safe_browsing::FileTypePolicies::GetInstance();
const PrefService* prefs = profile_->GetPrefs();
if (file_type_policies->GetFileDangerLevel(*filename, source_url, prefs) ==
safe_browsing::DownloadFileType::NOT_DANGEROUS)
return;
base::FilePath default_filename = base::FilePath::FromUTF8Unsafe(
l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
*filename = filename->AddExtension(default_filename.BaseName().value());
#endif
}
void ChromeDownloadManagerDelegate::SanitizeDownloadParameters(
download::DownloadUrlParameters* params) {
if (profile_->GetPrefs()->GetBoolean(
policy::policy_prefs::kForceGoogleSafeSearch)) {
GURL safe_url;
safe_search_api::ForceGoogleSafeSearch(params->url(), &safe_url);
if (!safe_url.is_empty())
params->set_url(std::move(safe_url));
}
}
void ChromeDownloadManagerDelegate::OpenDownloadUsingPlatformHandler(
DownloadItem* download) {
base::FilePath platform_path(
GetPlatformDownloadPath(download, PLATFORM_CURRENT_PATH));
DCHECK(!platform_path.empty());
platform_util::OpenItem(profile_, platform_path, platform_util::OPEN_FILE,
platform_util::OpenOperationCallback());
}
void ChromeDownloadManagerDelegate::OpenDownload(DownloadItem* download) {
DCHECK_EQ(DownloadItem::COMPLETE, download->GetState());
DCHECK(!download->GetTargetFilePath().empty());
if (!download->CanOpenDownload())
return;
if (!IsMostRecentDownloadItemAtFilePath(download))
return;
MaybeSendDangerousDownloadOpenedReport(download,
false /* show_download_in_folder */);
#if BUILDFLAG(IS_ANDROID)
DownloadUtils::OpenDownload(download, DownloadOpenSource::kUnknown);
#else
if (!DownloadItemModel(download).ShouldPreferOpeningInBrowser()) {
RecordDownloadOpen(DOWNLOAD_OPEN_METHOD_DEFAULT_PLATFORM,
download->GetMimeType());
OpenDownloadUsingPlatformHandler(download);
return;
}
Browser* browser = chrome::ScopedTabbedBrowserDisplayer(profile_).browser();
CHECK(browser && browser->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP));
content::OpenURLParams params(
net::FilePathToFileURL(download->GetTargetFilePath()),
content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false);
if (download->GetMimeType() == "application/x-x509-user-cert") {
chrome::ShowSettingsSubPage(browser, "certificates");
} else {
browser->OpenURL(params, /*navigation_handle_callback=*/{});
}
RecordDownloadOpen(DOWNLOAD_OPEN_METHOD_DEFAULT_BROWSER,
download->GetMimeType());
#endif // BUILDFLAG(IS_ANDROID)
}
bool ChromeDownloadManagerDelegate::IsMostRecentDownloadItemAtFilePath(
DownloadItem* download) {
Profile* profile = Profile::FromBrowserContext(
content::DownloadItemUtils::GetBrowserContext(download));
std::vector<Profile*> profiles_to_check =
profile->GetOriginalProfile()->GetAllOffTheRecordProfiles();
profiles_to_check.push_back(profile->GetOriginalProfile());
std::vector<raw_ptr<DownloadItem, VectorExperimental>> all_downloads;
for (auto* profile_to_check : profiles_to_check) {
content::DownloadManager* manager = profile_to_check->GetDownloadManager();
if (manager)
manager->GetAllDownloads(&all_downloads);
}
for (const download::DownloadItem* item : all_downloads) {
if (item->GetGuid() == download->GetGuid() ||
item->GetTargetFilePath() != download->GetTargetFilePath())
continue;
if (item->GetState() == DownloadItem::IN_PROGRESS)
return false;
}
return true;
}
void ChromeDownloadManagerDelegate::ShowDownloadInShell(
DownloadItem* download) {
if (!download->CanShowInFolder())
return;
MaybeSendDangerousDownloadOpenedReport(download,
true /* show_download_in_folder */);
base::FilePath platform_path(
GetPlatformDownloadPath(download, PLATFORM_CURRENT_PATH));
DCHECK(!platform_path.empty());
platform_util::ShowItemInFolder(profile_, platform_path);
}
std::string
ChromeDownloadManagerDelegate::ApplicationClientIdForFileScanning() {
return std::string(chrome::kApplicationClientIDStringForAVScanning);
}
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
DownloadProtectionService*
ChromeDownloadManagerDelegate::GetDownloadProtectionService() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
safe_browsing::SafeBrowsingService* sb_service =
g_browser_process->safe_browsing_service();
if (sb_service && sb_service->download_protection_service()) {
return sb_service->download_protection_service();
}
return nullptr;
}
#endif
void ChromeDownloadManagerDelegate::GetInsecureDownloadStatus(
download::DownloadItem* download,
const base::FilePath& virtual_path,
GetInsecureDownloadStatusCallback callback) {
DCHECK(download);
DownloadItem::InsecureDownloadStatus status =
GetInsecureDownloadStatusForDownload(profile_, virtual_path, download);
#if BUILDFLAG(IS_ANDROID)
// Allow insecure PDF download to go through if it is displayed inline.
if (download->IsTransient() && download->GetMimeType() == pdf::kPDFMimeType &&
!download->IsMustDownload()) {
if (ShouldOpenPdfInline() &&
base::FeatureList::IsEnabled(
download::features::kAllowedMixedContentInlinePdf)) {
status = DownloadItem::InsecureDownloadStatus::SAFE;
}
}
#endif // BUILDFLAG(IS_ANDROID)
std::move(callback).Run(status);
}
void ChromeDownloadManagerDelegate::NotifyExtensions(
DownloadItem* download,
const base::FilePath& virtual_path,
NotifyExtensionsCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!download->IsTransient());
#if BUILDFLAG(ENABLE_EXTENSIONS)
extensions::ExtensionDownloadsEventRouter* router =
DownloadCoreServiceFactory::GetForBrowserContext(profile_)
->GetExtensionEventRouter();
if (router) {
router->OnDeterminingFilename(download, virtual_path.BaseName(),
std::move(callback));
return;
}
#endif
std::move(callback).Run(base::FilePath(),
DownloadPathReservationTracker::UNIQUIFY);
}
void ChromeDownloadManagerDelegate::ReserveVirtualPath(
download::DownloadItem* download,
const base::FilePath& virtual_path,
bool create_directory,
DownloadPathReservationTracker::FilenameConflictAction conflict_action,
DownloadTargetDeterminerDelegate::ReservedPathCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!virtual_path.empty());
base::FilePath document_dir;
base::PathService::Get(chrome::DIR_USER_DOCUMENTS, &document_dir);
DownloadPathReservationTracker::GetReservedPath(
download, virtual_path, download_prefs_->DownloadPath(), document_dir,
create_directory, conflict_action, std::move(callback));
}
#if BUILDFLAG(IS_ANDROID)
void ChromeDownloadManagerDelegate::RequestIncognitoWarningConfirmation(
IncognitoWarningConfirmationCallback callback) {
download_message_bridge_->ShowIncognitoDownloadMessage(std::move(callback));
}
#endif
void ChromeDownloadManagerDelegate::RequestConfirmation(
DownloadItem* download,
const base::FilePath& suggested_path,
DownloadConfirmationReason reason,
DownloadTargetDeterminerDelegate::ConfirmationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!download->IsTransient());
// TODO(xingliu): We should abstract a DownloadFilePicker interface and make all
// platform use it.
#if BUILDFLAG(IS_ANDROID)
content::WebContents* web_contents =
content::DownloadItemUtils::GetWebContents(download);
if (reason == DownloadConfirmationReason::SAVE_AS) {
// If this is a 'Save As' download, just run without confirmation.
std::move(callback).Run(
DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
ui::SelectedFileInfo(suggested_path));
return;
}
if (!web_contents || reason == DownloadConfirmationReason::UNEXPECTED) {
// If there are no web_contents and there are no errors (ie. location
// dialog is only being requested because of a user preference),
// continue.
if (reason == DownloadConfirmationReason::PREFERENCE) {
std::move(callback).Run(
DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
ui::SelectedFileInfo(suggested_path));
return;
}
if (reason == DownloadConfirmationReason::TARGET_PATH_NOT_WRITEABLE) {
OnDownloadCanceled(download, true /* has_no_external_storage */);
std::move(callback).Run(DownloadConfirmationResult::CANCELED,
ui::SelectedFileInfo());
return;
}
// If we cannot reserve the path and the WebContents is already gone,
// there is no way to prompt user for a dialog. This could happen after
// chrome gets killed, and user tries to resume a download while another
// app has created the target file (not the temporary .crdownload file).
OnDownloadCanceled(download, false /* has_no_external_storage */);
std::move(callback).Run(DownloadConfirmationResult::CANCELED,
ui::SelectedFileInfo());
return;
}
if (reason == DownloadConfirmationReason::TARGET_CONFLICT) {
// If there is a file that already has the same name, try to generate a
// unique name for the new download (ie. "image (1).png" vs
// "image.png").
base::FilePath download_dir;
if (!base::android::GetDownloadsDirectory(&download_dir)) {
std::move(callback).Run(DownloadConfirmationResult::CANCELED,
ui::SelectedFileInfo());
return;
}
if (download->GetMimeType() == pdf::kPDFMimeType) {
download::RecordDuplicatePdfDownloadTriggered(/*open_inline=*/false);
}
if (!download_prefs_->PromptForDownload()) {
DuplicateDownloadDialogBridgeDelegate::GetInstance()->CreateDialog(
download, suggested_path, web_contents, std::move(callback));
return;
}
DownloadPathReservationTracker::GetReservedPath(
download, suggested_path, download_dir,
base::FilePath() /* fallback_directory */, true,
DownloadPathReservationTracker::UNIQUIFY,
base::BindOnce(
&ChromeDownloadManagerDelegate::GenerateUniqueFileNameDone,
weak_ptr_factory_.GetWeakPtr(), download->GetGuid(),
std::move(callback)));
return;
}
// Figure out type of dialog and display.
DownloadLocationDialogType dialog_type = DownloadLocationDialogType::DEFAULT;
switch (reason) {
case DownloadConfirmationReason::TARGET_NO_SPACE:
dialog_type = DownloadLocationDialogType::LOCATION_FULL;
break;
case DownloadConfirmationReason::TARGET_PATH_NOT_WRITEABLE:
dialog_type = DownloadLocationDialogType::LOCATION_NOT_FOUND;
break;
case DownloadConfirmationReason::NAME_TOO_LONG:
dialog_type = DownloadLocationDialogType::NAME_TOO_LONG;
break;
case DownloadConfirmationReason::PREFERENCE:
default:
break;
}
gfx::NativeWindow native_window = web_contents->GetTopLevelNativeWindow();
ShowDownloadDialog(
native_window, download->GetTotalBytes(), dialog_type, suggested_path,
base::BindOnce(&OnDownloadDialogClosed, std::move(callback)));
return;
#else // BUILDFLAG(IS_ANDROID)
// Desktop Chrome displays a file picker for all confirmation needs. We can do
// better.
if (is_file_picker_showing_) {
file_picker_callbacks_.emplace_back(
base::BindOnce(&ChromeDownloadManagerDelegate::ShowFilePicker,
weak_ptr_factory_.GetWeakPtr(), download->GetGuid(),
suggested_path, std::move(callback)));
} else {
is_file_picker_showing_ = true;
ShowFilePicker(download->GetGuid(), suggested_path, std::move(callback));
}
#endif // BUILDFLAG(IS_ANDROID)
}
void ChromeDownloadManagerDelegate::OnConfirmationCallbackComplete(
DownloadTargetDeterminerDelegate::ConfirmationCallback callback,
DownloadConfirmationResult result,
const ui::SelectedFileInfo& selected_file_info) {
std::move(callback).Run(result, selected_file_info);
if (!file_picker_callbacks_.empty()) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(file_picker_callbacks_.front()));
file_picker_callbacks_.pop_front();
} else {
is_file_picker_showing_ = false;
}
}
void ChromeDownloadManagerDelegate::ShowFilePicker(
const std::string& guid,
const base::FilePath& suggested_path,
DownloadTargetDeterminerDelegate::ConfirmationCallback callback) {
DownloadItem* download = download_manager_->GetDownloadByGuid(guid);
if (download) {
ShowFilePickerForDownload(download, suggested_path, std::move(callback));
} else {
OnConfirmationCallbackComplete(std::move(callback),
DownloadConfirmationResult::CANCELED,
ui::SelectedFileInfo());
}
}
void ChromeDownloadManagerDelegate::ShowFilePickerForDownload(
DownloadItem* download,
const base::FilePath& suggested_path,
DownloadTargetDeterminerDelegate::ConfirmationCallback callback) {
DCHECK(download);
DownloadFilePicker::ShowFilePicker(
download, suggested_path,
base::BindOnce(
&ChromeDownloadManagerDelegate::OnConfirmationCallbackComplete,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
#if BUILDFLAG(IS_ANDROID)
void ChromeDownloadManagerDelegate::GenerateUniqueFileNameDone(
const std::string& download_guid,
DownloadTargetDeterminerDelegate::ConfirmationCallback callback,
PathValidationResult result,
const base::FilePath& target_path) {
// After a new, unique filename has been generated, display the error dialog
// with the filename automatically set to be the unique filename.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (download::IsPathValidationSuccessful(result)) {
if (download_prefs_->PromptForDownload()) {
download::DownloadItem* download =
download_manager_->GetDownloadByGuid(download_guid);
content::WebContents* web_contents =
download ? content::DownloadItemUtils::GetWebContents(download)
: nullptr;
gfx::NativeWindow native_window =
web_contents ? web_contents->GetTopLevelNativeWindow() : nullptr;
// Null native window will be handled by ShowDownloadDialog().
ShowDownloadDialog(
native_window, 0 /* total_bytes */,
DownloadLocationDialogType::NAME_CONFLICT, target_path,
base::BindOnce(&OnDownloadDialogClosed, std::move(callback)));
return;
}
// If user chose not to show download location dialog, uses current unique
// target path.
std::move(callback).Run(
DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
ui::SelectedFileInfo(target_path));
} else {
// If the name generation failed, fail the download.
std::move(callback).Run(DownloadConfirmationResult::FAILED,
ui::SelectedFileInfo());
}
}
void ChromeDownloadManagerDelegate::OnDownloadCanceled(
download::DownloadItem* download,
bool has_no_external_storage) {
DownloadManagerService::OnDownloadCanceled(download, has_no_external_storage);
}
#endif // BUILDFLAG(IS_ANDROID)
void ChromeDownloadManagerDelegate::DetermineLocalPath(
DownloadItem* download,
const base::FilePath& virtual_path,
download::LocalPathCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
download::DetermineLocalPath(download, virtual_path, std::move(callback));
}
void ChromeDownloadManagerDelegate::CheckDownloadUrl(
DownloadItem* download,
const base::FilePath& suggested_path,
CheckDownloadUrlCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
safe_browsing::DownloadProtectionService* service =
GetDownloadProtectionService();
if (service) {
bool is_content_check_supported =
service->IsSupportedDownload(*download, suggested_path);
DVLOG(2) << __func__ << "() Start SB URL check for download = "
<< download->DebugString(false);
if (service->ShouldCheckDownloadUrl(download)) {
service->CheckDownloadUrl(
download,
base::BindOnce(&CheckDownloadUrlDone, std::move(callback),
download->GetUrlChain(), is_content_check_supported));
return;
}
}
#endif
std::move(callback).Run(download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
}
void ChromeDownloadManagerDelegate::GetFileMimeType(
const base::FilePath& path,
GetFileMimeTypeCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()}, base::BindOnce(&GetMimeType, path),
std::move(callback));
}
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
void ChromeDownloadManagerDelegate::CheckClientDownloadDone(
uint32_t download_id,
safe_browsing::DownloadCheckResult result) {
if (!download_manager_)
return;
DownloadItem* item = download_manager_->GetDownload(download_id);
if (!item ||
(item->GetState() != DownloadItem::IN_PROGRESS &&
item->GetDangerType() != download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING &&
item->GetDangerType() !=
download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING)) {
return;
}
DVLOG(2) << __func__ << "() download = " << item->DebugString(false)
<< " verdict = " << static_cast<int>(result);
// Indicates whether we expect future verdicts on this download. For example,
// if Safe Browsing is performing deep scanning, we will receive a more
// specific verdict later.
bool is_pending_scanning = false;
// We only mark the content as being dangerous if the download's safety state
// has not been set to DANGEROUS yet. We don't want to show two warnings.
if (item->GetDangerType() == download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS ||
item->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT ||
item->GetDangerType() == download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING ||
item->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING ||
item->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING ||
item->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING) {
download::DownloadDangerType danger_type =
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
switch (result) {
case safe_browsing::DownloadCheckResult::UNKNOWN:
case safe_browsing::DownloadCheckResult::SAFE:
// For DANGEROUS file types, we still want to warn the user, even if
// Safe Browsing is unsure about the file.
if (DownloadItemModel(item).GetDangerLevel() ==
DownloadFileType::DANGEROUS) {
danger_type = download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
}
break;
case safe_browsing::DownloadCheckResult::DANGEROUS:
danger_type = download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT;
break;
case safe_browsing::DownloadCheckResult::UNCOMMON:
danger_type = download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT;
break;
case safe_browsing::DownloadCheckResult::DANGEROUS_HOST:
danger_type = download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST;
break;
case safe_browsing::DownloadCheckResult::POTENTIALLY_UNWANTED:
danger_type = download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED;
break;
case safe_browsing::DownloadCheckResult::ALLOWLISTED_BY_POLICY:
danger_type = download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY;
break;
case safe_browsing::DownloadCheckResult::ASYNC_SCANNING:
is_pending_scanning = true;
danger_type = download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING;
break;
case safe_browsing::DownloadCheckResult::ASYNC_LOCAL_PASSWORD_SCANNING:
is_pending_scanning = true;
danger_type =
download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING;
break;
case safe_browsing::DownloadCheckResult::BLOCKED_PASSWORD_PROTECTED:
danger_type = download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED;
break;
case safe_browsing::DownloadCheckResult::BLOCKED_TOO_LARGE:
danger_type = download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE;
break;
case safe_browsing::DownloadCheckResult::SENSITIVE_CONTENT_WARNING:
danger_type = download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING;
break;
case safe_browsing::DownloadCheckResult::SENSITIVE_CONTENT_BLOCK:
danger_type = download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK;
break;
case safe_browsing::DownloadCheckResult::DEEP_SCANNED_SAFE:
danger_type = download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE;
break;
case safe_browsing::DownloadCheckResult::PROMPT_FOR_SCANNING:
danger_type = download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING;
is_pending_scanning = true;
break;
case safe_browsing::DownloadCheckResult::DANGEROUS_ACCOUNT_COMPROMISE:
danger_type =
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE;
break;
case safe_browsing::DownloadCheckResult::DEEP_SCANNED_FAILED:
danger_type = download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED;
break;
case safe_browsing::DownloadCheckResult::
PROMPT_FOR_LOCAL_PASSWORD_SCANNING:
is_pending_scanning = true;
danger_type =
download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING;
break;
case safe_browsing::DownloadCheckResult::BLOCKED_SCAN_FAILED:
danger_type = download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED;
break;
case safe_browsing::DownloadCheckResult::IMMEDIATE_DEEP_SCAN:
#if !BUILDFLAG(IS_ANDROID)
safe_browsing::DownloadProtectionService::UploadForConsumerDeepScanning(
item,
DownloadItemWarningData::DeepScanTrigger::
TRIGGER_IMMEDIATE_DEEP_SCAN,
/*password=*/std::nullopt);
#endif
// We return early because starting deep scanning immediately triggers
// this function with a `DownloadCheckResult` of `ASYNC_SCANNING`. Doing
// two updates would lead to two announced accessible alerts. See
// https://crbug.com/40926583.
return;
}
DCHECK_NE(danger_type,
download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT);
if (item->GetState() == DownloadItem::COMPLETE &&
(item->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING ||
item->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING)) {
// If the file was opened during async scanning, we override the danger
// type, since the user can no longer discard the download.
if (danger_type != download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
item->OnAsyncScanningCompleted(
download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS);
// Because the file has been opened before the verdict was available,
// the reporter must be manually notified that it needs to record the
// bypass. This is because the bypass wasn't reported on open to avoid
// sending a bypass event for a non-dangerous/sensitive file.
GetDownloadProtectionService()->ReportDelayedBypassEvent(item,
danger_type);
} else {
item->OnAsyncScanningCompleted(danger_type);
}
} else if (ShouldBlockFile(item, danger_type)) {
// Specifying a dangerous type here would take precedence over the
// blocking of the file. For BLOCKED_TOO_LARGE and
// BLOCKED_PASSWORD_PROTECTED, we want to display more clear UX, so
// allow those danger types.
if (!IsDangerTypeBlocked(danger_type)) {
danger_type = download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
MaybeReportDangerousDownloadBlocked(
download_prefs_->download_restriction(), "DANGEROUS_FILE_TYPE",
item->GetTargetFilePath().AsUTF8Unsafe(), item);
}
item->OnContentCheckCompleted(
danger_type, download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED);
} else {
item->OnContentCheckCompleted(danger_type,
download::DOWNLOAD_INTERRUPT_REASON_NONE);
}
}
if (!is_pending_scanning) {
SafeBrowsingState* state = static_cast<SafeBrowsingState*>(
item->GetUserData(&SafeBrowsingState::kSafeBrowsingUserDataKey));
state->CompleteDownload();
}
}
void ChromeDownloadManagerDelegate::CheckSavePackageScanningDone(
uint32_t download_id,
safe_browsing::DownloadCheckResult result) {
if (!download_manager_)
return;
DownloadItem* item = download_manager_->GetDownload(download_id);
if (!item || (item->GetState() != DownloadItem::IN_PROGRESS &&
item->GetDangerType() !=
download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING)) {
return;
}
// We only mark the content as being sensitive if the download's danger state
// has not been set yet. We don't want to show two warnings.
if (item->GetDangerType() == download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS ||
item->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT ||
item->GetDangerType() == download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING ||
item->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING) {
download::DownloadDangerType danger_type = SavePackageDangerType(result);
if (item->GetState() == DownloadItem::COMPLETE &&
item->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING) {
// If the save package was opened during async scanning, we override the
// danger type, since the user can no longer discard the download.
if (danger_type != download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
item->OnAsyncScanningCompleted(
download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS);
// Because the file has been opened before the verdict was available,
// the reporter must be manually notified that it needs to record the
// bypass. This is because the bypass wasn't reported on open to avoid
// sending a bypass event for a non-dangerous/sensitive file.
GetDownloadProtectionService()->ReportDelayedBypassEvent(item,
danger_type);
} else {
item->OnAsyncScanningCompleted(danger_type);
}
} else if (IsDangerTypeBlocked(danger_type)) {
item->OnContentCheckCompleted(
danger_type, download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED);
} else {
item->OnContentCheckCompleted(danger_type,
download::DOWNLOAD_INTERRUPT_REASON_NONE);
}
}
// RunSavePackageScanningCallback is called after OnAsyncScanningCompleted or
// OnContentCheckCompleted so that the package completes correctly after a
// scanning-specific UI has been applied to `item`.
switch (result) {
// These results imply the scanning is either not done or that the Save
// Package being allowed/blocked depends on user action following a
// warning, so the callback doesn't need to run.
case safe_browsing::DownloadCheckResult::ASYNC_SCANNING:
case safe_browsing::DownloadCheckResult::SENSITIVE_CONTENT_WARNING:
break;
case safe_browsing::DownloadCheckResult::UNKNOWN:
case safe_browsing::DownloadCheckResult::DEEP_SCANNED_SAFE:
enterprise_connectors::RunSavePackageScanningCallback(item,
/*allowed*/ true);
break;
case safe_browsing::DownloadCheckResult::BLOCKED_PASSWORD_PROTECTED:
case safe_browsing::DownloadCheckResult::BLOCKED_TOO_LARGE:
case safe_browsing::DownloadCheckResult::SENSITIVE_CONTENT_BLOCK:
case safe_browsing::DownloadCheckResult::BLOCKED_SCAN_FAILED:
enterprise_connectors::RunSavePackageScanningCallback(item,
/*allowed*/ false);
break;
default:
// These other results should never be returned.
NOTREACHED();
}
}
#endif // SAFE_BROWSING_DOWNLOAD_PROTECTION
#if BUILDFLAG(ENABLE_EXTENSIONS)
void ChromeDownloadManagerDelegate::OnInstallerDone(
const base::UnguessableToken& token,
content::DownloadOpenDelayedCallback callback,
const std::optional<CrxInstallError>& error) {
scoped_refptr<CrxInstaller> installer;
{
auto iter = running_crx_installs_.find(token);
CHECK(iter != running_crx_installs_.end());
installer = iter->second;
running_crx_installs_.erase(iter);
}
std::move(callback).Run(installer->did_handle_successfully());
}
#endif
void ChromeDownloadManagerDelegate::OnDownloadTargetDetermined(
uint32_t download_id,
download::DownloadTargetCallback callback,
download::DownloadTargetInfo target_info,
safe_browsing::DownloadFileType::DangerLevel danger_level) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DownloadItem* item = download_manager_->GetDownload(download_id);
if (item) {
DownloadItemModel model(item);
model.DetermineAndSetShouldPreferOpeningInBrowser(
target_info.target_path, target_info.is_filetype_handled_safely);
model.SetDangerLevel(danger_level);
}
if (ShouldBlockFile(item, target_info.danger_type)) {
MaybeReportDangerousDownloadBlocked(
download_prefs_->download_restriction(), "DANGEROUS_FILE_TYPE",
target_info.target_path.AsUTF8Unsafe(), item);
target_info.interrupt_reason =
download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED;
// A dangerous type would take precedence over the blocking of the file.
target_info.danger_type = download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
}
base::FilePath target_path = target_info.target_path;
#if BUILDFLAG(IS_ANDROID)
// Present an insecure download infobar when needed, and wait to initiate
// the download until the user decides what to do.
// On Desktop, this is handled using the unsafe-download warnings that are
// shown in parallel with the download. Those warnings don't exist for
// Android, so for simplicity we prompt before starting the download instead.
auto ids = target_info.insecure_download_status;
if (target_info.interrupt_reason ==
download::DOWNLOAD_INTERRUPT_REASON_NONE &&
(ids == download::DownloadItem::InsecureDownloadStatus::BLOCK ||
ids == download::DownloadItem::InsecureDownloadStatus::WARN)) {
auto* web_contents = content::DownloadItemUtils::GetWebContents(item);
gfx::NativeWindow native_window =
web_contents ? web_contents->GetTopLevelNativeWindow() : nullptr;
if (native_window && item) {
InsecureDownloadDialogBridge::GetInstance()->CreateDialog(
item, item->GetFileNameToReportUser(), native_window,
base::BindOnce(HandleInsecureDownloadInfoBarResult, item,
std::move(target_info), std::move(callback)));
return;
}
}
#endif // BUILDFLAG(IS_ANDROID)
// A separate reservation with the same target path may exist.
// If so, cancel the current reservation.
DownloadPathReservationTracker::CheckDownloadPathForExistingDownload(
target_path, item,
base::BindOnce(&OnCheckExistingDownloadPathDone, std::move(target_info),
std::move(callback)));
}
bool ChromeDownloadManagerDelegate::IsOpenInBrowserPreferredForFile(
const base::FilePath& path) {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_MAC)
if (path.MatchesExtension(FILE_PATH_LITERAL(".pdf"))) {
return !download_prefs_->ShouldOpenPdfInSystemReader();
}
#endif
// On Android, always prefer opening with an external app. On ChromeOS, there
// are no external apps so just allow all opens to be handled by the "System."
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS) && \
BUILDFLAG(ENABLE_PLUGINS)
// TODO(asanka): Consider other file types and MIME types.
// http://crbug.com/323561
if (path.MatchesExtension(FILE_PATH_LITERAL(".pdf")) ||
path.MatchesExtension(FILE_PATH_LITERAL(".htm")) ||
path.MatchesExtension(FILE_PATH_LITERAL(".html")) ||
path.MatchesExtension(FILE_PATH_LITERAL(".shtm")) ||
path.MatchesExtension(FILE_PATH_LITERAL(".shtml")) ||
path.MatchesExtension(FILE_PATH_LITERAL(".svg")) ||
path.MatchesExtension(FILE_PATH_LITERAL(".xht")) ||
path.MatchesExtension(FILE_PATH_LITERAL(".xhtm")) ||
path.MatchesExtension(FILE_PATH_LITERAL(".xhtml")) ||
path.MatchesExtension(FILE_PATH_LITERAL(".xsl")) ||
path.MatchesExtension(FILE_PATH_LITERAL(".xslt"))) {
return true;
}
#endif
return false;
}
bool ChromeDownloadManagerDelegate::ShouldBlockFile(
download::DownloadItem* item,
download::DownloadDangerType danger_type) const {
// Chrome-initiated background downloads should not be blocked.
if (item && !item->RequireSafetyChecks()) {
return false;
}
policy::DownloadRestriction download_restriction =
download_prefs_->download_restriction();
if (IsDangerTypeBlocked(danger_type))
return true;
bool file_type_dangerous =
(item && DownloadItemModel(item).GetDangerLevel() !=
DownloadFileType::NOT_DANGEROUS);
switch (download_restriction) {
case (policy::DownloadRestriction::NONE):
return false;
case (policy::DownloadRestriction::POTENTIALLY_DANGEROUS_FILES):
return danger_type != download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS ||
file_type_dangerous;
case (policy::DownloadRestriction::DANGEROUS_FILES): {
return (danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
danger_type ==
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE ||
file_type_dangerous);
}
case (policy::DownloadRestriction::MALICIOUS_FILES): {
return (danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
danger_type ==
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE);
}
case (policy::DownloadRestriction::ALL_FILES):
return true;
default:
LOG(ERROR) << "Invalid download restriction value: "
<< static_cast<int>(download_restriction);
}
return false;
}
void ChromeDownloadManagerDelegate::MaybeSendDangerousDownloadOpenedReport(
DownloadItem* download,
bool show_download_in_folder) {
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
safe_browsing::DownloadProtectionService* service =
GetDownloadProtectionService();
if (service) {
service->MaybeSendDangerousDownloadOpenedReport(download,
show_download_in_folder);
}
#endif
}
void ChromeDownloadManagerDelegate::MaybeSendDangerousDownloadCanceledReport(
DownloadItem* download,
bool is_shutdown) {
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
safe_browsing::SafeBrowsingService* sb_service =
g_browser_process->safe_browsing_service();
if (!sb_service) {
return;
}
// Note: We cannot go through download_protection_service here, because this
// function may be called at shutdown. The download_protection_service
// object may already be deleted at this point.
if (is_shutdown) {
sb_service->PersistDownloadReportAndSendOnNextStartup(
download,
safe_browsing::ClientSafeBrowsingReportRequest::
DANGEROUS_DOWNLOAD_PROFILE_CLOSED,
/*did_proceed=*/false, std::nullopt);
} else {
sb_service->SendDownloadReport(
download,
safe_browsing::ClientSafeBrowsingReportRequest::
DANGEROUS_DOWNLOAD_AUTO_DELETED,
/*did_proceed=*/false, std::nullopt);
}
#endif
}
void ChromeDownloadManagerDelegate::CheckDownloadAllowed(
const content::WebContents::Getter& web_contents_getter,
const GURL& url,
const std::string& request_method,
std::optional<url::Origin> request_initiator,
bool from_download_cross_origin_redirect,
bool content_initiated,
const std::string& mime_type,
std::optional<ui::PageTransition> page_transition,
content::CheckDownloadAllowedCallback check_download_allowed_cb) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_MAC)
// Don't download pdf if it is a file URL, as that might cause an infinite
// download loop if Chrome is not the system pdf viewer.
if (url.SchemeIsFile() && download_prefs_->ShouldOpenPdfInSystemReader()) {
base::FilePath path;
net::FileURLToFilePath(url, &path);
base::FilePath::StringType extension = path.Extension();
if (!extension.empty() && base::FilePath::CompareEqualIgnoreCase(
extension, FILE_PATH_LITERAL(".pdf"))) {
OnCheckDownloadAllowedFailed(std::move(check_download_allowed_cb));
return;
}
}
#endif
content::WebContents* web_contents = web_contents_getter.Run();
if (!web_contents) {
OnCheckDownloadAllowedFailed(std::move(check_download_allowed_cb));
return;
}
// Check whether download is restricted for saved tab groups.
if (tab_groups::RestrictDownloadOnSyncedTabs() &&
TabGroupSyncTabState::FromWebContents(web_contents)) {
OnCheckDownloadAllowedFailed(std::move(check_download_allowed_cb));
return;
}
#if BUILDFLAG(IS_ANDROID)
if (ShouldOpenPdfInline() && mime_type == pdf::kPDFMimeType) {
// If this is a forward/back navigation, the native page should trigger a
// download with default page transition type. Otherwise, we should cancel
// the download.
if (page_transition.has_value() &&
(page_transition.value() & ui::PAGE_TRANSITION_FORWARD_BACK)) {
OnCheckDownloadAllowedFailed(std::move(check_download_allowed_cb));
return;
}
NewNavigationObserver::GetInstance()->StartObserving(web_contents);
}
#endif
CanDownloadCallback cb = base::BindOnce(
&ChromeDownloadManagerDelegate::OnCheckDownloadAllowedComplete,
weak_ptr_factory_.GetWeakPtr(), std::move(check_download_allowed_cb));
#if BUILDFLAG(IS_ANDROID)
DownloadControllerBase::Get()->AcquireFileAccessPermission(
web_contents_getter,
base::BindOnce(&OnDownloadAcquireFileAccessPermissionDone,
web_contents_getter, url, request_method,
std::move(request_initiator), std::move(cb)));
#else
CheckCanDownload(web_contents_getter, url, request_method,
std::move(request_initiator),
from_download_cross_origin_redirect, std::move(cb));
#endif
}
download::QuarantineConnectionCallback
ChromeDownloadManagerDelegate::GetQuarantineConnectionCallback() {
return base::BindRepeating(
&ChromeDownloadManagerDelegate::ConnectToQuarantineService);
}
std::unique_ptr<download::DownloadItemRenameHandler>
ChromeDownloadManagerDelegate::GetRenameHandlerForDownload(
download::DownloadItem* download_item) {
#if BUILDFLAG(IS_CHROMEOS)
return policy::SkyvaultRenameHandler::CreateIfNeeded(download_item);
#else
return nullptr;
#endif // BUILDFLAG(IS_CHROMEOS)
}
void ChromeDownloadManagerDelegate::CheckSavePackageAllowed(
download::DownloadItem* download_item,
base::flat_map<base::FilePath, base::FilePath> save_package_files,
content::SavePackageAllowedCallback callback) {
DCHECK(download_item);
DCHECK(download_item->IsSavePackageDownload());
#if (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_MAC)) && \
BUILDFLAG(SAFE_BROWSING_AVAILABLE)
std::optional<enterprise_connectors::AnalysisSettings> settings =
safe_browsing::ShouldUploadBinaryForDeepScanning(download_item);
if (settings.has_value()) {
DownloadProtectionService* service = GetDownloadProtectionService();
// Save package never need malware scans, so exempt them from scanning if
// there are no other tags.
settings->tags.erase("malware");
if (!settings->tags.empty() && service) {
download_item->SetUserData(
enterprise_connectors::SavePackageScanningData::kKey,
std::make_unique<enterprise_connectors::SavePackageScanningData>(
std::move(callback)));
service->UploadSavePackageForDeepScanning(
download_item, std::move(save_package_files),
base::BindRepeating(
&ChromeDownloadManagerDelegate::CheckSavePackageScanningDone,
weak_ptr_factory_.GetWeakPtr(), download_item->GetId()),
std::move(settings.value()));
return;
}
}
#endif
std::move(callback).Run(true);
}
void ChromeDownloadManagerDelegate::OnCheckDownloadAllowedComplete(
content::CheckDownloadAllowedCallback check_download_allowed_cb,
bool storage_permission_granted,
bool allow) {
if (!storage_permission_granted) {
} else if (allow) {
// Presumes all downloads initiated by navigation use this throttle and
// nothing else does.
RecordDownloadSource(DOWNLOAD_INITIATED_BY_NAVIGATION);
}
std::move(check_download_allowed_cb).Run(allow);
}
#if !BUILDFLAG(IS_ANDROID)
void ChromeDownloadManagerDelegate::AttachExtraInfo(
download::DownloadItem* item) {
content::WebContents* web_contents =
content::DownloadItemUtils::GetWebContents(item);
Browser* browser =
web_contents ? chrome::FindBrowserWithTab(web_contents) : nullptr;
// Attach the info for whether the download came from a web app.
if (browser && web_app::AppBrowserController::IsWebApp(browser) &&
browser->app_controller()) {
DownloadItemWebAppData::CreateAndAttachToItem(
item, browser->app_controller()->app_id());
}
}
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID)
bool ChromeDownloadManagerDelegate::IsFromExternalApp(
download::DownloadItem* item) {
content::WebContents* web_contents =
content::DownloadItemUtils::GetWebContents(item);
TabModel* tab_model = TabModelList::GetTabModelForWebContents(web_contents);
if (!tab_model) {
return false;
}
for (int index = 0; index < tab_model->GetTabCount(); ++index) {
if (web_contents == tab_model->GetWebContentsAt(index)) {
return tab_model->GetTabAt(index)->GetLaunchType() ==
static_cast<int>(TabModel::TabLaunchType::FROM_EXTERNAL_APP);
}
}
return false;
}
bool ChromeDownloadManagerDelegate::ShouldOpenPdfInline() {
return ShouldOpenPdfInlineInternal(profile_->IsOffTheRecord());
}
bool ChromeDownloadManagerDelegate::IsDownloadRestrictedByPolicy() {
return download_prefs_->download_restriction() ==
policy::DownloadRestriction::ALL_FILES;
}
#endif // BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
ChromeDownloadManagerDelegate::SafeBrowsingState::~SafeBrowsingState() =
default;
const char ChromeDownloadManagerDelegate::SafeBrowsingState::
kSafeBrowsingUserDataKey[] = "Safe Browsing ID";
#endif // SAFE_BROWSING_DOWNLOAD_PROTECTION
base::WeakPtr<ChromeDownloadManagerDelegate>
ChromeDownloadManagerDelegate::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
// static
void ChromeDownloadManagerDelegate::ConnectToQuarantineService(
mojo::PendingReceiver<quarantine::mojom::Quarantine> receiver) {
#if BUILDFLAG(IS_WIN)
content::ServiceProcessHost::Launch(std::move(receiver),
content::ServiceProcessHost::Options()
.WithDisplayName("Quarantine Service")
.Pass());
#else // !BUILDFLAG(IS_WIN)
mojo::MakeSelfOwnedReceiver(std::make_unique<quarantine::QuarantineImpl>(),
std::move(receiver));
#endif // !BUILDFLAG(IS_WIN)
}
void ChromeDownloadManagerDelegate::OnManagerInitialized() {
#if BUILDFLAG(IS_ANDROID)
if (ShouldOpenPdfInlineInternal(/*incognito=*/false)) {
download::GetDownloadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce([]() { base::DeleteFile(GetTempPdfDir()); }));
}
#endif
CancelAllEphemeralWarnings();
}
void ChromeDownloadManagerDelegate::ScheduleCancelForEphemeralWarning(
const std::string& guid) {
if (!IsEphemeralWarningCancellationEnabled()) {
return;
}
LogCancelEphemeralWarningEvent(
CancelEphemeralWarningEvent::kCancellationScheduled);
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ChromeDownloadManagerDelegate::CancelForEphemeralWarning,
weak_ptr_factory_.GetWeakPtr(), guid),
kEphemeralWarningLifetimeBeforeCancel);
}
void ChromeDownloadManagerDelegate::CancelForEphemeralWarning(
const std::string& guid) {
CHECK(IsEphemeralWarningCancellationEnabled());
LogCancelEphemeralWarningEvent(
CancelEphemeralWarningEvent::kCancellationTriggered);
download::DownloadItem* download = download_manager_->GetDownloadByGuid(guid);
if (!download) {
LogCancelEphemeralWarningEvent(
CancelEphemeralWarningEvent::kCancellationFailedDownloadNotFound);
// The download may have been destroyed since the task was scheduled
return;
}
// Confirm that the user has not already acted on the warning.
if (std::make_unique<DownloadItemModel>(download)->IsEphemeralWarning()) {
LogCancelEphemeralWarningEvent(
CancelEphemeralWarningEvent::kCancellationSucceeded);
download->Cancel(/*user_cancel=*/false);
MaybeSendDangerousDownloadCanceledReport(download, /*is_shutdown=*/false);
} else {
LogCancelEphemeralWarningEvent(
CancelEphemeralWarningEvent::kCancellationFailedDownloadNotEphemeral);
}
}
void ChromeDownloadManagerDelegate::CancelAllEphemeralWarnings() {
if (!IsEphemeralWarningCancellationEnabled()) {
return;
}
content::DownloadManager::DownloadVector downloads;
download_manager_->GetAllDownloads(&downloads);
for (download::DownloadItem* download : downloads) {
auto model = std::make_unique<DownloadItemModel>(download);
if (model->IsEphemeralWarning() &&
model->GetState() != download::DownloadItem::CANCELLED) {
download->Cancel(/*user_cancel=*/false);
}
}
}
#if BUILDFLAG(IS_ANDROID)
void ChromeDownloadManagerDelegate::RequestIncognitoSavePackageConfirmationDone(
const GURL& url,
const base::FilePath& suggested_path,
content::SavePackagePathPickedCallback callback,
bool accept) {
if (!accept) {
return;
}
download::DetermineSavePackagePath(
url, suggested_path,
base::BindOnce(&OnDetermineSavePackagePathDone, std::move(callback)));
}
#endif