#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "base/command_line.h"
#include "base/files/file_path.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/location.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/uuid.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_core_service_impl.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/insecure_download_blocking.h"
#include "chrome/browser/tab_group_sync/tab_group_sync_tab_state.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.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_stats.h"
#include "components/download/public/common/download_target_info.h"
#include "components/download/public/common/mock_download_item.h"
#include "components/enterprise/buildflags/buildflags.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/buildflags.h"
#include "components/safe_browsing/content/common/file_type_policies_test_util.h"
#include "components/safe_search_api/safe_search_util.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/test/mock_download_manager.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "net/base/network_change_notifier.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/shell_dialogs/selected_file_info.h"
#include "url/origin.h"
#if BUILDFLAG(FULL_SAFE_BROWSING)
#include "chrome/browser/policy/dm_token_utils.h"
#include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
#include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
#include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#endif
#if BUILDFLAG(ENABLE_PLUGINS)
#include "content/public/browser/plugin_service.h"
#endif
#if BUILDFLAG(IS_ANDROID)
#include "base/android/build_info.h"
#include "chrome/browser/download/download_prompt_status.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/infobars/core/infobar.h"
#include "components/infobars/core/infobar_delegate.h"
#include "components/infobars/core/infobar_manager.h"
#endif
#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
#include "chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h"
#endif
DownloadItem;
DownloadPathReservationTracker;
PathValidationResult;
ConnectionType;
DownloadFileType;
_;
AnyNumber;
AtMost;
DoAll;
Invoke;
Ref;
Return;
ReturnArg;
ReturnPointee;
ReturnRef;
ReturnRefOfCopy;
SetArgPointee;
StrictMock;
WithArg;
Origin;
namespace {
class MockWebContentsDelegate : public content::WebContentsDelegate { … };
ACTION_P3(ScheduleCallback2, result0, result1) { … }
class TestChromeDownloadManagerDelegate : public ChromeDownloadManagerDelegate { … };
class TestDownloadCoreService : public DownloadCoreServiceImpl { … };
TestDownloadCoreService::TestDownloadCoreService(Profile* profile)
: … { … }
TestDownloadCoreService::~TestDownloadCoreService() = default;
ChromeDownloadManagerDelegate*
TestDownloadCoreService::GetDownloadManagerDelegate() { … }
static std::unique_ptr<KeyedService> CreateTestDownloadCoreService(
content::BrowserContext* browser_context) { … }
class ChromeDownloadManagerDelegateTest
: public ChromeRenderViewHostTestHarness { … };
ChromeDownloadManagerDelegateTest::ChromeDownloadManagerDelegateTest()
: … { … }
void ChromeDownloadManagerDelegateTest::SetUp() { … }
void ChromeDownloadManagerDelegateTest::TearDown() { … }
void ChromeDownloadManagerDelegateTest::VerifyAndClearExpectations() { … }
std::unique_ptr<download::MockDownloadItem>
ChromeDownloadManagerDelegateTest::CreateActiveDownloadItem(int32_t id) { … }
base::FilePath ChromeDownloadManagerDelegateTest::GetPathInDownloadDir(
const char* relative_path) { … }
download::DownloadTargetInfo
ChromeDownloadManagerDelegateTest::DetermineDownloadTarget(
DownloadItem* download_item) { … }
void ChromeDownloadManagerDelegateTest::OnConfirmationCallbackComplete(
DownloadTargetDeterminerDelegate::ConfirmationCallback callback,
DownloadConfirmationResult result,
const ui::SelectedFileInfo& selected_file_info) { … }
TestChromeDownloadManagerDelegate*
ChromeDownloadManagerDelegateTest::delegate() { … }
content::MockDownloadManager*
ChromeDownloadManagerDelegateTest::download_manager() { … }
DownloadPrefs* ChromeDownloadManagerDelegateTest::download_prefs() { … }
PrefService* ChromeDownloadManagerDelegateTest::pref_service() { … }
std::unique_ptr<download::MockDownloadItem>
ChromeDownloadManagerDelegateTest::PrepareDownloadItemForInsecureBlocking(
const GURL& download_url,
const std::optional<Origin>& request_initiator,
const std::optional<GURL>& redirect_url) { … }
void ExpectExtensionOnlyIn(const InsecureDownloadExtensions& ext,
const std::string& initiator,
const std::string& download,
base::HistogramTester& tester) { … }
void ChromeDownloadManagerDelegateTest::VerifyMixedContentExtensionOverride(
DownloadItem* download_item,
const base::FieldTrialParams& parameters,
InsecureDownloadExtensions extension,
download::DownloadInterruptReason interrupt_reason,
download::DownloadItem::InsecureDownloadStatus insecure_download_status) { … }
}
TEST_F(ChromeDownloadManagerDelegateTest, LastSavePath) { … }
TEST_F(ChromeDownloadManagerDelegateTest, ConflictAction) { … }
TEST_F(ChromeDownloadManagerDelegateTest, MaybeDangerousContent) { … }
TEST_F(ChromeDownloadManagerDelegateTest, DragAndDropDangerous) { … }
TEST_F(ChromeDownloadManagerDelegateTest, BlockedByPolicy) { … }
TEST_F(ChromeDownloadManagerDelegateTest, NoSafetyChecksNotBlockedByPolicy) { … }
#if BUILDFLAG(IS_ANDROID)
TEST_F(ChromeDownloadManagerDelegateTest, InterceptDownloadByOfflinePages) {
const GURL kUrl("http://example.com/foo");
std::string mime_type = "text/html";
bool should_intercept = delegate()->InterceptDownloadIfApplicable(
kUrl, "", "", mime_type, "", 10, false , nullptr);
EXPECT_TRUE(should_intercept);
should_intercept = delegate()->InterceptDownloadIfApplicable(
kUrl, "", "", mime_type, "", 10, true , nullptr);
EXPECT_FALSE(should_intercept);
should_intercept = delegate()->InterceptDownloadIfApplicable(
kUrl, "", "attachment" , mime_type, "", 10,
false , nullptr);
EXPECT_FALSE(should_intercept);
}
namespace {
class TestDownloadMessageBridge : public DownloadMessageBridge {
public:
TestDownloadMessageBridge() = default;
TestDownloadMessageBridge(const TestDownloadMessageBridge&) = delete;
TestDownloadMessageBridge& operator=(const TestDownloadMessageBridge&) =
delete;
void ShowUnsupportedDownloadMessage(
content::WebContents* web_contents) override {
message_shown_count_++;
}
int GetMessageShownCount() { return message_shown_count_; }
private:
int message_shown_count_;
};
}
TEST_F(ChromeDownloadManagerDelegateTest, InterceptDownloadForAutomotive) {
if (!base::android::BuildInfo::GetInstance()->is_automotive()) {
GTEST_SKIP() << "This test should only run on automotive.";
}
base::HistogramTester histograms;
TestDownloadMessageBridge* message_bridge = new TestDownloadMessageBridge();
delegate()->SetDownloadMessageBridgeForTesting(
static_cast<DownloadMessageBridge*>(message_bridge));
const GURL kUrl("http://example.com/foo");
std::string mime_type = "image/png";
bool should_intercept = delegate()->InterceptDownloadIfApplicable(
kUrl, "", "", mime_type, "", 10, false , nullptr);
EXPECT_FALSE(should_intercept);
mime_type = "application/pdf";
should_intercept = delegate()->InterceptDownloadIfApplicable(
kUrl, "", "", mime_type, "", 10, false , nullptr);
EXPECT_TRUE(should_intercept);
histograms.ExpectUniqueSample("Download.Blocked.ContentType.Automotive",
download::DownloadContent::PDF, 1);
EXPECT_EQ(1, message_bridge->GetMessageShownCount());
}
#endif
TEST_F(ChromeDownloadManagerDelegateTest,
BlockedAsActiveContent_HttpsTargetOk) { … }
TEST_F(ChromeDownloadManagerDelegateTest, BlockedAsActiveContent_HttpPageOk) { … }
TEST_F(ChromeDownloadManagerDelegateTest,
BlockedAsActiveContent_InferredInitiatorStillBlocked) { … }
TEST_F(ChromeDownloadManagerDelegateTest, BlockedAsActiveContent_HttpChain) { … }
TEST_F(ChromeDownloadManagerDelegateTest,
BlockedAsActiveContent_BenignExtensionsIgnored) { … }
TEST_F(ChromeDownloadManagerDelegateTest,
BlockedAsActiveContent_NonUniqueInitiator) { … }
TEST_F(ChromeDownloadManagerDelegateTest,
BlockedAsActiveContent_NonUniqueFinalUrl) { … }
TEST_F(ChromeDownloadManagerDelegateTest, BlockedAsActiveContent_Localhost) { … }
TEST_F(ChromeDownloadManagerDelegateTest,
BlockedAsActiveContent_LocalhostInitiator) { … }
TEST_F(ChromeDownloadManagerDelegateTest,
BlockedAsActiveContent_BlobConsideredSecure) { … }
TEST_F(ChromeDownloadManagerDelegateTest, BlockedAsActiveContent_SilentBlock) { … }
TEST_F(ChromeDownloadManagerDelegateTest, BlockedAsActiveContent_Warn) { … }
TEST_F(ChromeDownloadManagerDelegateTest, BlockedAsActiveContent_Block) { … }
#if !BUILDFLAG(IS_ANDROID)
TEST_F(ChromeDownloadManagerDelegateTest,
BlockedAsActiveContent_PolicyOverride) { … }
TEST_F(ChromeDownloadManagerDelegateTest, DownloadBlockedForSyncedTab) { … }
#endif
TEST_F(ChromeDownloadManagerDelegateTest, InsecureDownloadsBlocked) { … }
TEST_F(ChromeDownloadManagerDelegateTest,
InsecureDownloadsBlocked_ExclusionsRemovedInHFM) { … }
TEST_F(ChromeDownloadManagerDelegateTest,
InsecureDownloadsBlocked_InferredInitiatorBlocked) { … }
TEST_F(ChromeDownloadManagerDelegateTest, WithoutHistoryDbNextId) { … }
TEST_F(ChromeDownloadManagerDelegateTest, WithHistoryDbNextId) { … }
TEST_F(ChromeDownloadManagerDelegateTest, SanitizeGoogleSearchLink) { … }
#if !BUILDFLAG(IS_ANDROID)
namespace {
void VerifyFilePickerConfirmation(
DownloadConfirmationResult expected_result,
base::RepeatingClosure completion_closure,
DownloadConfirmationResult result,
const ui::SelectedFileInfo& selected_file_info) { … }
}
TEST_F(ChromeDownloadManagerDelegateTest,
RemovingDownloadBeforeShowingFilePicker) { … }
#endif
#if !BUILDFLAG(IS_ANDROID)
#if !BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(ChromeDownloadManagerDelegateTest, ScheduleCancelForEphemeralWarning) { … }
TEST_F(ChromeDownloadManagerDelegateTest,
ScheduleCancelForEphemeralWarning_DownloadKept) { … }
#endif
TEST_F(ChromeDownloadManagerDelegateTest, CancelAllEphemeralWarnings) { … }
#endif
#if BUILDFLAG(FULL_SAFE_BROWSING)
namespace {
ReportType;
struct SafeBrowsingTestParameters { … };
class TestDownloadProtectionService
: public safe_browsing::DownloadProtectionService { … };
class FakeSafeBrowsingService : public safe_browsing::TestSafeBrowsingService { … };
class ChromeDownloadManagerDelegateTestWithSafeBrowsing
: public ChromeDownloadManagerDelegateTest,
public ::testing::WithParamInterface<SafeBrowsingTestParameters> { … };
void ChromeDownloadManagerDelegateTestWithSafeBrowsing::SetUp() { … }
void ChromeDownloadManagerDelegateTestWithSafeBrowsing::TearDown() { … }
const SafeBrowsingTestParameters kSafeBrowsingTestCases[] = …;
INSTANTIATE_TEST_SUITE_P(…);
}
TEST_P(ChromeDownloadManagerDelegateTestWithSafeBrowsing, CheckClientDownload) { … }
TEST_P(ChromeDownloadManagerDelegateTestWithSafeBrowsing,
SkipCheckClientDownload) { … }
#if !BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(ChromeDownloadManagerDelegateTestWithSafeBrowsing,
AutoCanceledReport_Sent) { … }
TEST_F(ChromeDownloadManagerDelegateTestWithSafeBrowsing,
AutoCanceledReport_NotSentStandardProtection) { … }
TEST_F(ChromeDownloadManagerDelegateTestWithSafeBrowsing,
AutoCanceledReport_NotSentNotDangerous) { … }
#endif
TEST_F(ChromeDownloadManagerDelegateTestWithSafeBrowsing,
CanceledReportAtShutdown_Persisted) { … }
TEST_F(ChromeDownloadManagerDelegateTestWithSafeBrowsing,
TrustedSourcesPolicyNotTrusted) { … }
#if !BUILDFLAG(IS_WIN)
TEST_F(ChromeDownloadManagerDelegateTestWithSafeBrowsing,
TrustedSourcesPolicyTrusted) { … }
TEST_F(ChromeDownloadManagerDelegateTestWithSafeBrowsing,
TrustedSourcesDontExemptEnterpriseScans) { … }
#endif
#endif
#if BUILDFLAG(IS_ANDROID)
namespace {
class AndroidDownloadInfobarCounter
: public infobars::InfoBarManager::Observer {
public:
explicit AndroidDownloadInfobarCounter(content::WebContents* web_contents)
: infobar_manager_(
infobars::ContentInfoBarManager::FromWebContents(web_contents)) {
infobar_manager_->AddObserver(this);
}
~AndroidDownloadInfobarCounter() override {
infobar_manager_->RemoveObserver(this);
}
int CheckAndResetInfobarCount() {
int count = infobar_count_;
infobar_count_ = 0;
return count;
}
private:
void OnInfoBarAdded(infobars::InfoBar* infobar) override {
if (infobar->delegate()->GetIdentifier() ==
infobars::InfoBarDelegate::DUPLICATE_DOWNLOAD_INFOBAR_DELEGATE_ANDROID)
++infobar_count_;
infobar->delegate()->InfoBarDismissed();
infobar->RemoveSelf();
}
raw_ptr<infobars::ContentInfoBarManager> infobar_manager_ = nullptr;
int infobar_count_ = 0;
};
class TestDownloadDialogBridge : public DownloadDialogBridge {
public:
TestDownloadDialogBridge() = default;
TestDownloadDialogBridge(const TestDownloadDialogBridge&) = delete;
TestDownloadDialogBridge& operator=(const TestDownloadDialogBridge&) = delete;
void ShowDialog(gfx::NativeWindow native_window,
int64_t total_bytes,
ConnectionType connection_type,
DownloadLocationDialogType dialog_type,
const base::FilePath& suggested_path,
Profile* profile,
DownloadDialogBridge::DialogCallback callback) override {
dialog_shown_count_++;
dialog_type_ = dialog_type;
if (callback) {
DownloadDialogResult result;
result.location_result = DownloadLocationDialogResult::USER_CANCELED;
std::move(callback).Run(std::move(result));
}
}
int GetDialogShownCount() { return dialog_shown_count_; }
DownloadLocationDialogType GetDialogType() { return dialog_type_; }
void ResetStoredVariables() {
dialog_shown_count_ = 0;
dialog_type_ = DownloadLocationDialogType::NO_DIALOG;
}
private:
int dialog_shown_count_;
DownloadLocationDialogType dialog_type_;
DownloadTargetDeterminerDelegate::ConfirmationCallback
dialog_complete_callback_;
};
}
TEST_F(ChromeDownloadManagerDelegateTest, RequestConfirmation_Android) {
DeleteContents();
SetContents(CreateTestWebContents());
base::test::ScopedFeatureList scoped_list;
profile()->GetTestingPrefService()->SetInteger(
prefs::kPromptForDownloadAndroid,
static_cast<int>(DownloadPromptStatus::SHOW_PREFERENCE));
enum class WebContents { AVAILABLE, NONE };
enum class ExpectPath { FULL, EMPTY };
struct {
DownloadConfirmationReason confirmation_reason;
DownloadConfirmationResult expected_result;
WebContents web_contents;
DownloadLocationDialogType dialog_type;
ExpectPath path;
} kTestCases[] = {
{DownloadConfirmationReason::SAVE_AS,
DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
WebContents::AVAILABLE, DownloadLocationDialogType::NO_DIALOG,
ExpectPath::FULL},
{DownloadConfirmationReason::SAVE_AS,
DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
WebContents::NONE, DownloadLocationDialogType::NO_DIALOG,
ExpectPath::FULL},
{DownloadConfirmationReason::PREFERENCE,
DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
WebContents::NONE, DownloadLocationDialogType::NO_DIALOG,
ExpectPath::FULL},
{DownloadConfirmationReason::TARGET_CONFLICT,
DownloadConfirmationResult::CANCELED, WebContents::NONE,
DownloadLocationDialogType::NO_DIALOG, ExpectPath::EMPTY},
{DownloadConfirmationReason::TARGET_NO_SPACE,
DownloadConfirmationResult::CANCELED, WebContents::NONE,
DownloadLocationDialogType::NO_DIALOG, ExpectPath::EMPTY},
{DownloadConfirmationReason::TARGET_PATH_NOT_WRITEABLE,
DownloadConfirmationResult::CANCELED, WebContents::NONE,
DownloadLocationDialogType::NO_DIALOG, ExpectPath::EMPTY},
{DownloadConfirmationReason::NAME_TOO_LONG,
DownloadConfirmationResult::CANCELED, WebContents::NONE,
DownloadLocationDialogType::NO_DIALOG, ExpectPath::EMPTY},
{DownloadConfirmationReason::UNEXPECTED,
DownloadConfirmationResult::CANCELED, WebContents::AVAILABLE,
DownloadLocationDialogType::NO_DIALOG, ExpectPath::EMPTY},
{DownloadConfirmationReason::UNEXPECTED,
DownloadConfirmationResult::CANCELED, WebContents::NONE,
DownloadLocationDialogType::NO_DIALOG, ExpectPath::EMPTY},
{DownloadConfirmationReason::TARGET_CONFLICT,
DownloadConfirmationResult::CANCELED, WebContents::AVAILABLE,
DownloadLocationDialogType::NAME_CONFLICT, ExpectPath::EMPTY},
{DownloadConfirmationReason::TARGET_NO_SPACE,
DownloadConfirmationResult::CANCELED, WebContents::AVAILABLE,
DownloadLocationDialogType::LOCATION_FULL, ExpectPath::EMPTY},
{DownloadConfirmationReason::TARGET_PATH_NOT_WRITEABLE,
DownloadConfirmationResult::CANCELED, WebContents::AVAILABLE,
DownloadLocationDialogType::LOCATION_NOT_FOUND, ExpectPath::EMPTY},
{DownloadConfirmationReason::NAME_TOO_LONG,
DownloadConfirmationResult::CANCELED, WebContents::AVAILABLE,
DownloadLocationDialogType::NAME_TOO_LONG, ExpectPath::EMPTY},
};
EXPECT_CALL(*delegate(), RequestConfirmation_(_, _, _, _))
.WillRepeatedly(Invoke(
delegate(),
&TestChromeDownloadManagerDelegate::RequestConfirmationConcrete));
base::FilePath fake_path = GetPathInDownloadDir(FILE_PATH_LITERAL("foo.txt"));
GURL url("http://example.com");
TestDownloadDialogBridge* dialog_bridge = new TestDownloadDialogBridge();
delegate()->SetDownloadDialogBridgeForTesting(
static_cast<DownloadDialogBridge*>(dialog_bridge));
for (const auto& test_case : kTestCases) {
std::unique_ptr<download::MockDownloadItem> download_item =
CreateActiveDownloadItem(1);
content::DownloadItemUtils::AttachInfoForTesting(
download_item.get(), profile(),
test_case.web_contents == WebContents::AVAILABLE ? web_contents()
: nullptr);
EXPECT_CALL(*download_item, GetURL()).WillRepeatedly(ReturnRef(url));
dialog_bridge->ResetStoredVariables();
base::test::TestFuture<DownloadConfirmationResult,
const ui::SelectedFileInfo&>
future;
delegate()->RequestConfirmation(download_item.get(), fake_path,
test_case.confirmation_reason,
future.GetCallback());
EXPECT_EQ(test_case.expected_result, future.Get<0>());
EXPECT_EQ(test_case.path == ExpectPath::FULL
? ui::SelectedFileInfo(fake_path)
: ui::SelectedFileInfo(),
future.Get<1>());
EXPECT_EQ(
test_case.dialog_type != DownloadLocationDialogType::NO_DIALOG ? 1 : 0,
dialog_bridge->GetDialogShownCount());
EXPECT_EQ(test_case.dialog_type, dialog_bridge->GetDialogType());
EXPECT_CALL(*download_item, GetState())
.WillRepeatedly(Return(DownloadItem::COMPLETE));
download_item->NotifyObserversDownloadUpdated();
}
}
#endif