blob: 28804c58c31b8577c181e828c7ea4772de0b95ef [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.
#ifndef CHROME_BROWSER_DOWNLOAD_CHROME_DOWNLOAD_MANAGER_DELEGATE_H_
#define CHROME_BROWSER_DOWNLOAD_CHROME_DOWNLOAD_MANAGER_DELEGATE_H_
#include <stdint.h>
#include <deque>
#include <memory>
#include <string>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "chrome/browser/download/download_completion_blocker.h"
#include "chrome/browser/download/download_target_determiner_delegate.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/download/public/common/download_item.h"
#include "components/download/public/common/download_path_reservation_tracker.h"
#include "components/download/public/common/download_target_info.h"
#include "components/enterprise/buildflags/buildflags.h"
#include "components/safe_browsing/buildflags.h"
#include "components/safe_browsing/content/common/proto/download_file_types.pb.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/download_manager_delegate.h"
#include "extensions/buildflags/buildflags.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/shell_dialogs/selected_file_info.h"
#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/download/android/download_dialog_bridge.h"
#include "chrome/browser/download/android/download_message_bridge.h"
#endif
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
#include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
#include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
#endif
class DownloadPrefs;
class Profile;
namespace content {
class DownloadManager;
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
namespace extensions {
class CrxInstaller;
class CrxInstallError;
}
#endif
// This is the Chrome side helper for the download system.
class ChromeDownloadManagerDelegate
: public content::DownloadManagerDelegate,
public DownloadTargetDeterminerDelegate,
public content::DownloadManager::Observer {
public:
explicit ChromeDownloadManagerDelegate(Profile* profile);
ChromeDownloadManagerDelegate(const ChromeDownloadManagerDelegate&) = delete;
ChromeDownloadManagerDelegate& operator=(
const ChromeDownloadManagerDelegate&) = delete;
~ChromeDownloadManagerDelegate() override;
// Should be called before the first call to ShouldCompleteDownload() to
// disable SafeBrowsing checks for |item|.
static void DisableSafeBrowsing(download::DownloadItem* item);
// True when |danger_type| is one that is blocked for policy reasons (e.g.
// "file too large") as opposed to malicious content reasons.
static bool IsDangerTypeBlocked(download::DownloadDangerType danger_type);
void SetDownloadManager(content::DownloadManager* dm);
#if BUILDFLAG(IS_ANDROID)
void ShowDownloadDialog(gfx::NativeWindow native_window,
int64_t total_bytes,
DownloadLocationDialogType dialog_type,
const base::FilePath& suggested_path,
DownloadDialogBridge::DialogCallback callback);
void SetDownloadDialogBridgeForTesting(DownloadDialogBridge* bridge);
void SetDownloadMessageBridgeForTesting(DownloadMessageBridge* bridge);
#endif
// Callbacks passed to GetNextId() will not be called until the returned
// callback is called.
content::DownloadIdCallback GetDownloadIdReceiverCallback();
// content::DownloadManagerDelegate
void Shutdown() override;
void OnDownloadCanceledAtShutdown(download::DownloadItem* item) override;
void GetNextId(content::DownloadIdCallback callback) override;
bool DetermineDownloadTarget(
download::DownloadItem* item,
download::DownloadTargetCallback* callback) override;
bool ShouldAutomaticallyOpenFile(const GURL& url,
const base::FilePath& path) override;
bool ShouldAutomaticallyOpenFileByPolicy(const GURL& url,
const base::FilePath& path) override;
bool ShouldCompleteDownload(download::DownloadItem* item,
base::OnceClosure complete_callback) override;
bool ShouldOpenDownload(
download::DownloadItem* item,
content::DownloadOpenDelayedCallback callback) override;
bool ShouldObfuscateDownload(download::DownloadItem* item) override;
bool 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) override;
void GetSaveDir(content::BrowserContext* browser_context,
base::FilePath* website_save_dir,
base::FilePath* download_save_dir) override;
void ChooseSavePath(content::WebContents* web_contents,
const base::FilePath& suggested_path,
const base::FilePath::StringType& default_extension,
bool can_save_as_complete,
content::SavePackagePathPickedCallback callback) override;
void SanitizeSavePackageResourceName(base::FilePath* filename,
const GURL& source_url) override;
void SanitizeDownloadParameters(
download::DownloadUrlParameters* params) override;
void OpenDownload(download::DownloadItem* download) override;
void ShowDownloadInShell(download::DownloadItem* download) override;
std::string ApplicationClientIdForFileScanning() override;
void 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) override;
download::QuarantineConnectionCallback GetQuarantineConnectionCallback()
override;
std::unique_ptr<download::DownloadItemRenameHandler>
GetRenameHandlerForDownload(download::DownloadItem* download_item) override;
void CheckSavePackageAllowed(
download::DownloadItem* download_item,
base::flat_map<base::FilePath, base::FilePath> save_package_files,
content::SavePackageAllowedCallback callback) override;
#if BUILDFLAG(IS_ANDROID)
bool IsFromExternalApp(download::DownloadItem* item) override;
bool ShouldOpenPdfInline() override;
bool IsDownloadRestrictedByPolicy() override;
#else
void AttachExtraInfo(download::DownloadItem* item) override;
#endif // BUILDFLAG(IS_ANDROID)
// Opens a download using the platform handler. DownloadItem::OpenDownload,
// which ends up being handled by OpenDownload(), will open a download in the
// browser if doing so is preferred.
void OpenDownloadUsingPlatformHandler(download::DownloadItem* download);
DownloadPrefs* download_prefs() { return download_prefs_.get(); }
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
// The state of a safebrowsing check.
class SafeBrowsingState : public DownloadCompletionBlocker {
public:
SafeBrowsingState() = default;
SafeBrowsingState(const SafeBrowsingState&) = delete;
SafeBrowsingState& operator=(const SafeBrowsingState&) = delete;
~SafeBrowsingState() override;
// String pointer used for identifying safebrowsing data associated with
// a download item.
static const char kSafeBrowsingUserDataKey[];
};
// Callback function after the DownloadProtectionService completes.
void CheckClientDownloadDone(uint32_t download_id,
safe_browsing::DownloadCheckResult result);
// Callback function after scanning completes for a save package.
void CheckSavePackageScanningDone(uint32_t download_id,
safe_browsing::DownloadCheckResult result);
#endif // SAFE_BROWSING_DOWNLOAD_PROTECTION
base::WeakPtr<ChromeDownloadManagerDelegate> GetWeakPtr();
static void ConnectToQuarantineService(
mojo::PendingReceiver<quarantine::mojom::Quarantine> receiver);
// Return true if the downloaded file should be blocked based on the current
// download restriction pref, the file type, and |danger_type|.
bool ShouldBlockFile(download::DownloadItem* item,
download::DownloadDangerType danger_type) const;
// Schedules an ephemeral warning download to be canceled. It will only be
// canceled if it continues to be an ephemeral warning that hasn't been acted
// on when the scheduled time arrives. This should be scheduled after the user
// sees the warning in the UI, i.e. should not be scheduled before the user
// has a chance to see the warning.
// Note: Any scheduled cancellations will be abandoned at browser shutdown,
// but the cancellation should occur when the browser next starts up, in
// CancelAllEphemeralWarnings().
void ScheduleCancelForEphemeralWarning(const std::string& guid);
// Returns true if |path| should open in the browser.
virtual bool IsOpenInBrowserPreferredForFile(const base::FilePath& path);
protected:
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
virtual safe_browsing::DownloadProtectionService*
GetDownloadProtectionService();
#endif
// Show file picker for |download|.
virtual void ShowFilePickerForDownload(
download::DownloadItem* download,
const base::FilePath& suggested_path,
DownloadTargetDeterminerDelegate::ConfirmationCallback callback);
// DownloadTargetDeterminerDelegate. Protected for testing.
void GetInsecureDownloadStatus(
download::DownloadItem* download,
const base::FilePath& virtual_path,
GetInsecureDownloadStatusCallback callback) override;
void NotifyExtensions(download::DownloadItem* download,
const base::FilePath& suggested_virtual_path,
NotifyExtensionsCallback callback) override;
void ReserveVirtualPath(
download::DownloadItem* download,
const base::FilePath& virtual_path,
bool create_directory,
download::DownloadPathReservationTracker::FilenameConflictAction
conflict_action,
ReservedPathCallback callback) override;
#if BUILDFLAG(IS_ANDROID)
void RequestIncognitoWarningConfirmation(
IncognitoWarningConfirmationCallback) override;
#endif
void RequestConfirmation(download::DownloadItem* download,
const base::FilePath& suggested_virtual_path,
DownloadConfirmationReason reason,
ConfirmationCallback callback) override;
void DetermineLocalPath(download::DownloadItem* download,
const base::FilePath& virtual_path,
download::LocalPathCallback callback) override;
void CheckDownloadUrl(download::DownloadItem* download,
const base::FilePath& suggested_virtual_path,
CheckDownloadUrlCallback callback) override;
void GetFileMimeType(const base::FilePath& path,
GetFileMimeTypeCallback callback) override;
#if BUILDFLAG(IS_ANDROID)
virtual void OnDownloadCanceled(download::DownloadItem* download,
bool has_no_external_storage);
#endif
// Called when the file picker returns the confirmation result.
void OnConfirmationCallbackComplete(
DownloadTargetDeterminerDelegate::ConfirmationCallback callback,
DownloadConfirmationResult result,
const ui::SelectedFileInfo& selected_file_info);
// So that test classes that inherit from this for override purposes
// can call back into the DownloadManager.
raw_ptr<content::DownloadManager> download_manager_ = nullptr;
private:
friend class base::RefCountedThreadSafe<ChromeDownloadManagerDelegate>;
FRIEND_TEST_ALL_PREFIXES(ChromeDownloadManagerDelegateTest,
RequestConfirmation_Android);
FRIEND_TEST_ALL_PREFIXES(ChromeDownloadManagerDelegateTest,
CancelAllEphemeralWarnings);
using IdCallbackVector = std::vector<content::DownloadIdCallback>;
// Called to show a file picker for download with |guid|
void ShowFilePicker(
const std::string& guid,
const base::FilePath& suggested_path,
DownloadTargetDeterminerDelegate::ConfirmationCallback callback);
#if BUILDFLAG(ENABLE_EXTENSIONS)
// Called when CrxInstaller in running_crx_installs_ finishes installation.
void OnInstallerDone(const base::UnguessableToken& token,
content::DownloadOpenDelayedCallback callback,
const std::optional<extensions::CrxInstallError>& error);
#endif
// Internal gateways for ShouldCompleteDownload().
bool IsDownloadReadyForCompletion(
download::DownloadItem* item,
base::OnceClosure internal_complete_callback);
void ShouldCompleteDownloadInternal(uint32_t download_id,
base::OnceClosure user_complete_callback);
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
// Called when obfuscated download files are deobfuscated.
void OnDeobfuscationComplete(
base::OnceClosure callback,
base::expected<void, enterprise_obfuscation::Error> deobfuscation_result);
#endif
// Sets the next download id based on download database records, and runs all
// cached id callbacks.
void SetNextId(uint32_t id);
// Runs the |callback| with next id. Results in the download being started.
void ReturnNextId(content::DownloadIdCallback callback);
void OnDownloadTargetDetermined(
uint32_t download_id,
download::DownloadTargetCallback callback,
download::DownloadTargetInfo target_info,
safe_browsing::DownloadFileType::DangerLevel danger_level);
// Sends a download report when the dangerous download is opened. This action
// can be performed multiple times after the warning is bypassed, so this
// report can be sent multiple times for a single download.
void MaybeSendDangerousDownloadOpenedReport(download::DownloadItem* download,
bool show_download_in_folder);
// Sends a download report when the dangerous download is canceled
// automatically. This code path is NOT triggered if a user explicitly
// canceled the download.
void MaybeSendDangerousDownloadCanceledReport(
download::DownloadItem* download,
bool is_shutdown);
void OnCheckDownloadAllowedComplete(
content::CheckDownloadAllowedCallback check_download_allowed_cb,
bool storage_permission_granted,
bool allow);
// Returns whether this is the most recent download in the rare event where
// multiple downloads are associated with the same file path.
bool IsMostRecentDownloadItemAtFilePath(download::DownloadItem* download);
// Cancels a download if it's still an ephemeral warning (and has not been
// acted on by the user).
void CancelForEphemeralWarning(const std::string& guid);
// If the browser doesn't shut down cleanly, there can be ephemeral warnings
// that were not cleaned up. This function cleans them up on startup, when the
// download manager is initialized.
void CancelAllEphemeralWarnings();
// content::DownloadManager::Observer
void OnManagerInitialized() override;
#if BUILDFLAG(IS_ANDROID)
// Called after a unique file name is generated in the case that there is a
// TARGET_CONFLICT and the new file name should be displayed to the user.
void GenerateUniqueFileNameDone(
const std::string& download_guid,
DownloadTargetDeterminerDelegate::ConfirmationCallback callback,
download::PathValidationResult result,
const base::FilePath& target_path);
// Return true if the mime type is pdf and Chrome supports open this pdf.
bool IsPdfAndSupported(const std::string& mime_type,
content::WebContents* web_contents);
// Called after user interacted on the incognito download confirmation message
// before proceeding to save a package.
void RequestIncognitoSavePackageConfirmationDone(
const GURL& url,
const base::FilePath& suggested_path,
content::SavePackagePathPickedCallback callback,
bool accept);
#endif
raw_ptr<Profile, DanglingUntriaged> profile_;
#if BUILDFLAG(IS_ANDROID)
std::unique_ptr<DownloadDialogBridge> download_dialog_bridge_;
std::unique_ptr<DownloadMessageBridge> download_message_bridge_;
#endif
// If history database fails to initialize, this will always be kInvalidId.
// Otherwise, the first available download id is assigned from history
// database, and incremented by one for each download.
uint32_t next_download_id_;
// Whether |next_download_id_| is retrieved from history db.
bool next_id_retrieved_;
// The |GetNextId| callbacks that may be cached before loading the download
// database.
IdCallbackVector id_callbacks_;
std::unique_ptr<DownloadPrefs> download_prefs_;
#if BUILDFLAG(ENABLE_EXTENSIONS)
// CRX installs that are currently in progress.
std::map<base::UnguessableToken, scoped_refptr<extensions::CrxInstaller>>
running_crx_installs_;
#endif
// Outstanding callbacks to open file selection dialog.
std::deque<base::OnceClosure> file_picker_callbacks_;
// Whether a file picker dialog is showing.
bool is_file_picker_showing_;
base::WeakPtrFactory<ChromeDownloadManagerDelegate> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_DOWNLOAD_CHROME_DOWNLOAD_MANAGER_DELEGATE_H_