blob: 79e4a32cb8246c948ead6f34978d5628aa6738df [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "base/at_exit.h"
#include "base/files/file_path.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_path_override.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/value_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_confirmation_result.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_prompt_status.h"
#include "chrome/browser/download/download_stats.h"
#include "chrome/browser/download/download_target_determiner.h"
#include "chrome/browser/download/download_target_info.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/safe_browsing/file_type_policies.h"
#include "chrome/common/safe_browsing/file_type_policies_test_util.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/download/public/common/download_interrupt_reasons.h"
#include "components/download/public/common/mock_download_item.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_types.h"
#include "components/prefs/pref_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "extensions/buildflags/buildflags.h"
#include "net/base/mime_util.h"
#include "ppapi/buildflags/buildflags.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"
#if BUILDFLAG(ENABLE_PLUGINS)
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/plugin_service_filter.h"
#include "content/public/common/webplugininfo.h"
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/extension.h"
#endif
using ::testing::AnyNumber;
using ::testing::Invoke;
using ::testing::Ref;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::ReturnRefOfCopy;
using ::testing::Truly;
using ::testing::WithArg;
using ::testing::_;
using download::DownloadItem;
using ConflictAction = DownloadPathReservationTracker::FilenameConflictAction;
using safe_browsing::DownloadFileType;
using safe_browsing::FileTypePolicies;
namespace {
const char kTransientPathGenerationHistogram[] =
"Download.PathGenerationEvent.Transient";
const char kTransientPathValidationHistogram[] =
"Download.PathValidationResult.Transient";
template <typename T>
base::HistogramBase::Sample ToHistogramSample(T t) {
return static_cast<base::HistogramBase::Sample>(t);
}
// No-op delegate.
class NullWebContentsDelegate : public content::WebContentsDelegate {
public:
NullWebContentsDelegate() {}
~NullWebContentsDelegate() override {}
};
// Google Mock action that posts a task to the current message loop that invokes
// the first argument of the mocked method as a callback. Said argument must be
// a base::Callback<void(ParamType)>. |result| must be of |ParamType| and is
// bound as that parameter.
// Example:
// class FooClass {
// public:
// virtual void Foo(base::Callback<void(bool)> callback);
// };
// ...
// EXPECT_CALL(mock_fooclass_instance, Foo(callback))
// .WillOnce(ScheduleCallback(false));
ACTION_P(ScheduleCallback, result0) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
base::BindOnce(arg0, result0));
}
// Similar to ScheduleCallback, but binds 2 arguments.
ACTION_P2(ScheduleCallback2, result0, result1) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(arg0, result0, result1));
}
// Used with DownloadTestCase. Indicates the type of test case. The expectations
// for the test is set based on the type.
enum TestCaseType {
SAVE_AS,
AUTOMATIC,
FORCED, // Requires that forced_file_path be non-empty.
TRANSIENT,
};
// Used with DownloadTestCase. Type of intermediate filename to expect.
enum TestCaseExpectIntermediate {
EXPECT_CRDOWNLOAD, // Expect path/to/target.crdownload.
EXPECT_UNCONFIRMED, // Expect path/to/Unconfirmed xxx.crdownload.
EXPECT_LOCAL_PATH, // Expect target path.
EXPECT_EMPTY, // Expect empty path. Only for downloads which will be
// marked interrupted or cancelled.
};
// Typical download test case. Used with
// DownloadTargetDeterminerTest::RunTestCase().
struct DownloadTestCase {
// Type of test.
TestCaseType test_type;
// Expected danger type. Verified at the end of target determination.
download::DownloadDangerType expected_danger_type;
// Expected danger level. Verified at the end of target determination.
DownloadFileType::DangerLevel expected_danger_level;
// Value of DownloadItem::GetURL()
const char* url;
// Value of DownloadItem::GetMimeType()
const char* mime_type;
// Should be non-empty if |test_type| == FORCED. Value of GetForcedFilePath().
const base::FilePath::CharType* forced_file_path;
// Expected local path. Specified relative to the test download path.
const base::FilePath::CharType* expected_local_path;
// Expected target disposition. If this is TARGET_DISPOSITION_PROMPT, then the
// test run will expect ChromeDownloadManagerDelegate to prompt the user for a
// download location.
DownloadItem::TargetDisposition expected_disposition;
// Type of intermediate path to expect.
TestCaseExpectIntermediate expected_intermediate;
};
class MockDownloadTargetDeterminerDelegate
: public DownloadTargetDeterminerDelegate {
public:
MOCK_METHOD3(CheckDownloadUrl,
void(download::DownloadItem*,
const base::FilePath&,
const CheckDownloadUrlCallback&));
MOCK_METHOD3(NotifyExtensions,
void(download::DownloadItem*,
const base::FilePath&,
const NotifyExtensionsCallback&));
MOCK_METHOD4(RequestConfirmation,
void(download::DownloadItem*,
const base::FilePath&,
DownloadConfirmationReason,
const ConfirmationCallback&));
MOCK_METHOD3(DetermineLocalPath,
void(DownloadItem*, const base::FilePath&,
const LocalPathCallback&));
MOCK_METHOD5(ReserveVirtualPath,
void(DownloadItem*, const base::FilePath&, bool,
DownloadPathReservationTracker::FilenameConflictAction,
const ReservedPathCallback&));
MOCK_METHOD2(GetFileMimeType,
void(const base::FilePath&,
const GetFileMimeTypeCallback&));
void SetupDefaults() {
ON_CALL(*this, CheckDownloadUrl(_, _, _))
.WillByDefault(WithArg<2>(
ScheduleCallback(download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)));
ON_CALL(*this, NotifyExtensions(_, _, _))
.WillByDefault(WithArg<2>(
ScheduleCallback2(base::FilePath(),
DownloadPathReservationTracker::UNIQUIFY)));
ON_CALL(*this, ReserveVirtualPath(_, _, _, _, _))
.WillByDefault(Invoke(
&MockDownloadTargetDeterminerDelegate::NullReserveVirtualPath));
ON_CALL(*this, RequestConfirmation(_, _, _, _))
.WillByDefault(
Invoke(&MockDownloadTargetDeterminerDelegate::NullPromptUser));
ON_CALL(*this, DetermineLocalPath(_, _, _))
.WillByDefault(Invoke(
&MockDownloadTargetDeterminerDelegate::NullDetermineLocalPath));
ON_CALL(*this, GetFileMimeType(_, _))
.WillByDefault(WithArg<1>(
ScheduleCallback("")));
}
private:
static void NullReserveVirtualPath(
DownloadItem* download,
const base::FilePath& virtual_path,
bool create_directory,
DownloadPathReservationTracker::FilenameConflictAction conflict_action,
const DownloadTargetDeterminerDelegate::ReservedPathCallback& callback);
static void NullPromptUser(DownloadItem* download,
const base::FilePath& suggested_path,
DownloadConfirmationReason reason,
const ConfirmationCallback& callback);
static void NullDetermineLocalPath(
DownloadItem* download, const base::FilePath& virtual_path,
const LocalPathCallback& callback);
};
class DownloadTargetDeterminerTest : public ChromeRenderViewHostTestHarness {
public:
DownloadTargetDeterminerTest() = default;
// ::testing::Test
void SetUp() override;
void TearDown() override;
// Creates MockDownloadItem and sets up default expectations.
std::unique_ptr<download::MockDownloadItem> CreateActiveDownloadItem(
int32_t id,
const DownloadTestCase& test_case);
// Sets the AutoOpenBasedOnExtension user preference for |path|.
void EnableAutoOpenBasedOnExtension(const base::FilePath& path);
// Set the kDownloadDefaultDirectory managed preference to |path|.
void SetManagedDownloadPath(const base::FilePath& path);
// Set the kPromptForDownload user preference to |prompt|.
void SetPromptForDownload(bool prompt);
// Given the relative path |path|, returns the full path under the temporary
// downloads directory.
base::FilePath GetPathInDownloadDir(const base::FilePath::StringType& path);
// Run |test_case| using |item|.
void RunTestCase(const DownloadTestCase& test_case,
const base::FilePath& initial_virtual_path,
download::MockDownloadItem* item);
// Runs |test_case| with |item|. When the DownloadTargetDeterminer is done,
// returns the resulting DownloadTargetInfo.
std::unique_ptr<DownloadTargetInfo> RunDownloadTargetDeterminer(
const base::FilePath& initial_virtual_path,
download::MockDownloadItem* item);
// Run through |test_case_count| tests in |test_cases|. A new MockDownloadItem
// will be created for each test case and destroyed when the test case is
// complete.
void RunTestCasesWithActiveItem(const DownloadTestCase test_cases[],
size_t test_case_count);
// Verifies that |target_path|, |disposition|, |expected_danger_type| and
// |intermediate_path| matches the expectations of |test_case|. Posts
// |closure| to the current message loop when done.
void VerifyDownloadTarget(const DownloadTestCase& test_case,
const DownloadTargetInfo* target_info);
base::FilePath test_download_dir() const {
base::FilePath path;
CHECK(base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &path));
return path;
}
const base::FilePath& test_virtual_dir() const {
return test_virtual_dir_;
}
MockDownloadTargetDeterminerDelegate* delegate() {
return &delegate_;
}
DownloadPrefs* download_prefs() {
return download_prefs_.get();
}
private:
void SetUpFileTypePolicies();
// Resets the global cached DefaultDownloadDirectory instance.
base::ShadowingAtExitManager at_exit_manager_;
base::ScopedPathOverride download_dir_override_{
chrome::DIR_DEFAULT_DOWNLOADS};
std::unique_ptr<DownloadPrefs> download_prefs_;
::testing::NiceMock<MockDownloadTargetDeterminerDelegate> delegate_;
NullWebContentsDelegate web_contents_delegate_;
base::FilePath test_virtual_dir_;
safe_browsing::FileTypePoliciesTestOverlay file_type_configuration_;
DISALLOW_COPY_AND_ASSIGN(DownloadTargetDeterminerTest);
};
void DownloadTargetDeterminerTest::SetUp() {
ChromeRenderViewHostTestHarness::SetUp();
CHECK(profile());
download_prefs_.reset(new DownloadPrefs(profile()));
web_contents()->SetDelegate(&web_contents_delegate_);
test_virtual_dir_ = test_download_dir().Append(FILE_PATH_LITERAL("virtual"));
delegate_.SetupDefaults();
SetUpFileTypePolicies();
#if defined(OS_ANDROID)
profile()->GetTestingPrefService()->SetInteger(
prefs::kPromptForDownloadAndroid,
static_cast<int>(DownloadPromptStatus::DONT_SHOW));
#endif
}
void DownloadTargetDeterminerTest::TearDown() {
download_prefs_.reset();
ChromeRenderViewHostTestHarness::TearDown();
}
std::unique_ptr<download::MockDownloadItem>
DownloadTargetDeterminerTest::CreateActiveDownloadItem(
int32_t id,
const DownloadTestCase& test_case) {
std::unique_ptr<download::MockDownloadItem> item =
std::make_unique<::testing::NiceMock<download::MockDownloadItem>>();
GURL download_url(test_case.url);
std::vector<GURL> url_chain;
url_chain.push_back(download_url);
base::FilePath forced_file_path =
GetPathInDownloadDir(test_case.forced_file_path);
DownloadItem::TargetDisposition initial_disposition =
(test_case.test_type == SAVE_AS) ?
DownloadItem::TARGET_DISPOSITION_PROMPT :
DownloadItem::TARGET_DISPOSITION_OVERWRITE;
EXPECT_TRUE((test_case.test_type != FORCED) || !forced_file_path.empty());
content::DownloadItemUtils::AttachInfo(item.get(), profile(), web_contents());
ON_CALL(*item, GetDangerType())
.WillByDefault(Return(download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
ON_CALL(*item, GetForcedFilePath())
.WillByDefault(ReturnRefOfCopy(forced_file_path));
ON_CALL(*item, GetFullPath())
.WillByDefault(ReturnRefOfCopy(base::FilePath()));
ON_CALL(*item, GetHash())
.WillByDefault(ReturnRefOfCopy(std::string()));
ON_CALL(*item, GetId())
.WillByDefault(Return(id));
ON_CALL(*item, GetLastReason())
.WillByDefault(Return(download::DOWNLOAD_INTERRUPT_REASON_NONE));
ON_CALL(*item, GetMimeType())
.WillByDefault(Return(test_case.mime_type));
ON_CALL(*item, GetReferrerUrl())
.WillByDefault(ReturnRefOfCopy(download_url));
ON_CALL(*item, GetState())
.WillByDefault(Return(DownloadItem::IN_PROGRESS));
ON_CALL(*item, GetTargetDisposition())
.WillByDefault(Return(initial_disposition));
ON_CALL(*item, GetTargetFilePath())
.WillByDefault(ReturnRefOfCopy(base::FilePath()));
ON_CALL(*item, GetTransitionType())
.WillByDefault(Return(ui::PAGE_TRANSITION_LINK));
ON_CALL(*item, GetURL())
.WillByDefault(ReturnRefOfCopy(download_url));
ON_CALL(*item, GetUrlChain())
.WillByDefault(ReturnRefOfCopy(url_chain));
ON_CALL(*item, HasUserGesture())
.WillByDefault(Return(true));
ON_CALL(*item, IsDangerous())
.WillByDefault(Return(false));
ON_CALL(*item, IsTemporary())
.WillByDefault(Return(false));
ON_CALL(*item, IsTransient())
.WillByDefault(Return(test_case.test_type == TestCaseType::TRANSIENT));
return item;
}
void DownloadTargetDeterminerTest::EnableAutoOpenBasedOnExtension(
const base::FilePath& path) {
EXPECT_TRUE(download_prefs_->EnableAutoOpenBasedOnExtension(path));
}
void DownloadTargetDeterminerTest::SetManagedDownloadPath(
const base::FilePath& path) {
profile()->GetTestingPrefService()->SetManagedPref(
prefs::kDownloadDefaultDirectory, base::CreateFilePathValue(path));
}
void DownloadTargetDeterminerTest::SetPromptForDownload(bool prompt) {
profile()->GetTestingPrefService()->
SetBoolean(prefs::kPromptForDownload, prompt);
#if defined(OS_ANDROID)
DownloadPromptStatus download_prompt_status =
prompt ? DownloadPromptStatus::SHOW_PREFERENCE
: DownloadPromptStatus::DONT_SHOW;
profile()->GetTestingPrefService()->SetInteger(
prefs::kPromptForDownloadAndroid,
static_cast<int>(download_prompt_status));
#endif
}
base::FilePath DownloadTargetDeterminerTest::GetPathInDownloadDir(
const base::FilePath::StringType& relative_path) {
if (relative_path.empty())
return base::FilePath();
base::FilePath full_path(test_download_dir().Append(relative_path));
return full_path.NormalizePathSeparators();
}
void DownloadTargetDeterminerTest::RunTestCase(
const DownloadTestCase& test_case,
const base::FilePath& initial_virtual_path,
download::MockDownloadItem* item) {
std::unique_ptr<DownloadTargetInfo> target_info =
RunDownloadTargetDeterminer(initial_virtual_path, item);
VerifyDownloadTarget(test_case, target_info.get());
}
void CompletionCallbackWrapper(
const base::Closure& closure,
std::unique_ptr<DownloadTargetInfo>* target_info_receiver,
std::unique_ptr<DownloadTargetInfo> target_info) {
target_info_receiver->swap(target_info);
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, closure);
}
std::unique_ptr<DownloadTargetInfo>
DownloadTargetDeterminerTest::RunDownloadTargetDeterminer(
const base::FilePath& initial_virtual_path,
download::MockDownloadItem* item) {
std::unique_ptr<DownloadTargetInfo> target_info;
base::RunLoop run_loop;
DownloadTargetDeterminer::Start(
item, initial_virtual_path, DownloadPathReservationTracker::UNIQUIFY,
download_prefs_.get(), delegate(),
base::Bind(&CompletionCallbackWrapper, run_loop.QuitClosure(),
&target_info));
run_loop.Run();
::testing::Mock::VerifyAndClearExpectations(delegate());
return target_info;
}
void DownloadTargetDeterminerTest::RunTestCasesWithActiveItem(
const DownloadTestCase test_cases[],
size_t test_case_count) {
for (size_t i = 0; i < test_case_count; ++i) {
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(i, test_cases[i]);
SCOPED_TRACE(testing::Message() << "Running test case " << i);
RunTestCase(test_cases[i], base::FilePath(), item.get());
}
}
void DownloadTargetDeterminerTest::VerifyDownloadTarget(
const DownloadTestCase& test_case,
const DownloadTargetInfo* target_info) {
base::FilePath expected_local_path(
GetPathInDownloadDir(test_case.expected_local_path));
EXPECT_EQ(expected_local_path.value(), target_info->target_path.value());
EXPECT_EQ(test_case.expected_disposition, target_info->target_disposition);
EXPECT_EQ(test_case.expected_danger_type, target_info->danger_type);
EXPECT_EQ(test_case.expected_danger_level, target_info->danger_level);
switch (test_case.expected_intermediate) {
case EXPECT_CRDOWNLOAD:
EXPECT_EQ(DownloadTargetDeterminer::GetCrDownloadPath(
target_info->target_path).value(),
target_info->intermediate_path.value());
break;
case EXPECT_UNCONFIRMED:
// The paths (in English) look like: /path/Unconfirmed xxx.crdownload.
// Of this, we only check that the path is:
// 1. Not "/path/target.crdownload",
// 2. Points to the same directory as the target.
// 3. Has extension ".crdownload".
// 4. Basename starts with "Unconfirmed ".
EXPECT_NE(DownloadTargetDeterminer::GetCrDownloadPath(expected_local_path)
.value(),
target_info->intermediate_path.value());
EXPECT_EQ(expected_local_path.DirName().value(),
target_info->intermediate_path.DirName().value());
EXPECT_TRUE(target_info->intermediate_path.MatchesExtension(
FILE_PATH_LITERAL(".crdownload")));
EXPECT_EQ(0u,
target_info->intermediate_path.BaseName().value().find(
FILE_PATH_LITERAL("Unconfirmed ")));
break;
case EXPECT_LOCAL_PATH:
EXPECT_EQ(expected_local_path.value(),
target_info->intermediate_path.value());
break;
case EXPECT_EMPTY:
EXPECT_TRUE(target_info->intermediate_path.empty());
break;
}
}
void DownloadTargetDeterminerTest::SetUpFileTypePolicies() {
std::unique_ptr<safe_browsing::DownloadFileTypeConfig> fake_file_type_config =
std::make_unique<safe_browsing::DownloadFileTypeConfig>();
auto* file_type = fake_file_type_config->mutable_default_file_type();
file_type->set_uma_value(-1);
auto* platform_settings = file_type->add_platform_settings();
platform_settings->set_danger_level(DownloadFileType::NOT_DANGEROUS);
platform_settings->set_auto_open_hint(DownloadFileType::ALLOW_AUTO_OPEN);
file_type = fake_file_type_config->add_file_types();
file_type->set_extension("kindabad");
file_type->set_uma_value(-1);
platform_settings = file_type->add_platform_settings();
platform_settings->set_danger_level(DownloadFileType::ALLOW_ON_USER_GESTURE);
platform_settings->set_auto_open_hint(DownloadFileType::ALLOW_AUTO_OPEN);
file_type = fake_file_type_config->add_file_types();
file_type->set_extension("bad");
file_type->set_uma_value(-1);
platform_settings = file_type->add_platform_settings();
platform_settings->set_danger_level(DownloadFileType::DANGEROUS);
platform_settings->set_auto_open_hint(DownloadFileType::DISALLOW_AUTO_OPEN);
file_type_configuration_.SwapConfig(fake_file_type_config);
}
// static
void MockDownloadTargetDeterminerDelegate::NullReserveVirtualPath(
DownloadItem* download,
const base::FilePath& virtual_path,
bool create_directory,
DownloadPathReservationTracker::FilenameConflictAction conflict_action,
const DownloadTargetDeterminerDelegate::ReservedPathCallback& callback) {
callback.Run(PathValidationResult::SUCCESS, virtual_path);
}
// static
void MockDownloadTargetDeterminerDelegate::NullPromptUser(
DownloadItem* download,
const base::FilePath& suggested_path,
DownloadConfirmationReason reason,
const ConfirmationCallback& callback) {
callback.Run(DownloadConfirmationResult::CONFIRMED, suggested_path);
}
// static
void MockDownloadTargetDeterminerDelegate::NullDetermineLocalPath(
DownloadItem* download, const base::FilePath& virtual_path,
const LocalPathCallback& callback) {
callback.Run(virtual_path);
}
// NotifyExtensions implementation that overrides the path so that the target
// file is in a subdirectory called 'overridden'. If the extension is '.remove',
// the extension is removed.
void NotifyExtensionsOverridePath(
download::DownloadItem* download,
const base::FilePath& path,
const DownloadTargetDeterminerDelegate::NotifyExtensionsCallback&
callback) {
base::FilePath new_path =
base::FilePath()
.AppendASCII("overridden")
.Append(path.BaseName());
if (new_path.MatchesExtension(FILE_PATH_LITERAL(".remove")))
new_path = new_path.RemoveExtension();
callback.Run(new_path, DownloadPathReservationTracker::UNIQUIFY);
}
TEST_F(DownloadTargetDeterminerTest, Basic) {
const DownloadTestCase kBasicTestCases[] = {
{// Automatic Safe
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
{// Save_As Safe
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
{// Automatic Dangerous
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
{// Forced Safe
FORCED, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt", "",
FILE_PATH_LITERAL("forced-foo.txt"),
FILE_PATH_LITERAL("forced-foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH},
};
// The test assumes that .kindabad files have a danger level of
// ALLOW_ON_USER_GESTURE.
ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
RunTestCasesWithActiveItem(kBasicTestCases, arraysize(kBasicTestCases));
}
TEST_F(DownloadTargetDeterminerTest, CancelSaveAs) {
const DownloadTestCase kCancelSaveAsTestCases[] = {
{// 0: Save_As Safe, Cancelled.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL(""), DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_LOCAL_PATH}};
ON_CALL(*delegate(), RequestConfirmation(_, _, _, _))
.WillByDefault(WithArg<3>(ScheduleCallback2(
DownloadConfirmationResult::CANCELED, base::FilePath())));
RunTestCasesWithActiveItem(kCancelSaveAsTestCases,
arraysize(kCancelSaveAsTestCases));
}
// The SafeBrowsing check is performed early. Make sure that a download item
// that has been marked as DANGEROUS_URL behaves correctly.
TEST_F(DownloadTargetDeterminerTest, DangerousUrl) {
const DownloadTestCase kSafeBrowsingTestCases[] = {
{// 0: Automatic Dangerous URL
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.txt",
"", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
{// 1: Save As Dangerous URL
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.txt",
"", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_UNCONFIRMED},
{// 2: Forced Dangerous URL
FORCED, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.txt",
"", FILE_PATH_LITERAL("forced-foo.txt"),
FILE_PATH_LITERAL("forced-foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
{// 3: Automatic Dangerous URL + Dangerous file. Dangerous URL takes
// precedence.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.html",
"", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.html"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
{// 4: Save As Dangerous URL + Dangerous file
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.html",
"", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.html"), DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_UNCONFIRMED},
{// 5: Forced Dangerous URL + Dangerous file
FORCED, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.html",
"", FILE_PATH_LITERAL("forced-foo.html"),
FILE_PATH_LITERAL("forced-foo.html"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
};
ON_CALL(*delegate(), CheckDownloadUrl(_, _, _))
.WillByDefault(WithArg<2>(
ScheduleCallback(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL)));
RunTestCasesWithActiveItem(kSafeBrowsingTestCases,
arraysize(kSafeBrowsingTestCases));
}
// The SafeBrowsing check is performed early. Make sure that a download item
// that has been marked as MAYBE_DANGEROUS_CONTENT behaves correctly.
TEST_F(DownloadTargetDeterminerTest, MaybeDangerousContent) {
const DownloadTestCase kSafeBrowsingTestCases[] = {
{// 0: Automatic Maybe dangerous content
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://phishing.example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE, EXPECT_UNCONFIRMED},
{// 1: Automatic Maybe dangerous content with DANGEROUS type.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
DownloadFileType::DANGEROUS, "http://phishing.example.com/foo.bad", "",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.bad"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
{// 2: Save As Maybe dangerous content
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
DownloadFileType::NOT_DANGEROUS,
"http://phishing.example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_UNCONFIRMED},
{// 3: Forced Maybe dangerous content
FORCED, download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
DownloadFileType::NOT_DANGEROUS,
"http://phishing.example.com/foo.kindabad", "",
FILE_PATH_LITERAL("forced-foo.kindabad"),
FILE_PATH_LITERAL("forced-foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED}};
// Test assumptions:
ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
ASSERT_EQ(DownloadFileType::DANGEROUS,
safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
base::FilePath(FILE_PATH_LITERAL("foo.bad"))));
ON_CALL(*delegate(), CheckDownloadUrl(_, _, _))
.WillByDefault(WithArg<2>(ScheduleCallback(
download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT)));
RunTestCasesWithActiveItem(kSafeBrowsingTestCases,
arraysize(kSafeBrowsingTestCases));
}
// Test whether the last saved directory is used for 'Save As' downloads.
TEST_F(DownloadTargetDeterminerTest, LastSavePath) {
const DownloadTestCase kLastSavePathTestCasesPre[] = {
{// 0: If the last save path is empty, then the default download directory
// should be used.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD}};
// These test cases are run with a last save path set to a non-emtpy local
// download directory.
const DownloadTestCase kLastSavePathTestCasesPost[] = {
{// 0: This test case is run with the last download directory set to
// '<test_download_dir()>/foo'.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo/foo.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
{// 1: Start an automatic download. This should be saved to the user's
// default download directory and not the last used Save As directory.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
};
// This test case is run with the last save path set to a non-empty virtual
// directory.
const DownloadTestCase kLastSavePathTestCasesVirtual[] = {
{SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("bar.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_LOCAL_PATH},
};
{
SCOPED_TRACE(testing::Message()
<< "Running with default download path");
base::FilePath prompt_path =
GetPathInDownloadDir(FILE_PATH_LITERAL("foo.txt"));
EXPECT_CALL(*delegate(),
RequestConfirmation(_, prompt_path,
DownloadConfirmationReason::SAVE_AS, _));
RunTestCasesWithActiveItem(kLastSavePathTestCasesPre,
arraysize(kLastSavePathTestCasesPre));
}
// Try with a non-empty last save path.
{
SCOPED_TRACE(testing::Message()
<< "Running with local last_selected_directory");
download_prefs()->SetSaveFilePath(test_download_dir().AppendASCII("foo"));
base::FilePath prompt_path =
GetPathInDownloadDir(FILE_PATH_LITERAL("foo/foo.txt"));
EXPECT_CALL(*delegate(),
RequestConfirmation(_, prompt_path,
DownloadConfirmationReason::SAVE_AS, _));
RunTestCasesWithActiveItem(kLastSavePathTestCasesPost,
arraysize(kLastSavePathTestCasesPost));
}
// And again, but this time use a virtual directory.
{
SCOPED_TRACE(testing::Message()
<< "Running with virtual last_selected_directory");
base::FilePath last_selected_dir = test_virtual_dir().AppendASCII("foo");
base::FilePath virtual_path = last_selected_dir.AppendASCII("foo.txt");
download_prefs()->SetSaveFilePath(last_selected_dir);
EXPECT_CALL(*delegate(),
RequestConfirmation(_, last_selected_dir.AppendASCII("foo.txt"),
DownloadConfirmationReason::SAVE_AS, _));
EXPECT_CALL(*delegate(), DetermineLocalPath(_, virtual_path, _))
.WillOnce(WithArg<2>(ScheduleCallback(
GetPathInDownloadDir(FILE_PATH_LITERAL("bar.txt")))));
RunTestCasesWithActiveItem(kLastSavePathTestCasesVirtual,
arraysize(kLastSavePathTestCasesVirtual));
}
}
// These tests are run with the default downloads folder set to a virtual
// directory.
TEST_F(DownloadTargetDeterminerTest, DefaultVirtual) {
// The default download directory is the virutal path.
download_prefs()->SetDownloadPath(test_virtual_dir());
{
SCOPED_TRACE(testing::Message() << "Automatic Safe Download");
const DownloadTestCase kAutomaticDownloadToVirtualDir = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo-local.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH};
EXPECT_CALL(*delegate(), DetermineLocalPath(_, _, _))
.WillOnce(WithArg<2>(ScheduleCallback(
GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")))));
RunTestCasesWithActiveItem(&kAutomaticDownloadToVirtualDir, 1);
}
{
SCOPED_TRACE(testing::Message() << "Save As to virtual directory");
const DownloadTestCase kSaveAsToVirtualDir = {
SAVE_AS,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/bar.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo-local.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_LOCAL_PATH};
EXPECT_CALL(*delegate(), DetermineLocalPath(_, _, _))
.WillOnce(WithArg<2>(ScheduleCallback(
GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")))));
EXPECT_CALL(*delegate(), RequestConfirmation(
_, test_virtual_dir().AppendASCII("bar.txt"),
DownloadConfirmationReason::SAVE_AS, _))
.WillOnce(WithArg<3>(
ScheduleCallback2(DownloadConfirmationResult::CONFIRMED,
test_virtual_dir().AppendASCII("prompted.txt"))));
RunTestCasesWithActiveItem(&kSaveAsToVirtualDir, 1);
}
// "Save as" is not supported on Android.
{
SCOPED_TRACE(testing::Message() << "Save As to local directory");
const DownloadTestCase kSaveAsToLocalDir = {
SAVE_AS,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/bar.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo-x.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD};
EXPECT_CALL(*delegate(), RequestConfirmation(
_, test_virtual_dir().AppendASCII("bar.txt"),
DownloadConfirmationReason::SAVE_AS, _))
.WillOnce(WithArg<3>(ScheduleCallback2(
DownloadConfirmationResult::CONFIRMED,
GetPathInDownloadDir(FILE_PATH_LITERAL("foo-x.txt")))));
RunTestCasesWithActiveItem(&kSaveAsToLocalDir, 1);
}
{
SCOPED_TRACE(testing::Message() << "Forced safe download");
const DownloadTestCase kForcedSafe = {
FORCED,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"",
FILE_PATH_LITERAL("forced-foo.txt"),
FILE_PATH_LITERAL("forced-foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH};
RunTestCasesWithActiveItem(&kForcedSafe, 1);
}
}
// Test that an inactive download will still get a virtual or local download
// path.
TEST_F(DownloadTargetDeterminerTest, InactiveDownload) {
const DownloadTestCase kBaseTestCase = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD};
const struct {
TestCaseType type;
DownloadItem::TargetDisposition disposition;
} kTestCases[] = {{AUTOMATIC, DownloadItem::TARGET_DISPOSITION_OVERWRITE},
{SAVE_AS, DownloadItem::TARGET_DISPOSITION_PROMPT}};
for (const auto& test_case : kTestCases) {
DownloadTestCase download_test_case = kBaseTestCase;
download_test_case.test_type = test_case.type;
download_test_case.expected_disposition = test_case.disposition;
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(1, download_test_case);
EXPECT_CALL(*item.get(), GetState())
.WillRepeatedly(Return(download::DownloadItem::CANCELLED));
EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _)).Times(0);
EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _)).Times(0);
EXPECT_CALL(*delegate(), ReserveVirtualPath(_, _, _, _, _)).Times(0);
EXPECT_CALL(*delegate(), DetermineLocalPath(_, _, _)).Times(1);
// Each test case has a non-empty target path. The test will fail if the
// target determination doesn't result in that path.
RunTestCase(download_test_case, base::FilePath(), item.get());
}
}
// If the reserved path could not be verified, then the user should see a
// prompt.
TEST_F(DownloadTargetDeterminerTest, ReservationFailed_Confirmation) {
DownloadTestCase download_test_case = {
// 0: Automatic download. Since the reservation fails, the disposition of
// the target is to prompt, but the returned path is used.
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("bar.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD};
struct TestCase {
PathValidationResult result;
DownloadConfirmationReason expected_confirmation_reason;
} kTestCases[] = {
{PathValidationResult::SUCCESS, DownloadConfirmationReason::NONE},
{PathValidationResult::CONFLICT,
DownloadConfirmationReason::TARGET_CONFLICT},
{PathValidationResult::PATH_NOT_WRITABLE,
DownloadConfirmationReason::TARGET_PATH_NOT_WRITEABLE},
{PathValidationResult::NAME_TOO_LONG,
DownloadConfirmationReason::NAME_TOO_LONG}};
for (const auto& test_case : kTestCases) {
SCOPED_TRACE(::testing::Message() << "PathValidationResult "
<< static_cast<int>(test_case.result));
ON_CALL(*delegate(), ReserveVirtualPath(_, _, _, _, _))
.WillByDefault(WithArg<4>(ScheduleCallback2(
test_case.result,
GetPathInDownloadDir(FILE_PATH_LITERAL("bar.txt")))));
if (test_case.expected_confirmation_reason !=
DownloadConfirmationReason::NONE) {
EXPECT_CALL(
*delegate(),
RequestConfirmation(_, _, test_case.expected_confirmation_reason, _));
} else {
EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _)).Times(0);
}
download_test_case.expected_disposition =
test_case.expected_confirmation_reason ==
DownloadConfirmationReason::NONE
? download::DownloadItem::TARGET_DISPOSITION_OVERWRITE
: download::DownloadItem::TARGET_DISPOSITION_PROMPT;
std::unique_ptr<download::MockDownloadItem> item = CreateActiveDownloadItem(
static_cast<int>(test_case.result), download_test_case);
RunTestCase(download_test_case, base::FilePath(), item.get());
}
}
// If the local path could not be determined, the download should be cancelled.
TEST_F(DownloadTargetDeterminerTest, LocalPathFailed) {
const DownloadTestCase kLocalPathFailedCases[] = {
{// 0: Automatic download.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL(""), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH},
};
// The default download directory is the virtual path.
download_prefs()->SetDownloadPath(test_virtual_dir());
// Simulate failed call to DetermineLocalPath.
EXPECT_CALL(*delegate(), DetermineLocalPath(
_, GetPathInDownloadDir(FILE_PATH_LITERAL("virtual/foo.txt")), _))
.WillOnce(WithArg<2>(ScheduleCallback(base::FilePath())));
RunTestCasesWithActiveItem(kLocalPathFailedCases,
arraysize(kLocalPathFailedCases));
}
// Downloads that have a danger level of ALLOW_ON_USER_GESTURE should be marked
// as safe depending on whether there was a user gesture associated with the
// download and whether the referrer was visited prior to today.
TEST_F(DownloadTargetDeterminerTest, VisitedReferrer) {
const DownloadTestCase kVisitedReferrerCases[] = {
// http://visited.example.com/ is added to the history as a visit that
// happened prior to today.
{// 0: Safe download due to visiting referrer before.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://visited.example.com/foo.kindabad", "application/xml",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
{// 1: Dangerous due to not having visited referrer before.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://not-visited.example.com/foo.kindabad", "application/xml",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
{// 2: Safe because the user is being prompted.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://not-visited.example.com/foo.kindabad", "application/xml",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
{// 3: Safe because of forced path.
FORCED, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://not-visited.example.com/foo.kindabad", "application/xml",
FILE_PATH_LITERAL("foo.kindabad"),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH},
};
// This test assumes that the danger level of .kindabad files is
// ALLOW_ON_USER_GESTURE.
ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
// First the history service must exist.
ASSERT_TRUE(profile()->CreateHistoryService(false, false));
GURL url("http://visited.example.com/visited-link.html");
// The time of visit is picked to be several seconds prior to the most recent
// midnight.
base::Time time_of_visit(
base::Time::Now().LocalMidnight() - base::TimeDelta::FromSeconds(10));
history::HistoryService* history_service =
HistoryServiceFactory::GetForProfile(profile(),
ServiceAccessType::EXPLICIT_ACCESS);
ASSERT_TRUE(history_service);
history_service->AddPage(url, time_of_visit, history::SOURCE_BROWSED);
RunTestCasesWithActiveItem(kVisitedReferrerCases,
arraysize(kVisitedReferrerCases));
}
TEST_F(DownloadTargetDeterminerTest, TransitionType) {
const DownloadTestCase kSafeFile = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD};
const DownloadTestCase kAllowOnUserGesture = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://example.com/foo.kindabad",
"application/octet-stream",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED};
const DownloadTestCase kDangerousFile = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::DANGEROUS,
"http://example.com/foo.bad",
"application/octet-stream",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.bad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED};
const struct {
ui::PageTransition page_transition;
download::DownloadDangerType expected_danger_type;
const DownloadTestCase& template_download_test_case;
} kTestCases[] = {
{// Benign file type. Results in a danger type of NOT_DANGEROUS. Page
// transition type is irrelevant.
ui::PAGE_TRANSITION_LINK, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
kSafeFile},
{// File type is ALLOW_ON_USER_GESTURE. PAGE_TRANSITION_LINK doesn't
// cause file to be marked as safe.
ui::PAGE_TRANSITION_LINK, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
kAllowOnUserGesture},
{// File type is ALLOW_ON_USER_GESTURE. PAGE_TRANSITION_TYPED doesn't
// cause file to be marked as safe. TYPED can be used for certain
// types of explicit page transitions that aren't necessarily
// initiated by a user. Hence a resulting download may not be
// intentional.
ui::PAGE_TRANSITION_TYPED, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
kAllowOnUserGesture},
{// File type is ALLOW_ON_USER_GESTURE.
// PAGE_TRANSITION_FROM_ADDRESS_BAR causes file to be marked as safe.
static_cast<ui::PageTransition>(ui::PAGE_TRANSITION_TYPED |
ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, kAllowOnUserGesture},
{// File type is ALLOW_ON_USER_GESTURE.
// PAGE_TRANSITION_FROM_ADDRESS_BAR causes file to be marked as safe.
static_cast<ui::PageTransition>(ui::PAGE_TRANSITION_GENERATED |
ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, kAllowOnUserGesture},
{// File type is ALLOW_ON_USER_GESTURE.
// PAGE_TRANSITION_FROM_ADDRESS_BAR causes file to be marked as safe.
ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, kAllowOnUserGesture},
{// File type is DANGEROUS. PageTransition is irrelevant.
static_cast<ui::PageTransition>(ui::PAGE_TRANSITION_TYPED |
ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE, kDangerousFile},
};
// Test assumptions:
ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
ASSERT_EQ(DownloadFileType::DANGEROUS,
safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
base::FilePath(FILE_PATH_LITERAL("foo.bad"))));
ASSERT_EQ(DownloadFileType::NOT_DANGEROUS,
safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
base::FilePath(FILE_PATH_LITERAL("foo.txt"))));
for (const auto& test_case : kTestCases) {
// The template download test case describes what to expect if the page
// transition was LINK. If the expectation is that the page transition type
// causes the download to be considered safe, then download_test_case needs
// to be adjusted accordingly.
DownloadTestCase download_test_case = test_case.template_download_test_case;
download_test_case.expected_danger_type = test_case.expected_danger_type;
if (test_case.expected_danger_type ==
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
download_test_case.expected_danger_level =
DownloadFileType::NOT_DANGEROUS;
download_test_case.expected_intermediate = EXPECT_CRDOWNLOAD;
}
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(1, download_test_case);
EXPECT_CALL(*item, GetTransitionType())
.WillRepeatedly(Return(test_case.page_transition));
RunTestCase(download_test_case, base::FilePath(), item.get());
}
}
// These test cases are run with "Prompt for download" user preference set to
// true.
TEST_F(DownloadTargetDeterminerTest, PromptAlways_SafeAutomatic) {
const DownloadTestCase kSafeAutomatic = {
// 0: Safe Automatic - Should prompt because of "Prompt for download"
// preference setting.
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/automatic.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("automatic.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD};
SetPromptForDownload(true);
EXPECT_CALL(*delegate(),
RequestConfirmation(
_, GetPathInDownloadDir(FILE_PATH_LITERAL("automatic.txt")),
DownloadConfirmationReason::PREFERENCE, _));
RunTestCasesWithActiveItem(&kSafeAutomatic, 1);
}
TEST_F(DownloadTargetDeterminerTest, PromptAlways_SafeSaveAs) {
const DownloadTestCase kSafeSaveAs = {
// 1: Safe Save As - Should prompt because of "Save as" invocation.
SAVE_AS,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/save-as.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("save-as.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD};
SetPromptForDownload(true);
EXPECT_CALL(*delegate(),
RequestConfirmation(
_, GetPathInDownloadDir(FILE_PATH_LITERAL("save-as.txt")),
DownloadConfirmationReason::SAVE_AS, _));
RunTestCasesWithActiveItem(&kSafeSaveAs, 1);
}
TEST_F(DownloadTargetDeterminerTest, PromptAlways_SafeForced) {
const DownloadTestCase kSafeForced = {
// 2: Safe Forced - Shouldn't prompt.
FORCED,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"text/plain",
FILE_PATH_LITERAL("foo.txt"),
FILE_PATH_LITERAL("foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH};
SetPromptForDownload(true);
RunTestCasesWithActiveItem(&kSafeForced, 1);
}
TEST_F(DownloadTargetDeterminerTest, PromptAlways_AutoOpen) {
const DownloadTestCase kAutoOpen = {
// 3: Automatic - The filename extension is marked as one that we will
// open automatically. Shouldn't prompt.
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.dummy",
"",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.dummy"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD};
SetPromptForDownload(true);
EnableAutoOpenBasedOnExtension(
base::FilePath(FILE_PATH_LITERAL("dummy.dummy")));
RunTestCasesWithActiveItem(&kAutoOpen, 1);
}
// If an embedder responds to a RequestConfirmation with a new path and a
// CONTINUE_WITHOUT_CONFIRMATION, then we shouldn't consider the file as safe.
TEST_F(DownloadTargetDeterminerTest, ContinueWithoutConfirmation_SaveAs) {
const DownloadTestCase kTestCase = {
SAVE_AS,
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://example.com/save-as.kindabad",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED};
EXPECT_CALL(
*delegate(),
RequestConfirmation(
_, GetPathInDownloadDir(FILE_PATH_LITERAL("save-as.kindabad")),
DownloadConfirmationReason::SAVE_AS, _))
.WillOnce(WithArg<3>(ScheduleCallback2(
DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
GetPathInDownloadDir(FILE_PATH_LITERAL("foo.kindabad")))));
RunTestCasesWithActiveItem(&kTestCase, 1);
}
// Same as ContinueWithoutConfirmation_SaveAs, but the embedder response
// indicates that the user confirmed the path. Hence the danger level of the
// download and the disposition should be updated accordingly.
TEST_F(DownloadTargetDeterminerTest, ContinueWithConfirmation_SaveAs) {
const DownloadTestCase kTestCase = {
SAVE_AS,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/save-as.kindabad",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD};
EXPECT_CALL(
*delegate(),
RequestConfirmation(
_, GetPathInDownloadDir(FILE_PATH_LITERAL("save-as.kindabad")),
DownloadConfirmationReason::SAVE_AS, _))
.WillOnce(WithArg<3>(ScheduleCallback2(
DownloadConfirmationResult::CONFIRMED,
GetPathInDownloadDir(FILE_PATH_LITERAL("foo.kindabad")))));
RunTestCasesWithActiveItem(&kTestCase, 1);
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
// These test cases are run with "Prompt for download" user preference set to
// true. Automatic extension downloads shouldn't cause prompting.
// Android doesn't support extensions.
TEST_F(DownloadTargetDeterminerTest, PromptAlways_Extension) {
const DownloadTestCase kPromptingTestCases[] = {
{// 0: Automatic Browser Extension download. - Shouldn't prompt for
// browser extension downloads even if "Prompt for download"
// preference is set.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://example.com/foo.kindabad", extensions::Extension::kMimeType,
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
{// 1: Automatic User Script - Shouldn't prompt for user script downloads
// even if "Prompt for download" preference is set.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.user.js", "",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.user.js"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
};
SetPromptForDownload(true);
RunTestCasesWithActiveItem(kPromptingTestCases,
arraysize(kPromptingTestCases));
}
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
// If the download path is managed, then we don't show any prompts.
// Note that if the download path is managed, then PromptForDownload() is false.
TEST_F(DownloadTargetDeterminerTest, ManagedPath) {
const DownloadTestCase kManagedPathTestCases[] = {
{// 0: Automatic Safe
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
{// 1: Save_As Safe
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
};
SetManagedDownloadPath(test_download_dir());
ASSERT_TRUE(download_prefs()->IsDownloadPathManaged());
RunTestCasesWithActiveItem(kManagedPathTestCases,
arraysize(kManagedPathTestCases));
}
// Test basic functionality supporting extensions that want to override download
// filenames.
TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsSafe) {
const DownloadTestCase kNotifyExtensionsTestCases[] = {
{// 0: Automatic Safe
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("overridden/foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
{// 1: Save_As Safe
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("overridden/foo.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
{// 2: Automatic Dangerous
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("overridden/foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
{// 3: Forced Safe
FORCED, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt", "",
FILE_PATH_LITERAL("forced-foo.txt"),
FILE_PATH_LITERAL("forced-foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH},
};
ON_CALL(*delegate(), NotifyExtensions(_, _, _))
.WillByDefault(Invoke(&NotifyExtensionsOverridePath));
RunTestCasesWithActiveItem(kNotifyExtensionsTestCases,
arraysize(kNotifyExtensionsTestCases));
}
// Test that filenames provided by extensions are passed into SafeBrowsing
// checks and dangerous download checks.
TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsUnsafe) {
const DownloadTestCase kNotHandledBySafeBrowsing = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://example.com/foo.kindabad.remove",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("overridden/foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED};
const DownloadTestCase kHandledBySafeBrowsing = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://example.com/foo.kindabad.remove",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("overridden/foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED};
ON_CALL(*delegate(), NotifyExtensions(_, _, _))
.WillByDefault(Invoke(&NotifyExtensionsOverridePath));
RunTestCasesWithActiveItem(&kNotHandledBySafeBrowsing, 1);
ON_CALL(*delegate(), CheckDownloadUrl(_, _, _))
.WillByDefault(WithArg<2>(ScheduleCallback(
download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT)));
RunTestCasesWithActiveItem(&kHandledBySafeBrowsing, 1);
}
// Test that conflict actions set by extensions are passed correctly into
// ReserveVirtualPath.
TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsConflict) {
const DownloadTestCase kNotifyExtensionsTestCase = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("overridden/foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD};
const DownloadTestCase& test_case = kNotifyExtensionsTestCase;
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(0, test_case);
base::FilePath overridden_path(FILE_PATH_LITERAL("overridden/foo.txt"));
base::FilePath full_overridden_path =
GetPathInDownloadDir(overridden_path.value());
// First case: An extension sets the conflict_action to OVERWRITE.
EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _))
.WillOnce(WithArg<2>(
ScheduleCallback2(overridden_path,
DownloadPathReservationTracker::OVERWRITE)));
EXPECT_CALL(*delegate(),
ReserveVirtualPath(_, full_overridden_path, true,
DownloadPathReservationTracker::OVERWRITE, _))
.WillOnce(WithArg<4>(ScheduleCallback2(PathValidationResult::SUCCESS,
full_overridden_path)));
RunTestCase(test_case, base::FilePath(), item.get());
// Second case: An extension sets the conflict_action to PROMPT.
EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _))
.WillOnce(WithArg<2>(
ScheduleCallback2(overridden_path,
DownloadPathReservationTracker::PROMPT)));
EXPECT_CALL(*delegate(),
ReserveVirtualPath(_, full_overridden_path, true,
DownloadPathReservationTracker::PROMPT, _))
.WillOnce(WithArg<4>(ScheduleCallback2(PathValidationResult::SUCCESS,
full_overridden_path)));
RunTestCase(test_case, base::FilePath(), item.get());
}
// Test that relative paths returned by extensions are always relative to the
// default downloads path.
TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsDefaultPath) {
const DownloadTestCase kNotifyExtensionsTestCase = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("overridden/foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD};
const DownloadTestCase& test_case = kNotifyExtensionsTestCase;
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(0, test_case);
base::FilePath overridden_path(FILE_PATH_LITERAL("overridden/foo.txt"));
base::FilePath full_overridden_path =
GetPathInDownloadDir(overridden_path.value());
download_prefs()->SetSaveFilePath(GetPathInDownloadDir(
FILE_PATH_LITERAL("last_selected")));
EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _))
.WillOnce(WithArg<2>(
ScheduleCallback2(overridden_path,
DownloadPathReservationTracker::UNIQUIFY)));
RunTestCase(test_case, base::FilePath(), item.get());
}
TEST_F(DownloadTargetDeterminerTest, InitialVirtualPathUnsafe) {
const base::FilePath::CharType* kInitialPath =
FILE_PATH_LITERAL("some_path/bar.html");
const DownloadTestCase kInitialPathTestCase = {
// 0: Save As Save. The path generated based on the DownloadItem is safe,
// but the initial path is unsafe. However, the download is not considered
// dangerous since the user has been prompted.
SAVE_AS,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"text/plain",
FILE_PATH_LITERAL(""),
kInitialPath,
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD};
const DownloadTestCase& test_case = kInitialPathTestCase;
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(1, test_case);
EXPECT_CALL(*item, GetLastReason())
.WillRepeatedly(
Return(download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED));
EXPECT_CALL(*item, GetTargetDisposition())
.WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_PROMPT));
RunTestCase(test_case, GetPathInDownloadDir(kInitialPath), item.get());
}
// Prompting behavior for resumed downloads is based on the last interrupt
// reason. If the reason indicates that the target path may not be suitable for
// the download (ACCESS_DENIED, NO_SPACE, etc..), then the user should be
// prompted, and not otherwise. These test cases shouldn't result in prompting
// since the error is set to NETWORK_FAILED.
TEST_F(DownloadTargetDeterminerTest, ResumedNoPrompt) {
// All test cases run with GetPathInDownloadDir(kInitialPath) as the inital
// path.
const base::FilePath::CharType* kInitialPath =
FILE_PATH_LITERAL("some_path/bar.txt");
const DownloadTestCase kResumedTestCases[] = {
{// 0: Automatic Safe: Initial path is ignored since the user has not been
// prompted before.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
{// 1: Save_As Safe: Initial path used.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
kInitialPath, DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
{// 2: Automatic Dangerous: Initial path is ignored since the user hasn't
// been prompted before.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
{// 3: Forced Safe: Initial path is ignored due to the forced path.
FORCED, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt", "",
FILE_PATH_LITERAL("forced-foo.txt"),
FILE_PATH_LITERAL("forced-foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH},
};
// The test assumes that .kindabad files have a danger level of
// ALLOW_ON_USER_GESTURE.
ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
for (size_t i = 0; i < arraysize(kResumedTestCases); ++i) {
SCOPED_TRACE(testing::Message() << "Running test case " << i);
const DownloadTestCase& test_case = kResumedTestCases[i];
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(i, test_case);
base::FilePath expected_path =
GetPathInDownloadDir(test_case.expected_local_path);
ON_CALL(*item.get(), GetLastReason())
.WillByDefault(
Return(download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED));
// Extensions should be notified if a new path is being generated and there
// is no forced path. In the test cases above, this is true for tests with
// type == AUTOMATIC.
EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _))
.Times(test_case.test_type == AUTOMATIC ? 1 : 0);
EXPECT_CALL(*delegate(), ReserveVirtualPath(_, expected_path, false, _, _));
EXPECT_CALL(*delegate(), RequestConfirmation(_, expected_path, _, _))
.Times(0);
EXPECT_CALL(*delegate(), DetermineLocalPath(_, expected_path, _));
EXPECT_CALL(*delegate(), CheckDownloadUrl(_, expected_path, _));
RunTestCase(test_case, GetPathInDownloadDir(kInitialPath), item.get());
}
}
// Test that a forced download doesn't prompt, even if the interrupt reason
// suggests that the target path may not be suitable for downloads.
TEST_F(DownloadTargetDeterminerTest, ResumedForcedDownload) {
const base::FilePath::CharType* kInitialPath =
FILE_PATH_LITERAL("some_path/bar.txt");
const DownloadTestCase kResumedForcedDownload = {
// 3: Forced Safe
FORCED,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"",
FILE_PATH_LITERAL("forced-foo.txt"),
FILE_PATH_LITERAL("forced-foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH};
const DownloadTestCase& test_case = kResumedForcedDownload;
base::FilePath expected_path =
GetPathInDownloadDir(test_case.expected_local_path);
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(0, test_case);
ON_CALL(*item.get(), GetLastReason())
.WillByDefault(Return(download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE));
EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _))
.Times(test_case.test_type == AUTOMATIC ? 1 : 0);
EXPECT_CALL(*delegate(), ReserveVirtualPath(_, expected_path, false, _, _));
EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _)).Times(0);
EXPECT_CALL(*delegate(), DetermineLocalPath(_, expected_path, _));
EXPECT_CALL(*delegate(), CheckDownloadUrl(_, expected_path, _));
RunTestCase(test_case, GetPathInDownloadDir(kInitialPath), item.get());
}
// Prompting behavior for resumed downloads is based on the last interrupt
// reason. If the reason indicates that the target path may not be suitable for
// the download (ACCESS_DENIED, NO_SPACE, etc..), then the user should be
// prompted, and not otherwise. These test cases result in prompting since the
// error is set to NO_SPACE.
TEST_F(DownloadTargetDeterminerTest, ResumedWithPrompt) {
// All test cases run with GetPathInDownloadDir(kInitialPath) as the inital
// path.
const base::FilePath::CharType* kInitialPath =
FILE_PATH_LITERAL("some_path/bar.txt");
const DownloadTestCase kResumedTestCases[] = {
{// 0: Automatic Safe
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
{// 1: Save_As Safe
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
kInitialPath, DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
{
// 2: Automatic Dangerous
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.kindabad",
"", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_PROMPT, EXPECT_CRDOWNLOAD,
},
{
// 3: Automatic Dangerous
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.bad", "",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.bad"), DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD,
},
};
ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
for (size_t i = 0; i < arraysize(kResumedTestCases); ++i) {
SCOPED_TRACE(testing::Message() << "Running test case " << i);
download_prefs()->SetSaveFilePath(test_download_dir());
const DownloadTestCase& test_case = kResumedTestCases[i];
base::FilePath expected_path =
GetPathInDownloadDir(test_case.expected_local_path);
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(i, test_case);
ON_CALL(*item.get(), GetLastReason())
.WillByDefault(
Return(download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE));
EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _))
.Times(test_case.test_type == AUTOMATIC ? 1 : 0);
EXPECT_CALL(*delegate(), ReserveVirtualPath(_, expected_path, false, _, _));
EXPECT_CALL(*delegate(), RequestConfirmation(_, expected_path, _, _));
EXPECT_CALL(*delegate(), DetermineLocalPath(_, expected_path, _));
EXPECT_CALL(*delegate(), CheckDownloadUrl(_, expected_path, _));
RunTestCase(test_case, GetPathInDownloadDir(kInitialPath), item.get());
}
}
// Test intermediate filename generation for resumed downloads.
TEST_F(DownloadTargetDeterminerTest, IntermediateNameForResumed) {
// All test cases run with GetPathInDownloadDir(kInitialPath) as the inital
// path.
const base::FilePath::CharType kInitialPath[] =
FILE_PATH_LITERAL("some_path/bar.txt");
struct IntermediateNameTestCase {
// General test case settings.
DownloadTestCase general;
// Value of DownloadItem::GetFullPath() during test run, relative
// to test download path.
const base::FilePath::CharType* initial_intermediate_path;
// Expected intermediate path relatvie to the test download path. An exact
// match is performed if this string is non-empty. Ignored otherwise.
const base::FilePath::CharType* expected_intermediate_path;
} kIntermediateNameTestCases[] = {
{{// 0: Automatic Safe
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
FILE_PATH_LITERAL("bar.txt.crdownload"),
FILE_PATH_LITERAL("foo.txt.crdownload")},
{{// 1: Save_As Safe
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
"text/plain", FILE_PATH_LITERAL(""),
kInitialPath, DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
FILE_PATH_LITERAL("foo.txt.crdownload"),
FILE_PATH_LITERAL("some_path/bar.txt.crdownload")},
{{// 2: Automatic Dangerous
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
FILE_PATH_LITERAL("Unconfirmed abcd.crdownload"),
FILE_PATH_LITERAL("Unconfirmed abcd.crdownload")},
{{// 3: Automatic Dangerous
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
DownloadFileType::ALLOW_ON_USER_GESTURE,
"http://example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.kindabad"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_UNCONFIRMED},
FILE_PATH_LITERAL("other_path/Unconfirmed abcd.crdownload"),
// Rely on the EXPECT_UNCONFIRMED check in the general test settings. A
// new intermediate path of the form "Unconfirmed <number>.crdownload"
// should be generated for this case since the initial intermediate path
// is in the wrong directory.
FILE_PATH_LITERAL("")},
{{// 3: Forced Safe: Initial path is ignored due to the forced path.
FORCED, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt", "",
FILE_PATH_LITERAL("forced-foo.txt"),
FILE_PATH_LITERAL("forced-foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH},
FILE_PATH_LITERAL("forced-foo.txt"),
FILE_PATH_LITERAL("forced-foo.txt")},
};
// The test assumes that .kindabad files have a danger level of
// ALLOW_ON_USER_GESTURE.
ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
for (size_t i = 0; i < arraysize(kIntermediateNameTestCases); ++i) {
SCOPED_TRACE(testing::Message() << "Running test case " << i);
const IntermediateNameTestCase& test_case = kIntermediateNameTestCases[i];
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(i, test_case.general);
ON_CALL(*item.get(), GetLastReason())
.WillByDefault(
Return(download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED));
ON_CALL(*item.get(), GetFullPath())
.WillByDefault(ReturnRefOfCopy(
GetPathInDownloadDir(test_case.initial_intermediate_path)));
ON_CALL(*item.get(), GetDangerType())
.WillByDefault(Return(test_case.general.expected_danger_type));
std::unique_ptr<DownloadTargetInfo> target_info =
RunDownloadTargetDeterminer(GetPathInDownloadDir(kInitialPath),
item.get());
VerifyDownloadTarget(test_case.general, target_info.get());
base::FilePath expected_intermediate_path =
GetPathInDownloadDir(test_case.expected_intermediate_path);
if (!expected_intermediate_path.empty())
EXPECT_EQ(expected_intermediate_path, target_info->intermediate_path);
}
}
// Test MIME type determination based on the target filename.
TEST_F(DownloadTargetDeterminerTest, MIMETypeDetermination) {
// All test cases run with GetPathInDownloadDir(kInitialPath) as the inital
// path.
const base::FilePath::CharType kInitialPath[] =
FILE_PATH_LITERAL("some_path/bar.txt");
struct MIMETypeTestCase {
// General test case settings.
DownloadTestCase general;
// Expected MIME type for test case.
const char* expected_mime_type;
} kMIMETypeTestCases[] = {
{{// 0:
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.png",
"image/png", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.png"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
"image/png"},
{{// 1: Empty MIME type in response.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.png", "",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.png"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
"image/png"},
{{// 2: Forced path.
FORCED, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.abc", "",
FILE_PATH_LITERAL("foo.png"),
FILE_PATH_LITERAL("foo.png"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
"image/png"},
{{// 3: Unknown file type.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.notarealext",
"", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.notarealext"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
""},
{{// 4: Unknown file type.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.notarealext",
"image/png", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.notarealext"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
""},
{{// 5: x-x509-user-cert mime-type.
AUTOMATIC, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.notarealext",
"application/x-x509-user-cert", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("user.crt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD},
""},
};
ON_CALL(*delegate(), GetFileMimeType(
GetPathInDownloadDir(FILE_PATH_LITERAL("foo.png")), _))
.WillByDefault(WithArg<1>(
ScheduleCallback("image/png")));
for (size_t i = 0; i < arraysize(kMIMETypeTestCases); ++i) {
SCOPED_TRACE(testing::Message() << "Running test case " << i);
const MIMETypeTestCase& test_case = kMIMETypeTestCases[i];
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(i, test_case.general);
std::unique_ptr<DownloadTargetInfo> target_info =
RunDownloadTargetDeterminer(GetPathInDownloadDir(kInitialPath),
item.get());
EXPECT_EQ(test_case.expected_mime_type, target_info->mime_type);
}
}
// Test that a user validated download won't be treated as dangerous.
TEST_F(DownloadTargetDeterminerTest, ResumedWithUserValidatedDownload) {
const base::FilePath::CharType kInitialPath[] =
FILE_PATH_LITERAL("some_path/bar.txt");
const base::FilePath::CharType* kIntermediatePath =
FILE_PATH_LITERAL("foo.crx.crdownload");
const DownloadTestCase kUserValidatedTestCase = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.crx",
"",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.crx"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD};
const DownloadTestCase& test_case = kUserValidatedTestCase;
std::unique_ptr<download::MockDownloadItem> item(
CreateActiveDownloadItem(0, test_case));
base::FilePath expected_path =
GetPathInDownloadDir(test_case.expected_local_path);
ON_CALL(*item.get(), GetDangerType())
.WillByDefault(Return(download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED));
ON_CALL(*item.get(), GetFullPath())
.WillByDefault(ReturnRefOfCopy(GetPathInDownloadDir(kIntermediatePath)));
ON_CALL(*item.get(), GetLastReason())
.WillByDefault(
Return(download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED));
EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _));
EXPECT_CALL(*delegate(), ReserveVirtualPath(_, expected_path, false, _, _));
EXPECT_CALL(*delegate(), DetermineLocalPath(_, expected_path, _));
EXPECT_CALL(*delegate(), CheckDownloadUrl(_, expected_path, _)).Times(0);
EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _)).Times(0);
RunTestCase(test_case, GetPathInDownloadDir(kInitialPath), item.get());
}
// Test that verifies transient download target determination.
TEST_F(DownloadTargetDeterminerTest, TransientDownload) {
const base::FilePath::CharType kInitialPath[] =
FILE_PATH_LITERAL("some_path/bar.txt");
DownloadTestCase transient_test_case = {
TRANSIENT,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo",
"",
FILE_PATH_LITERAL("12345"), /* forced_file_path */
FILE_PATH_LITERAL("12345"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH};
std::unique_ptr<download::MockDownloadItem> item(
CreateActiveDownloadItem(0, transient_test_case));
base::FilePath expected_path =
GetPathInDownloadDir(transient_test_case.expected_local_path);
EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _)).Times(0);
EXPECT_CALL(*delegate(), ReserveVirtualPath(_, expected_path, false,
ConflictAction::OVERWRITE, _))
.Times(1);
EXPECT_CALL(*delegate(), DetermineLocalPath(_, expected_path, _)).Times(1);
EXPECT_CALL(*delegate(), CheckDownloadUrl(_, expected_path, _)).Times(1);
EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _)).Times(0);
base::HistogramTester histogram_tester;
RunTestCase(transient_test_case, GetPathInDownloadDir(kInitialPath),
item.get());
histogram_tester.ExpectBucketCount(
kTransientPathGenerationHistogram,
ToHistogramSample<DownloadPathGenerationEvent>(
DownloadPathGenerationEvent::USE_FORCE_PATH),
1);
histogram_tester.ExpectBucketCount(
kTransientPathValidationHistogram,
ToHistogramSample<PathValidationResult>(PathValidationResult::SUCCESS),
1);
histogram_tester.ExpectTotalCount(kTransientPathGenerationHistogram, 1);
histogram_tester.ExpectTotalCount(kTransientPathValidationHistogram, 1);
}
// Simulate resumption on transient download. The download item does not provide
// forced path in this case.
TEST_F(DownloadTargetDeterminerTest, TransientDownloadResumption) {
const base::FilePath::CharType kInitialPath[] =
FILE_PATH_LITERAL("some_path/bar.txt");
base::FilePath expected_path = GetPathInDownloadDir(kInitialPath);
DownloadTestCase transient_test_case = {
TRANSIENT,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo",
"",
FILE_PATH_LITERAL(""), /* forced_file_path */
kInitialPath,
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH};
// Simulate resumption that provides the full path and a failure reason.
std::unique_ptr<download::MockDownloadItem> item(
CreateActiveDownloadItem(0, transient_test_case));
ON_CALL(*item.get(), GetFullPath())
.WillByDefault(ReturnRefOfCopy(expected_path));
ON_CALL(*item.get(), GetLastReason())
.WillByDefault(
Return(download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED));
EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _)).Times(0);
EXPECT_CALL(*delegate(), ReserveVirtualPath(_, expected_path, false,
ConflictAction::OVERWRITE, _))
.Times(1);
EXPECT_CALL(*delegate(), DetermineLocalPath(_, expected_path, _));
EXPECT_CALL(*delegate(), CheckDownloadUrl(_, expected_path, _)).Times(1);
EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _)).Times(0);
base::HistogramTester histogram_tester;
RunTestCase(transient_test_case, GetPathInDownloadDir(kInitialPath),
item.get());
histogram_tester.ExpectBucketCount(
kTransientPathGenerationHistogram,
ToHistogramSample<DownloadPathGenerationEvent>(
DownloadPathGenerationEvent::USE_EXISTING_VIRTUAL_PATH),
1);
histogram_tester.ExpectBucketCount(
kTransientPathValidationHistogram,
ToHistogramSample<PathValidationResult>(PathValidationResult::SUCCESS),
1);
histogram_tester.ExpectTotalCount(kTransientPathGenerationHistogram, 1);
histogram_tester.ExpectTotalCount(kTransientPathValidationHistogram, 1);
}
#if BUILDFLAG(ENABLE_PLUGINS)
void DummyGetPluginsCallback(
const base::Closure& closure,
const std::vector<content::WebPluginInfo>& plugins) {
closure.Run();
}
void ForceRefreshOfPlugins() {
#if !defined(OS_WIN)
// Prevent creation of a utility process for loading plugins. Doing so breaks
// unit_tests since /proc/self/exe can't be run as a utility process.
content::RenderProcessHost::SetRunRendererInProcess(true);
#endif
base::RunLoop run_loop;
content::PluginService::GetInstance()->GetPlugins(
base::BindOnce(&DummyGetPluginsCallback, run_loop.QuitClosure()));
run_loop.Run();
#if !defined(OS_WIN)
content::RenderProcessHost::SetRunRendererInProcess(false);
#endif
}
class MockPluginServiceFilter : public content::PluginServiceFilter {
public:
MOCK_METHOD1(MockPluginAvailable, bool(const base::FilePath&));
bool IsPluginAvailable(int render_process_id,
int render_view_id,
const void* context,
const GURL& url,
const url::Origin& main_frame_origin,
content::WebPluginInfo* plugin) override {
return MockPluginAvailable(plugin->path);
}
bool CanLoadPlugin(int render_process_id,
const base::FilePath& path) override {
return true;
}
};
class ScopedRegisterInternalPlugin {
public:
ScopedRegisterInternalPlugin(content::PluginService* plugin_service,
content::WebPluginInfo::PluginType type,
const base::FilePath& path,
const char* mime_type,
const char* extension)
: plugin_service_(plugin_service),
plugin_path_(path) {
content::WebPluginMimeType plugin_mime_type(mime_type,
extension,
"Test file");
content::WebPluginInfo plugin_info(base::string16(),
path,
base::string16(),
base::string16());
plugin_info.mime_types.push_back(plugin_mime_type);
plugin_info.type = type;
plugin_service->RegisterInternalPlugin(plugin_info, true);
plugin_service->RefreshPlugins();
ForceRefreshOfPlugins();
}
~ScopedRegisterInternalPlugin() {
plugin_service_->UnregisterInternalPlugin(plugin_path_);
plugin_service_->RefreshPlugins();
ForceRefreshOfPlugins();
}
const base::FilePath& path() { return plugin_path_; }
private:
content::PluginService* plugin_service_;
base::FilePath plugin_path_;
};
// We use a slightly different test fixture for tests that touch plugins. SetUp
// needs to disable plugin discovery and we rely on the base class'
// ShadowingAtExitManager to discard the tainted PluginService. Unfortunately,
// PluginService carries global state.
class DownloadTargetDeterminerTestWithPlugin
: public DownloadTargetDeterminerTest {
public:
DownloadTargetDeterminerTestWithPlugin()
: old_plugin_service_filter_(NULL) {}
void SetUp() override {
DownloadTargetDeterminerTest::SetUp();
content::PluginService* plugin_service =
content::PluginService::GetInstance();
plugin_service->Init();
old_plugin_service_filter_ = plugin_service->GetFilter();
plugin_service->SetFilter(&mock_plugin_filter_);
}
void TearDown() override {
content::PluginService::GetInstance()->SetFilter(
old_plugin_service_filter_);
DownloadTargetDeterminerTest::TearDown();
}
protected:
content::PluginServiceFilter* old_plugin_service_filter_;
testing::StrictMock<MockPluginServiceFilter> mock_plugin_filter_;
};
// Check if secure handling of filetypes is determined correctly for PPAPI
// plugins.
TEST_F(DownloadTargetDeterminerTestWithPlugin, CheckForSecureHandling_PPAPI) {
// All test cases run with GetPathInDownloadDir(kInitialPath) as the inital
// path.
const base::FilePath::CharType kInitialPath[] =
FILE_PATH_LITERAL("some_path/bar.txt");
const char kTestMIMEType[] = "application/x-example-should-not-exist";
DownloadTestCase kSecureHandlingTestCase = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.fakeext",
"",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.fakeext"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD};
content::PluginService* plugin_service =
content::PluginService::GetInstance();
// Verify our test assumptions.
{
ForceRefreshOfPlugins();
std::vector<content::WebPluginInfo> info;
ASSERT_FALSE(plugin_service->GetPluginInfoArray(
GURL(), kTestMIMEType, false, &info, NULL));
ASSERT_EQ(0u, info.size())
<< "Name: " << info[0].name << ", Path: " << info[0].path.value();
}
ON_CALL(*delegate(), GetFileMimeType(
GetPathInDownloadDir(FILE_PATH_LITERAL("foo.fakeext")), _))
.WillByDefault(WithArg<1>(
ScheduleCallback(kTestMIMEType)));
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(1, kSecureHandlingTestCase);
std::unique_ptr<DownloadTargetInfo> target_info = RunDownloadTargetDeterminer(
GetPathInDownloadDir(kInitialPath), item.get());
EXPECT_FALSE(target_info->is_filetype_handled_safely);
// Register a PPAPI plugin. This should count as handling the filetype
// securely.
ScopedRegisterInternalPlugin ppapi_plugin(
plugin_service,
content::WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS,
test_download_dir().AppendASCII("ppapi"),
kTestMIMEType,
"fakeext");
EXPECT_CALL(mock_plugin_filter_, MockPluginAvailable(ppapi_plugin.path()))
.WillRepeatedly(Return(true));
target_info = RunDownloadTargetDeterminer(
GetPathInDownloadDir(kInitialPath), item.get());
EXPECT_TRUE(target_info->is_filetype_handled_safely);
// Try disabling the plugin. Handling should no longer be considered secure.
EXPECT_CALL(mock_plugin_filter_, MockPluginAvailable(ppapi_plugin.path()))
.WillRepeatedly(Return(false));
target_info = RunDownloadTargetDeterminer(
GetPathInDownloadDir(kInitialPath), item.get());
EXPECT_FALSE(target_info->is_filetype_handled_safely);
}
// Check if secure handling of filetypes is determined correctly for
// BrowserPlugins.
TEST_F(DownloadTargetDeterminerTestWithPlugin,
CheckForSecureHandling_BrowserPlugin) {
// All test cases run with GetPathInDownloadDir(kInitialPath) as the inital
// path.
const base::FilePath::CharType kInitialPath[] =
FILE_PATH_LITERAL("some_path/bar.txt");
const char kTestMIMEType[] = "application/x-example-should-not-exist";
DownloadTestCase kSecureHandlingTestCase = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.fakeext",
"",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.fakeext"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_CRDOWNLOAD};
content::PluginService* plugin_service =
content::PluginService::GetInstance();
// Verify our test assumptions.
{
ForceRefreshOfPlugins();
std::vector<content::WebPluginInfo> info;
ASSERT_FALSE(plugin_service->GetPluginInfoArray(
GURL(), kTestMIMEType, false, &info, NULL));
ASSERT_EQ(0u, info.size())
<< "Name: " << info[0].name << ", Path: " << info[0].path.value();
}
ON_CALL(*delegate(), GetFileMimeType(
GetPathInDownloadDir(FILE_PATH_LITERAL("foo.fakeext")), _))
.WillByDefault(WithArg<1>(
ScheduleCallback(kTestMIMEType)));
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(1, kSecureHandlingTestCase);
std::unique_ptr<DownloadTargetInfo> target_info = RunDownloadTargetDeterminer(
GetPathInDownloadDir(kInitialPath), item.get());
EXPECT_FALSE(target_info->is_filetype_handled_safely);
// Register a BrowserPlugin. This should count as handling the filetype
// securely.
ScopedRegisterInternalPlugin browser_plugin(
plugin_service,
content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN,
test_download_dir().AppendASCII("browser_plugin"),
kTestMIMEType,
"fakeext");
EXPECT_CALL(mock_plugin_filter_, MockPluginAvailable(browser_plugin.path()))
.WillRepeatedly(Return(true));
target_info = RunDownloadTargetDeterminer(
GetPathInDownloadDir(kInitialPath), item.get());
EXPECT_TRUE(target_info->is_filetype_handled_safely);
// Try disabling the plugin. Handling should no longer be considered secure.
EXPECT_CALL(mock_plugin_filter_, MockPluginAvailable(browser_plugin.path()))
.WillRepeatedly(Return(false));
target_info = RunDownloadTargetDeterminer(
GetPathInDownloadDir(kInitialPath), item.get());
EXPECT_FALSE(target_info->is_filetype_handled_safely);
}
#endif // BUILDFLAG(ENABLE_PLUGINS)
} // namespace