diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-07-31 15:50:41 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 12:35:23 +0000 |
commit | 7b2ffa587235a47d4094787d72f38102089f402a (patch) | |
tree | 30e82af9cbab08a7fa028bb18f4f2987a3f74dfa /chromium/components/download | |
parent | d94af01c90575348c4e81a418257f254b6f8d225 (diff) | |
download | qtwebengine-chromium-7b2ffa587235a47d4094787d72f38102089f402a.tar.gz |
BASELINE: Update Chromium to 76.0.3809.94
Change-Id: I321c3f5f929c105aec0f98c5091ef6108822e647
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/components/download')
109 files changed, 1636 insertions, 2077 deletions
diff --git a/chromium/components/download/BUILD.gn b/chromium/components/download/BUILD.gn index 5e81255abf2..a9b5439e99d 100644 --- a/chromium/components/download/BUILD.gn +++ b/chromium/components/download/BUILD.gn @@ -14,7 +14,6 @@ group("unit_tests") { "//components/download/internal/common:unit_tests", "//components/download/public/common:unit_tests", "//components/download/public/task:unit_tests", - "//components/download/quarantine:unit_tests", ] } diff --git a/chromium/components/download/content/DEPS b/chromium/components/download/content/DEPS index 98f5933ddb9..aea8a9407dc 100644 --- a/chromium/components/download/content/DEPS +++ b/chromium/components/download/content/DEPS @@ -5,4 +5,5 @@ include_rules = [ "+content/public/test", "+base", "+net", + "+services/network/public/cpp/shared_url_loader_factory.h", ] diff --git a/chromium/components/download/content/factory/download_service_factory_helper.cc b/chromium/components/download/content/factory/download_service_factory_helper.cc index 720653b3d2c..3bd089b9563 100644 --- a/chromium/components/download/content/factory/download_service_factory_helper.cc +++ b/chromium/components/download/content/factory/download_service_factory_helper.cc @@ -4,6 +4,8 @@ #include "components/download/content/factory/download_service_factory_helper.h" +#include <utility> + #include "base/files/file_path.h" #include "build/build_config.h" #include "components/download/content/factory/navigation_monitor_factory.h" @@ -25,6 +27,7 @@ #include "components/download/public/task/empty_task_scheduler.h" #include "components/leveldb_proto/content/proto_database_provider_factory.h" #include "components/leveldb_proto/public/proto_database_provider.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" #if defined(OS_ANDROID) #include "components/download/internal/background_service/android/battery_status_listener_android.h" @@ -46,7 +49,7 @@ const base::FilePath::CharType kFilesStorageDir[] = FILE_PATH_LITERAL("Files"); // Helper function to create download service with different implementation // details. -DownloadService* CreateDownloadServiceInternal( +std::unique_ptr<DownloadService> CreateDownloadServiceInternal( SimpleFactoryKey* simple_factory_key, std::unique_ptr<DownloadClientMap> clients, std::unique_ptr<Configuration> config, @@ -89,15 +92,13 @@ DownloadService* CreateDownloadServiceInternal( files_storage_dir); logger->SetLogSource(controller.get()); - return new DownloadServiceImpl(std::move(config), std::move(logger), - std::move(controller)); + return std::make_unique<DownloadServiceImpl>( + std::move(config), std::move(logger), std::move(controller)); } // Create download service for normal profile. -DownloadService* BuildDownloadService( - content::BrowserContext* browser_context, +std::unique_ptr<DownloadService> BuildDownloadService( SimpleFactoryKey* simple_factory_key, - PrefService* prefs, std::unique_ptr<DownloadClientMap> clients, network::NetworkConnectionTracker* network_connection_tracker, const base::FilePath& storage_dir, @@ -107,7 +108,6 @@ DownloadService* BuildDownloadService( auto config = Configuration::CreateFromFinch(); auto driver = std::make_unique<DownloadDriverImpl>( - content::BrowserContext::GetDownloadManager(browser_context), download_manager_coordinator); auto entry_db_storage_dir = storage_dir.Append(kEntryDBStorageDir); @@ -131,19 +131,19 @@ DownloadService* BuildDownloadService( } // Create download service for incognito mode without any database or file IO. -DownloadService* BuildInMemoryDownloadService( +std::unique_ptr<DownloadService> BuildInMemoryDownloadService( SimpleFactoryKey* simple_factory_key, std::unique_ptr<DownloadClientMap> clients, network::NetworkConnectionTracker* network_connection_tracker, const base::FilePath& storage_dir, - BlobTaskProxy::BlobContextGetter blob_context_getter, + BlobContextGetterFactoryPtr blob_context_getter_factory, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) { auto config = Configuration::CreateFromFinch(); auto download_factory = std::make_unique<InMemoryDownloadFactory>( - url_loader_factory.get(), blob_context_getter, io_task_runner); - auto driver = - std::make_unique<InMemoryDownloadDriver>(std::move(download_factory)); + url_loader_factory.get(), io_task_runner); + auto driver = std::make_unique<InMemoryDownloadDriver>( + std::move(download_factory), std::move(blob_context_getter_factory)); auto store = std::make_unique<NoopStore>(); auto task_scheduler = std::make_unique<EmptyTaskScheduler>(); diff --git a/chromium/components/download/content/factory/download_service_factory_helper.h b/chromium/components/download/content/factory/download_service_factory_helper.h index f683e03c44b..4d4d4e24f90 100644 --- a/chromium/components/download/content/factory/download_service_factory_helper.h +++ b/chromium/components/download/content/factory/download_service_factory_helper.h @@ -11,15 +11,10 @@ #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" -#include "components/download/internal/background_service/blob_task_proxy.h" +#include "components/download/public/background_service/blob_context_getter_factory.h" #include "components/download/public/background_service/clients.h" class SimpleFactoryKey; -class PrefService; - -namespace content { -class BrowserContext; -} // namespace content namespace network { class NetworkConnectionTracker; @@ -42,10 +37,8 @@ class TaskScheduler; // will act as an in-memory only service (this means no auto-retries after // restarts, no files written on completion, etc.). // |background_task_runner| will be used for all disk reads and writes. -DownloadService* BuildDownloadService( - content::BrowserContext* browser_context, +std::unique_ptr<DownloadService> BuildDownloadService( SimpleFactoryKey* simple_factory_key, - PrefService* prefs, std::unique_ptr<DownloadClientMap> clients, network::NetworkConnectionTracker* network_connection_tracker, const base::FilePath& storage_dir, @@ -55,12 +48,12 @@ DownloadService* BuildDownloadService( // Create download service used in incognito mode, without any database or // download file IO. -DownloadService* BuildInMemoryDownloadService( +std::unique_ptr<DownloadService> BuildInMemoryDownloadService( SimpleFactoryKey* simple_factory_key, std::unique_ptr<DownloadClientMap> clients, network::NetworkConnectionTracker* network_connection_tracker, const base::FilePath& storage_dir, - BlobTaskProxy::BlobContextGetter blob_context_getter, + BlobContextGetterFactoryPtr blob_context_getter_factory, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory); diff --git a/chromium/components/download/content/internal/download_driver_impl.cc b/chromium/components/download/content/internal/download_driver_impl.cc index deff1ab8f2b..ea7b4d55ad6 100644 --- a/chromium/components/download/content/internal/download_driver_impl.cc +++ b/chromium/components/download/content/internal/download_driver_impl.cc @@ -10,15 +10,16 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/feature_list.h" #include "base/metrics/histogram_functions.h" #include "base/strings/string_number_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/memory_usage_estimator.h" #include "components/download/internal/background_service/driver_entry.h" +#include "components/download/public/common/download_features.h" #include "components/download/public/common/download_interrupt_reasons.h" #include "components/download/public/common/download_url_parameters.h" #include "components/download/public/common/simple_download_manager_coordinator.h" -#include "content/public/browser/browser_thread.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" @@ -113,18 +114,19 @@ DriverEntry DownloadDriverImpl::CreateDriverEntry( } DownloadDriverImpl::DownloadDriverImpl( - content::DownloadManager* manager, SimpleDownloadManagerCoordinator* download_manager_coordinator) - : download_manager_(manager), - client_(nullptr), + : client_(nullptr), download_manager_coordinator_(download_manager_coordinator), + is_ready_(false), weak_ptr_factory_(this) { - DCHECK(download_manager_); - DCHECK(!download_manager_coordinator_ || - !download_manager_coordinator_->HasSetDownloadManager()); + DCHECK(download_manager_coordinator_); + DETACH_FROM_SEQUENCE(sequence_checker_); } -DownloadDriverImpl::~DownloadDriverImpl() = default; +DownloadDriverImpl::~DownloadDriverImpl() { + if (download_manager_coordinator_) + download_manager_coordinator_->GetNotifier()->RemoveObserver(this); +} void DownloadDriverImpl::Initialize(DownloadDriver::Client* client) { DCHECK(!client_); @@ -132,13 +134,12 @@ void DownloadDriverImpl::Initialize(DownloadDriver::Client* client) { DCHECK(client_); // |download_manager_| may be shut down. Informs the client. - if (!download_manager_) { + if (!download_manager_coordinator_) { client_->OnDriverReady(false); return; } - notifier_ = - std::make_unique<AllDownloadItemNotifier>(download_manager_, this); + download_manager_coordinator_->GetNotifier()->AddObserver(this); } void DownloadDriverImpl::HardRecover() { @@ -149,8 +150,8 @@ void DownloadDriverImpl::HardRecover() { } bool DownloadDriverImpl::IsReady() const { - return client_ && download_manager_ && - download_manager_->IsManagerInitialized(); + return client_ && download_manager_coordinator_ && + download_manager_coordinator_->initialized(); } void DownloadDriverImpl::Start( @@ -161,7 +162,7 @@ void DownloadDriverImpl::Start( const net::NetworkTrafficAnnotationTag& traffic_annotation) { DCHECK(!request_params.url.is_empty()); DCHECK(!guid.empty()); - if (!download_manager_) + if (!download_manager_coordinator_) return; std::unique_ptr<DownloadUrlParameters> download_url_params( @@ -187,9 +188,9 @@ void DownloadDriverImpl::Start( download_url_params->set_upload_progress_callback( base::BindRepeating(&DownloadDriverImpl::OnUploadProgress, weak_ptr_factory_.GetWeakPtr(), guid)); - download_manager_->DownloadUrl(std::move(download_url_params), - nullptr /* blob_data_handle */, - nullptr /* blob_url_loader_factory */); + download_url_params->set_require_safety_checks( + request_params.require_safety_checks); + download_manager_coordinator_->DownloadUrl(std::move(download_url_params)); } void DownloadDriverImpl::Remove(const std::string& guid, bool remove_file) { @@ -205,9 +206,9 @@ void DownloadDriverImpl::Remove(const std::string& guid, bool remove_file) { void DownloadDriverImpl::DoRemoveDownload(const std::string& guid, bool remove_file) { - if (!download_manager_) + if (!download_manager_coordinator_) return; - DownloadItem* item = download_manager_->GetDownloadByGuid(guid); + DownloadItem* item = download_manager_coordinator_->GetDownloadByGuid(guid); // Cancels the download and removes the persisted records in content layer. if (item) { // Remove the download file for completed download. @@ -218,25 +219,25 @@ void DownloadDriverImpl::DoRemoveDownload(const std::string& guid, } void DownloadDriverImpl::Pause(const std::string& guid) { - if (!download_manager_) + if (!download_manager_coordinator_) return; - DownloadItem* item = download_manager_->GetDownloadByGuid(guid); + DownloadItem* item = download_manager_coordinator_->GetDownloadByGuid(guid); if (item) item->Pause(); } void DownloadDriverImpl::Resume(const std::string& guid) { - if (!download_manager_) + if (!download_manager_coordinator_) return; - DownloadItem* item = download_manager_->GetDownloadByGuid(guid); + DownloadItem* item = download_manager_coordinator_->GetDownloadByGuid(guid); if (item) item->Resume(true); } base::Optional<DriverEntry> DownloadDriverImpl::Find(const std::string& guid) { - if (!download_manager_) + if (!download_manager_coordinator_) return base::nullopt; - DownloadItem* item = download_manager_->GetDownloadByGuid(guid); + DownloadItem* item = download_manager_coordinator_->GetDownloadByGuid(guid); if (item) return CreateDriverEntry(item); return base::nullopt; @@ -244,11 +245,11 @@ base::Optional<DriverEntry> DownloadDriverImpl::Find(const std::string& guid) { std::set<std::string> DownloadDriverImpl::GetActiveDownloads() { std::set<std::string> guids; - if (!download_manager_) + if (!download_manager_coordinator_) return guids; std::vector<DownloadItem*> items; - download_manager_->GetAllDownloads(&items); + download_manager_coordinator_->GetAllDownloads(&items); for (auto* item : items) { DriverEntry::State state = ToDriverEntryState(item->GetState()); @@ -260,12 +261,12 @@ std::set<std::string> DownloadDriverImpl::GetActiveDownloads() { } size_t DownloadDriverImpl::EstimateMemoryUsage() const { - return base::trace_event::EstimateMemoryUsage(guid_to_remove_) + - notifier_->EstimateMemoryUsage(); + return base::trace_event::EstimateMemoryUsage(guid_to_remove_); } -void DownloadDriverImpl::OnDownloadUpdated(content::DownloadManager* manager, - DownloadItem* item) { +void DownloadDriverImpl::OnDownloadUpdated( + SimpleDownloadManagerCoordinator* coordinator, + DownloadItem* item) { DCHECK(client_); // Blocks the observer call if we asked to remove the download. if (guid_to_remove_.find(item->GetGuid()) != guid_to_remove_.end()) @@ -287,14 +288,16 @@ void DownloadDriverImpl::OnDownloadUpdated(content::DownloadManager* manager, } } -void DownloadDriverImpl::OnDownloadRemoved(content::DownloadManager* manager, - DownloadItem* download) { +void DownloadDriverImpl::OnDownloadRemoved( + SimpleDownloadManagerCoordinator* coordinator, + DownloadItem* download) { guid_to_remove_.erase(download->GetGuid()); // |download| is about to be deleted. } -void DownloadDriverImpl::OnDownloadCreated(content::DownloadManager* manager, - DownloadItem* item) { +void DownloadDriverImpl::OnDownloadCreated( + SimpleDownloadManagerCoordinator* coordinator, + DownloadItem* item) { if (guid_to_remove_.find(item->GetGuid()) != guid_to_remove_.end()) { // Client has removed the download before content persistence layer created // the record, remove the download immediately. @@ -317,21 +320,30 @@ void DownloadDriverImpl::OnDownloadCreated(content::DownloadManager* manager, void DownloadDriverImpl::OnUploadProgress(const std::string& guid, uint64_t bytes_uploaded) { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); client_->OnUploadProgress(guid, bytes_uploaded); } -void DownloadDriverImpl::OnManagerInitialized( - content::DownloadManager* manager) { - DCHECK_EQ(download_manager_, manager); - DCHECK(client_); - DCHECK(download_manager_); +void DownloadDriverImpl::OnDownloadsInitialized( + SimpleDownloadManagerCoordinator* coordinator, + bool active_downloads_only) { + DCHECK_EQ(download_manager_coordinator_, coordinator); + DCHECK(download_manager_coordinator_); + + if (!client_) + return; + + if (is_ready_) + return; + client_->OnDriverReady(true); + is_ready_ = true; } -void DownloadDriverImpl::OnManagerGoingDown(content::DownloadManager* manager) { - DCHECK_EQ(download_manager_, manager); - download_manager_ = nullptr; +void DownloadDriverImpl::OnManagerGoingDown( + SimpleDownloadManagerCoordinator* coordinator) { + DCHECK_EQ(download_manager_coordinator_, coordinator); + download_manager_coordinator_ = nullptr; } void DownloadDriverImpl::OnHardRecoverComplete(bool success) { diff --git a/chromium/components/download/content/internal/download_driver_impl.h b/chromium/components/download/content/internal/download_driver_impl.h index 67702bfa160..1fca209d5e6 100644 --- a/chromium/components/download/content/internal/download_driver_impl.h +++ b/chromium/components/download/content/internal/download_driver_impl.h @@ -11,11 +11,10 @@ #include "base/files/file_path.h" #include "base/memory/weak_ptr.h" -#include "components/download/content/public/all_download_item_notifier.h" +#include "base/sequence_checker.h" #include "components/download/internal/background_service/download_driver.h" #include "components/download/public/background_service/download_params.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/download_manager.h" +#include "components/download/public/common/all_download_event_notifier.h" #include "net/traffic_annotation/network_traffic_annotation.h" namespace download { @@ -27,14 +26,13 @@ struct DriverEntry; // Aggregates and handles all interaction between download service and content // download logic. class DownloadDriverImpl : public DownloadDriver, - public AllDownloadItemNotifier::Observer { + public AllDownloadEventNotifier::Observer { public: // Creates a driver entry based on a download item. static DriverEntry CreateDriverEntry(const download::DownloadItem* item); // Create the driver. DownloadDriverImpl( - content::DownloadManager* manager, SimpleDownloadManagerCoordinator* download_manager_coordinator); ~DownloadDriverImpl() override; @@ -56,14 +54,16 @@ class DownloadDriverImpl : public DownloadDriver, size_t EstimateMemoryUsage() const override; private: - // content::AllDownloadItemNotifier::Observer implementation. - void OnManagerInitialized(content::DownloadManager* manager) override; - void OnManagerGoingDown(content::DownloadManager* manager) override; - void OnDownloadCreated(content::DownloadManager* manager, + // content::AllDownloadEventNotifier::Observer implementation. + void OnDownloadsInitialized(SimpleDownloadManagerCoordinator* coordinator, + bool active_downloads_only) override; + void OnManagerGoingDown( + SimpleDownloadManagerCoordinator* coordinator) override; + void OnDownloadCreated(SimpleDownloadManagerCoordinator* coordinator, download::DownloadItem* item) override; - void OnDownloadUpdated(content::DownloadManager* manager, + void OnDownloadUpdated(SimpleDownloadManagerCoordinator* coordinator, download::DownloadItem* item) override; - void OnDownloadRemoved(content::DownloadManager* manager, + void OnDownloadRemoved(SimpleDownloadManagerCoordinator* coordinator, download::DownloadItem* item) override; void OnUploadProgress(const std::string& guid, uint64_t bytes_uploaded); @@ -73,15 +73,9 @@ class DownloadDriverImpl : public DownloadDriver, // Remove the download, used to be posted to the task queue. void DoRemoveDownload(const std::string& guid, bool remove_file); - // Low level download handle. - content::DownloadManager* download_manager_; - // The client that receives updates from low level download logic. DownloadDriver::Client* client_; - // Built lazily on initialize and destroyed when/if the manager is torn down. - std::unique_ptr<AllDownloadItemNotifier> notifier_; - // Pending guid set of downloads that will be removed soon. std::set<std::string> guid_to_remove_; @@ -89,6 +83,11 @@ class DownloadDriverImpl : public DownloadDriver, // no longer used. SimpleDownloadManagerCoordinator* download_manager_coordinator_; + // Whether this object is ready to handle download requests. + bool is_ready_; + + SEQUENCE_CHECKER(sequence_checker_); + // Only used to post tasks on the same thread. base::WeakPtrFactory<DownloadDriverImpl> weak_ptr_factory_; diff --git a/chromium/components/download/content/internal/download_driver_impl_unittest.cc b/chromium/components/download/content/internal/download_driver_impl_unittest.cc index 28628fda993..a4bdcca950f 100644 --- a/chromium/components/download/content/internal/download_driver_impl_unittest.cc +++ b/chromium/components/download/content/internal/download_driver_impl_unittest.cc @@ -7,13 +7,14 @@ #include <memory> #include <string> +#include "base/bind_helpers.h" #include "base/guid.h" #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "components/download/content/public/all_download_item_notifier.h" #include "components/download/internal/background_service/test/mock_download_driver_client.h" +#include "components/download/public/common//mock_simple_download_manager.h" #include "content/public/test/fake_download_item.h" -#include "content/public/test/mock_download_manager.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" #include "testing/gmock/include/gmock/gmock.h" @@ -52,18 +53,22 @@ MATCHER_P(DriverEntryEqual, entry, "") { class DownloadDriverImplTest : public testing::Test { public: DownloadDriverImplTest() - : task_runner_(new base::TestSimpleTaskRunner), handle_(task_runner_) {} + : coordinator_(base::NullCallback()), + task_runner_(new base::TestSimpleTaskRunner), + handle_(task_runner_) {} ~DownloadDriverImplTest() override = default; void SetUp() override { EXPECT_CALL(mock_client_, IsTrackingDownload(_)) .WillRepeatedly(Return(true)); - driver_ = std::make_unique<DownloadDriverImpl>(&mock_manager_, nullptr); + driver_ = std::make_unique<DownloadDriverImpl>(&coordinator_); + coordinator_.SetSimpleDownloadManager(&mock_manager_, true); } // TODO(xingliu): implements test download manager for embedders to test. - NiceMock<content::MockDownloadManager> mock_manager_; + SimpleDownloadManagerCoordinator coordinator_; + NiceMock<MockSimpleDownloadManager> mock_manager_; MockDriverClient mock_client_; std::unique_ptr<DownloadDriverImpl> driver_; @@ -77,20 +82,18 @@ class DownloadDriverImplTest : public testing::Test { // Ensure the download manager can be initialized after the download driver. TEST_F(DownloadDriverImplTest, ManagerLateInitialization) { - EXPECT_CALL(mock_manager_, IsManagerInitialized()) - .Times(1) - .WillOnce(Return(false)); driver_->Initialize(&mock_client_); - EXPECT_CALL(mock_client_, OnDriverReady(true)); - static_cast<AllDownloadItemNotifier::Observer*>(driver_.get()) - ->OnManagerInitialized(&mock_manager_); + EXPECT_CALL(mock_client_, OnDriverReady(true)).Times(1); + static_cast<AllDownloadEventNotifier::Observer*>(driver_.get()) + ->OnDownloadsInitialized(&coordinator_, true); + + EXPECT_CALL(mock_client_, OnDriverReady(true)).Times(0); + static_cast<AllDownloadEventNotifier::Observer*>(driver_.get()) + ->OnDownloadsInitialized(&coordinator_, false); } TEST_F(DownloadDriverImplTest, TestHardRecover) { - EXPECT_CALL(mock_manager_, IsManagerInitialized()) - .Times(1) - .WillOnce(Return(false)); driver_->Initialize(&mock_client_); EXPECT_CALL(mock_client_, OnDriverHardRecoverComplete(true)).Times(1); @@ -102,14 +105,10 @@ TEST_F(DownloadDriverImplTest, TestHardRecover) { TEST_F(DownloadDriverImplTest, RemoveBeforeCreated) { using DownloadState = download::DownloadItem::DownloadState; - EXPECT_CALL(mock_manager_, IsManagerInitialized()) - .Times(1) - .WillOnce(Return(false)); driver_->Initialize(&mock_client_); EXPECT_CALL(mock_client_, OnDriverReady(true)); - static_cast<AllDownloadItemNotifier::Observer*>(driver_.get()) - ->OnManagerInitialized(&mock_manager_); + mock_manager_.NotifyOnDownloadInitialized(); const std::string kTestGuid = "abc"; content::FakeDownloadItem fake_item; @@ -126,8 +125,8 @@ TEST_F(DownloadDriverImplTest, RemoveBeforeCreated) { ON_CALL(mock_manager_, GetDownloadByGuid(kTestGuid)) .WillByDefault(Return(&fake_item)); EXPECT_CALL(mock_client_, OnDownloadCreated(_)).Times(0); - static_cast<AllDownloadItemNotifier::Observer*>(driver_.get()) - ->OnDownloadCreated(&mock_manager_, &fake_item); + static_cast<AllDownloadEventNotifier::Observer*>(driver_.get()) + ->OnDownloadCreated(&coordinator_, &fake_item); task_runner_->RunUntilIdle(); // Expect a remove call down to content layer download item. @@ -139,9 +138,7 @@ TEST_F(DownloadDriverImplTest, DownloadItemUpdateEvents) { using DownloadState = download::DownloadItem::DownloadState; using DownloadInterruptReason = download::DownloadInterruptReason; - EXPECT_CALL(mock_manager_, IsManagerInitialized()) - .Times(1) - .WillOnce(Return(true)); + mock_manager_.NotifyOnDownloadInitialized(); EXPECT_CALL(mock_client_, OnDriverReady(true)).Times(1); driver_->Initialize(&mock_client_); @@ -155,13 +152,13 @@ TEST_F(DownloadDriverImplTest, DownloadItemUpdateEvents) { EXPECT_CALL(mock_client_, OnDownloadUpdated(DriverEntryEqual(entry))) .Times(1) .RetiresOnSaturation(); - static_cast<AllDownloadItemNotifier::Observer*>(driver_.get()) - ->OnDownloadUpdated(&mock_manager_, &fake_item); + static_cast<AllDownloadEventNotifier::Observer*>(driver_.get()) + ->OnDownloadUpdated(&coordinator_, &fake_item); // Nothing happens for cancelled state. fake_item.SetState(DownloadState::CANCELLED); - static_cast<AllDownloadItemNotifier::Observer*>(driver_.get()) - ->OnDownloadUpdated(&mock_manager_, &fake_item); + static_cast<AllDownloadEventNotifier::Observer*>(driver_.get()) + ->OnDownloadUpdated(&coordinator_, &fake_item); fake_item.SetReceivedBytes(1024); fake_item.SetState(DownloadState::COMPLETE); @@ -170,8 +167,8 @@ TEST_F(DownloadDriverImplTest, DownloadItemUpdateEvents) { EXPECT_CALL(mock_client_, OnDownloadSucceeded(DriverEntryEqual(entry))) .Times(1) .RetiresOnSaturation(); - static_cast<AllDownloadItemNotifier::Observer*>(driver_.get()) - ->OnDownloadUpdated(&mock_manager_, &fake_item); + static_cast<AllDownloadEventNotifier::Observer*>(driver_.get()) + ->OnDownloadUpdated(&coordinator_, &fake_item); fake_item.SetState(DownloadState::INTERRUPTED); fake_item.SetLastReason( @@ -181,8 +178,8 @@ TEST_F(DownloadDriverImplTest, DownloadItemUpdateEvents) { FailureType::RECOVERABLE)) .Times(1) .RetiresOnSaturation(); - static_cast<AllDownloadItemNotifier::Observer*>(driver_.get()) - ->OnDownloadUpdated(&mock_manager_, &fake_item); + static_cast<AllDownloadEventNotifier::Observer*>(driver_.get()) + ->OnDownloadUpdated(&coordinator_, &fake_item); } TEST_F(DownloadDriverImplTest, TestGetActiveDownloadsCall) { @@ -208,9 +205,7 @@ TEST_F(DownloadDriverImplTest, TestGetActiveDownloadsCall) { ON_CALL(mock_manager_, GetAllDownloads(_)) .WillByDefault(PopulateVector(items)); - EXPECT_CALL(mock_manager_, IsManagerInitialized()) - .Times(1) - .WillOnce(Return(true)); + mock_manager_.NotifyOnDownloadInitialized(); EXPECT_CALL(mock_client_, OnDriverReady(true)).Times(1); driver_->Initialize(&mock_client_); diff --git a/chromium/components/download/internal/background_service/controller_impl.cc b/chromium/components/download/internal/background_service/controller_impl.cc index 2177e6a8253..c16357d94b4 100644 --- a/chromium/components/download/internal/background_service/controller_impl.cc +++ b/chromium/components/download/internal/background_service/controller_impl.cc @@ -9,7 +9,6 @@ #include <utility> #include "base/bind.h" -#include "base/callback_helpers.h" #include "base/optional.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" @@ -388,8 +387,7 @@ void ControllerImpl::HandleTaskFinished(DownloadTaskType task_type, return; if (status != stats::ScheduledTaskStatus::CANCELLED_ON_STOP) { - base::ResetAndReturn(&task_finished_callbacks_[task_type]) - .Run(needs_reschedule); + std::move(task_finished_callbacks_[task_type]).Run(needs_reschedule); } // TODO(dtrainor): It might be useful to log how many downloads we have // running when we're asked to stop processing. @@ -438,13 +436,8 @@ void ControllerImpl::OnDownloadCreated(const DriverEntry& download) { download::Client* client = clients_->GetClient(entry->client); DCHECK(client); - using ShouldDownload = download::Client::ShouldDownload; - ShouldDownload should_download = client->OnDownloadStarted( - download.guid, download.url_chain, download.response_headers); - stats::LogStartDownloadResponse(entry->client, should_download); - if (should_download == ShouldDownload::ABORT) { - HandleCompleteDownload(CompletionType::ABORT, entry->guid); - } + client->OnDownloadStarted(download.guid, download.url_chain, + download.response_headers); } void ControllerImpl::OnDownloadFailed(const DriverEntry& download, @@ -1098,8 +1091,8 @@ void ControllerImpl::NotifyServiceOfStartup() { if (init_callback_.is_null()) return; - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::ResetAndReturn(&init_callback_)); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + std::move(init_callback_)); } void ControllerImpl::HandleStartDownloadResponse( diff --git a/chromium/components/download/internal/background_service/controller_impl_unittest.cc b/chromium/components/download/internal/background_service/controller_impl_unittest.cc index 0cd66071ce7..f1524e58f69 100644 --- a/chromium/components/download/internal/background_service/controller_impl_unittest.cc +++ b/chromium/components/download/internal/background_service/controller_impl_unittest.cc @@ -1221,23 +1221,20 @@ TEST_F(DownloadServiceControllerImplTest, DownloadCompletionTest) { Entry entry1 = test::BuildBasicEntry(Entry::State::ACTIVE); Entry entry2 = test::BuildBasicEntry(Entry::State::ACTIVE); Entry entry3 = test::BuildBasicEntry(Entry::State::ACTIVE); - Entry entry4 = test::BuildBasicEntry(Entry::State::ACTIVE); - entry4.scheduling_params.cancel_time = base::Time::Now(); - std::vector<Entry> entries = {entry1, entry2, entry3, entry4}; + entry3.scheduling_params.cancel_time = base::Time::Now(); + std::vector<Entry> entries = {entry1, entry2, entry3}; DriverEntry dentry1 = BuildDriverEntry(entry1, DriverEntry::State::IN_PROGRESS); - // dentry2 will effectively be created by the test to simulate a start - // download. - DriverEntry dentry3 = - BuildDriverEntry(entry3, DriverEntry::State::IN_PROGRESS); - driver_->AddTestData(std::vector<DriverEntry>{dentry1, dentry3}); + DriverEntry dentry2 = + BuildDriverEntry(entry2, DriverEntry::State::IN_PROGRESS); + driver_->AddTestData(std::vector<DriverEntry>{dentry1, dentry2}); EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1); // Test FailureReason::TIMEDOUT. EXPECT_CALL(*client_, - OnDownloadFailed(entry4.guid, _, Client::FailureReason::TIMEDOUT)) + OnDownloadFailed(entry3.guid, _, Client::FailureReason::TIMEDOUT)) .Times(1); // Set up the Controller. @@ -1254,21 +1251,11 @@ TEST_F(DownloadServiceControllerImplTest, DownloadCompletionTest) { .Times(1); controller_->CancelDownload(entry1.guid); - // Test FailureReason::ABORTED. - EXPECT_CALL(*client_, OnDownloadStarted(entry2.guid, _, _)) - .Times(1) - .WillOnce(Return(Client::ShouldDownload::ABORT)); - EXPECT_CALL(*client_, - OnDownloadFailed(entry2.guid, _, Client::FailureReason::ABORTED)) - .Times(1); - driver_->Start(RequestParams(), entry2.guid, entry2.target_file_path, nullptr, - TRAFFIC_ANNOTATION_FOR_TESTS); - // Test FailureReason::NETWORK. EXPECT_CALL(*client_, - OnDownloadFailed(entry3.guid, _, Client::FailureReason::NETWORK)) + OnDownloadFailed(entry2.guid, _, Client::FailureReason::NETWORK)) .Times(1); - driver_->NotifyDownloadFailed(dentry3, FailureType::NOT_RECOVERABLE); + driver_->NotifyDownloadFailed(dentry2, FailureType::NOT_RECOVERABLE); task_runner_->RunUntilIdle(); } diff --git a/chromium/components/download/internal/background_service/debugging_client.cc b/chromium/components/download/internal/background_service/debugging_client.cc index 9bc4298b639..4c1879c0b82 100644 --- a/chromium/components/download/internal/background_service/debugging_client.cc +++ b/chromium/components/download/internal/background_service/debugging_client.cc @@ -17,12 +17,10 @@ void DebuggingClient::OnServiceInitialized( void DebuggingClient::OnServiceUnavailable() {} -Client::ShouldDownload DebuggingClient::OnDownloadStarted( +void DebuggingClient::OnDownloadStarted( const std::string& guid, const std::vector<GURL>& url_chain, - const scoped_refptr<const net::HttpResponseHeaders>& headers) { - return Client::ShouldDownload::CONTINUE; -} + const scoped_refptr<const net::HttpResponseHeaders>& headers) {} void DebuggingClient::OnDownloadUpdated(const std::string& guid, uint64_t bytes_uploaded, diff --git a/chromium/components/download/internal/background_service/debugging_client.h b/chromium/components/download/internal/background_service/debugging_client.h index 74db9bf55ee..252c81141f4 100644 --- a/chromium/components/download/internal/background_service/debugging_client.h +++ b/chromium/components/download/internal/background_service/debugging_client.h @@ -23,7 +23,7 @@ class DebuggingClient : public Client { bool state_lost, const std::vector<DownloadMetaData>& downloads) override; void OnServiceUnavailable() override; - ShouldDownload OnDownloadStarted( + void OnDownloadStarted( const std::string& guid, const std::vector<GURL>& url_chain, const scoped_refptr<const net::HttpResponseHeaders>& headers) override; diff --git a/chromium/components/download/internal/background_service/in_memory_download.cc b/chromium/components/download/internal/background_service/in_memory_download.cc index 15ab8286017..6b3ac7d5abd 100644 --- a/chromium/components/download/internal/background_service/in_memory_download.cc +++ b/chromium/components/download/internal/background_service/in_memory_download.cc @@ -32,21 +32,19 @@ InMemoryDownloadImpl::InMemoryDownloadImpl( const net::NetworkTrafficAnnotationTag& traffic_annotation, Delegate* delegate, network::mojom::URLLoaderFactory* url_loader_factory, - BlobTaskProxy::BlobContextGetter blob_context_getter, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) : InMemoryDownload(guid), request_params_(request_params), request_body_(std::move(request_body)), traffic_annotation_(traffic_annotation), url_loader_factory_(url_loader_factory), - blob_task_proxy_( - BlobTaskProxy::Create(blob_context_getter, io_task_runner)), io_task_runner_(io_task_runner), delegate_(delegate), completion_notified_(false), started_(false), weak_ptr_factory_(this) { DCHECK(!guid_.empty()); + DCHECK(delegate_); } InMemoryDownloadImpl::~InMemoryDownloadImpl() { @@ -55,6 +53,17 @@ InMemoryDownloadImpl::~InMemoryDownloadImpl() { void InMemoryDownloadImpl::Start() { DCHECK(state_ == State::INITIAL) << "Only call Start() for new download."; + state_ = State::RETRIEVE_BLOB_CONTEXT; + delegate_->RetrieveBlobContextGetter( + base::BindOnce(&InMemoryDownloadImpl::OnRetrievedBlobContextGetter, + weak_ptr_factory_.GetWeakPtr())); +} + +void InMemoryDownloadImpl::OnRetrievedBlobContextGetter( + BlobContextGetter blob_context_getter) { + DCHECK(state_ == State::RETRIEVE_BLOB_CONTEXT); + blob_task_proxy_ = + BlobTaskProxy::Create(blob_context_getter, io_task_runner_); SendRequest(); state_ = State::IN_PROGRESS; } @@ -69,7 +78,7 @@ void InMemoryDownloadImpl::Resume() { switch (state_) { case State::INITIAL: - NOTREACHED(); + case State::RETRIEVE_BLOB_CONTEXT: return; case State::IN_PROGRESS: // Let the network pipe continue to read data. @@ -115,8 +124,7 @@ void InMemoryDownloadImpl::OnDataReceived(base::StringPiece string_piece, std::move(resume).Run(); // TODO(xingliu): Throttle the update frequency. See https://crbug.com/809674. - if (delegate_) - delegate_->OnDownloadProgress(this); + delegate_->OnDownloadProgress(this); } void InMemoryDownloadImpl::OnComplete(bool success) { @@ -183,8 +191,7 @@ void InMemoryDownloadImpl::NotifyDelegateDownloadComplete() { return; completion_notified_ = true; - if (delegate_) - delegate_->OnDownloadComplete(this); + delegate_->OnDownloadComplete(this); } void InMemoryDownloadImpl::SendRequest() { @@ -228,14 +235,12 @@ void InMemoryDownloadImpl::OnResponseStarted( started_ = true; response_headers_ = response_head.headers; - if (delegate_) - delegate_->OnDownloadStarted(this); + delegate_->OnDownloadStarted(this); } void InMemoryDownloadImpl::OnUploadProgress(uint64_t position, uint64_t total) { bytes_uploaded_ = position; - if (delegate_) - delegate_->OnUploadProgress(this); + delegate_->OnUploadProgress(this); } void InMemoryDownloadImpl::Reset() { diff --git a/chromium/components/download/internal/background_service/in_memory_download.h b/chromium/components/download/internal/background_service/in_memory_download.h index 4b0baa7e14f..950d38f9ab8 100644 --- a/chromium/components/download/internal/background_service/in_memory_download.h +++ b/chromium/components/download/internal/background_service/in_memory_download.h @@ -15,6 +15,7 @@ #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "components/download/internal/background_service/blob_task_proxy.h" +#include "components/download/public/background_service/blob_context_getter_factory.h" #include "components/download/public/background_service/download_params.h" #include "services/network/public/cpp/simple_url_loader.h" #include "services/network/public/cpp/simple_url_loader_stream_consumer.h" @@ -38,18 +39,23 @@ struct RequestParams; // Used by download service in Incognito mode, where download files shouldn't // be persisted to disk. // -// Life cycle: The object is created before creating the network request. -// Call Start() to send the network request. +// Life cycle: The object is created before sending the network request. +// Call Start() to retrieve the blob storage context and send the network +// request. class InMemoryDownload { public: - // Report download progress with in-memory download backend. class Delegate { public: + // Report download progress with in-memory download backend. virtual void OnDownloadStarted(InMemoryDownload* download) = 0; virtual void OnDownloadProgress(InMemoryDownload* download) = 0; virtual void OnDownloadComplete(InMemoryDownload* download) = 0; virtual void OnUploadProgress(InMemoryDownload* download) = 0; + // Retrieves the blob storage context getter. + virtual void RetrieveBlobContextGetter( + BlobContextGetterCallback callback) = 0; + protected: virtual ~Delegate() = default; }; @@ -69,12 +75,15 @@ class InMemoryDownload { // States of the download. enum class State { - // The object is created but network request has not been sent. + // The object is just created. INITIAL, + // Waiting to retrieve BlobStorageContextGetter. + RETRIEVE_BLOB_CONTEXT, + // Download is in progress, including the following procedures. - // 1. Transfer network data. - // 2. Save to blob storage. + // 1. Send the network request and transfer data from network. + // 2. Save the data to blob storage. IN_PROGRESS, // The download can fail due to: @@ -161,7 +170,6 @@ class InMemoryDownloadImpl : public network::SimpleURLLoaderStreamConsumer, const net::NetworkTrafficAnnotationTag& traffic_annotation, Delegate* delegate, network::mojom::URLLoaderFactory* url_loader_factory, - BlobTaskProxy::BlobContextGetter blob_context_getter, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner); ~InMemoryDownloadImpl() override; @@ -172,6 +180,9 @@ class InMemoryDownloadImpl : public network::SimpleURLLoaderStreamConsumer, void Pause() override; void Resume() override; + // Called when the BlobStorageContextGetter is ready to use. + void OnRetrievedBlobContextGetter(BlobContextGetter blob_context_getter); + std::unique_ptr<storage::BlobDataHandle> ResultAsBlob() const override; size_t EstimateMemoryUsage() const override; diff --git a/chromium/components/download/internal/background_service/in_memory_download_driver.cc b/chromium/components/download/internal/background_service/in_memory_download_driver.cc index 7cc50015adf..69fed665443 100644 --- a/chromium/components/download/internal/background_service/in_memory_download_driver.cc +++ b/chromium/components/download/internal/background_service/in_memory_download_driver.cc @@ -14,7 +14,7 @@ namespace { DriverEntry::State ToDriverEntryState(InMemoryDownload::State state) { switch (state) { case InMemoryDownload::State::INITIAL: - return DriverEntry::State::IN_PROGRESS; + case InMemoryDownload::State::RETRIEVE_BLOB_CONTEXT: case InMemoryDownload::State::IN_PROGRESS: return DriverEntry::State::IN_PROGRESS; case InMemoryDownload::State::FAILED: @@ -55,10 +55,8 @@ DriverEntry CreateDriverEntry(const InMemoryDownload& download) { InMemoryDownloadFactory::InMemoryDownloadFactory( network::mojom::URLLoaderFactory* url_loader_factory, - BlobTaskProxy::BlobContextGetter blob_context_getter, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) : url_loader_factory_(url_loader_factory), - blob_context_getter_(blob_context_getter), io_task_runner_(io_task_runner) {} InMemoryDownloadFactory::~InMemoryDownloadFactory() = default; @@ -72,12 +70,15 @@ std::unique_ptr<InMemoryDownload> InMemoryDownloadFactory::Create( DCHECK(url_loader_factory_); return std::make_unique<InMemoryDownloadImpl>( guid, request_params, std::move(request_body), traffic_annotation, - delegate, url_loader_factory_, blob_context_getter_, io_task_runner_); + delegate, url_loader_factory_, io_task_runner_); } InMemoryDownloadDriver::InMemoryDownloadDriver( - std::unique_ptr<InMemoryDownload::Factory> download_factory) - : client_(nullptr), download_factory_(std::move(download_factory)) {} + std::unique_ptr<InMemoryDownload::Factory> download_factory, + BlobContextGetterFactoryPtr blob_context_getter_factory) + : client_(nullptr), + download_factory_(std::move(download_factory)), + blob_context_getter_factory_(std::move(blob_context_getter_factory)) {} InMemoryDownloadDriver::~InMemoryDownloadDriver() = default; @@ -182,6 +183,7 @@ void InMemoryDownloadDriver::OnDownloadComplete(InMemoryDownload* download) { // OnDownloadSucceeded. return; case InMemoryDownload::State::INITIAL: + case InMemoryDownload::State::RETRIEVE_BLOB_CONTEXT: case InMemoryDownload::State::IN_PROGRESS: NOTREACHED(); return; @@ -194,4 +196,9 @@ void InMemoryDownloadDriver::OnUploadProgress(InMemoryDownload* download) { client_->OnUploadProgress(download->guid(), download->bytes_uploaded()); } +void InMemoryDownloadDriver::RetrieveBlobContextGetter( + BlobContextGetterCallback callback) { + blob_context_getter_factory_->RetrieveBlobContextGetter(std::move(callback)); +} + } // namespace download diff --git a/chromium/components/download/internal/background_service/in_memory_download_driver.h b/chromium/components/download/internal/background_service/in_memory_download_driver.h index a50daddb699..4d2bbcabb4f 100644 --- a/chromium/components/download/internal/background_service/in_memory_download_driver.h +++ b/chromium/components/download/internal/background_service/in_memory_download_driver.h @@ -24,7 +24,6 @@ class InMemoryDownloadFactory : public InMemoryDownload::Factory { public: InMemoryDownloadFactory( network::mojom::URLLoaderFactory* url_loader_factory, - BlobTaskProxy::BlobContextGetter blob_context_getter, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner); ~InMemoryDownloadFactory() override; @@ -39,7 +38,6 @@ class InMemoryDownloadFactory : public InMemoryDownload::Factory { network::mojom::URLLoaderFactory* url_loader_factory_; - BlobTaskProxy::BlobContextGetter blob_context_getter_; scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; DISALLOW_COPY_AND_ASSIGN(InMemoryDownloadFactory); @@ -50,8 +48,9 @@ class InMemoryDownloadFactory : public InMemoryDownload::Factory { class InMemoryDownloadDriver : public DownloadDriver, public InMemoryDownload::Delegate { public: - explicit InMemoryDownloadDriver( - std::unique_ptr<InMemoryDownload::Factory> download_factory); + InMemoryDownloadDriver( + std::unique_ptr<InMemoryDownload::Factory> download_factory, + BlobContextGetterFactoryPtr blob_context_getter_factory); ~InMemoryDownloadDriver() override; private: @@ -77,6 +76,7 @@ class InMemoryDownloadDriver : public DownloadDriver, void OnDownloadProgress(InMemoryDownload* download) override; void OnDownloadComplete(InMemoryDownload* download) override; void OnUploadProgress(InMemoryDownload* download) override; + void RetrieveBlobContextGetter(BlobContextGetterCallback callback) override; // The client that receives updates from low level download logic. DownloadDriver::Client* client_; @@ -84,6 +84,9 @@ class InMemoryDownloadDriver : public DownloadDriver, // The factory used to create in memory download objects. std::unique_ptr<InMemoryDownload::Factory> download_factory_; + // Used to retrieve BlobStorageContextGetter. + BlobContextGetterFactoryPtr blob_context_getter_factory_; + // A map of GUID and in memory download, which holds download data. std::map<std::string, std::unique_ptr<InMemoryDownload>> downloads_; diff --git a/chromium/components/download/internal/background_service/in_memory_download_driver_unittest.cc b/chromium/components/download/internal/background_service/in_memory_download_driver_unittest.cc index 65de0d0a2ee..432884237bc 100644 --- a/chromium/components/download/internal/background_service/in_memory_download_driver_unittest.cc +++ b/chromium/components/download/internal/background_service/in_memory_download_driver_unittest.cc @@ -7,6 +7,7 @@ #include <memory> #include "components/download/internal/background_service/test/mock_download_driver_client.h" +#include "components/download/public/background_service/blob_context_getter_factory.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "services/network/public/cpp/resource_request_body.h" #include "storage/browser/blob/blob_data_handle.h" @@ -23,6 +24,17 @@ MATCHER_P(DriverEntryEqual, entry, "") { entry.bytes_downloaded == arg.bytes_downloaded; } +// BlobContextGetterFactory implementation that does nothing. +class NoopBlobContextGetterFactory : public BlobContextGetterFactory { + public: + NoopBlobContextGetterFactory() = default; + ~NoopBlobContextGetterFactory() override = default; + + private: + void RetrieveBlobContextGetter(BlobContextGetterCallback callback) override {} + DISALLOW_COPY_AND_ASSIGN(NoopBlobContextGetterFactory); +}; + // Test in memory download that doesn't do complex IO. class TestInMemoryDownload : public InMemoryDownload { public: @@ -106,7 +118,10 @@ class InMemoryDownloadDriverTest : public testing::Test { void SetUp() override { auto factory = std::make_unique<TestInMemoryDownloadFactory>(); factory_ = factory.get(); - driver_ = std::make_unique<InMemoryDownloadDriver>(std::move(factory)); + auto blob_context_getter_factory = + std::make_unique<NoopBlobContextGetterFactory>(); + driver_ = std::make_unique<InMemoryDownloadDriver>( + std::move(factory), std::move(blob_context_getter_factory)); driver()->Initialize(&driver_client_); } diff --git a/chromium/components/download/internal/background_service/in_memory_download_unittest.cc b/chromium/components/download/internal/background_service/in_memory_download_unittest.cc index 532d31c06e4..19a7e6d353f 100644 --- a/chromium/components/download/internal/background_service/in_memory_download_unittest.cc +++ b/chromium/components/download/internal/background_service/in_memory_download_unittest.cc @@ -45,9 +45,17 @@ void SetValue(T* address, T value) { *address = value; } +// Must run on IO thread task runner. +base::WeakPtr<storage::BlobStorageContext> BlobStorageContextGetter( + storage::BlobStorageContext* blob_context) { + DCHECK(blob_context); + return blob_context->AsWeakPtr(); +} + class MockDelegate : public InMemoryDownload::Delegate { public: - MockDelegate() = default; + MockDelegate(BlobContextGetter blob_context_getter) + : blob_context_getter_(blob_context_getter) {} void WaitForCompletion() { DCHECK(!run_loop_.running()); @@ -62,20 +70,18 @@ class MockDelegate : public InMemoryDownload::Delegate { run_loop_.Quit(); } MOCK_METHOD1(OnUploadProgress, void(InMemoryDownload*)); + void RetrieveBlobContextGetter( + base::OnceCallback<void(BlobContextGetter)> callback) override { + std::move(callback).Run(blob_context_getter_); + } private: base::RunLoop run_loop_; + BlobContextGetter blob_context_getter_; DISALLOW_COPY_AND_ASSIGN(MockDelegate); }; -// Must run on IO thread task runner. -base::WeakPtr<storage::BlobStorageContext> BlobStorageContextGetter( - storage::BlobStorageContext* blob_context) { - DCHECK(blob_context); - return blob_context->AsWeakPtr(); -} - class InMemoryDownloadTest : public testing::Test { public: InMemoryDownloadTest() = default; @@ -94,6 +100,11 @@ class InMemoryDownloadTest : public testing::Test { loop.Quit(); })); loop.Run(); + + auto blob_storage_context_getter = base::BindRepeating( + &BlobStorageContextGetter, blob_storage_context_.get()); + mock_delegate_ = + std::make_unique<NiceMock<MockDelegate>>(blob_storage_context_getter); } void TearDown() override { @@ -108,13 +119,11 @@ class InMemoryDownloadTest : public testing::Test { download_ = std::make_unique<InMemoryDownloadImpl>( base::GenerateGUID(), request_params, /* request_body= */ nullptr, TRAFFIC_ANNOTATION_FOR_TESTS, delegate(), &url_loader_factory_, - base::BindRepeating(&BlobStorageContextGetter, - blob_storage_context_.get()), io_thread_->task_runner()); } InMemoryDownload* download() { return download_.get(); } - MockDelegate* delegate() { return &mock_delegate_; } + MockDelegate* delegate() { return mock_delegate_.get(); } network::TestURLLoaderFactory* url_loader_factory() { return &url_loader_factory_; } @@ -166,7 +175,7 @@ class InMemoryDownloadTest : public testing::Test { base::test::ScopedTaskEnvironment scoped_task_environment_; std::unique_ptr<InMemoryDownloadImpl> download_; - NiceMock<MockDelegate> mock_delegate_; + std::unique_ptr<NiceMock<MockDelegate>> mock_delegate_; // Used by SimpleURLLoader network backend. network::TestURLLoaderFactory url_loader_factory_; diff --git a/chromium/components/download/internal/background_service/proto/request.proto b/chromium/components/download/internal/background_service/proto/request.proto index ae2b7bed13d..485bf6b6ae9 100644 --- a/chromium/components/download/internal/background_service/proto/request.proto +++ b/chromium/components/download/internal/background_service/proto/request.proto @@ -19,4 +19,5 @@ message RequestParams { optional string method = 2; repeated RequestHeader headers = 3; optional bool fetch_error_body = 4; + optional bool require_safety_checks = 5 [default = true]; } diff --git a/chromium/components/download/internal/background_service/proto_conversions.cc b/chromium/components/download/internal/background_service/proto_conversions.cc index 4e074ef9085..630abdf5691 100644 --- a/chromium/components/download/internal/background_service/proto_conversions.cc +++ b/chromium/components/download/internal/background_service/proto_conversions.cc @@ -245,6 +245,7 @@ RequestParams ProtoConversions::RequestParamsFromProto( request_params.url = GURL(proto.url()); request_params.method = proto.method(); request_params.fetch_error_body = proto.fetch_error_body(); + request_params.require_safety_checks = proto.require_safety_checks(); for (int i = 0; i < proto.headers_size(); i++) { protodb::RequestHeader header = proto.headers(i); @@ -259,6 +260,7 @@ void ProtoConversions::RequestParamsToProto(const RequestParams& request_params, proto->set_url(request_params.url.spec()); proto->set_method(request_params.method); proto->set_fetch_error_body(request_params.fetch_error_body); + proto->set_require_safety_checks(request_params.require_safety_checks); int i = 0; net::HttpRequestHeaders::Iterator iter(request_params.request_headers); @@ -291,7 +293,7 @@ Entry ProtoConversions::EntryFromProto(const protodb::Entry& proto) { entry.cleanup_attempt_count = proto.cleanup_attempt_count(); entry.has_upload_data = proto.has_upload_data(); entry.traffic_annotation = - net::MutableNetworkTrafficAnnotationTag({proto.traffic_annotation()}); + net::CreateMutableNetworkTrafficAnnotationTag(proto.traffic_annotation()); entry.bytes_downloaded = proto.bytes_downloaded(); for (const auto& url : proto.url_chain()) entry.url_chain.emplace_back(url); diff --git a/chromium/components/download/internal/background_service/proto_conversions_unittest.cc b/chromium/components/download/internal/background_service/proto_conversions_unittest.cc index 46e9bdc5c92..692389ce813 100644 --- a/chromium/components/download/internal/background_service/proto_conversions_unittest.cc +++ b/chromium/components/download/internal/background_service/proto_conversions_unittest.cc @@ -98,6 +98,7 @@ TEST_F(ProtoConversionsTest, RequestParamsWithHeadersConversion) { expected.url = GURL(TEST_URL); expected.method = "GET"; expected.fetch_error_body = true; + expected.require_safety_checks = false; expected.request_headers.SetHeader("key1", "value1"); expected.request_headers.SetHeader("key2", "value2"); @@ -108,6 +109,7 @@ TEST_F(ProtoConversionsTest, RequestParamsWithHeadersConversion) { EXPECT_EQ(expected.url, actual.url); EXPECT_EQ(expected.method, actual.method); EXPECT_EQ(expected.fetch_error_body, actual.fetch_error_body); + EXPECT_EQ(expected.require_safety_checks, actual.require_safety_checks); std::string out; actual.request_headers.GetHeader("key1", &out); diff --git a/chromium/components/download/internal/background_service/stats.cc b/chromium/components/download/internal/background_service/stats.cc index a5062f36555..dc627fd7a2f 100644 --- a/chromium/components/download/internal/background_service/stats.cc +++ b/chromium/components/download/internal/background_service/stats.cc @@ -221,19 +221,6 @@ void LogStartDownloadResult(DownloadClient client, DownloadParams::StartResult::COUNT); } -void LogStartDownloadResponse(DownloadClient client, - Client::ShouldDownload should_download) { - // Total count for each start response. - std::string name("Download.Service.Request.StartResponse"); - base::UmaHistogramEnumeration(name, should_download, - Client::ShouldDownload::COUNT); - - // Total count for each client response with client suffix. - name.append(".").append(ClientToHistogramSuffix(client)); - base::UmaHistogramEnumeration(name, should_download, - Client::ShouldDownload::COUNT); -} - void LogDownloadParams(const DownloadParams& params) { UMA_HISTOGRAM_ENUMERATION("Download.Service.Request.BatteryRequirement", params.scheduling_params.battery_requirements, @@ -400,5 +387,11 @@ void LogHashPresence(bool hash_exists) { UMA_HISTOGRAM_BOOLEAN("Download.Service.Finish.ReportedHash", hash_exists); } +void LogDownloadClientInflatedFullBrowser(DownloadClient client) { + std::string client_name(ClientToHistogramSuffix(client)); + base::UmaHistogramBoolean( + "Download.Service.Clients.InflatedFullBrowser." + client_name, true); +} + } // namespace stats } // namespace download diff --git a/chromium/components/download/internal/background_service/stats.h b/chromium/components/download/internal/background_service/stats.h index 49de816c97f..d92385aa429 100644 --- a/chromium/components/download/internal/background_service/stats.h +++ b/chromium/components/download/internal/background_service/stats.h @@ -159,10 +159,6 @@ void LogServiceApiAction(DownloadClient client, ServiceApiAction action); void LogStartDownloadResult(DownloadClient client, DownloadParams::StartResult result); -// Logs the client response to StartDownload() attempt on the service. -void LogStartDownloadResponse(DownloadClient client, - download::Client::ShouldDownload should_download); - // Logs the download parameters when StartDownload() is called. void LogDownloadParams(const DownloadParams& params); @@ -230,6 +226,10 @@ void LogHasUploadData(DownloadClient client, bool has_upload_data); // Records whether or not a completed entry has a hash value. void LogHashPresence(bool hash_exists); +// Records count of reduced mode to full browser transitions requested by each +// client. +void LogDownloadClientInflatedFullBrowser(DownloadClient client); + } // namespace stats } // namespace download diff --git a/chromium/components/download/internal/common/BUILD.gn b/chromium/components/download/internal/common/BUILD.gn index e0b03cffe6a..419ee87c0c6 100644 --- a/chromium/components/download/internal/common/BUILD.gn +++ b/chromium/components/download/internal/common/BUILD.gn @@ -74,8 +74,10 @@ source_set("internal") { "//components/leveldb_proto", "//mojo/public/c/system", "//net", + "//services/device/public/mojom:mojom", "//services/metrics/public/cpp:ukm_builders", "//services/network/public/cpp", + "//services/service_manager/public/cpp:cpp", ] if (is_android) { @@ -130,6 +132,7 @@ source_set("unit_tests") { } sources = [ + "all_download_event_notifier_unittest.cc", "base_file_unittest.cc", "base_file_win_unittest.cc", "download_db_cache_unittest.cc", diff --git a/chromium/components/download/internal/common/DEPS b/chromium/components/download/internal/common/DEPS index 713aa400ea7..015703b0a6f 100644 --- a/chromium/components/download/internal/common/DEPS +++ b/chromium/components/download/internal/common/DEPS @@ -22,7 +22,9 @@ include_rules = [ "+net/http/http_util.h", "+net/traffic_annotation/network_traffic_annotation.h", "+net/url_request/url_request_context_getter.h", + "+services/device/public/mojom", "+services/metrics/public/cpp", "+services/network/public/cpp", "+services/network/public/mojom", + "+services/service_manager/public/cpp", ] diff --git a/chromium/components/download/internal/common/all_download_event_notifier_unittest.cc b/chromium/components/download/internal/common/all_download_event_notifier_unittest.cc new file mode 100644 index 00000000000..5d63d4da46b --- /dev/null +++ b/chromium/components/download/internal/common/all_download_event_notifier_unittest.cc @@ -0,0 +1,149 @@ +// Copyright 2019 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 "components/download/public/common/all_download_event_notifier.h" + +#include "base/bind_helpers.h" +#include "base/macros.h" +#include "components/download/public/common/mock_download_item.h" +#include "components/download/public/common/mock_simple_download_manager.h" +#include "components/download/public/common/simple_download_manager_coordinator.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::NiceMock; + +namespace download { +namespace { + +class MockNotifierObserver : public AllDownloadEventNotifier::Observer { + public: + MockNotifierObserver() = default; + ~MockNotifierObserver() override = default; + + MOCK_METHOD2(OnDownloadsInitialized, + void(SimpleDownloadManagerCoordinator* manager, + bool active_downloads_only)); + MOCK_METHOD2(OnDownloadCreated, + void(SimpleDownloadManagerCoordinator* manager, + DownloadItem* item)); + MOCK_METHOD2(OnDownloadUpdated, + void(SimpleDownloadManagerCoordinator* manager, + DownloadItem* item)); + MOCK_METHOD2(OnDownloadOpened, + void(SimpleDownloadManagerCoordinator* manager, + DownloadItem* item)); + MOCK_METHOD2(OnDownloadRemoved, + void(SimpleDownloadManagerCoordinator* manager, + DownloadItem* item)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockNotifierObserver); +}; + +class AllDownloadEventNotifierTest : public testing::Test { + public: + AllDownloadEventNotifierTest() : coordinator_(base::NullCallback()) {} + + ~AllDownloadEventNotifierTest() override = default; + + SimpleDownloadManagerCoordinator* coordinator() { return &coordinator_; } + + MockDownloadItem& item() { return item_; } + + MockNotifierObserver& observer() { return observer_; } + + private: + NiceMock<MockDownloadItem> item_; + SimpleDownloadManagerCoordinator coordinator_; + NiceMock<MockNotifierObserver> observer_; + DISALLOW_COPY_AND_ASSIGN(AllDownloadEventNotifierTest); +}; + +} // namespace + +// Tests that OnDownloadCreated() will be propagated correctly. +TEST_F(AllDownloadEventNotifierTest, OnDownloadCreated) { + NiceMock<MockSimpleDownloadManager> manager; + coordinator()->SetSimpleDownloadManager(&manager, true); + coordinator()->GetNotifier()->AddObserver(&observer()); + EXPECT_CALL(observer(), OnDownloadCreated(coordinator(), &item())); + manager.NotifyOnNewDownloadCreated(&item()); +} + +// Tests that OnDownloadsInitialized() will be propated correctly. +TEST_F(AllDownloadEventNotifierTest, OnDownloadsInitialized) { + NiceMock<MockSimpleDownloadManager> manager; + coordinator()->SetSimpleDownloadManager(&manager, false); + coordinator()->GetNotifier()->AddObserver(&observer()); + EXPECT_CALL(observer(), OnDownloadsInitialized(coordinator(), true)); + manager.NotifyOnDownloadInitialized(); + + NiceMock<MockSimpleDownloadManager> manager2; + coordinator()->SetSimpleDownloadManager(&manager2, true); + EXPECT_CALL(observer(), OnDownloadsInitialized(coordinator(), false)); + manager2.NotifyOnDownloadInitialized(); +} + +// Tests that the full manaager is set before the in progress manager +// initializes. +TEST_F(AllDownloadEventNotifierTest, + SetFullManagerBeforeInProgressManagerInitializes) { + NiceMock<MockSimpleDownloadManager> manager; + coordinator()->SetSimpleDownloadManager(&manager, false); + coordinator()->GetNotifier()->AddObserver(&observer()); + + // Sets the full manager before the first one is initialized. + NiceMock<MockSimpleDownloadManager> manager2; + coordinator()->SetSimpleDownloadManager(&manager2, true); + + EXPECT_CALL(observer(), OnDownloadsInitialized(coordinator(), _)).Times(0); + // Initializing the first manager shouldn't impact the coordinator now. + manager.NotifyOnDownloadInitialized(); + EXPECT_CALL(observer(), OnDownloadsInitialized(coordinator(), false)); + manager2.NotifyOnDownloadInitialized(); +} + +// Tests that OnDownloadUpdated() will be propagated correctly. +TEST_F(AllDownloadEventNotifierTest, OnDownloadUpdated) { + NiceMock<MockSimpleDownloadManager> manager; + coordinator()->SetSimpleDownloadManager(&manager, true); + coordinator()->GetNotifier()->AddObserver(&observer()); + EXPECT_CALL(observer(), OnDownloadCreated(coordinator(), &item())); + manager.NotifyOnNewDownloadCreated(&item()); + + EXPECT_CALL(observer(), OnDownloadUpdated(coordinator(), &item())); + item().NotifyObserversDownloadUpdated(); +} + +// Tests that observer will be notified if it is added after manager +// initialization. +TEST_F(AllDownloadEventNotifierTest, AddObserverAfterManagerInitialization) { + NiceMock<MockSimpleDownloadManager> manager; + coordinator()->SetSimpleDownloadManager(&manager, true); + manager.NotifyOnDownloadInitialized(); + EXPECT_CALL(observer(), OnDownloadsInitialized(coordinator(), false)); + coordinator()->GetNotifier()->AddObserver(&observer()); +} + +// Tests that observer will only be notified once if multiple manager calls +// OnDownloadCreated() on the same download. +TEST_F(AllDownloadEventNotifierTest, MultipleOnDownloadCreatedOnSameDownload) { + NiceMock<MockSimpleDownloadManager> manager; + coordinator()->GetNotifier()->AddObserver(&observer()); + coordinator()->SetSimpleDownloadManager(&manager, false); + EXPECT_CALL(observer(), OnDownloadCreated(coordinator(), &item())).Times(1); + manager.NotifyOnNewDownloadCreated(&item()); + + // Sets the full manager and call OnDownloadCreated() again. + NiceMock<MockSimpleDownloadManager> manager2; + coordinator()->SetSimpleDownloadManager(&manager2, true); + manager2.NotifyOnNewDownloadCreated(&item()); + + EXPECT_CALL(observer(), OnDownloadUpdated(coordinator(), &item())); + item().NotifyObserversDownloadUpdated(); +} + +} // namespace download diff --git a/chromium/components/download/internal/common/android/download_collection_bridge.cc b/chromium/components/download/internal/common/android/download_collection_bridge.cc index 6dfc75e3fa2..ae075552619 100644 --- a/chromium/components/download/internal/common/android/download_collection_bridge.cc +++ b/chromium/components/download/internal/common/android/download_collection_bridge.cc @@ -154,10 +154,7 @@ void DownloadCollectionBridge::GetDisplayNamesForDownloads( std::move(cb).Run(std::move(result)); return; } - jsize count = env->GetArrayLength(jdisplay_infos.obj()); - for (jsize i = 0; i < count; ++i) { - base::android::ScopedJavaLocalRef<jobject> jdisplay_info( - env, env->GetObjectArrayElement(jdisplay_infos.obj(), i)); + for (auto jdisplay_info : jdisplay_infos.ReadElements<jobject>()) { ScopedJavaLocalRef<jstring> juri = Java_DisplayNameInfo_getDownloadUri(env, jdisplay_info); ScopedJavaLocalRef<jstring> jdisplay_name = @@ -177,6 +174,21 @@ bool DownloadCollectionBridge::NeedToRetrieveDisplayNames() { return Java_DownloadCollectionBridge_needToRetrieveDisplayNames(env); } +// static +base::FilePath DownloadCollectionBridge::GetDisplayName( + const base::FilePath& download_uri) { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef<jstring> jdownload_uri = + ConvertUTF8ToJavaString(env, download_uri.value()); + ScopedJavaLocalRef<jstring> jdisplay_name = + Java_DownloadCollectionBridge_getDisplayName(env, jdownload_uri); + if (jdisplay_name) { + std::string display_name = ConvertJavaStringToUTF8(env, jdisplay_name); + return base::FilePath(display_name); + } + return base::FilePath(); +} + jint JNI_DownloadCollectionBridge_GetExpirationDurationInDays(JNIEnv* env) { std::string finch_value = base::GetFieldTrialParamValueByFeature( features::kRefreshExpirationDate, kDownloadExpirationDurationFinchKey); diff --git a/chromium/components/download/internal/common/android/download_collection_bridge.h b/chromium/components/download/internal/common/android/download_collection_bridge.h index 17cb1a500a5..c3afaae41fe 100644 --- a/chromium/components/download/internal/common/android/download_collection_bridge.h +++ b/chromium/components/download/internal/common/android/download_collection_bridge.h @@ -72,6 +72,10 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadCollectionBridge { // Gets the display name for all downloads. // Called on non UI thread. static void GetDisplayNamesForDownloads(GetDisplayNamesCallback cb); + + // Gets the display name for a download. + static base::FilePath GetDisplayName(const base::FilePath& download_uri); + private: DISALLOW_COPY_AND_ASSIGN(DownloadCollectionBridge); }; diff --git a/chromium/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java b/chromium/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java index e45a5b67fa3..29c2a74c9a7 100644 --- a/chromium/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java +++ b/chromium/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java @@ -161,6 +161,15 @@ public class DownloadCollectionBridge { protected void refreshExpirationDate(final String intermediateUri) {} /** + * Gets the display name for a download. + * @param downloadUri Uri of the download. + * @return the display name of the download. + */ + protected String getDisplayNameForUri(final String downloadUri) { + return null; + } + + /** * Creates an intermediate URI for download to be written into. On completion, call * nativeOnCreateIntermediateUriResult() with |callbackId|. * @param fileName Name of the file. @@ -287,5 +296,15 @@ public class DownloadCollectionBridge { return nativeGetExpirationDurationInDays(); } + /** + * Gets the display name for a download. + * @param downloadUri Uri of the download. + * @return the display name of the download. + */ + @CalledByNative + private static String getDisplayName(final String downloadUri) { + return getDownloadCollectionBridge().getDisplayNameForUri(downloadUri); + } + private static native int nativeGetExpirationDurationInDays(); } diff --git a/chromium/components/download/internal/common/base_file.cc b/chromium/components/download/internal/common/base_file.cc index d2b4aca398e..d220a7bc132 100644 --- a/chromium/components/download/internal/common/base_file.cc +++ b/chromium/components/download/internal/common/base_file.cc @@ -154,9 +154,10 @@ DownloadInterruptReason BaseFile::WriteDataToFile(int64_t offset, if (detached_) RecordDownloadCount(APPEND_TO_DETACHED_FILE_COUNT); - if (!file_.IsValid()) + if (!file_.IsValid()) { return LogInterruptReason("No file stream on append", 0, DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); + } // TODO(phajdan.jr): get rid of this check. if (data_len == 0) @@ -204,6 +205,27 @@ DownloadInterruptReason BaseFile::WriteDataToFile(int64_t offset, return DOWNLOAD_INTERRUPT_REASON_NONE; } +bool BaseFile::ValidateDataInFile(int64_t offset, + const char* data, + size_t data_len) { + if (!file_.IsValid()) + return false; + + // Only validate the first chunk of the file. So |offset| cannot be + // larger than bytes received. + if (offset > bytes_so_far_) + return false; + + if (data_len <= 0) + return true; + + std::unique_ptr<char[]> buffer(new char[data_len]); + if (file_.Read(offset, buffer.get(), data_len) <= 0) + return false; + + return memcmp(data, buffer.get(), data_len) == 0; +} + DownloadInterruptReason BaseFile::Rename(const base::FilePath& new_path) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DownloadInterruptReason rename_result = DOWNLOAD_INTERRUPT_REASON_NONE; diff --git a/chromium/components/download/internal/common/base_file_unittest.cc b/chromium/components/download/internal/common/base_file_unittest.cc index f86edf1bcc0..921ad87a2fe 100644 --- a/chromium/components/download/internal/common/base_file_unittest.cc +++ b/chromium/components/download/internal/common/base_file_unittest.cc @@ -757,4 +757,25 @@ TEST_F(BaseFileTest, WriteDataToSparseFile) { ExpectHashValue(kHashOfTestData1To3, base_file_->Finish()); } +// Test that validating data in a file works. +TEST_F(BaseFileTest, ValidateDataInFile) { + ASSERT_TRUE(InitializeFile()); + ASSERT_TRUE(AppendDataToFile(kTestData1)); + + ASSERT_TRUE(base_file_->ValidateDataInFile(0, "Let's", 5)); + ASSERT_TRUE(base_file_->ValidateDataInFile(1, "et's ", 5)); + ASSERT_TRUE(base_file_->ValidateDataInFile( + 0, "Let's write some data to the file!\n", kTestDataLength1)); + ASSERT_TRUE(base_file_->ValidateDataInFile(kTestDataLength1 - 1, "\n", 1)); + ASSERT_FALSE(base_file_->ValidateDataInFile(kTestDataLength1, "\n", 1)); + ASSERT_FALSE(base_file_->ValidateDataInFile(kTestDataLength1 - 1, "y\n", 2)); + ASSERT_FALSE(base_file_->ValidateDataInFile(0, "et's ", 5)); + ASSERT_FALSE(base_file_->ValidateDataInFile( + 0, "Let's write some data to the file1\n", kTestDataLength1)); + ASSERT_FALSE(base_file_->ValidateDataInFile( + 0, "Let's write some data to the file1!\n", kTestDataLength1 + 1)); + + base_file_->Finish(); +} + } // namespace download diff --git a/chromium/components/download/internal/common/download_file_impl.cc b/chromium/components/download/internal/common/download_file_impl.cc index e433cb0822c..13f52e034d4 100644 --- a/chromium/components/download/internal/common/download_file_impl.cc +++ b/chromium/components/download/internal/common/download_file_impl.cc @@ -4,6 +4,7 @@ #include "components/download/public/common/download_file_impl.h" +#include <algorithm> #include <string> #include <utility> @@ -58,13 +59,24 @@ const int kUnknownContentLength = -1; DownloadFileImpl::SourceStream::SourceStream( int64_t offset, int64_t length, + int64_t starting_file_write_offset, std::unique_ptr<InputStream> stream) : offset_(offset), length_(length), + starting_file_write_offset_(starting_file_write_offset), + bytes_read_(0), bytes_written_(0), finished_(false), index_(0u), - input_stream_(std::move(stream)) {} + input_stream_(std::move(stream)) { + CHECK_LE(offset_, starting_file_write_offset_); + CHECK_GE(offset_, 0); + DCHECK(length <= 0 || length >= starting_file_write_offset - offset) + << "Not enough for content validation. offset = " << offset + << ", length = " << length + << " , starting_file_write_offset = " << starting_file_write_offset + << "."; +} DownloadFileImpl::SourceStream::~SourceStream() = default; @@ -72,19 +84,25 @@ void DownloadFileImpl::SourceStream::Initialize() { input_stream_->Initialize(); } -void DownloadFileImpl::SourceStream::OnWriteBytesToDisk(int64_t bytes_write) { - bytes_written_ += bytes_write; +void DownloadFileImpl::SourceStream::OnBytesConsumed(int64_t bytes_read, + int64_t bytes_written) { + CHECK_GE(bytes_read, bytes_written); + bytes_read_ += bytes_read; + bytes_written_ += bytes_written; } void DownloadFileImpl::SourceStream::TruncateLengthWithWrittenDataBlock( - int64_t offset, + int64_t received_slice_offset, int64_t bytes_written) { DCHECK_GT(bytes_written, 0); if (length_ == kNoBytesToWrite) return; - if (offset <= offset_) { - if (offset + bytes_written > offset_) { + if (received_slice_offset <= starting_file_write_offset_) { + // If validation has completed, mark the stream as finished if the file + // write position already has data. + if (received_slice_offset + bytes_written > starting_file_write_offset_ && + GetRemainingBytesToValidate() == 0) { length_ = kNoBytesToWrite; finished_ = true; } @@ -92,8 +110,12 @@ void DownloadFileImpl::SourceStream::TruncateLengthWithWrittenDataBlock( } if (length_ == DownloadSaveInfo::kLengthFullContent || - length_ > offset - offset_) { - length_ = offset - offset_; + (length_ > received_slice_offset - offset_ && + length_ > starting_file_write_offset_ - offset_)) { + // Stream length should always include the validation data, unless the + // response is too short. + length_ = + std::max(received_slice_offset, starting_file_write_offset_) - offset_; } } @@ -123,23 +145,15 @@ InputStream::StreamState DownloadFileImpl::SourceStream::Read( return input_stream_->Read(data, length); } -DownloadFileImpl::DownloadFileImpl( - std::unique_ptr<DownloadSaveInfo> save_info, - const base::FilePath& default_download_directory, - std::unique_ptr<InputStream> stream, - uint32_t download_id, - base::WeakPtr<DownloadDestinationObserver> observer) - : DownloadFileImpl(std::move(save_info), - default_download_directory, - download_id, - observer) { - source_streams_[save_info_->offset] = std::make_unique<SourceStream>( - save_info_->offset, save_info_->length, std::move(stream)); +size_t DownloadFileImpl::SourceStream::GetRemainingBytesToValidate() { + int64_t bytes_remaining = starting_file_write_offset_ - offset_ - bytes_read_; + return bytes_remaining < 0 ? 0 : bytes_remaining; } DownloadFileImpl::DownloadFileImpl( std::unique_ptr<DownloadSaveInfo> save_info, const base::FilePath& default_download_directory, + std::unique_ptr<InputStream> stream, uint32_t download_id, base::WeakPtr<DownloadDestinationObserver> observer) : file_(download_id), @@ -161,6 +175,10 @@ DownloadFileImpl::DownloadFileImpl( TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("download", "DownloadFileActive", download_id); + source_streams_[save_info_->offset] = std::make_unique<SourceStream>( + save_info_->offset, save_info_->length, + save_info_->GetStartingFileWriteOffset(), std::move(stream)); + DETACH_FROM_SEQUENCE(sequence_checker_); } @@ -194,7 +212,7 @@ void DownloadFileImpl::Initialize( bytes_so_far += received_slice.received_bytes; } } else { - bytes_so_far = save_info_->offset; + bytes_so_far = save_info_->GetStartingFileWriteOffset(); } int64_t bytes_wasted = 0; DownloadInterruptReason reason = file_.Initialize( @@ -237,7 +255,7 @@ void DownloadFileImpl::AddInputStream(std::unique_ptr<InputStream> stream, } DCHECK(source_streams_.find(offset) == source_streams_.end()); source_streams_[offset] = - std::make_unique<SourceStream>(offset, length, std::move(stream)); + std::make_unique<SourceStream>(offset, length, offset, std::move(stream)); OnSourceStreamAdded(source_streams_[offset].get()); } @@ -254,46 +272,73 @@ void DownloadFileImpl::OnSourceStreamAdded(SourceStream* source_stream) { RegisterAndActivateStream(source_stream); } -DownloadInterruptReason DownloadFileImpl::WriteDataToFile(int64_t offset, - const char* data, - size_t data_len) { +DownloadInterruptReason DownloadFileImpl::ValidateAndWriteDataToFile( + int64_t offset, + const char* data, + size_t bytes_to_validate, + size_t bytes_to_write) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - WillWriteToDisk(data_len); - return file_.WriteDataToFile(offset, data, data_len); + // Check if some of the data is for validation purpose. + if (bytes_to_validate > 0 && + !file_.ValidateDataInFile(offset, data, bytes_to_validate)) { + return DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH; + } + // If there is no data to write, just return DOWNLOAD_INTERRUPT_REASON_NONE + // and read the next chunk. + if (bytes_to_write <= 0) + return DOWNLOAD_INTERRUPT_REASON_NONE; + // Write the remaining data to disk. + WillWriteToDisk(bytes_to_write); + return file_.WriteDataToFile(offset + bytes_to_validate, + data + bytes_to_validate, bytes_to_write); } bool DownloadFileImpl::CalculateBytesToWrite(SourceStream* source_stream, size_t bytes_available_to_write, + size_t* bytes_to_validate, size_t* bytes_to_write) { + *bytes_to_validate = 0; if (source_stream->length() == kNoBytesToWrite) { *bytes_to_write = 0; return true; } + // First calculate the number of bytes to validate. + *bytes_to_write = bytes_available_to_write; + size_t remaining_bytes_to_validate = + source_stream->GetRemainingBytesToValidate(); + if (remaining_bytes_to_validate > 0) { + *bytes_to_validate = + std::min(remaining_bytes_to_validate, bytes_available_to_write); + *bytes_to_write -= *bytes_to_validate; + } + if (source_stream->length() != DownloadSaveInfo::kLengthFullContent && + source_stream->bytes_read() + + static_cast<int64_t>(bytes_available_to_write) > + source_stream->length()) { + // Total bytes to consume is capped by the length of the stream. + int64_t bytes_to_consume = + source_stream->length() - source_stream->bytes_read(); + // The validation data should always be streamed. + DCHECK_GE(bytes_to_consume, static_cast<int64_t>(*bytes_to_validate)); + *bytes_to_write = bytes_to_consume - *bytes_to_validate; + return true; + } + // If a new slice finds that its target position has already been written, - // terminate the stream. - if (source_stream->bytes_written() == 0) { + // terminate the stream if there are no bytes to validate. + if (source_stream->bytes_written() == 0 && *bytes_to_write > 0) { for (const auto& received_slice : received_slices_) { - if (received_slice.offset <= source_stream->offset() && + if (received_slice.offset <= + source_stream->starting_file_write_offset() && received_slice.offset + received_slice.received_bytes > - source_stream->offset()) { + source_stream->starting_file_write_offset()) { *bytes_to_write = 0; return true; } } } - if (source_stream->length() != DownloadSaveInfo::kLengthFullContent && - source_stream->bytes_written() + - static_cast<int64_t>(bytes_available_to_write) > - source_stream->length()) { - // Write a partial buffer as the incoming data exceeds the length limit. - *bytes_to_write = source_stream->length() - source_stream->bytes_written(); - return true; - } - - *bytes_to_write = bytes_available_to_write; return false; } @@ -331,8 +376,12 @@ void DownloadFileImpl::CreateIntermediateUriForPublish( DownloadCollectionBridge::CreateIntermediateUriForPublish( original_url, referrer_url, file_name, mime_type); DownloadInterruptReason reason = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - if (!content_path.empty()) + if (!content_path.empty()) { reason = file_.Rename(content_path); + display_name_ = DownloadCollectionBridge::GetDisplayName(content_path); + } + if (display_name_.empty()) + display_name_ = file_name; OnRenameComplete(reason, content_path, callback); } @@ -341,6 +390,10 @@ void DownloadFileImpl::PublishDownload( DownloadInterruptReason reason = file_.PublishDownload(); OnRenameComplete(reason, file_.full_path(), callback); } + +base::FilePath DownloadFileImpl::GetDisplayName() { + return display_name_; +} #endif // defined(OS_ANDROID) base::TimeDelta DownloadFileImpl::GetRetryDelayForFailedRename( @@ -363,8 +416,9 @@ DownloadInterruptReason DownloadFileImpl::HandleStreamCompletionStatus( DownloadInterruptReason reason = source_stream->GetCompletionStatus(); if (source_stream->length() == DownloadSaveInfo::kLengthFullContent && !received_slices_.empty() && - (source_stream->offset() == received_slices_.back().offset + - received_slices_.back().received_bytes) && + (source_stream->starting_file_write_offset() == + received_slices_.back().offset + + received_slices_.back().received_bytes) && reason == DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE) { // We are probably reaching the end of the stream, don't treat this // as an error. @@ -510,6 +564,7 @@ void DownloadFileImpl::StreamActive(SourceStream* source_stream, size_t incoming_data_size = 0; size_t total_incoming_data_size = 0; size_t num_buffers = 0; + size_t bytes_to_validate = 0; size_t bytes_to_write = 0; bool should_terminate = false; InputStream::StreamState state(InputStream::EMPTY); @@ -526,24 +581,26 @@ void DownloadFileImpl::StreamActive(SourceStream* source_stream, break; case InputStream::HAS_DATA: { ++num_buffers; - should_terminate = CalculateBytesToWrite( - source_stream, incoming_data_size, &bytes_to_write); + should_terminate = + CalculateBytesToWrite(source_stream, incoming_data_size, + &bytes_to_validate, &bytes_to_write); DCHECK_GE(incoming_data_size, bytes_to_write); - reason = WriteDataToFile( - source_stream->offset() + source_stream->bytes_written(), - incoming_data->data(), bytes_to_write); + reason = ValidateAndWriteDataToFile( + source_stream->offset() + source_stream->bytes_read(), + incoming_data->data(), bytes_to_validate, bytes_to_write); bytes_seen_ += bytes_to_write; - total_incoming_data_size += bytes_to_write; + total_incoming_data_size += incoming_data_size; if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) { int64_t prev_bytes_written = source_stream->bytes_written(); - source_stream->OnWriteBytesToDisk(bytes_to_write); + source_stream->OnBytesConsumed(incoming_data_size, bytes_to_write); if (!IsSparseFile()) break; // If the write operation creates a new slice, add it to the // |received_slices_| and update all the entries in // |source_streams_|. if (bytes_to_write > 0 && prev_bytes_written == 0) { - AddNewSlice(source_stream->offset(), bytes_to_write); + AddNewSlice(source_stream->starting_file_write_offset(), + bytes_to_write); } else { received_slices_[source_stream->index()].received_bytes += bytes_to_write; @@ -613,7 +670,7 @@ void DownloadFileImpl::NotifyObserver(SourceStream* source_stream, } SetPotentialFileLength(source_stream->offset() + - source_stream->bytes_written()); + source_stream->bytes_read()); } num_active_streams_--; @@ -700,10 +757,10 @@ void DownloadFileImpl::AddNewSlice(int64_t offset, int64_t length) { // Update the index of exising SourceStreams. for (auto& stream : source_streams_) { SourceStream* source_stream = stream.second.get(); - if (source_stream->offset() > offset) { + if (source_stream->starting_file_write_offset() > offset) { if (slice_added && source_stream->bytes_written() > 0) source_stream->set_index(source_stream->index() + 1); - } else if (source_stream->offset() == offset) { + } else if (source_stream->starting_file_write_offset() == offset) { source_stream->set_index(index); } else { source_stream->TruncateLengthWithWrittenDataBlock(offset, length); @@ -738,26 +795,29 @@ void DownloadFileImpl::HandleStreamError(SourceStream* source_stream, source_stream->set_finished(true); num_active_streams_--; - // If previous stream has already written data at the starting offset of - // the error stream. The download can complete. - bool can_recover_from_error = (source_stream->length() == kNoBytesToWrite); - - // See if the previous stream can download the full content. - // If the current stream has written some data, length of all preceding - // streams will be truncated. - if (IsSparseFile() && !can_recover_from_error) { - SourceStream* preceding_neighbor = FindPrecedingNeighbor(source_stream); - while (preceding_neighbor) { - if (CanRecoverFromError(source_stream, preceding_neighbor)) { - can_recover_from_error = true; - break; - } + bool can_recover_from_error = false; + if (reason != DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH) { + // If previous stream has already written data at the starting offset of + // the error stream. The download can complete. + can_recover_from_error = (source_stream->length() == kNoBytesToWrite); + + // See if the previous stream can download the full content. + // If the current stream has written some data, length of all preceding + // streams will be truncated. + if (IsSparseFile() && !can_recover_from_error) { + SourceStream* preceding_neighbor = FindPrecedingNeighbor(source_stream); + while (preceding_neighbor) { + if (CanRecoverFromError(source_stream, preceding_neighbor)) { + can_recover_from_error = true; + break; + } - // If the neighbor cannot recover the error and it has already created - // a slice, just interrupt the download. - if (preceding_neighbor->bytes_written() > 0) - break; - preceding_neighbor = FindPrecedingNeighbor(preceding_neighbor); + // If the neighbor cannot recover the error and it has already created + // a slice, just interrupt the download. + if (preceding_neighbor->bytes_written() > 0) + break; + preceding_neighbor = FindPrecedingNeighbor(preceding_neighbor); + } } } @@ -786,8 +846,9 @@ DownloadFileImpl::SourceStream* DownloadFileImpl::FindPrecedingNeighbor( int64_t max_preceding_offset = 0; SourceStream* ret = nullptr; for (auto& stream : source_streams_) { - int64_t offset = stream.second->offset(); - if (offset < source_stream->offset() && offset >= max_preceding_offset) { + int64_t offset = stream.second->starting_file_write_offset(); + if (offset < source_stream->starting_file_write_offset() && + offset >= max_preceding_offset) { ret = stream.second.get(); max_preceding_offset = offset; } @@ -807,6 +868,9 @@ void DownloadFileImpl::DebugStates() const { DVLOG(1) << "Total source stream count = " << source_streams_.size(); for (const auto& stream : source_streams_) { DVLOG(1) << "Source stream, offset = " << stream.second->offset() + << " , bytes_read = " << stream.second->bytes_read() + << " , starting_file_write_offset = " + << stream.second->starting_file_write_offset() << " , bytes_written = " << stream.second->bytes_written() << " , is_finished = " << stream.second->is_finished() << " , length = " << stream.second->length() diff --git a/chromium/components/download/internal/common/download_file_unittest.cc b/chromium/components/download/internal/common/download_file_unittest.cc index fb89b4c5797..a3d72dc2be9 100644 --- a/chromium/components/download/internal/common/download_file_unittest.cc +++ b/chromium/components/download/internal/common/download_file_unittest.cc @@ -192,15 +192,22 @@ class DownloadFileTest : public testing::Test { closure.Run(); } - bool CreateDownloadFile(int offset, bool calculate_hash) { - return CreateDownloadFile(offset, 0, calculate_hash, - DownloadItem::ReceivedSlices()); + bool CreateDownloadFile(bool calculate_hash) { + return CreateDownloadFile(0, calculate_hash, DownloadItem::ReceivedSlices(), + -1); } - bool CreateDownloadFile(int offset, - int length, + bool CreateDownloadFile(int length, bool calculate_hash, const DownloadItem::ReceivedSlices& received_slices) { + return CreateDownloadFile(length, calculate_hash, + DownloadItem::ReceivedSlices(), -1); + } + + bool CreateDownloadFile(int length, + bool calculate_hash, + const DownloadItem::ReceivedSlices& received_slices, + int file_offset) { // There can be only one. DCHECK(!download_file_); @@ -213,8 +220,23 @@ class DownloadFileTest : public testing::Test { .RetiresOnSaturation(); std::unique_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo()); - save_info->offset = offset; + // Fill the file by repeatedly copying |kTestData1| if |file_offset| is + // positive. + if (file_offset > 0) { + base::CreateTemporaryFileInDir(download_dir_.GetPath(), + &save_info->file_path); + int len = file_offset; + int data_len = strlen(kTestData1); + while (len > 0) { + int bytes_to_write = len > data_len ? data_len : len; + base::AppendToFile(save_info->file_path, kTestData1, bytes_to_write); + len -= bytes_to_write; + } + } + + save_info->offset = 0; save_info->length = length; + save_info->file_offset = file_offset; download_file_.reset(new TestDownloadFileImpl( std::move(save_info), download_dir_.GetPath(), @@ -462,6 +484,9 @@ class DownloadFileTest : public testing::Test { int64_t bytes_; int64_t bytes_per_sec_; + // Keep track of what data should be saved to the disk file. + std::string expected_data_; + private: void SetRenameResult(const base::Closure& closure, DownloadInterruptReason* reason_p, @@ -476,9 +501,6 @@ class DownloadFileTest : public testing::Test { } base::test::ScopedTaskEnvironment scoped_task_environment_; - - // Keep track of what data should be saved to the disk file. - std::string expected_data_; }; // DownloadFile::RenameAndAnnotate and DownloadFile::RenameAndUniquify have a @@ -531,7 +553,7 @@ const int DownloadFileTest::kDummyRequestId = 67; // Rename the file before any data is downloaded, after some has, after it all // has, and after it's closed. TEST_P(DownloadFileTestWithRename, RenameFileFinal) { - ASSERT_TRUE(CreateDownloadFile(0, true)); + ASSERT_TRUE(CreateDownloadFile(true)); base::FilePath initial_path(download_file_->FullPath()); EXPECT_TRUE(base::PathExists(initial_path)); base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1")); @@ -601,7 +623,7 @@ TEST_P(DownloadFileTestWithRename, RenameFileFinal) { // the above test because it only applies to RenameAndAnnotate(). // RenameAndUniquify() doesn't overwrite by design. TEST_F(DownloadFileTest, RenameOverwrites) { - ASSERT_TRUE(CreateDownloadFile(0, true)); + ASSERT_TRUE(CreateDownloadFile(true)); base::FilePath initial_path(download_file_->FullPath()); EXPECT_TRUE(base::PathExists(initial_path)); base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1")); @@ -631,7 +653,7 @@ TEST_F(DownloadFileTest, RenameOverwrites) { // DownloadFileTestWithRename test because this only applies to // RenameAndUniquify(). TEST_F(DownloadFileTest, RenameUniquifies) { - ASSERT_TRUE(CreateDownloadFile(0, true)); + ASSERT_TRUE(CreateDownloadFile(true)); base::FilePath initial_path(download_file_->FullPath()); EXPECT_TRUE(base::PathExists(initial_path)); base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1")); @@ -654,7 +676,7 @@ TEST_F(DownloadFileTest, RenameUniquifies) { // Test that RenameAndUniquify doesn't try to uniquify in the case where the // target filename is the same as the current filename. TEST_F(DownloadFileTest, RenameRecognizesSelfConflict) { - ASSERT_TRUE(CreateDownloadFile(0, true)); + ASSERT_TRUE(CreateDownloadFile(true)); base::FilePath initial_path(download_file_->FullPath()); EXPECT_TRUE(base::PathExists(initial_path)); @@ -671,7 +693,7 @@ TEST_F(DownloadFileTest, RenameRecognizesSelfConflict) { // Test to make sure we get the proper error on failure. TEST_P(DownloadFileTestWithRename, RenameError) { - ASSERT_TRUE(CreateDownloadFile(0, true)); + ASSERT_TRUE(CreateDownloadFile(true)); base::FilePath initial_path(download_file_->FullPath()); // Create a subdirectory. @@ -726,7 +748,7 @@ void TestRenameCompletionCallback(const base::Closure& closure, // base::MessageLoopCurrent::Get(). Each RunLoop processes that queue until it // sees a QuitClosure() targeted at itself, at which point it stops processing. TEST_P(DownloadFileTestWithRename, RenameWithErrorRetry) { - ASSERT_TRUE(CreateDownloadFile(0, true)); + ASSERT_TRUE(CreateDownloadFile(true)); base::FilePath initial_path(download_file_->FullPath()); // Create a subdirectory. @@ -797,7 +819,7 @@ TEST_P(DownloadFileTestWithRename, RenameWithErrorRetry) { // Various tests of the StreamActive method. TEST_F(DownloadFileTest, StreamEmptySuccess) { - ASSERT_TRUE(CreateDownloadFile(0, true)); + ASSERT_TRUE(CreateDownloadFile(true)); base::FilePath initial_path(download_file_->FullPath()); EXPECT_TRUE(base::PathExists(initial_path)); @@ -813,7 +835,7 @@ TEST_F(DownloadFileTest, StreamEmptySuccess) { } TEST_F(DownloadFileTest, StreamEmptyError) { - ASSERT_TRUE(CreateDownloadFile(0, true)); + ASSERT_TRUE(CreateDownloadFile(true)); base::FilePath initial_path(download_file_->FullPath()); EXPECT_TRUE(base::PathExists(initial_path)); @@ -842,7 +864,7 @@ TEST_F(DownloadFileTest, StreamEmptyError) { } TEST_F(DownloadFileTest, StreamNonEmptySuccess) { - ASSERT_TRUE(CreateDownloadFile(0, true)); + ASSERT_TRUE(CreateDownloadFile(true)); base::FilePath initial_path(download_file_->FullPath()); EXPECT_TRUE(base::PathExists(initial_path)); @@ -858,7 +880,7 @@ TEST_F(DownloadFileTest, StreamNonEmptySuccess) { } TEST_F(DownloadFileTest, StreamNonEmptyError) { - ASSERT_TRUE(CreateDownloadFile(0, true)); + ASSERT_TRUE(CreateDownloadFile(true)); base::FilePath initial_path(download_file_->FullPath()); EXPECT_TRUE(base::PathExists(initial_path)); @@ -888,6 +910,58 @@ TEST_F(DownloadFileTest, StreamNonEmptyError) { DestroyDownloadFile(0); } +// Tests that if file content validation succeeds, all the remaining data will +// be writing to the file. +TEST_F(DownloadFileTest, FileContentValidationSuccess) { + int stream_length = strlen(kTestData1) * 2; + + ASSERT_TRUE(CreateDownloadFile( + stream_length /* length */, true /* calculate_hash */, + DownloadItem::ReceivedSlices(), strlen(kTestData1) - 1)); + base::FilePath initial_path(download_file_->FullPath()); + EXPECT_TRUE(base::PathExists(initial_path)); + + const char* chunks1[] = {kTestData1, kTestData1}; + ::testing::Sequence s1; + SetupDataAppend(chunks1, 2 /* num_chunks */, input_stream_, s1); + SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, input_stream_, s1); + EXPECT_CALL(*(observer_.get()), MockDestinationCompleted(_, _)); + sink_callback_.Run(MOJO_RESULT_OK); + VerifyStreamAndSize(); + base::RunLoop().RunUntilIdle(); + DestroyDownloadFile(0); +} + +// Tests that if file content validation fails, an error will occur and no data +// will be written. +TEST_F(DownloadFileTest, FileContentValidationFail) { + int file_length = strlen(kTestData2) - 1; + int stream_length = strlen(kTestData1) + strlen(kTestData2); + + ASSERT_TRUE(CreateDownloadFile(stream_length /* length */, + true /* calculate_hash */, + DownloadItem::ReceivedSlices(), file_length)); + base::FilePath initial_path(download_file_->FullPath()); + EXPECT_TRUE(base::PathExists(initial_path)); + std::string file_content = std::string(kTestData1, 0, file_length); + expected_data_ = file_content; + VerifyStreamAndSize(); + + const char* chunks1[] = {kTestData2, kTestData1}; + ::testing::Sequence s1; + // Only 1 chunk will be read, and it will generate an error after + // failing the validation. + SetupDataAppend(chunks1, 1 /* num_chunks */, input_stream_, s1); + EXPECT_CALL(*input_stream_, ClearDataReadyCallback()); + EXPECT_CALL(*(observer_.get()), + MockDestinationError(DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH, + file_length, _)); + sink_callback_.Run(MOJO_RESULT_OK); + base::RunLoop().RunUntilIdle(); + expected_data_ = file_content; + DestroyDownloadFile(0); +} + // Tests for concurrent streams handling, used for parallel download. // // Activate both streams at the same time. @@ -895,7 +969,7 @@ TEST_F(DownloadFileTest, MultipleStreamsWrite) { int64_t stream_0_length = GetBuffersLength(kTestData6, 2); int64_t stream_1_length = GetBuffersLength(kTestData7, 2); - ASSERT_TRUE(CreateDownloadFile(0, stream_0_length, true, + ASSERT_TRUE(CreateDownloadFile(stream_0_length, true, DownloadItem::ReceivedSlices())); PrepareStream(&input_stream_, 0, false, true, kTestData6, 2); @@ -934,7 +1008,7 @@ TEST_F(DownloadFileTest, MutipleStreamsLimitedLength) { // "Range:50-". int64_t stream_2_length = GetBuffersLength(kTestData6, 2); - ASSERT_TRUE(CreateDownloadFile(0, stream_0_length, true, + ASSERT_TRUE(CreateDownloadFile(stream_0_length, true, DownloadItem::ReceivedSlices())); PrepareStream(&input_stream_, 0, false, true, kTestData6, 2); @@ -986,7 +1060,7 @@ TEST_F(DownloadFileTest, MutipleStreamsLimitedLength) { TEST_F(DownloadFileTest, MultipleStreamsFirstStreamWriteAllData) { int64_t stream_0_length = GetBuffersLength(kTestData8, 4); - ASSERT_TRUE(CreateDownloadFile(0, DownloadSaveInfo::kLengthFullContent, true, + ASSERT_TRUE(CreateDownloadFile(DownloadSaveInfo::kLengthFullContent, true, DownloadItem::ReceivedSlices())); PrepareStream(&input_stream_, 0, false, true, kTestData8, 4); @@ -1019,7 +1093,7 @@ TEST_F(DownloadFileTest, MultipleStreamsFirstStreamWriteAllData) { TEST_F(DownloadFileTest, SecondStreamStartingOffsetAlreadyWritten) { int64_t stream_0_length = GetBuffersLength(kTestData6, 2); - ASSERT_TRUE(CreateDownloadFile(0, stream_0_length, true, + ASSERT_TRUE(CreateDownloadFile(stream_0_length, true, DownloadItem::ReceivedSlices())); Sequence seq; diff --git a/chromium/components/download/internal/common/download_item_impl.cc b/chromium/components/download/internal/common/download_item_impl.cc index b1bc7b7d249..bd90ec09ce9 100644 --- a/chromium/components/download/internal/common/download_item_impl.cc +++ b/chromium/components/download/internal/common/download_item_impl.cc @@ -46,6 +46,7 @@ #include "components/download/internal/common/download_job_impl.h" #include "components/download/internal/common/parallel_download_utils.h" #include "components/download/public/common/download_danger_type.h" +#include "components/download/public/common/download_features.h" #include "components/download/public/common/download_file.h" #include "components/download/public/common/download_interrupt_reasons.h" #include "components/download/public/common/download_item_impl_delegate.h" @@ -225,6 +226,7 @@ DownloadItemImpl::RequestInfo::RequestInfo( const GURL& site_url, const GURL& tab_url, const GURL& tab_referrer_url, + const base::Optional<url::Origin>& request_initiator, const std::string& suggested_filename, const base::FilePath& forced_file_path, ui::PageTransition transition_type, @@ -236,6 +238,7 @@ DownloadItemImpl::RequestInfo::RequestInfo( site_url(site_url), tab_url(tab_url), tab_referrer_url(tab_referrer_url), + request_initiator(request_initiator), suggested_filename(suggested_filename), forced_file_path(forced_file_path), transition_type(transition_type), @@ -290,6 +293,7 @@ DownloadItemImpl::DownloadItemImpl( const GURL& site_url, const GURL& tab_url, const GURL& tab_refererr_url, + const base::Optional<url::Origin>& request_initiator, const std::string& mime_type, const std::string& original_mime_type, base::Time start_time, @@ -314,6 +318,7 @@ DownloadItemImpl::DownloadItemImpl( site_url, tab_url, tab_refererr_url, + request_initiator, std::string(), base::FilePath(), ui::PAGE_TRANSITION_LINK, @@ -363,6 +368,7 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, info.site_url, info.tab_url, info.tab_referrer_url, + info.request_initiator, base::UTF16ToUTF8(info.save_info->suggested_name), info.save_info->file_path, info.transition_type ? info.transition_type.value() @@ -421,7 +427,7 @@ DownloadItemImpl::DownloadItemImpl( weak_ptr_factory_(this) { job_ = DownloadJobFactory::CreateJob(this, std::move(request_handle), DownloadCreateInfo(), true, nullptr, - nullptr); + nullptr, nullptr); delegate_->Attach(); Init(true /* actively downloading */, TYPE_SAVE_PAGE_AS); } @@ -796,6 +802,11 @@ const GURL& DownloadItemImpl::GetTabReferrerUrl() const { return request_info_.tab_referrer_url; } +const base::Optional<url::Origin>& DownloadItemImpl::GetRequestInitiator() + const { + return request_info_.request_initiator; +} + std::string DownloadItemImpl::GetSuggestedFilename() const { return request_info_.suggested_filename; } @@ -986,8 +997,7 @@ bool DownloadItemImpl::CanOpenDownload() { // they aren't owned by the download system. const bool is_complete = GetState() == DownloadItem::COMPLETE; return (!IsDone() || is_complete) && !IsTemporary() && - !file_externally_removed_ && - delegate_->IsMostRecentDownloadItemAtFilePath(this); + !file_externally_removed_; } bool DownloadItemImpl::ShouldOpenFileBasedOnExtension() { @@ -1133,8 +1143,10 @@ ResumeMode DownloadItemImpl::GetResumeMode() const { // We also can't continue if we don't have some verifier to make sure // we're getting the same file. bool restart_required = - (GetFullPath().empty() || (etag_.empty() && last_modified_time_.empty())); - + (GetFullPath().empty() || + (!HasStrongValidators() && + !base::FeatureList::IsEnabled( + features::kAllowDownloadResumptionWithoutStrongValidators))); // We won't auto-restart if we've used up our attempts or the // download has been paused by user action. bool user_action_required = @@ -1144,6 +1156,10 @@ ResumeMode DownloadItemImpl::GetResumeMode() const { user_action_required); } +bool DownloadItemImpl::HasStrongValidators() const { + return !etag_.empty() || !last_modified_time_.empty(); +} + void DownloadItemImpl::UpdateValidatorsOnResumption( const DownloadCreateInfo& new_create_info) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -1167,10 +1183,24 @@ void DownloadItemImpl::UpdateValidatorsOnResumption( // a full request rather than a partial. Full restarts clobber validators. if (etag_ != new_create_info.etag || last_modified_time_ != new_create_info.last_modified) { + if (destination_info_.received_bytes > 0) { + RecordResumptionRestartCount( + ResumptionRestartCountTypes::kStrongValidatorChangesCount); + } received_slices_.clear(); destination_info_.received_bytes = 0; } + if (destination_info_.received_bytes > 0 && new_create_info.offset == 0) { + if (!base::FeatureList::IsEnabled( + features::kAllowDownloadResumptionWithoutStrongValidators) || + GetDownloadValidationLengthConfig() > + destination_info_.received_bytes) { + RecordResumptionRestartCount( + ResumptionRestartCountTypes::kRequestedByServerCount); + } + } + request_info_.url_chain.insert(request_info_.url_chain.end(), chain_iter, new_create_info.url_chain.end()); etag_ = new_create_info.etag; @@ -1398,7 +1428,8 @@ void DownloadItemImpl::Start( download_file_ = std::move(file); job_ = DownloadJobFactory::CreateJob( this, std::move(req_handle), new_create_info, false, - std::move(url_loader_factory_getter), url_request_context_getter); + std::move(url_loader_factory_getter), url_request_context_getter, + delegate_ ? delegate_->GetServiceManagerConnector() : nullptr); if (job_->IsParallelizable()) { RecordParallelizableDownloadCount(START_COUNT, IsParallelDownloadEnabled()); } @@ -1644,9 +1675,9 @@ void DownloadItemImpl::OnDownloadRenamedToIntermediateName( #if defined(OS_ANDROID) // For content URIs, target file path is the same as the current path. if (full_path.IsContentUri()) { - if (display_name_.empty()) - SetDisplayName(GetTargetFilePath().BaseName()); destination_info_.target_path = full_path; + if (display_name_.empty()) + SetDisplayName(download_file_->GetDisplayName()); } #endif // defined(OS_ANDROID) } else { @@ -1833,7 +1864,8 @@ void DownloadItemImpl::Completed() { bool is_parallelizable = job_ && job_->IsParallelizable(); RecordDownloadCompleted(GetReceivedBytes(), is_parallelizable, - download_source_); + download_source_, has_resumed_, + HasStrongValidators()); if (!delegate_->IsOffTheRecord()) { RecordDownloadCountWithSource(COMPLETED_COUNT_NORMAL_PROFILE, download_source_); @@ -2312,6 +2344,13 @@ void DownloadItemImpl::ResumeInterruptedDownload( mode == ResumeMode::USER_RESTART) { LOG_IF(ERROR, !GetFullPath().empty()) << "Download full path should be empty before resumption"; + if (destination_info_.received_bytes > 0) { + if (!HasStrongValidators()) { + RecordResumptionRestartCount( + ResumptionRestartCountTypes::kMissingStrongValidatorsCount); + } + RecordResumptionRestartReason(last_reason_); + } destination_info_.received_bytes = 0; last_modified_time_.clear(); etag_.clear(); @@ -2363,6 +2402,17 @@ void DownloadItemImpl::ResumeInterruptedDownload( download_params->set_hash_of_partial_file(GetHash()); download_params->set_hash_state(std::move(hash_state_)); download_params->set_guid(guid_); + if (!HasStrongValidators() && download_params->offset() > 0 && + base::FeatureList::IsEnabled( + features::kAllowDownloadResumptionWithoutStrongValidators)) { + download_params->set_use_if_range(false); + download_params->set_file_offset(download_params->offset()); + int64_t validation_length = GetDownloadValidationLengthConfig(); + download_params->set_offset(download_params->offset() > validation_length + ? download_params->offset() - + validation_length + : 0); + } // TODO(xingliu): Read |fetch_error_body| and |request_headers_| from the // cache, and don't copy them into DownloadItemImpl. @@ -2391,10 +2441,12 @@ void DownloadItemImpl::ResumeInterruptedDownload( DownloadUkmHelper::RecordDownloadResumed(ukm_download_id_, GetResumeMode(), time_since_start); + RecordDownloadResumed(HasStrongValidators()); delegate_->ResumeInterruptedDownload(std::move(download_params), request_info_.site_url); + has_resumed_ = true; if (job_) job_->Resume(false); } diff --git a/chromium/components/download/internal/common/download_item_impl_delegate.cc b/chromium/components/download/internal/common/download_item_impl_delegate.cc index cc3fb6af5dd..f5dff20abdc 100644 --- a/chromium/components/download/internal/common/download_item_impl_delegate.cc +++ b/chromium/components/download/internal/common/download_item_impl_delegate.cc @@ -103,4 +103,9 @@ bool DownloadItemImplDelegate::IsActiveNetworkMetered() const { void DownloadItemImplDelegate::ReportBytesWasted(DownloadItemImpl* download) {} +service_manager::Connector* +DownloadItemImplDelegate::GetServiceManagerConnector() { + return nullptr; +} + } // namespace download diff --git a/chromium/components/download/internal/common/download_item_impl_unittest.cc b/chromium/components/download/internal/common/download_item_impl_unittest.cc index 02488f1ed77..f202fda37a0 100644 --- a/chromium/components/download/internal/common/download_item_impl_unittest.cc +++ b/chromium/components/download/internal/common/download_item_impl_unittest.cc @@ -14,7 +14,6 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/callback_helpers.h" #include "base/containers/circular_deque.h" #include "base/containers/queue.h" #include "base/files/file_util.h" @@ -242,7 +241,7 @@ class DownloadItemTest : public testing::Test { DownloadItemTest() : task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::UI, - base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED), + base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::QUEUED), next_download_id_(DownloadItem::kInvalidId + 1) { create_info_.reset(new DownloadCreateInfo()); create_info_->save_info = @@ -849,10 +848,10 @@ TEST_F(DownloadItemTest, AutomaticResumption_AttemptLimit) { EXPECT_CALL(*mock_download_file_ref, RenameAndUniquify(_, _)).Times(i == 0); ASSERT_FALSE(callback.is_null()); - base::ResetAndReturn(&callback).Run( - target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, - DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path, - DOWNLOAD_INTERRUPT_REASON_NONE); + std::move(callback).Run(target_path, + DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + intermediate_path, DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); // Use a continuable interrupt. diff --git a/chromium/components/download/internal/common/download_job_factory.cc b/chromium/components/download/internal/common/download_job_factory.cc index f3cea3aef72..0c351882c8f 100644 --- a/chromium/components/download/internal/common/download_job_factory.cc +++ b/chromium/components/download/internal/common/download_job_factory.cc @@ -98,7 +98,8 @@ std::unique_ptr<DownloadJob> DownloadJobFactory::CreateJob( bool is_save_package_download, scoped_refptr<download::DownloadURLLoaderFactoryGetter> url_loader_factory_getter, - net::URLRequestContextGetter* url_request_context_getter) { + net::URLRequestContextGetter* url_request_context_getter, + service_manager::Connector* connector) { if (is_save_package_download) { return std::make_unique<SavePackageDownloadJob>(download_item, std::move(req_handle)); @@ -109,7 +110,8 @@ std::unique_ptr<DownloadJob> DownloadJobFactory::CreateJob( if (IsParallelDownloadEnabled() && is_parallelizable) { return std::make_unique<ParallelDownloadJob>( download_item, std::move(req_handle), create_info, - std::move(url_loader_factory_getter), url_request_context_getter); + std::move(url_loader_factory_getter), url_request_context_getter, + connector); } // An ordinary download job. diff --git a/chromium/components/download/internal/common/download_path_reservation_tracker.cc b/chromium/components/download/internal/common/download_path_reservation_tracker.cc index 5c02e27b657..8a4004127f7 100644 --- a/chromium/components/download/internal/common/download_path_reservation_tracker.cc +++ b/chromium/components/download/internal/common/download_path_reservation_tracker.cc @@ -26,6 +26,7 @@ #include "base/third_party/icu/icu_utf.h" #include "base/time/time.h" #include "build/build_config.h" +#include "components/download/public/common/download_features.h" #include "components/download/public/common/download_item.h" #include "components/filename_generation/filename_generation.h" #include "net/base/filename_util.h" @@ -57,10 +58,6 @@ const size_t kZoneIdentifierLength = sizeof(":Zone.Identifier") - 1; // Map of download path reservations. Each reserved path is associated with a // ReservationKey=DownloadItem*. This object is destroyed in |Revoke()| when // there are no more reservations. -// -// It is not an error, although undesirable, to have multiple DownloadItem*s -// that are mapped to the same path. This can happen if a reservation is created -// that is supposed to overwrite an existing reservation. ReservationMap* g_reservation_map = NULL; base::LazySequencedTaskRunner g_sequenced_task_runner = @@ -122,6 +119,21 @@ bool IsFileNameInUse(const base::FilePath& path) { return false; } +// Returns true if the given path is in use by a path reservation, +// and has a different key then the item arg. +bool IsAdditionalPathReserved(const base::FilePath& path, DownloadItem* item) { + if (!g_reservation_map) + return false; + + for (ReservationMap::const_iterator iter = g_reservation_map->begin(); + iter != g_reservation_map->end(); ++iter) { + if (iter->first != item && base::FilePath::CompareEqualIgnoreCase( + iter->second.value(), path.value())) + return true; + } + return false; +} + // Returns true if the given path is in use by any path reservation or the // file system. Called on the task runner returned by // DownloadPathReservationTracker::GetTaskRunner(). @@ -238,6 +250,11 @@ PathValidationResult ResolveReservationConflicts( : PathValidationResult::CONFLICT; case DownloadPathReservationTracker::OVERWRITE: + if (base::FeatureList::IsEnabled( + features::kPreventDownloadsWithSamePath) && + IsPathReserved(*target_path)) { + return PathValidationResult::CONFLICT; + } return PathValidationResult::SUCCESS; case DownloadPathReservationTracker::PROMPT: @@ -520,4 +537,11 @@ DownloadPathReservationTracker::GetTaskRunner() { return g_sequenced_task_runner.Get(); } +// static +bool DownloadPathReservationTracker::CheckDownloadPathForExistingDownload( + const base::FilePath& target_path, + DownloadItem* download_item) { + return IsAdditionalPathReserved(target_path, download_item); +} + } // namespace download diff --git a/chromium/components/download/internal/common/download_path_reservation_tracker_unittest.cc b/chromium/components/download/internal/common/download_path_reservation_tracker_unittest.cc index acdffd42f45..1b6ccde7434 100644 --- a/chromium/components/download/internal/common/download_path_reservation_tracker_unittest.cc +++ b/chromium/components/download/internal/common/download_path_reservation_tracker_unittest.cc @@ -15,10 +15,12 @@ #include "base/observer_list.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" +#include "base/test/scoped_feature_list.h" #include "base/test/scoped_task_environment.h" #include "base/test/test_file_util.h" #include "base/time/time.h" #include "build/build_config.h" +#include "components/download/public/common/download_features.h" #include "components/download/public/common/download_path_reservation_tracker.h" #include "components/download/public/common/mock_download_item.h" #include "net/base/filename_util.h" @@ -352,6 +354,12 @@ TEST_F(DownloadPathReservationTrackerTest, ConflictWithSource) { // Multiple reservations for the same path should uniquify around each other. TEST_F(DownloadPathReservationTrackerTest, ConflictingReservations) { + // When kPreventDownloadsWithSamePath is disabled, + // an OVERWRITE reservation with a conflicting path should return success + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndDisableFeature( + features::kPreventDownloadsWithSamePath); + std::unique_ptr<MockDownloadItem> item1 = CreateDownloadItem(1); base::FilePath path( GetPathInDownloadsDirectory(FILE_PATH_LITERAL("foo.txt"))); @@ -411,6 +419,8 @@ TEST_F(DownloadPathReservationTrackerTest, ConflictingReservations) { EXPECT_TRUE(IsPathInUse(path)); EXPECT_FALSE(IsPathInUse(uniquified_path)); + EXPECT_EQ(PathValidationResult::SUCCESS, result); + EXPECT_EQ(path.value(), reserved_path1.value()); EXPECT_EQ(path.value(), reserved_path3.value()); @@ -418,6 +428,42 @@ TEST_F(DownloadPathReservationTrackerTest, ConflictingReservations) { SetDownloadItemState(item3.get(), DownloadItem::COMPLETE); } +// When kPreventDownloadsWithSamePath flag is enabled, an OVERWRITE reservation +// with the same path as an active reservation should return a CONFLICT result. +TEST_F(DownloadPathReservationTrackerTest, ConflictingReservation_Prevented) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kPreventDownloadsWithSamePath); + + std::unique_ptr<MockDownloadItem> item1 = CreateDownloadItem(1); + base::FilePath path( + GetPathInDownloadsDirectory(FILE_PATH_LITERAL("foo.txt"))); + ASSERT_FALSE(IsPathInUse(path)); + + base::FilePath reserved_path; + PathValidationResult result = PathValidationResult::NAME_TOO_LONG; + DownloadPathReservationTracker::FilenameConflictAction conflict_action = + DownloadPathReservationTracker::OVERWRITE; + bool create_directory = false; + CallGetReservedPath(item1.get(), path, create_directory, conflict_action, + &reserved_path, &result); + EXPECT_TRUE(IsPathInUse(path)); + EXPECT_EQ(PathValidationResult::SUCCESS, result); + EXPECT_EQ(path.value(), reserved_path.value()); + + std::unique_ptr<MockDownloadItem> item2 = CreateDownloadItem(2); + base::FilePath reserved_path2; + CallGetReservedPath(item2.get(), path, create_directory, conflict_action, + &reserved_path2, &result); + + EXPECT_TRUE(IsPathInUse(path)); + EXPECT_EQ(PathValidationResult::CONFLICT, result); + EXPECT_EQ(path.value(), reserved_path2.value()); + + SetDownloadItemState(item1.get(), DownloadItem::COMPLETE); + SetDownloadItemState(item2.get(), DownloadItem::COMPLETE); +} + // Two active downloads shouldn't be able to reserve paths that only differ by // case. TEST_F(DownloadPathReservationTrackerTest, ConflictingCaseReservations) { diff --git a/chromium/components/download/internal/common/download_response_handler.cc b/chromium/components/download/internal/common/download_response_handler.cc index 7ab6994b362..d203a3cf163 100644 --- a/chromium/components/download/internal/common/download_response_handler.cc +++ b/chromium/components/download/internal/common/download_response_handler.cc @@ -6,6 +6,7 @@ #include <memory> +#include "base/metrics/histogram_functions.h" #include "components/download/public/common/download_stats.h" #include "components/download/public/common/download_url_parameters.h" #include "components/download/public/common/download_utils.h" @@ -80,7 +81,7 @@ DownloadResponseHandler::DownloadResponseHandler( RecordDownloadCountWithSource(UNTHROTTLED_COUNT, download_source); } if (resource_request->request_initiator.has_value()) - origin_ = resource_request->request_initiator.value().GetURL(); + request_initiator_ = resource_request->request_initiator; } DownloadResponseHandler::~DownloadResponseHandler() = default; @@ -103,10 +104,12 @@ void DownloadResponseHandler::OnReceiveResponse( // suggested name for the security origin of the downlaod URL. However, this // assumption doesn't hold if there were cross origin redirects. Therefore, // clear the suggested_name for such requests. - if (origin_.is_valid() && !create_info_->url_chain.back().SchemeIsBlob() && + if (request_initiator_.has_value() && + !create_info_->url_chain.back().SchemeIsBlob() && !create_info_->url_chain.back().SchemeIs(url::kAboutScheme) && !create_info_->url_chain.back().SchemeIs(url::kDataScheme) && - origin_ != create_info_->url_chain.back().GetOrigin()) { + request_initiator_.value() != + url::Origin::Create(create_info_->url_chain.back())) { create_info_->save_info->suggested_name.clear(); } @@ -143,6 +146,7 @@ DownloadResponseHandler::CreateDownloadCreateInfo( create_info->request_headers = request_headers_; create_info->request_origin = request_origin_; create_info->download_source = download_source_; + create_info->request_initiator = request_initiator_; HandleResponseHeaders(head.headers.get(), create_info.get()); return create_info; @@ -194,7 +198,7 @@ void DownloadResponseHandler::OnUploadProgress( } void DownloadResponseHandler::OnReceiveCachedMetadata( - const std::vector<uint8_t>& data) {} + mojo_base::BigBuffer data) {} void DownloadResponseHandler::OnTransferSizeUpdated( int32_t transfer_size_diff) {} @@ -226,6 +230,11 @@ void DownloadResponseHandler::OnComplete( ConvertInterruptReasonToMojoNetworkRequestStatus(reason)); } + if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { + base::UmaHistogramSparse("Download.MapErrorNetworkFailed.NetworkService", + std::abs(status.error_code)); + } + if (started_) { delegate_->OnResponseCompleted(); return; diff --git a/chromium/components/download/internal/common/download_stats.cc b/chromium/components/download/internal/common/download_stats.cc index b7367a029ad..a46e7db8f5c 100644 --- a/chromium/components/download/internal/common/download_stats.cc +++ b/chromium/components/download/internal/common/download_stats.cc @@ -12,6 +12,7 @@ #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/string_util.h" +#include "build/build_config.h" #include "components/download/public/common/download_interrupt_reasons.h" #include "net/http/http_content_disposition.h" #include "net/http/http_util.h" @@ -447,6 +448,29 @@ constexpr const base::FilePath::CharType* kDangerousFileTypes[] = { FILE_PATH_LITERAL(".configprofile"), // 362 FILE_PATH_LITERAL(".internetconnect"), // 363 FILE_PATH_LITERAL(".networkconnect"), // 364 + FILE_PATH_LITERAL(".bmp"), // 365 + FILE_PATH_LITERAL(".css"), // 366 + FILE_PATH_LITERAL(".ehtml"), // 367 + FILE_PATH_LITERAL(".flac"), // 368 + FILE_PATH_LITERAL(".ico"), // 369 + FILE_PATH_LITERAL(".jfif"), // 370 + FILE_PATH_LITERAL(".m4a"), // 371 + FILE_PATH_LITERAL(".m4v"), // 372 + FILE_PATH_LITERAL(".mpeg"), // 373 + FILE_PATH_LITERAL(".mpg"), // 374 + FILE_PATH_LITERAL(".oga"), // 375 + FILE_PATH_LITERAL(".ogg"), // 376 + FILE_PATH_LITERAL(".ogm"), // 377 + FILE_PATH_LITERAL(".ogv"), // 378 + FILE_PATH_LITERAL(".opus"), // 379 + FILE_PATH_LITERAL(".pjp"), // 380 + FILE_PATH_LITERAL(".pjpeg"), // 381 + FILE_PATH_LITERAL(".svgz"), // 382 + FILE_PATH_LITERAL(".text"), // 383 + FILE_PATH_LITERAL(".tiff"), // 384 + FILE_PATH_LITERAL(".weba"), // 385 + FILE_PATH_LITERAL(".webm"), // 386 + FILE_PATH_LITERAL(".xbm"), // 387 // NOTE! When you add a type here, please add the UMA value as a comment. // These must all match DownloadItem.DangerousFileType in // enums.xml. From 263 onward, they should also match @@ -543,7 +567,9 @@ void RecordDownloadCountWithSource(DownloadCountTypes type, void RecordDownloadCompleted(int64_t download_len, bool is_parallelizable, - DownloadSource download_source) { + DownloadSource download_source, + bool has_resumed, + bool has_strong_validators) { RecordDownloadCountWithSource(COMPLETED_COUNT, download_source); int64_t max = 1024 * 1024 * 1024; // One Terabyte. download_len /= 1024; // In Kilobytes @@ -553,6 +579,11 @@ void RecordDownloadCompleted(int64_t download_len, UMA_HISTOGRAM_CUSTOM_COUNTS("Download.DownloadSize.Parallelizable", download_len, 1, max, 256); } + + if (has_resumed) { + base::UmaHistogramBoolean("Download.ResumptionComplete.HasStrongValidators", + has_strong_validators); + } } void RecordDownloadDeletion(base::Time completion_time, @@ -1223,4 +1254,29 @@ void RecordDuplicateInProgressDownloadIdCount(int count) { count, 1, 10, 11); } +void RecordResumptionRestartReason(DownloadInterruptReason reason) { + base::UmaHistogramSparse("Download.ResumptionRestart.Reason", reason); +} + +void RecordResumptionRestartCount(ResumptionRestartCountTypes type) { + base::UmaHistogramEnumeration("Download.ResumptionRestart.Counts", type); +} + +void RecordDownloadResumed(bool has_strong_validators) { + base::UmaHistogramBoolean("Download.ResumptionStart.HasStrongValidators", + has_strong_validators); +} + +#if defined(OS_ANDROID) +void RecordFirstBackgroundDownloadInterruptReason( + DownloadInterruptReason reason, + bool download_started) { + if (download_started) + base::UmaHistogramSparse("MobileDownload.FirstBackground.StartedReason", + reason); + else + base::UmaHistogramSparse("MobileDownload.FirstBackground.Reason", reason); +} +#endif // defined(OS_ANDROID) + } // namespace download diff --git a/chromium/components/download/internal/common/download_utils.cc b/chromium/components/download/internal/common/download_utils.cc index 5478f941f2b..43c571344a1 100644 --- a/chromium/components/download/internal/common/download_utils.cc +++ b/chromium/components/download/internal/common/download_utils.cc @@ -7,11 +7,14 @@ #include "base/files/file_util.h" #include "base/format_macros.h" #include "base/i18n/file_util_icu.h" +#include "base/metrics/field_trial_params.h" #include "base/rand_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "components/download/public/common/download_create_info.h" +#include "components/download/public/common/download_features.h" #include "components/download/public/common/download_interrupt_reasons_utils.h" #include "components/download/public/common/download_save_info.h" #include "components/download/public/common/download_stats.h" @@ -29,6 +32,14 @@ namespace download { namespace { +// Default value for |kDownloadContentValidationLengthFinchKey|, when no +// parameter is specified. +const int64_t kDefaultContentValidationLength = 1024; + +// If the file_offset value from SaveInfo is equal to this, no content +// validation will be performed and download stream will be written to +// file starting at the offset from the response. +const int64_t kInvalidFileWriteOffset = -1; void AppendExtraHeaders(net::HttpRequestHeaders* headers, DownloadUrlParameters* params) { @@ -162,6 +173,7 @@ DownloadInterruptReason HandleSuccessfulServerResponse( // The response can be HTTP 200 or other error code when // |fetch_error_body| is true. save_info->offset = 0; + save_info->file_offset = kInvalidFileWriteOffset; save_info->hash_of_partial_file.clear(); save_info->hash_state.reset(); return DOWNLOAD_INTERRUPT_REASON_NONE; @@ -312,9 +324,13 @@ std::unique_ptr<net::HttpRequestHeaders> GetAdditionalRequestHeaders( bool has_etag = !params->etag().empty(); // Strong validator(i.e. etag or last modified) is required in range requests - // for download resumption and parallel download. - DCHECK(has_etag || has_last_modified); - if (!has_etag && !has_last_modified) { + // for download resumption and parallel download, unless + // |kAllowDownloadResumptionWithoutStrongValidators| is enabled. + bool allow_resumption = + has_etag || has_last_modified || + base::FeatureList::IsEnabled( + features::kAllowDownloadResumptionWithoutStrongValidators); + if (!allow_resumption) { DVLOG(1) << "Creating partial request without strong validators."; AppendExtraHeaders(headers.get(), params); return headers; @@ -590,4 +606,14 @@ download::DownloadItem::DownloadRenameResult RenameDownloadedFile( : download::DownloadItem::DownloadRenameResult:: FAILURE_NAME_INVALID; } + +int64_t GetDownloadValidationLengthConfig() { + std::string finch_value = base::GetFieldTrialParamValueByFeature( + features::kAllowDownloadResumptionWithoutStrongValidators, + kDownloadContentValidationLengthFinchKey); + int64_t result; + return base::StringToInt64(finch_value, &result) + ? result + : kDefaultContentValidationLength; +} } // namespace download diff --git a/chromium/components/download/internal/common/download_worker.cc b/chromium/components/download/internal/common/download_worker.cc index 05f82616ecd..ad5bec88089 100644 --- a/chromium/components/download/internal/common/download_worker.cc +++ b/chromium/components/download/internal/common/download_worker.cc @@ -16,6 +16,7 @@ #include "net/url_request/url_request_context_getter.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/service_manager/public/cpp/connector.h" namespace download { namespace { @@ -55,10 +56,12 @@ void CreateUrlDownloadHandler( url_loader_factory_getter, const URLSecurityPolicy& url_security_policy, scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { auto downloader = UrlDownloadHandlerFactory::Create( std::move(params), delegate, std::move(url_loader_factory_getter), - url_security_policy, std::move(url_request_context_getter), task_runner); + url_security_policy, std::move(url_request_context_getter), + std::move(connector), task_runner); task_runner->PostTask( FROM_HERE, base::BindOnce(&UrlDownloadHandler::Delegate::OnUrlDownloadHandlerCreated, @@ -87,13 +90,15 @@ void DownloadWorker::SendRequest( std::unique_ptr<DownloadUrlParameters> params, scoped_refptr<download::DownloadURLLoaderFactoryGetter> url_loader_factory_getter, - scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) { + scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, + service_manager::Connector* connector) { GetIOTaskRunner()->PostTask( FROM_HERE, base::BindOnce(&CreateUrlDownloadHandler, std::move(params), weak_factory_.GetWeakPtr(), std::move(url_loader_factory_getter), base::BindRepeating(&IsURLSafe), std::move(url_request_context_getter), + connector ? connector->Clone() : nullptr, base::ThreadTaskRunnerHandle::Get())); } diff --git a/chromium/components/download/internal/common/download_worker.h b/chromium/components/download/internal/common/download_worker.h index ec8147106bb..6021c0603af 100644 --- a/chromium/components/download/internal/common/download_worker.h +++ b/chromium/components/download/internal/common/download_worker.h @@ -17,7 +17,11 @@ namespace net { class URLRequestContextGetter; -} +} // namespace net + +namespace service_manager { +class Connector; +} // namespace service_manager namespace download { class DownloadURLLoaderFactoryGetter; @@ -53,7 +57,8 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadWorker std::unique_ptr<DownloadUrlParameters> params, scoped_refptr<download::DownloadURLLoaderFactoryGetter> url_loader_factory_getter, - scoped_refptr<net::URLRequestContextGetter> url_request_context_getter); + scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, + service_manager::Connector* connector); // Download operations. void Pause(); diff --git a/chromium/components/download/internal/common/in_progress_download_manager.cc b/chromium/components/download/internal/common/in_progress_download_manager.cc index 2b3b61ce504..1c05b2fc4ce 100644 --- a/chromium/components/download/internal/common/in_progress_download_manager.cc +++ b/chromium/components/download/internal/common/in_progress_download_manager.cc @@ -27,6 +27,7 @@ #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/resource_response.h" +#include "services/service_manager/public/cpp/connector.h" #if defined(OS_ANDROID) #include "components/download/internal/common/android/download_collection_bridge.h" @@ -55,14 +56,15 @@ std::unique_ptr<DownloadItemImpl> CreateDownloadItemImpl( in_progress_info->current_path, in_progress_info->target_path, in_progress_info->url_chain, in_progress_info->referrer_url, in_progress_info->site_url, in_progress_info->tab_url, - in_progress_info->tab_referrer_url, in_progress_info->mime_type, - in_progress_info->original_mime_type, in_progress_info->start_time, - in_progress_info->end_time, in_progress_info->etag, - in_progress_info->last_modified, in_progress_info->received_bytes, - in_progress_info->total_bytes, in_progress_info->auto_resume_count, - in_progress_info->hash, in_progress_info->state, - in_progress_info->danger_type, in_progress_info->interrupt_reason, - in_progress_info->paused, in_progress_info->metered, false, base::Time(), + in_progress_info->tab_referrer_url, base::nullopt, + in_progress_info->mime_type, in_progress_info->original_mime_type, + in_progress_info->start_time, in_progress_info->end_time, + in_progress_info->etag, in_progress_info->last_modified, + in_progress_info->received_bytes, in_progress_info->total_bytes, + in_progress_info->auto_resume_count, in_progress_info->hash, + in_progress_info->state, in_progress_info->danger_type, + in_progress_info->interrupt_reason, in_progress_info->paused, + in_progress_info->metered, false, base::Time(), in_progress_info->transient, in_progress_info->received_slices); } @@ -86,13 +88,15 @@ void BeginResourceDownload( const GURL& site_url, const GURL& tab_url, const GURL& tab_referrer_url, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner) { DCHECK(GetIOTaskRunner()->BelongsToCurrentThread()); UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader( ResourceDownloader::BeginDownload( download_manager, std::move(params), std::move(request), std::move(url_loader_factory_getter), url_security_policy, site_url, - tab_url, tab_referrer_url, is_new_download, false, main_task_runner) + tab_url, tab_referrer_url, is_new_download, false, + std::move(connector), main_task_runner) .release(), base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get())); @@ -114,6 +118,7 @@ void CreateDownloadHandlerForNavigation( network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, scoped_refptr<DownloadURLLoaderFactoryGetter> url_loader_factory_getter, const URLSecurityPolicy& url_security_policy, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner) { DCHECK(GetIOTaskRunner()->BelongsToCurrentThread()); UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader( @@ -123,7 +128,7 @@ void CreateDownloadHandlerForNavigation( std::move(url_chain), std::move(response), std::move(cert_status), std::move(url_loader_client_endpoints), std::move(url_loader_factory_getter), url_security_policy, - main_task_runner) + std::move(connector), main_task_runner) .release(), base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get())); @@ -143,17 +148,35 @@ void OnDownloadDisplayNamesReturned( #endif } // namespace +bool InProgressDownloadManager::Delegate::InterceptDownload( + const DownloadCreateInfo& download_create_info) { + return false; +} + +base::FilePath +InProgressDownloadManager::Delegate::GetDefaultDownloadDirectory() { + return base::FilePath(); +} + +net::URLRequestContextGetter* +InProgressDownloadManager::Delegate::GetURLRequestContextGetter( + const DownloadCreateInfo& download_create_info) { + return nullptr; +} + InProgressDownloadManager::InProgressDownloadManager( Delegate* delegate, const base::FilePath& in_progress_db_dir, const IsOriginSecureCallback& is_origin_secure_cb, - const URLSecurityPolicy& url_security_policy) + const URLSecurityPolicy& url_security_policy, + service_manager::Connector* connector) : delegate_(delegate), file_factory_(new DownloadFileFactory()), download_start_observer_(nullptr), is_origin_secure_cb_(is_origin_secure_cb), url_security_policy_(url_security_policy), use_empty_db_(in_progress_db_dir.empty()), + connector_(connector), weak_factory_(this) { Initialize(in_progress_db_dir); } @@ -186,10 +209,21 @@ void InProgressDownloadManager::OnUrlDownloadHandlerCreated( url_download_handlers_.push_back(std::move(downloader)); } -bool InProgressDownloadManager::DownloadUrl( +void InProgressDownloadManager::DownloadUrl( std::unique_ptr<DownloadUrlParameters> params) { - DCHECK(!delegate_); - DCHECK(params->is_transient()); + if (!CanDownload(params.get())) + return; + + // Start the new download, the download should be saved to the file path + // specifcied in the |params|. + BeginDownload(std::move(params), url_loader_factory_getter_, + true /* is_new_download */, GURL() /* site_url */, + GURL() /* tab_url */, GURL() /* tab_referral_url */); +} + +bool InProgressDownloadManager::CanDownload(DownloadUrlParameters* params) { + if (!params->is_transient()) + return false; if (!url_loader_factory_getter_) return false; @@ -200,11 +234,6 @@ bool InProgressDownloadManager::DownloadUrl( if (params->file_path().empty()) return false; - // Start the new download, the download should be saved to the file path - // specifcied in the |params|. - BeginDownload(std::move(params), url_loader_factory_getter_, - true /* is_new_download */, GURL() /* site_url */, - GURL() /* tab_url */, GURL() /* tab_referral_url */); return true; } @@ -234,11 +263,12 @@ void InProgressDownloadManager::BeginDownload( CreateResourceRequest(params.get()); GetIOTaskRunner()->PostTask( FROM_HERE, - base::BindOnce(&BeginResourceDownload, std::move(params), - std::move(request), std::move(url_loader_factory_getter), - url_security_policy_, is_new_download, - weak_factory_.GetWeakPtr(), site_url, tab_url, - tab_referrer_url, base::ThreadTaskRunnerHandle::Get())); + base::BindOnce( + &BeginResourceDownload, std::move(params), std::move(request), + std::move(url_loader_factory_getter), url_security_policy_, + is_new_download, weak_factory_.GetWeakPtr(), site_url, tab_url, + tab_referrer_url, connector_ ? connector_->Clone() : nullptr, + base::ThreadTaskRunnerHandle::Get())); } void InProgressDownloadManager::InterceptDownloadFromNavigation( @@ -262,6 +292,7 @@ void InProgressDownloadManager::InterceptDownloadFromNavigation( std::move(response), std::move(cert_status), std::move(url_loader_client_endpoints), std::move(url_loader_factory_getter), url_security_policy_, + connector_ ? connector_->Clone() : nullptr, base::ThreadTaskRunnerHandle::Get())); } @@ -284,17 +315,26 @@ void InProgressDownloadManager::ShutDown() { void InProgressDownloadManager::DetermineDownloadTarget( DownloadItemImpl* download, const DownloadTargetCallback& callback) { - // TODO(http://crbug.com/851581): handle the case that |target_path| and - // |intermediate_path| are empty. base::FilePath target_path = download->GetTargetFilePath().empty() ? download->GetForcedFilePath() : download->GetTargetFilePath(); base::FilePath intermediate_path = download->GetFullPath().empty() ? download->GetForcedFilePath() : download->GetFullPath(); +#if defined(OS_ANDROID) + // If final target is a content URI, the intermediate path should + // be identical to it. + if (target_path.IsContentUri()) + intermediate_path = target_path; +#endif + if (!target_path.empty() && intermediate_path.empty()) { + if (intermediate_path_cb_) + intermediate_path = intermediate_path_cb_.Run(target_path); + } callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, download->GetDangerType(), intermediate_path, - DOWNLOAD_INTERRUPT_REASON_NONE); + intermediate_path.empty() ? DOWNLOAD_INTERRUPT_REASON_FILE_FAILED + : DOWNLOAD_INTERRUPT_REASON_NONE); } void InProgressDownloadManager::ResumeInterruptedDownload( @@ -366,7 +406,9 @@ void InProgressDownloadManager::StartDownload( info->mime_type, is_origin_secure_cb_); } - if (delegate_) { + // If the download cannot be found locally, ask |delegate_| to provide the + // DownloadItem. + if (delegate_ && !GetDownloadByGuid(info->guid)) { delegate_->StartDownloadItem( std::move(info), on_started, base::BindOnce(&InProgressDownloadManager::StartDownloadWithItem, @@ -511,6 +553,7 @@ void InProgressDownloadManager::OnDownloadNamesRetrieved( if (num_duplicates > 0) RecordDuplicateInProgressDownloadIdCount(num_duplicates); OnInitialized(); + OnDownloadsInitialized(); } std::vector<std::unique_ptr<download::DownloadItemImpl>> @@ -535,6 +578,26 @@ void InProgressDownloadManager::OnAllInprogressDownloadsLoaded() { display_names_.reset(); } +void InProgressDownloadManager::SetDelegate(Delegate* delegate) { + delegate_ = delegate; + if (initialized_) + OnDownloadsInitialized(); +} + +void InProgressDownloadManager::OnDownloadsInitialized() { + if (delegate_) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&InProgressDownloadManager::NotifyDownloadsInitialized, + weak_factory_.GetWeakPtr())); + } +} + +void InProgressDownloadManager::NotifyDownloadsInitialized() { + if (delegate_) + delegate_->OnDownloadsInitialized(); +} + void InProgressDownloadManager::AddInProgressDownloadForTest( std::unique_ptr<download::DownloadItemImpl> download) { in_progress_downloads_.push_back(std::move(download)); diff --git a/chromium/components/download/internal/common/parallel_download_job.cc b/chromium/components/download/internal/common/parallel_download_job.cc index 376700af397..1ac7dbbb753 100644 --- a/chromium/components/download/internal/common/parallel_download_job.cc +++ b/chromium/components/download/internal/common/parallel_download_job.cc @@ -27,7 +27,8 @@ ParallelDownloadJob::ParallelDownloadJob( const DownloadCreateInfo& create_info, scoped_refptr<download::DownloadURLLoaderFactoryGetter> url_loader_factory_getter, - net::URLRequestContextGetter* url_request_context_getter) + net::URLRequestContextGetter* url_request_context_getter, + service_manager::Connector* connector) : DownloadJobImpl(download_item, std::move(request_handle), true), initial_request_offset_(create_info.offset), initial_received_slices_(download_item->GetReceivedSlices()), @@ -35,7 +36,8 @@ ParallelDownloadJob::ParallelDownloadJob( requests_sent_(false), is_canceled_(false), url_loader_factory_getter_(std::move(url_loader_factory_getter)), - url_request_context_getter_(url_request_context_getter) {} + url_request_context_getter_(url_request_context_getter), + connector_(connector) {} ParallelDownloadJob::~ParallelDownloadJob() = default; @@ -298,7 +300,7 @@ void ParallelDownloadJob::CreateRequest(int64_t offset, int64_t length) { // Send the request. worker->SendRequest(std::move(download_params), url_loader_factory_getter_, - url_request_context_getter_); + url_request_context_getter_, connector_); DCHECK(workers_.find(offset) == workers_.end()); workers_[offset] = std::move(worker); } diff --git a/chromium/components/download/internal/common/parallel_download_job.h b/chromium/components/download/internal/common/parallel_download_job.h index 1c791294b61..71c45c861f6 100644 --- a/chromium/components/download/internal/common/parallel_download_job.h +++ b/chromium/components/download/internal/common/parallel_download_job.h @@ -19,7 +19,11 @@ namespace net { class URLRequestContextGetter; -} +} // namespace net + +namespace service_manager { +class Connector; +} // namespace service_manager namespace download { @@ -38,7 +42,8 @@ class COMPONENTS_DOWNLOAD_EXPORT ParallelDownloadJob const DownloadCreateInfo& create_info, scoped_refptr<download::DownloadURLLoaderFactoryGetter> url_loader_factory_getter, - net::URLRequestContextGetter* url_request_context_getter); + net::URLRequestContextGetter* url_request_context_getter, + service_manager::Connector* connector); ~ParallelDownloadJob() override; // DownloadJobImpl implementation. @@ -122,6 +127,9 @@ class COMPONENTS_DOWNLOAD_EXPORT ParallelDownloadJob // is disabled. scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_; + // Connector used for establishing the connection to the ServiceManager. + service_manager::Connector* connector_; + DISALLOW_COPY_AND_ASSIGN(ParallelDownloadJob); }; diff --git a/chromium/components/download/internal/common/parallel_download_job_unittest.cc b/chromium/components/download/internal/common/parallel_download_job_unittest.cc index 3a46e76c054..d227cf4232e 100644 --- a/chromium/components/download/internal/common/parallel_download_job_unittest.cc +++ b/chromium/components/download/internal/common/parallel_download_job_unittest.cc @@ -69,6 +69,7 @@ class ParallelDownloadJobForTest : public ParallelDownloadJob { std::move(request_handle), create_info, nullptr, + nullptr, nullptr), request_count_(request_count), min_slice_size_(min_slice_size), @@ -114,9 +115,9 @@ class ParallelDownloadJobForTest : public ParallelDownloadJob { class ParallelDownloadJobTest : public testing::Test { public: ParallelDownloadJobTest() - : task_environment_( - base::test::ScopedTaskEnvironment::MainThreadType::UI, - base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {} + : task_environment_(base::test::ScopedTaskEnvironment::MainThreadType::UI, + base::test::ScopedTaskEnvironment:: + ThreadPoolExecutionMode::QUEUED) {} void CreateParallelJob(int64_t initial_request_offset, int64_t content_length, diff --git a/chromium/components/download/internal/common/parallel_download_utils.cc b/chromium/components/download/internal/common/parallel_download_utils.cc index bc7266fea61..5aa3ad60325 100644 --- a/chromium/components/download/internal/common/parallel_download_utils.cc +++ b/chromium/components/download/internal/common/parallel_download_utils.cc @@ -102,7 +102,7 @@ bool CanRecoverFromError( // the error stream. if (error_stream->length() > 0) { return error_stream->offset() + error_stream->length() <= - preceding_neighbor->offset() + preceding_neighbor->bytes_written(); + preceding_neighbor->offset() + preceding_neighbor->bytes_read(); } return false; diff --git a/chromium/components/download/internal/common/parallel_download_utils_unittest.cc b/chromium/components/download/internal/common/parallel_download_utils_unittest.cc index af8aab73d76..b0c8462d916 100644 --- a/chromium/components/download/internal/common/parallel_download_utils_unittest.cc +++ b/chromium/components/download/internal/common/parallel_download_utils_unittest.cc @@ -43,7 +43,8 @@ class ParallelDownloadUtilsRecoverErrorTest EXPECT_CALL(*input_stream_, GetCompletionStatus()) .WillRepeatedly(Return(DOWNLOAD_INTERRUPT_REASON_NONE)); return std::make_unique<DownloadFileImpl::SourceStream>( - offset, length, std::unique_ptr<MockInputStream>(input_stream_)); + offset, length, offset, + std::unique_ptr<MockInputStream>(input_stream_)); } protected: @@ -166,7 +167,7 @@ TEST_P(ParallelDownloadUtilsRecoverErrorTest, EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); // Even if it has written some data. - preceding_stream->OnWriteBytesToDisk(1000u); + preceding_stream->OnBytesConsumed(1000u, 1000u); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); // Now capped the length of preceding stream with different values. @@ -177,14 +178,15 @@ TEST_P(ParallelDownloadUtilsRecoverErrorTest, preceding_stream->set_finished(false); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); preceding_stream->set_finished(true); - preceding_stream->OnWriteBytesToDisk(kErrorStreamOffset - preceding_offset); + int64_t bytes_consumed = kErrorStreamOffset - preceding_offset; + preceding_stream->OnBytesConsumed(bytes_consumed, bytes_consumed); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); // Inject an error results in failure, even if data written exceeds the first // byte of error stream. EXPECT_CALL(*input_stream_, GetCompletionStatus()) .WillRepeatedly(Return(DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE)); - preceding_stream->OnWriteBytesToDisk(1000u); + preceding_stream->OnBytesConsumed(1000u, 1000u); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); // Make preceding stream can reach the first byte of error stream. @@ -194,9 +196,9 @@ TEST_P(ParallelDownloadUtilsRecoverErrorTest, preceding_stream->set_finished(false); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); preceding_stream->set_finished(true); - preceding_stream->OnWriteBytesToDisk(kErrorStreamOffset - preceding_offset); + preceding_stream->OnBytesConsumed(bytes_consumed, bytes_consumed); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); - preceding_stream->OnWriteBytesToDisk(1); + preceding_stream->OnBytesConsumed(1, 1); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); // Preceding stream that never download data won't recover the error stream. @@ -229,11 +231,13 @@ TEST_P(ParallelDownloadUtilsRecoverErrorTest, // Since the preceding stream can never reach the starting offset, for an // unfinished stream, we rely on length instead of bytes written. EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); - preceding_stream->OnWriteBytesToDisk(kErrorStreamOffset - preceding_offset); + int64_t bytes_consumed = kErrorStreamOffset - preceding_offset; + preceding_stream->OnBytesConsumed(bytes_consumed, bytes_consumed); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); - preceding_stream->OnWriteBytesToDisk(kErrorStreamLength - 1); + preceding_stream->OnBytesConsumed(kErrorStreamLength - 1, + kErrorStreamLength - 1); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); - preceding_stream->OnWriteBytesToDisk(1); + preceding_stream->OnBytesConsumed(1, 1); // Create preceding stream that can reach the upper bound of error stream. // Since it's unfinished, it potentially can take over error stream's work @@ -248,11 +252,12 @@ TEST_P(ParallelDownloadUtilsRecoverErrorTest, // Finished preceding stream only checks data written. preceding_stream = CreateSourceStream(preceding_offset, 1); preceding_stream->set_finished(true); - preceding_stream->OnWriteBytesToDisk(kErrorStreamOffset - preceding_offset); + preceding_stream->OnBytesConsumed(bytes_consumed, bytes_consumed); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); - preceding_stream->OnWriteBytesToDisk(kErrorStreamLength - 1); + preceding_stream->OnBytesConsumed(kErrorStreamLength - 1, + kErrorStreamLength - 1); EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); - preceding_stream->OnWriteBytesToDisk(1); + preceding_stream->OnBytesConsumed(1, 1); EXPECT_TRUE(CanRecoverFromError(error_stream.get(), preceding_stream.get())); // Even if inject an error, since data written has cover the upper bound of diff --git a/chromium/components/download/internal/common/resource_downloader.cc b/chromium/components/download/internal/common/resource_downloader.cc index b5b86b05098..5ddb3408e76 100644 --- a/chromium/components/download/internal/common/resource_downloader.cc +++ b/chromium/components/download/internal/common/resource_downloader.cc @@ -10,7 +10,10 @@ #include "components/download/public/common/download_url_loader_factory_getter.h" #include "components/download/public/common/stream_handle_input_stream.h" #include "components/download/public/common/url_download_request_handle.h" +#include "services/device/public/mojom/constants.mojom.h" +#include "services/device/public/mojom/wake_lock_provider.mojom.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/service_manager/public/cpp/connector.h" namespace network { struct ResourceResponseHead; @@ -34,7 +37,7 @@ class URLLoaderStatusMonitor : public network::mojom::URLLoaderClient { void OnUploadProgress(int64_t current_position, int64_t total_size, OnUploadProgressCallback callback) override {} - void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override {} + void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override {} void OnTransferSizeUpdated(int32_t transfer_size_diff) override {} void OnStartLoadingResponseBody( mojo::ScopedDataPipeConsumerHandle body) override {} @@ -67,12 +70,14 @@ std::unique_ptr<ResourceDownloader> ResourceDownloader::BeginDownload( const GURL& tab_referrer_url, bool is_new_download, bool is_parallel_request, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { auto downloader = std::make_unique<ResourceDownloader>( delegate, std::move(request), params->render_process_host_id(), params->render_frame_host_routing_id(), site_url, tab_url, tab_referrer_url, is_new_download, task_runner, - std::move(url_loader_factory_getter), url_security_policy); + std::move(url_loader_factory_getter), url_security_policy, + std::move(connector)); downloader->Start(std::move(params), is_parallel_request); return downloader; @@ -95,11 +100,13 @@ ResourceDownloader::InterceptNavigationResponse( scoped_refptr<download::DownloadURLLoaderFactoryGetter> url_loader_factory_getter, const URLSecurityPolicy& url_security_policy, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { auto downloader = std::make_unique<ResourceDownloader>( delegate, std::move(resource_request), render_process_id, render_frame_id, site_url, tab_url, tab_referrer_url, true, task_runner, - std::move(url_loader_factory_getter), url_security_policy); + std::move(url_loader_factory_getter), url_security_policy, + std::move(connector)); downloader->InterceptResponse(std::move(response), std::move(url_chain), cert_status, std::move(url_loader_client_endpoints)); @@ -118,7 +125,8 @@ ResourceDownloader::ResourceDownloader( const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, scoped_refptr<download::DownloadURLLoaderFactoryGetter> url_loader_factory_getter, - const URLSecurityPolicy& url_security_policy) + const URLSecurityPolicy& url_security_policy, + std::unique_ptr<service_manager::Connector> connector) : delegate_(delegate), resource_request_(std::move(resource_request)), is_new_download_(is_new_download), @@ -130,7 +138,9 @@ ResourceDownloader::ResourceDownloader( delegate_task_runner_(task_runner), url_loader_factory_getter_(std::move(url_loader_factory_getter)), url_security_policy_(url_security_policy), - weak_ptr_factory_(this) {} + weak_ptr_factory_(this) { + RequestWakeLock(connector.get()); +} ResourceDownloader::~ResourceDownloader() = default; @@ -254,10 +264,27 @@ void ResourceDownloader::CancelRequest() { } void ResourceDownloader::Destroy() { + if (wake_lock_) + wake_lock_->CancelWakeLock(); delegate_task_runner_->PostTask( FROM_HERE, base::BindOnce(&UrlDownloadHandler::Delegate::OnUrlDownloadStopped, delegate_, this)); } +void ResourceDownloader::RequestWakeLock( + service_manager::Connector* connector) { + if (!connector) + return; + device::mojom::WakeLockProviderPtr wake_lock_provider; + connector->BindInterface(device::mojom::kServiceName, + mojo::MakeRequest(&wake_lock_provider)); + wake_lock_provider->GetWakeLockWithoutContext( + device::mojom::WakeLockType::kPreventAppSuspension, + device::mojom::WakeLockReason::kOther, "Download in progress", + mojo::MakeRequest(&wake_lock_)); + + wake_lock_->RequestWakeLock(); +} + } // namespace download diff --git a/chromium/components/download/internal/common/resource_downloader.h b/chromium/components/download/internal/common/resource_downloader.h index 63664cfbe8f..b2f8ec766fe 100644 --- a/chromium/components/download/internal/common/resource_downloader.h +++ b/chromium/components/download/internal/common/resource_downloader.h @@ -12,9 +12,14 @@ #include "components/download/public/common/url_download_handler.h" #include "mojo/public/cpp/bindings/binding.h" #include "net/cert/cert_status_flags.h" +#include "services/device/public/mojom/wake_lock.mojom.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/mojom/url_loader.mojom.h" +namespace service_manager { +class Connector; +} // namespace service_manager + namespace download { class DownloadURLLoaderFactoryGetter; @@ -36,6 +41,7 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader const GURL& tab_referrer_url, bool is_new_download, bool is_parallel_request, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); // Create a ResourceDownloader from a navigation that turns to be a download. @@ -56,6 +62,7 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader scoped_refptr<download::DownloadURLLoaderFactoryGetter> url_loader_factory_getter, const URLSecurityPolicy& url_security_policy, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); ResourceDownloader( @@ -70,7 +77,8 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, scoped_refptr<download::DownloadURLLoaderFactoryGetter> url_loader_factory_getter, - const URLSecurityPolicy& url_security_policy); + const URLSecurityPolicy& url_security_policy, + std::unique_ptr<service_manager::Connector> connector); ~ResourceDownloader() override; // download::DownloadResponseHandler::Delegate @@ -101,6 +109,9 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader // Ask the |delegate_| to destroy this object. void Destroy(); + // Requests the wake lock using |connector|. + void RequestWakeLock(service_manager::Connector* connector); + base::WeakPtr<download::UrlDownloadHandler::Delegate> delegate_; // The ResourceRequest for this object. @@ -154,6 +165,11 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader // Used to check if the URL is safe to request. URLSecurityPolicy url_security_policy_; + // Used to keep the system from sleeping while a download is ongoing. If the + // system enters power saving mode while a download is alive, it can cause + // download to be interrupted. + device::mojom::WakeLockPtr wake_lock_; + base::WeakPtrFactory<ResourceDownloader> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ResourceDownloader); diff --git a/chromium/components/download/internal/common/simple_download_manager.cc b/chromium/components/download/internal/common/simple_download_manager.cc index 9cac3131863..5401b468bca 100644 --- a/chromium/components/download/internal/common/simple_download_manager.cc +++ b/chromium/components/download/internal/common/simple_download_manager.cc @@ -15,6 +15,8 @@ SimpleDownloadManager::~SimpleDownloadManager() { void SimpleDownloadManager::AddObserver(Observer* observer) { simple_download_manager_observers_.AddObserver(observer); + if (initialized_) + NotifyInitialized(); } void SimpleDownloadManager::RemoveObserver(Observer* observer) { @@ -23,18 +25,7 @@ void SimpleDownloadManager::RemoveObserver(Observer* observer) { void SimpleDownloadManager::OnInitialized() { initialized_ = true; - for (auto& callback : std::move(on_initialized_callbacks_)) - std::move(callback).Run(); -} - -void SimpleDownloadManager::NotifyWhenInitialized( - base::OnceClosure on_initialized_cb) { - if (initialized_) { - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - std::move(on_initialized_cb)); - return; - } - on_initialized_callbacks_.emplace_back(std::move(on_initialized_cb)); + NotifyInitialized(); } void SimpleDownloadManager::OnNewDownloadCreated(DownloadItem* download) { @@ -42,4 +33,9 @@ void SimpleDownloadManager::OnNewDownloadCreated(DownloadItem* download) { observer.OnDownloadCreated(download); } +void SimpleDownloadManager::NotifyInitialized() { + for (auto& observer : simple_download_manager_observers_) + observer.OnDownloadsInitialized(); +} + } // namespace download diff --git a/chromium/components/download/internal/common/simple_download_manager_coordinator.cc b/chromium/components/download/internal/common/simple_download_manager_coordinator.cc index ca14a7f40b0..bf1a4f42df9 100644 --- a/chromium/components/download/internal/common/simple_download_manager_coordinator.cc +++ b/chromium/components/download/internal/common/simple_download_manager_coordinator.cc @@ -12,13 +12,20 @@ namespace download { -SimpleDownloadManagerCoordinator::SimpleDownloadManagerCoordinator() +SimpleDownloadManagerCoordinator::SimpleDownloadManagerCoordinator( + const DownloadWhenFullManagerStartsCallBack& + download_when_full_manager_starts_cb) : simple_download_manager_(nullptr), has_all_history_downloads_(false), + current_manager_has_all_history_downloads_(false), initialized_(false), + download_when_full_manager_starts_cb_( + download_when_full_manager_starts_cb), weak_factory_(this) {} SimpleDownloadManagerCoordinator::~SimpleDownloadManagerCoordinator() { + if (simple_download_manager_) + simple_download_manager_->RemoveObserver(this); for (auto& observer : observers_) observer.OnManagerGoingDown(); } @@ -26,18 +33,24 @@ SimpleDownloadManagerCoordinator::~SimpleDownloadManagerCoordinator() { void SimpleDownloadManagerCoordinator::SetSimpleDownloadManager( SimpleDownloadManager* simple_download_manager, bool manages_all_history_downloads) { - DCHECK(simple_download_manager_); + DCHECK(simple_download_manager); + // Make sure we won't transition from a full manager to a in-progress manager, + DCHECK(!current_manager_has_all_history_downloads_ || + manages_all_history_downloads); + if (simple_download_manager_) simple_download_manager_->RemoveObserver(this); + current_manager_has_all_history_downloads_ = manages_all_history_downloads; simple_download_manager_ = simple_download_manager; simple_download_manager_->AddObserver(this); - simple_download_manager_->NotifyWhenInitialized(base::BindOnce( - &SimpleDownloadManagerCoordinator::OnManagerInitialized, - weak_factory_.GetWeakPtr(), manages_all_history_downloads)); } void SimpleDownloadManagerCoordinator::AddObserver(Observer* observer) { observers_.AddObserver(observer); + if (initialized_) { + for (auto& observer : observers_) + observer.OnDownloadsInitialized(!has_all_history_downloads_); + } } void SimpleDownloadManagerCoordinator::RemoveObserver(Observer* observer) { @@ -46,13 +59,22 @@ void SimpleDownloadManagerCoordinator::RemoveObserver(Observer* observer) { void SimpleDownloadManagerCoordinator::DownloadUrl( std::unique_ptr<DownloadUrlParameters> parameters) { - if (simple_download_manager_) + bool result = simple_download_manager_ + ? simple_download_manager_->CanDownload(parameters.get()) + : false; + if (result) { simple_download_manager_->DownloadUrl(std::move(parameters)); + return; + } + + if (!current_manager_has_all_history_downloads_) + download_when_full_manager_starts_cb_.Run(std::move(parameters)); } void SimpleDownloadManagerCoordinator::GetAllDownloads( std::vector<DownloadItem*>* downloads) { - simple_download_manager_->GetAllDownloads(downloads); + if (simple_download_manager_) + simple_download_manager_->GetAllDownloads(downloads); } DownloadItem* SimpleDownloadManagerCoordinator::GetDownloadByGuid( @@ -62,16 +84,11 @@ DownloadItem* SimpleDownloadManagerCoordinator::GetDownloadByGuid( return nullptr; } -bool SimpleDownloadManagerCoordinator::HasSetDownloadManager() { - return simple_download_manager_; -} - -void SimpleDownloadManagerCoordinator::OnManagerInitialized( - bool has_all_history_downloads) { +void SimpleDownloadManagerCoordinator::OnDownloadsInitialized() { initialized_ = true; - has_all_history_downloads_ = has_all_history_downloads; + has_all_history_downloads_ = current_manager_has_all_history_downloads_; for (auto& observer : observers_) - observer.OnDownloadsInitialized(!has_all_history_downloads); + observer.OnDownloadsInitialized(!has_all_history_downloads_); } void SimpleDownloadManagerCoordinator::OnManagerGoingDown() { diff --git a/chromium/components/download/internal/common/url_download_handler_factory.cc b/chromium/components/download/internal/common/url_download_handler_factory.cc index b88c1ff4385..c295bc9dfaa 100644 --- a/chromium/components/download/internal/common/url_download_handler_factory.cc +++ b/chromium/components/download/internal/common/url_download_handler_factory.cc @@ -12,6 +12,7 @@ #include "components/download/public/common/download_utils.h" #include "net/url_request/url_request_context_getter.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/service_manager/public/cpp/connector.h" namespace download { @@ -31,6 +32,7 @@ class DefaultUrlDownloadHandlerFactory : public UrlDownloadHandlerFactory { url_loader_factory_getter, const URLSecurityPolicy& url_security_policy, scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) override { std::unique_ptr<network::ResourceRequest> request = CreateResourceRequest(params.get()); @@ -38,7 +40,7 @@ class DefaultUrlDownloadHandlerFactory : public UrlDownloadHandlerFactory { download::ResourceDownloader::BeginDownload( delegate, std::move(params), std::move(request), std::move(url_loader_factory_getter), url_security_policy, GURL(), - GURL(), GURL(), true, true, task_runner) + GURL(), GURL(), true, true, std::move(connector), task_runner) .release(), base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get())); } @@ -66,6 +68,7 @@ UrlDownloadHandlerFactory::Create( url_loader_factory_getter, const URLSecurityPolicy& url_security_policy, scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { base::AutoLock auto_lock(GetURLDownloadHandlerFactoryLock()); if (!g_url_download_handler_factory) @@ -73,7 +76,7 @@ UrlDownloadHandlerFactory::Create( return g_url_download_handler_factory->CreateUrlDownloadHandler( std::move(params), delegate, std::move(url_loader_factory_getter), std::move(url_security_policy), std::move(url_request_context_getter), - task_runner); + std::move(connector), task_runner); } // static diff --git a/chromium/components/download/public/background_service/BUILD.gn b/chromium/components/download/public/background_service/BUILD.gn index 424ed0bf9f0..d309ca9488b 100644 --- a/chromium/components/download/public/background_service/BUILD.gn +++ b/chromium/components/download/public/background_service/BUILD.gn @@ -9,6 +9,8 @@ if (is_android) { source_set("public") { sources = [ + "blob_context_getter_factory.h", + "client.cc", "client.h", "clients.h", "download_metadata.cc", diff --git a/chromium/components/download/public/background_service/blob_context_getter_factory.h b/chromium/components/download/public/background_service/blob_context_getter_factory.h new file mode 100644 index 00000000000..d9cf66ad547 --- /dev/null +++ b/chromium/components/download/public/background_service/blob_context_getter_factory.h @@ -0,0 +1,41 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_BLOB_CONTEXT_GETTER_FACTORY_H_ +#define COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_BLOB_CONTEXT_GETTER_FACTORY_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" + +namespace storage { +class BlobStorageContext; +} // namespace storage + +namespace download { + +using BlobContextGetter = + base::RepeatingCallback<base::WeakPtr<storage::BlobStorageContext>()>; +using BlobContextGetterCallback = base::OnceCallback<void(BlobContextGetter)>; + +// Retrieves a blob storage context getter on main thread. +class BlobContextGetterFactory { + public: + virtual void RetrieveBlobContextGetter( + BlobContextGetterCallback callback) = 0; + + virtual ~BlobContextGetterFactory() = default; + + protected: + BlobContextGetterFactory() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(BlobContextGetterFactory); +}; + +using BlobContextGetterFactoryPtr = std::unique_ptr<BlobContextGetterFactory>; + +} // namespace download + +#endif // COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_BLOB_CONTEXT_GETTER_FACTORY_H_ diff --git a/chromium/components/download/public/background_service/client.cc b/chromium/components/download/public/background_service/client.cc new file mode 100644 index 00000000000..68f9120a69b --- /dev/null +++ b/chromium/components/download/public/background_service/client.cc @@ -0,0 +1,22 @@ +// Copyright 2019 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 "components/download/public/background_service/client.h" + +namespace download { + +void Client::OnDownloadStarted( + const std::string& guid, + const std::vector<GURL>& url_chain, + const scoped_refptr<const net::HttpResponseHeaders>& headers) {} + +void Client::OnDownloadUpdated(const std::string& guid, + uint64_t bytes_uploaded, + uint64_t bytes_downloaded) {} + +void Client::OnDownloadFailed(const std::string& guid, + const download::CompletionInfo& completion_info, + download::Client::FailureReason reason) {} + +} // namespace download diff --git a/chromium/components/download/public/background_service/client.h b/chromium/components/download/public/background_service/client.h index da5956cab18..1374d0d83eb 100644 --- a/chromium/components/download/public/background_service/client.h +++ b/chromium/components/download/public/background_service/client.h @@ -30,19 +30,6 @@ using GetUploadDataCallback = // when the DownloadService is created (see the factory). class Client { public: - // Used by OnDownloadStarted to determine whether or not the DownloadService - // should continue downloading the file or abort the attempt. - enum class ShouldDownload { - // Continue to download the file. - CONTINUE, - - // Abort the download. - ABORT, - - // The count of entries for the enum. - COUNT, - }; - // Used by OnDownloadFailed to determine the reason of the abort. enum class FailureReason { // Used when the download has been aborted after reaching a threshold where @@ -88,13 +75,12 @@ class Client { // Called when the DownloadService fails to initialize and should not be used. virtual void OnServiceUnavailable() = 0; - // Return whether or not the download should be aborted (potentially in - // response to |headers|). The download will be downloading at the time this - // call is made. - virtual ShouldDownload OnDownloadStarted( + // Will be called when a download has been started and the headers have been + // retrieved. The download will be downloading at the time this call is made. + virtual void OnDownloadStarted( const std::string& guid, const std::vector<GURL>& url_chain, - const scoped_refptr<const net::HttpResponseHeaders>& headers) = 0; + const scoped_refptr<const net::HttpResponseHeaders>& headers); // Will be called when there is an update to the current progress state of the // underlying download. Note that |bytes_downloaded| may go backwards if the @@ -104,7 +90,7 @@ class Client { // driver. virtual void OnDownloadUpdated(const std::string& guid, uint64_t bytes_uploaded, - uint64_t bytes_downloaded) = 0; + uint64_t bytes_downloaded); // Called when a download failed. Check FailureReason for a list of possible // reasons why this failure occurred. Note that this will also be called for @@ -112,7 +98,7 @@ class Client { // and response headers filled in if available. virtual void OnDownloadFailed(const std::string& guid, const download::CompletionInfo& info, - FailureReason reason) = 0; + FailureReason reason); // Called when a download has been successfully completed. // The file and the database record will be automatically removed if it is not diff --git a/chromium/components/download/public/background_service/download_params.cc b/chromium/components/download/public/background_service/download_params.cc index d5c123846c1..00a3ccbd9c8 100644 --- a/chromium/components/download/public/background_service/download_params.cc +++ b/chromium/components/download/public/background_service/download_params.cc @@ -20,7 +20,8 @@ bool SchedulingParams::operator==(const SchedulingParams& rhs) const { priority == rhs.priority && cancel_time == rhs.cancel_time; } -RequestParams::RequestParams() : method("GET"), fetch_error_body(false) {} +RequestParams::RequestParams() + : method("GET"), fetch_error_body(false), require_safety_checks(true) {} RequestParams::RequestParams(const RequestParams& other) = default; diff --git a/chromium/components/download/public/background_service/download_params.h b/chromium/components/download/public/background_service/download_params.h index aadde2f4b4b..4445bbbbcfd 100644 --- a/chromium/components/download/public/background_service/download_params.h +++ b/chromium/components/download/public/background_service/download_params.h @@ -107,6 +107,9 @@ struct RequestParams { // If the request will fetch HTTP error response body and treat them as // a successful download. bool fetch_error_body; + + // Whether the download is not trustworthy and requires safe browsing checks. + bool require_safety_checks; }; // The parameters that describe a download request made to the DownloadService. diff --git a/chromium/components/download/public/common/BUILD.gn b/chromium/components/download/public/common/BUILD.gn index 71b8180719d..5937c55370e 100644 --- a/chromium/components/download/public/common/BUILD.gn +++ b/chromium/components/download/public/common/BUILD.gn @@ -134,6 +134,8 @@ source_set("test_support") { "mock_download_item_impl.h", "mock_input_stream.cc", "mock_input_stream.h", + "mock_simple_download_manager.cc", + "mock_simple_download_manager.h", ] public_deps = [ diff --git a/chromium/components/download/public/common/all_download_event_notifier.h b/chromium/components/download/public/common/all_download_event_notifier.h index 90275c18e07..d9a8989b5d1 100644 --- a/chromium/components/download/public/common/all_download_event_notifier.h +++ b/chromium/components/download/public/common/all_download_event_notifier.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "base/observer_list.h" +#include "components/download/public/common/download_export.h" #include "components/download/public/common/download_item.h" #include "components/download/public/common/simple_download_manager_coordinator.h" @@ -16,7 +17,7 @@ namespace download { // Observes all the download events from a single // SimpleDownloadManagerCoordinator. -class AllDownloadEventNotifier +class COMPONENTS_DOWNLOAD_EXPORT AllDownloadEventNotifier : public SimpleDownloadManagerCoordinator::Observer, public DownloadItem::Observer { public: diff --git a/chromium/components/download/public/common/base_file.h b/chromium/components/download/public/common/base_file.h index d6716233e0a..dd8ed1b1993 100644 --- a/chromium/components/download/public/common/base_file.h +++ b/chromium/components/download/public/common/base_file.h @@ -114,6 +114,10 @@ class COMPONENTS_DOWNLOAD_EXPORT BaseFile { const char* data, size_t data_len); + // Validates that the content starting from |offset| matches that of |data| + // with the given length. + bool ValidateDataInFile(int64_t offset, const char* data, size_t data_len); + // Rename the download file. Returns a DownloadInterruptReason indicating the // result of the operation. A return code of NONE indicates that the rename // was successful. After a failure, the full_path() and in_progress() can be diff --git a/chromium/components/download/public/common/download_create_info.h b/chromium/components/download/public/common/download_create_info.h index 7c82695416b..19552ad5b07 100644 --- a/chromium/components/download/public/common/download_create_info.h +++ b/chromium/components/download/public/common/download_create_info.h @@ -27,6 +27,7 @@ #include "services/metrics/public/cpp/ukm_source_id.h" #include "ui/base/page_transition_types.h" #include "url/gurl.h" +#include "url/origin.h" namespace net { class HttpResponseHeaders; @@ -67,6 +68,9 @@ struct COMPONENTS_DOWNLOAD_EXPORT DownloadCreateInfo { // The referrer URL of the tab that started us. GURL tab_referrer_url; + // The origin of the requester that originally initiated the download. + base::Optional<url::Origin> request_initiator; + // The time when the download started. base::Time start_time; diff --git a/chromium/components/download/public/common/download_features.cc b/chromium/components/download/public/common/download_features.cc index 6929f5a798b..7e945dd4815 100644 --- a/chromium/components/download/public/common/download_features.cc +++ b/chromium/components/download/public/common/download_features.cc @@ -38,5 +38,16 @@ const base::Feature kRefreshExpirationDate{"RefreshExpirationDate", base::FEATURE_ENABLED_BY_DEFAULT}; #endif +const base::Feature kPreventDownloadsWithSamePath{ + "PreventDownloadsWithSamePath", base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kUseInProgressDownloadManagerForDownloadService{ + "UseInProgressDownloadManagerForDownloadService", + base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kAllowDownloadResumptionWithoutStrongValidators{ + "AllowDownloadResumptionWithoutStrongValidators", + base::FEATURE_DISABLED_BY_DEFAULT}; } // namespace features + } // namespace download diff --git a/chromium/components/download/public/common/download_features.h b/chromium/components/download/public/common/download_features.h index b802cbf2840..a3d7fda74f6 100644 --- a/chromium/components/download/public/common/download_features.h +++ b/chromium/components/download/public/common/download_features.h @@ -33,6 +33,18 @@ COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature kRefreshExpirationDate; #endif +// Whether concurrent downloads to the same target path should be allowed. +COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature + kPreventDownloadsWithSamePath; + +// Whether in-progress download manager will be used to initialize download +// service. +COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature + kUseInProgressDownloadManagerForDownloadService; + +// Whether download resumption is allowed when there are no strong validators. +COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature + kAllowDownloadResumptionWithoutStrongValidators; } // namespace features } // namespace download diff --git a/chromium/components/download/public/common/download_file.h b/chromium/components/download/public/common/download_file.h index 50561a801aa..545b4d186aa 100644 --- a/chromium/components/download/public/common/download_file.h +++ b/chromium/components/download/public/common/download_file.h @@ -121,6 +121,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadFile { // Publishes the download to public. Once completes, |callback| is called with // the final content URI. virtual void PublishDownload(const RenameCompletionCallback& callback) = 0; + + // Returns the suggested file path from the system. + virtual base::FilePath GetDisplayName() = 0; #endif // defined(OS_ANDROID) }; diff --git a/chromium/components/download/public/common/download_file_impl.h b/chromium/components/download/public/common/download_file_impl.h index 1f82b9d92d4..c3dee68fc39 100644 --- a/chromium/components/download/public/common/download_file_impl.h +++ b/chromium/components/download/public/common/download_file_impl.h @@ -84,6 +84,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadFileImpl : public DownloadFile { const std::string& mime_type, const RenameCompletionCallback& callback) override; void PublishDownload(const RenameCompletionCallback& callback) override; + base::FilePath GetDisplayName() override; #endif // defined(OS_ANDROID) // Wrapper of a ByteStreamReader or ScopedDataPipeConsumerHandle, and the meta @@ -98,17 +99,19 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadFileImpl : public DownloadFile { public: SourceStream(int64_t offset, int64_t length, + int64_t starting_file_write_offset, std::unique_ptr<InputStream> stream); ~SourceStream(); void Initialize(); - // Called after successfully writing a buffer to disk. - void OnWriteBytesToDisk(int64_t bytes_write); + // Called after successfully reading and writing a buffer from stream. + void OnBytesConsumed(int64_t bytes_read, int64_t bytes_written); // Given a data block that is already written, truncate the length of this - // object to avoid overwriting that block. - void TruncateLengthWithWrittenDataBlock(int64_t offset, + // object to avoid overwriting that block. Data used for validation purpose + // will not be truncated. + void TruncateLengthWithWrittenDataBlock(int64_t received_slice_offset, int64_t bytes_written); // Registers the callback that will be called when data is ready. @@ -135,8 +138,15 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadFileImpl : public DownloadFile { InputStream::StreamState Read(scoped_refptr<net::IOBuffer>* data, size_t* length); + // Returning the remaining bytes to validate. + size_t GetRemainingBytesToValidate(); + int64_t offset() const { return offset_; } int64_t length() const { return length_; } + int64_t starting_file_write_offset() const { + return starting_file_write_offset_; + } + int64_t bytes_read() const { return bytes_read_; } int64_t bytes_written() const { return bytes_written_; } bool is_finished() const { return finished_; } void set_finished(bool finish) { finished_ = finish; } @@ -144,15 +154,24 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadFileImpl : public DownloadFile { void set_index(size_t index) { index_ = index; } private: - // Starting position for the stream to write to disk. + // Starting position of the stream, this is from the network response. int64_t offset_; // The maximum length to write to the disk. If set to 0, keep writing until // the stream depletes. int64_t length_; - // Number of bytes written to disk from the stream. - // Next write position is (|offset_| + |bytes_written_|). + // All the data received before this offset are used for validation purpose + // and will not be written to disk. This value should always be no less than + // |offset_|. + int64_t starting_file_write_offset_; + + // Number of bytes read from the stream. + // Next read position is (|offset_| + |bytes_read_|). + int64_t bytes_read_; + + // Number of bytes written to the disk. This does not include the bytes used + // for validation. int64_t bytes_written_; // If all the data read from the stream has been successfully written to @@ -171,11 +190,13 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadFileImpl : public DownloadFile { protected: // For test class overrides. - // Write data from the offset to the file. - // On OS level, it will seek to the |offset| and write from there. - virtual DownloadInterruptReason WriteDataToFile(int64_t offset, - const char* data, - size_t data_len); + // Validate the first |bytes_to_validate| bytes and write the next + // |bytes_to_write| bytes of data from the offset to the file. + virtual DownloadInterruptReason ValidateAndWriteDataToFile( + int64_t offset, + const char* data, + size_t bytes_to_validate, + size_t bytes_to_write); virtual base::TimeDelta GetRetryDelayForFailedRename(int attempt_number); @@ -187,11 +208,6 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadFileImpl : public DownloadFile { private: friend class DownloadFileTest; - DownloadFileImpl(std::unique_ptr<DownloadSaveInfo> save_info, - const base::FilePath& default_downloads_directory, - uint32_t download_id, - base::WeakPtr<DownloadDestinationObserver> observer); - // Options for RenameWithRetryInternal. enum RenameOption { UNIQUIFY = 1 << 0, // If there's already a file on disk that conflicts with @@ -236,13 +252,14 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadFileImpl : public DownloadFile { void WillWriteToDisk(size_t data_len); // For a given SourceStream object and the bytes available to write, determine - // the actual number of bytes it can write to the disk. For parallel - // downloading, if the first disk IO writes to a location that is already - // written by another stream, the current stream should stop writing. Returns - // true if the stream can write no more data and should be finished, returns - // false otherwise. + // the number of bytes to validate and the number of bytes it can write to the + // disk. For parallel downloading, if the first disk IO writes to a location + // that is already written by another stream, the current stream should stop + // writing. Returns true if the stream can write no more data and should be + // finished, returns false otherwise. bool CalculateBytesToWrite(SourceStream* source_stream, size_t bytes_available_to_write, + size_t* bytes_to_validate, size_t* bytes_to_write); // Called when a new SourceStream object is added. @@ -346,6 +363,10 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadFileImpl : public DownloadFile { // TaskRunner to post updates to the |observer_|. scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; +#if defined(OS_ANDROID) + base::FilePath display_name_; +#endif // defined(OS_ANDROID) + SEQUENCE_CHECKER(sequence_checker_); base::WeakPtr<DownloadDestinationObserver> observer_; diff --git a/chromium/components/download/public/common/download_item.h b/chromium/components/download/public/common/download_item.h index cfd9d19c3cc..df473cfe887 100644 --- a/chromium/components/download/public/common/download_item.h +++ b/chromium/components/download/public/common/download_item.h @@ -26,12 +26,14 @@ #include "base/callback_forward.h" #include "base/files/file_path.h" #include "base/memory/ref_counted.h" +#include "base/optional.h" #include "base/strings/string16.h" #include "base/supports_user_data.h" #include "components/download/public/common/download_danger_type.h" #include "components/download/public/common/download_export.h" #include "components/download/public/common/download_interrupt_reasons.h" #include "ui/base/page_transition_types.h" +#include "url/origin.h" class GURL; @@ -282,6 +284,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItem : public base::SupportsUserData { // Referrer URL for top level frame. virtual const GURL& GetTabReferrerUrl() const = 0; + // Origin of the original originator of this download, before redirects, etc. + virtual const base::Optional<url::Origin>& GetRequestInitiator() const = 0; + // For downloads initiated via <a download>, this is the suggested download // filename from the download attribute. virtual std::string GetSuggestedFilename() const = 0; diff --git a/chromium/components/download/public/common/download_item_factory.h b/chromium/components/download/public/common/download_item_factory.h index 0ceeea52ad6..8453cd85349 100644 --- a/chromium/components/download/public/common/download_item_factory.h +++ b/chromium/components/download/public/common/download_item_factory.h @@ -15,8 +15,10 @@ #include <string> #include <vector> +#include "base/optional.h" #include "components/download/public/common/download_export.h" #include "components/download/public/common/download_item.h" +#include "url/origin.h" class GURL; @@ -46,6 +48,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemFactory { const GURL& site_url, const GURL& tab_url, const GURL& tab_refererr_url, + const base::Optional<url::Origin>& initiator_origin, const std::string& mime_type, const std::string& original_mime_type, base::Time start_time, diff --git a/chromium/components/download/public/common/download_item_impl.h b/chromium/components/download/public/common/download_item_impl.h index 85f45a5e654..0da7255e41a 100644 --- a/chromium/components/download/public/common/download_item_impl.h +++ b/chromium/components/download/public/common/download_item_impl.h @@ -16,6 +16,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "base/optional.h" #include "base/threading/thread_checker_impl.h" #include "base/time/time.h" #include "components/download/public/common/download_create_info.h" @@ -27,6 +28,7 @@ #include "components/download/public/common/download_url_parameters.h" #include "components/download/public/common/resume_mode.h" #include "url/gurl.h" +#include "url/origin.h" namespace net { class URLRequestContextGetter; @@ -53,6 +55,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl const GURL& site_url, const GURL& tab_url, const GURL& tab_referrer_url, + const base::Optional<url::Origin>& request_initiator, const std::string& suggested_filename, const base::FilePath& forced_file_path, ui::PageTransition transition_type, @@ -79,6 +82,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl // The URL of the referrer of the tab that initiated the download. GURL tab_referrer_url; + // The origin of the requester that originally initiated the download. + base::Optional<url::Origin> request_initiator; + // Filename suggestion from DownloadSaveInfo. It could, among others, be the // suggested filename in 'download' attribute of an anchor. Details: // http://www.whatwg.org/specs/web-apps/current-work/#downloading-hyperlinks @@ -171,6 +177,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl const GURL& site_url, const GURL& tab_url, const GURL& tab_referrer_url, + const base::Optional<url::Origin>& request_initiator, const std::string& mime_type, const std::string& original_mime_type, base::Time start_time, @@ -242,6 +249,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl const GURL& GetSiteUrl() const override; const GURL& GetTabUrl() const override; const GURL& GetTabReferrerUrl() const override; + const base::Optional<url::Origin>& GetRequestInitiator() const override; std::string GetSuggestedFilename() const override; const scoped_refptr<const net::HttpResponseHeaders>& GetResponseHeaders() const override; @@ -637,6 +645,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl // INTERRUPTED state. ResumeMode GetResumeMode() const; + // Whether strong validators are present. + bool HasStrongValidators() const; + DownloadItem::DownloadRenameResult RenameDownloadedFile( const std::string& name); void RenameDownloadedFileDone(RenameDownloadCallback callback, @@ -815,6 +826,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl // UKM ID for reporting, default to 0 if uninitialized. uint64_t ukm_download_id_ = 0; + // Whether download has been resumed. + bool has_resumed_ = false; + THREAD_CHECKER(thread_checker_); base::WeakPtrFactory<DownloadItemImpl> weak_ptr_factory_; diff --git a/chromium/components/download/public/common/download_item_impl_delegate.h b/chromium/components/download/public/common/download_item_impl_delegate.h index a6eb542950c..e67be44238b 100644 --- a/chromium/components/download/public/common/download_item_impl_delegate.h +++ b/chromium/components/download/public/common/download_item_impl_delegate.h @@ -15,6 +15,10 @@ #include "components/download/public/common/download_item.h" #include "components/download/public/common/download_url_parameters.h" +namespace service_manager { +class Connector; +} // namespace service_manager + namespace download { struct DownloadEntry; class DownloadItemImpl; @@ -113,6 +117,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImplDelegate { // Report extra bytes wasted during resumption. virtual void ReportBytesWasted(DownloadItemImpl* download); + // Gets the ServiceManager connector that can be used on UI thread. + virtual service_manager::Connector* GetServiceManagerConnector(); + private: // For "Outlives attached DownloadItemImpl" invariant assertion. int count_; diff --git a/chromium/components/download/public/common/download_job_factory.h b/chromium/components/download/public/common/download_job_factory.h index cd44f441cad..d6660c1073c 100644 --- a/chromium/components/download/public/common/download_job_factory.h +++ b/chromium/components/download/public/common/download_job_factory.h @@ -13,7 +13,11 @@ namespace net { class URLRequestContextGetter; -} +} // namespace net + +namespace service_manager { +class Connector; +} // namespace service_manager namespace download { @@ -32,7 +36,8 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadJobFactory { bool is_save_package_download, scoped_refptr<download::DownloadURLLoaderFactoryGetter> url_loader_factory_getter, - net::URLRequestContextGetter* url_request_context_getter); + net::URLRequestContextGetter* url_request_context_getter, + service_manager::Connector* connector); private: DISALLOW_COPY_AND_ASSIGN(DownloadJobFactory); diff --git a/chromium/components/download/public/common/download_path_reservation_tracker.h b/chromium/components/download/public/common/download_path_reservation_tracker.h index 446a2de057c..6c0b83a12cb 100644 --- a/chromium/components/download/public/common/download_path_reservation_tracker.h +++ b/chromium/components/download/public/common/download_path_reservation_tracker.h @@ -111,6 +111,14 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadPathReservationTracker { // tests. static bool IsPathInUseForTesting(const base::FilePath& path); + // Returns true if there is an existing path reservation, separate from the + // download_item. Called by + // ChromeDownloadManagerDelegate::OnDownloadTargetDetermined to see if there + // is a target collision. + static bool CheckDownloadPathForExistingDownload( + const base::FilePath& target_path, + DownloadItem* download_item); + static scoped_refptr<base::SequencedTaskRunner> GetTaskRunner(); }; diff --git a/chromium/components/download/public/common/download_response_handler.h b/chromium/components/download/public/common/download_response_handler.h index b0967deba49..ce94c9a0560 100644 --- a/chromium/components/download/public/common/download_response_handler.h +++ b/chromium/components/download/public/common/download_response_handler.h @@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/optional.h" #include "components/download/public/common/download_create_info.h" #include "components/download/public/common/download_export.h" #include "components/download/public/common/download_source.h" @@ -17,6 +18,7 @@ #include "net/cert/cert_status_flags.h" #include "services/network/public/cpp/resource_response.h" #include "services/network/public/mojom/url_loader.mojom.h" +#include "url/origin.h" namespace download { @@ -60,7 +62,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadResponseHandler void OnUploadProgress(int64_t current_position, int64_t total_size, OnUploadProgressCallback callback) override; - void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override; + void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override; void OnTransferSizeUpdated(int32_t transfer_size_diff) override; void OnStartLoadingResponseBody( mojo::ScopedDataPipeConsumerHandle body) override; @@ -94,7 +96,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadResponseHandler DownloadSource download_source_; net::CertStatus cert_status_; bool has_strong_validators_; - GURL origin_; + base::Optional<url::Origin> request_initiator_; bool is_partial_request_; bool completed_; diff --git a/chromium/components/download/public/common/download_save_info.cc b/chromium/components/download/public/common/download_save_info.cc index 2b25b628c44..c66a6003f03 100644 --- a/chromium/components/download/public/common/download_save_info.cc +++ b/chromium/components/download/public/common/download_save_info.cc @@ -15,4 +15,8 @@ DownloadSaveInfo::~DownloadSaveInfo() = default; DownloadSaveInfo::DownloadSaveInfo(DownloadSaveInfo&& that) = default; +int64_t DownloadSaveInfo::GetStartingFileWriteOffset() { + return file_offset >= 0 ? file_offset : offset; +} + } // namespace download diff --git a/chromium/components/download/public/common/download_save_info.h b/chromium/components/download/public/common/download_save_info.h index a1e00c8755a..9f249c7bda6 100644 --- a/chromium/components/download/public/common/download_save_info.h +++ b/chromium/components/download/public/common/download_save_info.h @@ -30,6 +30,8 @@ struct COMPONENTS_DOWNLOAD_EXPORT DownloadSaveInfo { ~DownloadSaveInfo(); DownloadSaveInfo(DownloadSaveInfo&& that); + int64_t GetStartingFileWriteOffset(); + // If non-empty, contains the full target path of the download that has been // determined prior to download initiation. This is considered to be a trusted // path. @@ -42,9 +44,18 @@ struct COMPONENTS_DOWNLOAD_EXPORT DownloadSaveInfo { // If valid, contains the source data stream for the file contents. base::File file; - // The file offset at which to start the download. + // The offset sent to the server when requesting the download. During + // resumption, |offset| could be smaller than the downloaded content length. + // This is because download may request some data to validate whether the + // content has changed. int64_t offset = 0; + // The file offset to start writing to disk. If this value is negative, + // download stream will be writing to the disk starting at |offset|. + // Otherwise, this value will be used. Data received before |file_offset| are + // used for validation purpose. + int64_t file_offset = -1; + // The number of the bytes to download from |offset|. // Ask to retrieve segment of the download file when length is greater than 0. // Request the rest of the file starting from |offset|, when length is diff --git a/chromium/components/download/public/common/download_stats.h b/chromium/components/download/public/common/download_stats.h index b01123d1a91..7e10804bac4 100644 --- a/chromium/components/download/public/common/download_stats.h +++ b/chromium/components/download/public/common/download_stats.h @@ -15,6 +15,7 @@ #include "base/callback.h" #include "base/optional.h" +#include "build/build_config.h" #include "components/download/public/common/download_content.h" #include "components/download/public/common/download_danger_type.h" #include "components/download/public/common/download_export.h" @@ -219,6 +220,21 @@ enum class ParallelDownloadCreationEvent { COUNT, }; +// Reason for download to restart during resumption. These enum values are +// persisted to logs, and should therefore never be renumbered nor removed. +enum class ResumptionRestartCountTypes { + // The download is restarted due to server response. + kRequestedByServerCount = 0, + + // Strong validator changes. + kStrongValidatorChangesCount = 1, + + // No strong validators are present. + kMissingStrongValidatorsCount = 2, + + kMaxValue = kMissingStrongValidatorsCount +}; + // Increment one of the above counts. COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadCount(DownloadCountTypes type); @@ -231,7 +247,9 @@ COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadCountWithSource( COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadCompleted( int64_t download_len, bool is_parallelizable, - DownloadSource download_source); + DownloadSource download_source, + bool has_resumed, + bool has_strong_validators); // Record download deletion event. COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadDeletion( @@ -410,6 +428,30 @@ COMPONENTS_DOWNLOAD_EXPORT void RecordInProgressDBCount( COMPONENTS_DOWNLOAD_EXPORT void RecordDuplicateInProgressDownloadIdCount( int count); + +// Records the interrupt reason that causes download to restart. +COMPONENTS_DOWNLOAD_EXPORT void RecordResumptionRestartReason( + DownloadInterruptReason reason); + +// Records the interrupt reason that causes download to restart. +COMPONENTS_DOWNLOAD_EXPORT void RecordResumptionStrongValidators( + DownloadInterruptReason reason); + +COMPONENTS_DOWNLOAD_EXPORT void RecordResumptionRestartCount( + ResumptionRestartCountTypes type); + +// Records that download was resumed. +COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadResumed( + bool has_strong_validators); + +#if defined(OS_ANDROID) +// Records the download interrupt reason for the first background download. +// If |download_started| is true, this records the last interrupt reason +// before download is started manually or by the task scheduler. +COMPONENTS_DOWNLOAD_EXPORT void RecordFirstBackgroundDownloadInterruptReason( + DownloadInterruptReason reason, + bool download_started); +#endif // defined(OS_ANDROID) } // namespace download #endif // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_DOWNLOAD_STATS_H_ diff --git a/chromium/components/download/public/common/download_url_parameters.h b/chromium/components/download/public/common/download_url_parameters.h index eb9422c7947..f276eddce7a 100644 --- a/chromium/components/download/public/common/download_url_parameters.h +++ b/chromium/components/download/public/common/download_url_parameters.h @@ -180,6 +180,12 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUrlParameters { // See |DownloadSaveInfo.length|. void set_length(int64_t length) { save_info_.length = length; } + // Sets the offset to start writing to the file. If set, The data received + // before |file_offset| are discarded or are used for validation purpose. + void set_file_offset(int64_t file_offset) { + save_info_.file_offset = file_offset; + } + // If |offset| is non-zero, then |hash_of_partial_file| contains the raw // SHA-256 hash of the first |offset| bytes of the target file. Only // meaningful if a partial file exists and is identified by either the diff --git a/chromium/components/download/public/common/download_utils.h b/chromium/components/download/public/common/download_utils.h index dfeb282af22..9e609fee9f1 100644 --- a/chromium/components/download/public/common/download_utils.h +++ b/chromium/components/download/public/common/download_utils.h @@ -101,6 +101,15 @@ COMPONENTS_DOWNLOAD_EXPORT bool DeleteDownloadedFile( COMPONENTS_DOWNLOAD_EXPORT download::DownloadItem::DownloadRenameResult RenameDownloadedFile(const base::FilePath& from_path, const base::FilePath& to_path); + +// Finch parameter key value for number of bytes used for content validation +// during resumption. +constexpr char kDownloadContentValidationLengthFinchKey[] = + "download_validation_length"; + +// Get the number of bytes to validate from finch configuration. +int64_t GetDownloadValidationLengthConfig(); + } // namespace download #endif // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_DOWNLOAD_UTILS_H_ diff --git a/chromium/components/download/public/common/in_progress_download_manager.h b/chromium/components/download/public/common/in_progress_download_manager.h index 08aa242dd8a..aaa886466d5 100644 --- a/chromium/components/download/public/common/in_progress_download_manager.h +++ b/chromium/components/download/public/common/in_progress_download_manager.h @@ -24,11 +24,15 @@ namespace net { class URLRequestContextGetter; -} +} // namespace net namespace network { struct ResourceResponse; -} +} // namespace network + +namespace service_manager { +class Connector; +} // namespace service_manager namespace download { @@ -54,13 +58,16 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager // Class to be notified when download starts/stops. class COMPONENTS_DOWNLOAD_EXPORT Delegate { public: + // Called when in-progress downloads are initialized. + virtual void OnDownloadsInitialized() {} + // Intercepts the download to another system if applicable. Returns true if // the download was intercepted. virtual bool InterceptDownload( - const DownloadCreateInfo& download_create_info) = 0; + const DownloadCreateInfo& download_create_info); // Gets the default download directory. - virtual base::FilePath GetDefaultDownloadDirectory() = 0; + virtual base::FilePath GetDefaultDownloadDirectory(); // Gets the download item for the given |download_create_info|. // TODO(qinmin): remove this method and let InProgressDownloadManager @@ -68,23 +75,25 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager virtual void StartDownloadItem( std::unique_ptr<DownloadCreateInfo> info, const DownloadUrlParameters::OnStartedCallback& on_started, - StartDownloadItemCallback callback) = 0; + StartDownloadItemCallback callback) {} // Gets the URLRequestContextGetter for sending requests. // TODO(qinmin): remove this once network service is fully enabled. virtual net::URLRequestContextGetter* GetURLRequestContextGetter( - const DownloadCreateInfo& download_create_info) = 0; + const DownloadCreateInfo& download_create_info); }; using IsOriginSecureCallback = base::RepeatingCallback<bool(const GURL&)>; InProgressDownloadManager(Delegate* delegate, const base::FilePath& in_progress_db_dir, const IsOriginSecureCallback& is_origin_secure_cb, - const URLSecurityPolicy& url_security_policy); + const URLSecurityPolicy& url_security_policy, + service_manager::Connector* connector); ~InProgressDownloadManager() override; // SimpleDownloadManager implementation. - bool DownloadUrl(std::unique_ptr<DownloadUrlParameters> params) override; + void DownloadUrl(std::unique_ptr<DownloadUrlParameters> params) override; + bool CanDownload(DownloadUrlParameters* params) override; void GetAllDownloads( SimpleDownloadManager::DownloadVector* downloads) override; DownloadItem* GetDownloadByGuid(const std::string& guid) override; @@ -139,6 +148,15 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager download_start_observer_ = observer; } + // Callback to generate an intermediate file path from the given target file + // path; + using IntermediatePathCallback = + base::RepeatingCallback<base::FilePath(const base::FilePath&)>; + void set_intermediate_path_cb( + const IntermediatePathCallback& intermediate_path_cb) { + intermediate_path_cb_ = intermediate_path_cb; + } + // Called to get all in-progress DownloadItemImpl. // TODO(qinmin): remove this method once InProgressDownloadManager owns // all in-progress downloads. @@ -164,7 +182,7 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager url_loader_factory_getter_ = std::move(url_loader_factory_getter); } - void set_delegate(Delegate* delegate) { delegate_ = delegate; } + void SetDelegate(Delegate* delegate); void set_is_origin_secure_cb( const IsOriginSecureCallback& is_origin_secure_cb) { @@ -205,6 +223,12 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager DownloadItemImpl* download, bool should_persist_new_download); + // Called when downloads are initialized. + void OnDownloadsInitialized(); + + // Called to notify |delegate_| that downloads are initialized. + void NotifyDownloadsInitialized(); + // Active download handlers. std::vector<UrlDownloadHandler::UniqueUrlDownloadHandlerPtr> url_download_handlers_; @@ -234,6 +258,9 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager // callback to check if an origin is secure. IsOriginSecureCallback is_origin_secure_cb_; + // callback to generate the intermediate file path. + IntermediatePathCallback intermediate_path_cb_; + // A list of in-progress download items, could be null if DownloadManagerImpl // is managing all downloads. std::vector<std::unique_ptr<DownloadItemImpl>> in_progress_downloads_; @@ -255,6 +282,9 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager // Whether this object uses an empty database and no history will be saved. bool use_empty_db_; + // Connector to the service manager. + service_manager::Connector* connector_; + base::WeakPtrFactory<InProgressDownloadManager> weak_factory_; DISALLOW_COPY_AND_ASSIGN(InProgressDownloadManager); diff --git a/chromium/components/download/public/common/mock_download_file.h b/chromium/components/download/public/common/mock_download_file.h index 53f016cb4ac..b30b1672c35 100644 --- a/chromium/components/download/public/common/mock_download_file.h +++ b/chromium/components/download/public/common/mock_download_file.h @@ -75,6 +75,7 @@ class MockDownloadFile : public DownloadFile { const std::string& mime_type, const RenameCompletionCallback& callback)); MOCK_METHOD1(PublishDownload, void(const RenameCompletionCallback& callback)); + MOCK_METHOD0(GetDisplayName, base::FilePath()); #endif // defined(OS_ANDROID) }; diff --git a/chromium/components/download/public/common/mock_download_item.h b/chromium/components/download/public/common/mock_download_item.h index 98420c1f6aa..09ea72f712a 100644 --- a/chromium/components/download/public/common/mock_download_item.h +++ b/chromium/components/download/public/common/mock_download_item.h @@ -12,11 +12,13 @@ #include "base/callback.h" #include "base/observer_list.h" +#include "base/optional.h" #include "base/time/time.h" #include "components/download/public/common/download_interrupt_reasons.h" #include "components/download/public/common/download_item.h" #include "testing/gmock/include/gmock/gmock.h" #include "url/gurl.h" +#include "url/origin.h" namespace download { @@ -65,6 +67,7 @@ class MockDownloadItem : public DownloadItem { MOCK_CONST_METHOD0(GetSiteUrl, const GURL&()); MOCK_CONST_METHOD0(GetTabUrl, const GURL&()); MOCK_CONST_METHOD0(GetTabReferrerUrl, const GURL&()); + MOCK_CONST_METHOD0(GetRequestInitiator, const base::Optional<url::Origin>&()); MOCK_CONST_METHOD0(GetSuggestedFilename, std::string()); MOCK_CONST_METHOD0(GetContentDisposition, std::string()); MOCK_CONST_METHOD0(GetResponseHeaders, diff --git a/chromium/components/download/public/common/mock_download_item_impl.cc b/chromium/components/download/public/common/mock_download_item_impl.cc index 5282defc5f2..05b6a9117fc 100644 --- a/chromium/components/download/public/common/mock_download_item_impl.cc +++ b/chromium/components/download/public/common/mock_download_item_impl.cc @@ -17,6 +17,7 @@ MockDownloadItemImpl::MockDownloadItemImpl(DownloadItemImplDelegate* delegate) GURL(), GURL(), GURL(), + url::Origin(), "application/octet-stream", "application/octet-stream", base::Time(), diff --git a/chromium/components/download/public/common/mock_download_item_impl.h b/chromium/components/download/public/common/mock_download_item_impl.h index 7bbb9589d24..612485d1f7b 100644 --- a/chromium/components/download/public/common/mock_download_item_impl.h +++ b/chromium/components/download/public/common/mock_download_item_impl.h @@ -10,11 +10,13 @@ #include <vector> #include "base/macros.h" +#include "base/optional.h" #include "components/download/public/common/download_create_info.h" #include "components/download/public/common/download_file.h" #include "components/download/public/common/download_item_impl.h" #include "components/download/public/common/download_request_handle_interface.h" #include "testing/gmock/include/gmock/gmock.h" +#include "url/origin.h" namespace download { @@ -81,6 +83,7 @@ class MockDownloadItemImpl : public DownloadItemImpl { MOCK_CONST_METHOD0(GetReferrerUrl, const GURL&()); MOCK_CONST_METHOD0(GetTabUrl, const GURL&()); MOCK_CONST_METHOD0(GetTabReferrerUrl, const GURL&()); + MOCK_CONST_METHOD0(GetRequestInitiator, const base::Optional<url::Origin>&()); MOCK_CONST_METHOD0(GetSuggestedFilename, std::string()); MOCK_CONST_METHOD0(GetContentDisposition, std::string()); MOCK_CONST_METHOD0(GetMimeType, std::string()); diff --git a/chromium/components/download/public/common/mock_simple_download_manager.cc b/chromium/components/download/public/common/mock_simple_download_manager.cc new file mode 100644 index 00000000000..0189b58078c --- /dev/null +++ b/chromium/components/download/public/common/mock_simple_download_manager.cc @@ -0,0 +1,22 @@ +// Copyright 2019 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 "components/download/public/common/mock_simple_download_manager.h" + +namespace download { + +MockSimpleDownloadManager::MockSimpleDownloadManager() = default; + +MockSimpleDownloadManager::~MockSimpleDownloadManager() = default; + +void MockSimpleDownloadManager::NotifyOnDownloadInitialized() { + OnInitialized(); +} + +void MockSimpleDownloadManager::NotifyOnNewDownloadCreated(DownloadItem* item) { + for (auto& observer : simple_download_manager_observers_) + observer.OnDownloadCreated(item); +} + +} // namespace download diff --git a/chromium/components/download/public/common/mock_simple_download_manager.h b/chromium/components/download/public/common/mock_simple_download_manager.h new file mode 100644 index 00000000000..9a878f7cef7 --- /dev/null +++ b/chromium/components/download/public/common/mock_simple_download_manager.h @@ -0,0 +1,44 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_DOWNLOAD_PUBLIC_COMMON_MOCK_SIMPLE_DOWNLOAD_MANAGER_H_ +#define COMPONENTS_DOWNLOAD_PUBLIC_COMMON_MOCK_SIMPLE_DOWNLOAD_MANAGER_H_ + +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/observer_list.h" +#include "components/download/public/common/simple_download_manager.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace download { + +class MockSimpleDownloadManager : public SimpleDownloadManager { + public: + MockSimpleDownloadManager(); + ~MockSimpleDownloadManager() override; + + // Dispatches an OnNewDownloadCreated() notification to observers. + void NotifyOnNewDownloadCreated(DownloadItem* item); + + // Notifies observers that downloads is initialized. + void NotifyOnDownloadInitialized(); + + void DownloadUrl(std::unique_ptr<DownloadUrlParameters> params) override { + DownloadUrlMock(params.get()); + } + + MOCK_METHOD1(CanDownload, bool(DownloadUrlParameters*)); + MOCK_METHOD1(DownloadUrlMock, void(DownloadUrlParameters*)); + MOCK_METHOD1(GetDownloadByGuid, DownloadItem*(const std::string&)); + MOCK_METHOD1(GetAllDownloads, void(DownloadVector* downloads)); + + private: + base::ObserverList<Observer>::Unchecked observers_; +}; + +} // namespace download + +#endif // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_MOCK_SIMPLE_DOWNLOAD_MANAGER_H_ diff --git a/chromium/components/download/public/common/simple_download_manager.h b/chromium/components/download/public/common/simple_download_manager.h index 234138e47f5..8a3bd82d5a4 100644 --- a/chromium/components/download/public/common/simple_download_manager.h +++ b/chromium/components/download/public/common/simple_download_manager.h @@ -26,6 +26,7 @@ class COMPONENTS_DOWNLOAD_EXPORT SimpleDownloadManager { Observer() = default; virtual ~Observer() = default; + virtual void OnDownloadsInitialized() {} virtual void OnManagerGoingDown() {} virtual void OnDownloadCreated(DownloadItem* item) {} @@ -39,14 +40,14 @@ class COMPONENTS_DOWNLOAD_EXPORT SimpleDownloadManager { void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); - // Calls the callback if this object becomes initialized. - void NotifyWhenInitialized(base::OnceClosure callback); - // Download a URL given by the |params|. Returns true if the download could // take place, or false otherwise. - virtual bool DownloadUrl( + virtual void DownloadUrl( std::unique_ptr<DownloadUrlParameters> parameters) = 0; + // Returns whether the manager can handle this download. + virtual bool CanDownload(DownloadUrlParameters* parameters) = 0; + using DownloadVector = std::vector<DownloadItem*>; // Add all download items to |downloads|, no matter the type or state, without // clearing |downloads| first. @@ -62,15 +63,14 @@ class COMPONENTS_DOWNLOAD_EXPORT SimpleDownloadManager { // Called when a new download is created. void OnNewDownloadCreated(DownloadItem* download); + // Notify observers that this object is initialized. + void NotifyInitialized(); + // Whether this object is initialized. bool initialized_ = false; // Observers that want to be notified of changes to the set of downloads. base::ObserverList<Observer>::Unchecked simple_download_manager_observers_; - - private: - // Callbacks to call once this object is initialized. - std::vector<base::OnceClosure> on_initialized_callbacks_; }; } // namespace download diff --git a/chromium/components/download/public/common/simple_download_manager_coordinator.h b/chromium/components/download/public/common/simple_download_manager_coordinator.h index 03ebf4e643c..c610486ed2a 100644 --- a/chromium/components/download/public/common/simple_download_manager_coordinator.h +++ b/chromium/components/download/public/common/simple_download_manager_coordinator.h @@ -9,6 +9,7 @@ #include <string> #include <vector> +#include "base/callback.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "components/download/public/common/download_export.h" @@ -42,7 +43,10 @@ class COMPONENTS_DOWNLOAD_EXPORT SimpleDownloadManagerCoordinator DISALLOW_COPY_AND_ASSIGN(Observer); }; - SimpleDownloadManagerCoordinator(); + using DownloadWhenFullManagerStartsCallBack = + base::RepeatingCallback<void(std::unique_ptr<DownloadUrlParameters>)>; + SimpleDownloadManagerCoordinator(const DownloadWhenFullManagerStartsCallBack& + download_when_full_manager_starts_cb); ~SimpleDownloadManagerCoordinator() override; void AddObserver(Observer* observer); @@ -63,22 +67,20 @@ class COMPONENTS_DOWNLOAD_EXPORT SimpleDownloadManagerCoordinator // Get the download item for |guid|. DownloadItem* GetDownloadByGuid(const std::string& guid); - // Return whether this object has download manager set. - bool HasSetDownloadManager(); - // Returns a non-empty notifier to be used for observing download events. AllDownloadEventNotifier* GetNotifier(); + // Whether this object is initialized. + bool initialized() const { return initialized_; } + bool has_all_history_downloads() const { return has_all_history_downloads_; } private: // SimpleDownloadManager::Observer implementation. + void OnDownloadsInitialized() override; void OnManagerGoingDown() override; void OnDownloadCreated(DownloadItem* item) override; - // Called when |simple_download_manager_| is initialized. - void OnManagerInitialized(bool has_all_history_downloads); - SimpleDownloadManager* simple_download_manager_; // Object for notifying others about various download events. @@ -87,10 +89,16 @@ class COMPONENTS_DOWNLOAD_EXPORT SimpleDownloadManagerCoordinator // Whether all the history downloads are ready. bool has_all_history_downloads_; + // Whether current SimpleDownloadManager has all history downloads. + bool current_manager_has_all_history_downloads_; + // Whether this object is initialized and active downloads are ready to be // retrieved. bool initialized_; + // Callback to download the url when full manager becomes ready. + DownloadWhenFullManagerStartsCallBack download_when_full_manager_starts_cb_; + // Observers that want to be notified of changes to the set of downloads. base::ObserverList<Observer>::Unchecked observers_; diff --git a/chromium/components/download/public/common/url_download_handler_factory.h b/chromium/components/download/public/common/url_download_handler_factory.h index bbe7195e199..8b30691e11d 100644 --- a/chromium/components/download/public/common/url_download_handler_factory.h +++ b/chromium/components/download/public/common/url_download_handler_factory.h @@ -11,7 +11,11 @@ namespace net { class URLRequestContextGetter; -} +} // namespace net + +namespace service_manager { +class Connector; +} // namespace service_manager namespace download { class DownloadURLLoaderFactoryGetter; @@ -35,6 +39,7 @@ class COMPONENTS_DOWNLOAD_EXPORT UrlDownloadHandlerFactory { url_loader_factory_getter, const URLSecurityPolicy& url_security_policy, scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); virtual ~UrlDownloadHandlerFactory(); @@ -52,6 +57,7 @@ class COMPONENTS_DOWNLOAD_EXPORT UrlDownloadHandlerFactory { url_loader_factory_getter, const URLSecurityPolicy& url_security_policy, scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, + std::unique_ptr<service_manager::Connector> connector, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) = 0; }; diff --git a/chromium/components/download/quarantine/BUILD.gn b/chromium/components/download/quarantine/BUILD.gn index 4ce7b5bdc11..395de65b0b6 100644 --- a/chromium/components/download/quarantine/BUILD.gn +++ b/chromium/components/download/quarantine/BUILD.gn @@ -2,113 +2,13 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//build/config/chromecast_build.gni") - -if (is_android) { - import("//build/config/android/rules.gni") -} - static_library("quarantine") { sources = [ "quarantine.cc", "quarantine.h", - "quarantine_features_win.cc", - "quarantine_features_win.h", - "quarantine_mac.mm", - "quarantine_win.cc", - ] - - deps = [ - ":common", - "//base", - "//net", - "//url", - ] - - if (is_mac) { - libs = [ - "Carbon.framework", - "Foundation.framework", - ] - } -} - -source_set("common") { - sources = [ - "common_mac.h", - "common_mac.mm", - "common_win.cc", - "common_win.h", ] deps = [ - "//base", + "//components/services/quarantine", ] - - if (is_mac) { - libs = [ - "Carbon.framework", - "Foundation.framework", - ] - } -} - -source_set("test_support") { - testonly = true - - sources = [ - "test_support.cc", - "test_support.h", - "test_support_mac.mm", - "test_support_win.cc", - ] - - deps = [ - ":common", - ":quarantine", - "//base", - "//url", - ] - - if (is_mac) { - libs = [ - "Carbon.framework", - "Foundation.framework", - ] - } -} - -source_set("unit_tests") { - testonly = true - - sources = [ - "quarantine_mac_unittest.mm", - "quarantine_win_unittest.cc", - ] - - # Chromecasts do not have extended attributes enabled; even if it were - # enabled, the devices use tmpfs which restricts the extended attributes that - # can be set such that quarantining still would not work. (The platform - # specific tests include a runtime guard to skip tests that need xattr.) - if (!is_chromecast) { - sources += [ "quarantine_unittest.cc" ] - } - - deps = [ - ":common", - ":quarantine", - ":test_support", - "//base", - "//base/test:test_support", - "//net", - "//testing/gtest", - "//url", - ] - - if (is_mac) { - libs = [ - "Carbon.framework", - "Foundation.framework", - ] - } } diff --git a/chromium/components/download/quarantine/DEPS b/chromium/components/download/quarantine/DEPS index 9d3031f8980..ad6fcad01aa 100644 --- a/chromium/components/download/quarantine/DEPS +++ b/chromium/components/download/quarantine/DEPS @@ -1,3 +1,3 @@ include_rules = [ - "+net/base/filename_util.h", + "+components/services/quarantine/quarantine.h", ] diff --git a/chromium/components/download/quarantine/common_mac.h b/chromium/components/download/quarantine/common_mac.h deleted file mode 100644 index 179bb137ce0..00000000000 --- a/chromium/components/download/quarantine/common_mac.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2018 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. - -#ifndef COMPONENTS_DOWNLOAD_QUARANTINE_COMMON_MAC_H_ -#define COMPONENTS_DOWNLOAD_QUARANTINE_COMMON_MAC_H_ - -#import <Foundation/Foundation.h> - -#include "base/mac/scoped_nsobject.h" - -namespace base { -class FilePath; -} - -namespace download { - -bool GetQuarantineProperties( - const base::FilePath& file, - base::scoped_nsobject<NSMutableDictionary>* properties); - -} // namespace download - -#endif // COMPONENTS_DOWNLOAD_QUARANTINE_COMMON_MAC_H_ diff --git a/chromium/components/download/quarantine/common_mac.mm b/chromium/components/download/quarantine/common_mac.mm deleted file mode 100644 index a8ccaeba2b3..00000000000 --- a/chromium/components/download/quarantine/common_mac.mm +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2018 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 "components/download/quarantine/common_mac.h" - -#import <ApplicationServices/ApplicationServices.h> - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/mac/availability.h" -#include "base/mac/foundation_util.h" -#include "base/mac/mac_logging.h" -#include "base/mac/mac_util.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/strings/sys_string_conversions.h" - -namespace download { - -bool GetQuarantineProperties( - const base::FilePath& file, - base::scoped_nsobject<NSMutableDictionary>* properties) { - base::scoped_nsobject<NSURL> file_url([[NSURL alloc] - initFileURLWithPath:base::SysUTF8ToNSString(file.value())]); - if (!file_url) - return false; - - NSError* error = nil; - id quarantine_properties = nil; - BOOL success = [file_url getResourceValue:&quarantine_properties - forKey:NSURLQuarantinePropertiesKey - error:&error]; - if (!success) { - std::string error_message(error ? error.description.UTF8String : ""); - LOG(WARNING) << "Unable to get quarantine attributes for file " - << file.value() << ". Error: " << error_message; - return false; - } - - if (!quarantine_properties) - return true; - - NSDictionary* quarantine_properties_dict = - base::mac::ObjCCast<NSDictionary>(quarantine_properties); - if (!quarantine_properties_dict) { - LOG(WARNING) << "Quarantine properties have wrong class: " - << base::SysNSStringToUTF8( - [[quarantine_properties class] description]); - return false; - } - - properties->reset([quarantine_properties_dict mutableCopy]); - return true; -} - -} // namespace download diff --git a/chromium/components/download/quarantine/common_win.cc b/chromium/components/download/quarantine/common_win.cc deleted file mode 100644 index f9d5cd9de6f..00000000000 --- a/chromium/components/download/quarantine/common_win.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2018 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 "components/download/quarantine/common_win.h" - -namespace download { - -// [MS-FSCC] Section 5.6.1 -const base::FilePath::CharType kZoneIdentifierStreamSuffix[] = - FILE_PATH_LITERAL(":Zone.Identifier"); - -} // namespace download diff --git a/chromium/components/download/quarantine/common_win.h b/chromium/components/download/quarantine/common_win.h deleted file mode 100644 index b75de7b811a..00000000000 --- a/chromium/components/download/quarantine/common_win.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018 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. - -#ifndef COMPONENTS_DOWNLOAD_QUARANTINE_COMMON_WIN_H_ -#define COMPONENTS_DOWNLOAD_QUARANTINE_COMMON_WIN_H_ - -#include "base/files/file_path.h" - -namespace download { - -extern const base::FilePath::CharType kZoneIdentifierStreamSuffix[]; - -} // namespace download - -#endif // COMPONENTS_DOWNLOAD_QUARANTINE_COMMON_WIN_H_ diff --git a/chromium/components/download/quarantine/quarantine.cc b/chromium/components/download/quarantine/quarantine.cc index ac5b109edd0..b576c4de1f7 100644 --- a/chromium/components/download/quarantine/quarantine.cc +++ b/chromium/components/download/quarantine/quarantine.cc @@ -4,19 +4,14 @@ #include "components/download/quarantine/quarantine.h" -#include "build/build_config.h" - -#if !defined(OS_WIN) && !defined(OS_MACOSX) - namespace download { QuarantineFileResult QuarantineFile(const base::FilePath& file, const GURL& source_url, const GURL& referrer_url, const std::string& client_guid) { - return QuarantineFileResult::OK; + return quarantine::QuarantineFile(file, source_url, referrer_url, + client_guid); } } // namespace download - -#endif // !WIN && !MAC diff --git a/chromium/components/download/quarantine/quarantine.h b/chromium/components/download/quarantine/quarantine.h index 8393e4a707a..eb6e94691f3 100644 --- a/chromium/components/download/quarantine/quarantine.h +++ b/chromium/components/download/quarantine/quarantine.h @@ -7,6 +7,8 @@ #include <string> +#include "components/services/quarantine/quarantine.h" + class GURL; namespace base { @@ -15,62 +17,17 @@ class FilePath; namespace download { -// Return value for QuarantineFile. -enum class QuarantineFileResult { - OK, // Success. - ACCESS_DENIED, // Access to the file was denied. The safety of the file could - // not be determined. - BLOCKED_BY_POLICY, // Downloads from |source_url| are not allowed by policy. - // The file has been deleted. - ANNOTATION_FAILED, // Unable to write the mark-of-the-web or otherwise - // annotate the file as being downloaded from - // |source_url|. - FILE_MISSING, // |file| does not name a valid file. - SECURITY_CHECK_FAILED, // An unknown error occurred while checking |file|. - // The file may have been deleted. - VIRUS_INFECTED // |file| was found to be infected by a virus and was deleted. -}; +using quarantine::QuarantineFileResult; // Quarantine a file that was downloaded from the internet. +// See components/services/quarantine/quarantine.h for a full explanation. // -// Ensures that |file| is handled as safely as possible given that it was -// downloaded from |source_url|. The details of how a downloaded file is handled -// are platform dependent. Please refer to the individual quarantine_<os> -// implementation. -// -// This function should be called for all files downloaded from the internet and -// placed in a manner discoverable by the user, or exposed to an external -// application. Furthermore, it should be called: -// -// * **AFTER** all the data has been written to the file. On Windows, registered -// anti-virus products will be invoked for scanning the contents of the file. -// Hence it's important to have the final contents of the file be available at -// the point at which this function is called. -// -// Exception: Zero-length files will be handled solely on the basis of the -// |source_url| and the file type. This exception accommodates situations -// where the file contents cannot be determined before it is made visible to -// an external application. -// -// * **AFTER** the file has been renamed to its final name. The file type is -// significant and is derived from the filename. -// -// * **BEFORE** the file is made visible to an external application or the user. -// Security checks and mark-of-the-web annotations must be made prior to -// exposing the file externally. -// -// Note that it is possible for this method to take a long time to complete -// (several seconds or more). In addition to blocking during this time, this -// delay also introduces a window during which a browser shutdown may leave the -// downloaded file unannotated. +// Forwards to the definition in components/services/quarantine. +// This is a temporary state before the quarantine service is fully implemented, +// when the call sites will be updated to use the service directly. // -// Parameters: -// |file| : Final name of the file. -// |source_url|: URL from which the file content was downloaded. This is empty -// for off-the-record download. -// |referrer_url|: Referring URL. This is empty for off-the-record download. -// |client_guid|: Only used on Windows. Identifies the client application -// that downloaded the file. +// TODO: Delete this file (and all of components/download/quarantine) +// when the quarantine service is implemented. QuarantineFileResult QuarantineFile(const base::FilePath& file, const GURL& source_url, const GURL& referrer_url, diff --git a/chromium/components/download/quarantine/quarantine_features_win.cc b/chromium/components/download/quarantine/quarantine_features_win.cc deleted file mode 100644 index 6c67f2e1e17..00000000000 --- a/chromium/components/download/quarantine/quarantine_features_win.cc +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2018 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 "components/download/quarantine/quarantine_features_win.h" - -namespace download { - -// This feature controls whether the InvokeAttachmentServices function will be -// called. Has no effect on machines that are domain-joined, where the function -// is always called. -const base::Feature kInvokeAttachmentServices{"InvokeAttachmentServices", - base::FEATURE_ENABLED_BY_DEFAULT}; - -} // namespace download diff --git a/chromium/components/download/quarantine/quarantine_features_win.h b/chromium/components/download/quarantine/quarantine_features_win.h deleted file mode 100644 index 7363a0f4f88..00000000000 --- a/chromium/components/download/quarantine/quarantine_features_win.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018 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. - -#ifndef COMPONENTS_DOWNLOAD_QUARANTINE_QUARANTINE_FEATURES_WIN_H_ -#define COMPONENTS_DOWNLOAD_QUARANTINE_QUARANTINE_FEATURES_WIN_H_ - -#include "base/feature_list.h" - -namespace download { - -extern const base::Feature kInvokeAttachmentServices; - -} // namespace download - -#endif // COMPONENTS_DOWNLOAD_QUARANTINE_QUARANTINE_FEATURES_WIN_H_ diff --git a/chromium/components/download/quarantine/quarantine_mac.mm b/chromium/components/download/quarantine/quarantine_mac.mm deleted file mode 100644 index 26b783a0ec5..00000000000 --- a/chromium/components/download/quarantine/quarantine_mac.mm +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2016 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 "components/download/quarantine/quarantine.h" - -#import <ApplicationServices/ApplicationServices.h> -#import <Foundation/Foundation.h> - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/mac/foundation_util.h" -#include "base/mac/mac_logging.h" -#include "base/mac/mac_util.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/mac/scoped_nsobject.h" -#include "base/strings/sys_string_conversions.h" -#include "base/threading/scoped_blocking_call.h" -#include "components/download/quarantine/common_mac.h" -#include "url/gurl.h" - -namespace { - -bool SetQuarantineProperties(const base::FilePath& file, - NSDictionary* properties) { - base::scoped_nsobject<NSURL> file_url([[NSURL alloc] - initFileURLWithPath:base::SysUTF8ToNSString(file.value())]); - if (!file_url) - return false; - - NSError* error = nil; - bool success = [file_url setResourceValue:properties - forKey:NSURLQuarantinePropertiesKey - error:&error]; - if (!success) { - std::string error_message(error ? error.description.UTF8String : ""); - LOG(WARNING) << "Unable to set quarantine attributes on file " - << file.value() << ". Error: " << error_message; - return false; - } - return true; -} - -} // namespace - -namespace download { - -namespace { - -// As of Mac OS X 10.4 ("Tiger"), files can be tagged with metadata describing -// various attributes. Metadata is integrated with the system's Spotlight -// feature and is searchable. Ordinarily, metadata can only be set by -// Spotlight importers, which requires that the importer own the target file. -// However, there's an attribute intended to describe the origin of a -// file, that can store the source URL and referrer of a downloaded file. -// It's stored as a "com.apple.metadata:kMDItemWhereFroms" extended attribute, -// structured as a binary1-format plist containing a list of sources. This -// attribute can only be populated by the downloader, not a Spotlight importer. -// Safari on 10.4 and later populates this attribute. -// -// With this metadata set, you can locate downloads by performing a Spotlight -// search for their source or referrer URLs, either from within the Spotlight -// UI or from the command line: -// mdfind 'kMDItemWhereFroms == "http://releases.mozilla.org/*"' -// -// There is no documented API to set metadata on a file directly as of the -// 10.5 SDK. The MDSetItemAttribute function does exist to perform this task, -// but it's undocumented. -// -// Note that the Metadata.framework in CoreServices has been superseded by the -// NSMetadata API (e.g. kMDItemWhereFroms -> NSMetadataItemWhereFromsKey, etc). -// The NSMetadata API still is a query-only interface, with no way to set -// attributes, so we continue to use the original API. -bool AddOriginMetadataToFile(const base::FilePath& file, - const GURL& source, - const GURL& referrer) { - base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, - base::BlockingType::MAY_BLOCK); - - // There's no declaration for MDItemSetAttribute in any known public SDK. - // It exists in the 10.4 and 10.5 runtimes. To play it safe, do the lookup - // at runtime instead of declaring it ourselves and linking against what's - // provided. This has two benefits: - // - If Apple relents and declares the function in a future SDK (it's - // happened before), our build won't break. - // - If Apple removes or renames the function in a future runtime, the - // loader won't refuse to let the application launch. Instead, we'll - // silently fail to set any metadata. - using MDItemSetAttribute_type = - OSStatus (*)(MDItemRef, CFStringRef, CFTypeRef); - static MDItemSetAttribute_type md_item_set_attribute_func = - []() -> MDItemSetAttribute_type { - CFBundleRef metadata_bundle = - CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Metadata")); - if (!metadata_bundle) - return nullptr; - - return reinterpret_cast<MDItemSetAttribute_type>( - CFBundleGetFunctionPointerForName(metadata_bundle, - CFSTR("MDItemSetAttribute"))); - }(); - if (!md_item_set_attribute_func) - return false; - - NSString* file_path = base::mac::FilePathToNSString(file); - if (!file_path) - return false; - - base::ScopedCFTypeRef<MDItemRef> md_item( - MDItemCreate(kCFAllocatorDefault, base::mac::NSToCFCast(file_path))); - if (!md_item) { - LOG(WARNING) << "MDItemCreate failed for path " << file.value(); - return false; - } - - // We won't put any more than 2 items into the attribute. - NSMutableArray* list = [NSMutableArray arrayWithCapacity:2]; - - // Follow Safari's lead: the first item in the list is the source URL of the - // downloaded file. If the referrer is known, store that, too. The URLs may be - // empty (e.g. files downloaded in Incognito mode); don't add empty URLs. - NSString* origin_url = base::SysUTF8ToNSString(source.spec()); - if (origin_url && [origin_url length]) - [list addObject:origin_url]; - NSString* referrer_url = base::SysUTF8ToNSString(referrer.spec()); - if (referrer_url && [referrer_url length]) - [list addObject:referrer_url]; - - if ([list count]) { - md_item_set_attribute_func(md_item, kMDItemWhereFroms, - base::mac::NSToCFCast(list)); - } - - return true; -} - -// Adds quarantine metadata to the file, assuming it has already been -// quarantined by the OS. -// |source| should be the source URL for the download, and |referrer| should be -// the URL the user initiated the download from. - -// The OS will automatically quarantine files due to the -// LSFileQuarantineEnabled entry in our Info.plist, but it knows relatively -// little about the files. We add more information about the download to -// improve the UI shown by the OS when the users tries to open the file. -bool AddQuarantineMetadataToFile(const base::FilePath& file, - const GURL& source, - const GURL& referrer) { - base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, - base::BlockingType::MAY_BLOCK); - base::scoped_nsobject<NSMutableDictionary> properties; - bool success = GetQuarantineProperties(file, &properties); - - if (!success) - return false; - - if (!properties) { - // If there are no quarantine properties, then the file isn't quarantined - // (e.g., because the user has set up exclusions for certain file types). - // We don't want to add any metadata, because that will cause the file to - // be quarantined against the user's wishes. - return true; - } - - // kLSQuarantineAgentNameKey, kLSQuarantineAgentBundleIdentifierKey, and - // kLSQuarantineTimeStampKey are set for us (see LSQuarantine.h), so we only - // need to set the values that the OS can't infer. - - if (![properties valueForKey:(NSString*)kLSQuarantineTypeKey]) { - CFStringRef type = source.SchemeIsHTTPOrHTTPS() - ? kLSQuarantineTypeWebDownload - : kLSQuarantineTypeOtherDownload; - [properties setValue:(NSString*)type - forKey:(NSString*)kLSQuarantineTypeKey]; - } - - if (![properties valueForKey:(NSString*)kLSQuarantineOriginURLKey] && - referrer.is_valid()) { - NSString* referrer_url = base::SysUTF8ToNSString(referrer.spec()); - [properties setValue:referrer_url - forKey:(NSString*)kLSQuarantineOriginURLKey]; - } - - if (![properties valueForKey:(NSString*)kLSQuarantineDataURLKey] && - source.is_valid()) { - NSString* origin_url = base::SysUTF8ToNSString(source.spec()); - [properties setValue:origin_url forKey:(NSString*)kLSQuarantineDataURLKey]; - } - - return SetQuarantineProperties(file, properties); -} - -} // namespace - -QuarantineFileResult QuarantineFile(const base::FilePath& file, - const GURL& source_url, - const GURL& referrer_url, - const std::string& client_guid) { - if (!base::PathExists(file)) - return QuarantineFileResult::FILE_MISSING; - - // Don't consider it an error if we fail to add origin metadata. - AddOriginMetadataToFile(file, source_url, referrer_url); - bool quarantine_succeeded = - AddQuarantineMetadataToFile(file, source_url, referrer_url); - return quarantine_succeeded ? QuarantineFileResult::OK - : QuarantineFileResult::ANNOTATION_FAILED; -} - -} // namespace download diff --git a/chromium/components/download/quarantine/quarantine_mac_unittest.mm b/chromium/components/download/quarantine/quarantine_mac_unittest.mm deleted file mode 100644 index d37d5eefa9e..00000000000 --- a/chromium/components/download/quarantine/quarantine_mac_unittest.mm +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2016 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 "components/download/quarantine/quarantine.h" - -#include <sys/xattr.h> - -#import <ApplicationServices/ApplicationServices.h> -#import <Foundation/Foundation.h> - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/logging.h" -#include "base/mac/foundation_util.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/mac/scoped_nsobject.h" -#include "base/strings/sys_string_conversions.h" -#include "components/download/quarantine/test_support.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/gtest_mac.h" -#include "url/gurl.h" - -namespace download { -namespace { - -class QuarantineMacTest : public testing::Test { - public: - QuarantineMacTest() - : source_url_("http://www.source.example.com"), - referrer_url_("http://www.referrer.example.com") {} - - protected: - void SetUp() override { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - ASSERT_TRUE( - base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &test_file_)); - file_url_.reset([[NSURL alloc] - initFileURLWithPath:base::SysUTF8ToNSString(test_file_.value())]); - - base::scoped_nsobject<NSMutableDictionary> properties( - [[NSMutableDictionary alloc] init]); - [properties - setValue:@"com.google.Chrome" - forKey:static_cast<NSString*>(kLSQuarantineAgentBundleIdentifierKey)]; - [properties setValue:@"Google Chrome.app" - forKey:static_cast<NSString*>(kLSQuarantineAgentNameKey)]; - [properties setValue:@(1) forKey:@"kLSQuarantineIsOwnedByCurrentUserKey"]; - bool success = [file_url_ setResourceValue:properties - forKey:NSURLQuarantinePropertiesKey - error:nullptr]; - ASSERT_TRUE(success); - } - - base::ScopedTempDir temp_dir_; - base::FilePath test_file_; - GURL source_url_; - GURL referrer_url_; - base::scoped_nsobject<NSURL> file_url_; -}; - -TEST_F(QuarantineMacTest, CheckMetadataSetCorrectly) { - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file_, source_url_, referrer_url_, "")); - EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_)); -} - -TEST_F(QuarantineMacTest, SetMetadataMultipleTimes) { - GURL dummy_url("http://www.dummy.example.com"); - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file_, source_url_, referrer_url_, "")); - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file_, dummy_url, dummy_url, "")); - EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_)); -} - -TEST_F(QuarantineMacTest, IsFileQuarantined_NoFile) { - base::FilePath does_not_exist = temp_dir_.GetPath().AppendASCII("a.jar"); - EXPECT_FALSE(IsFileQuarantined(does_not_exist, GURL(), GURL())); -} - -TEST_F(QuarantineMacTest, IsFileQuarantined_NoAnnotationsOnFile) { - EXPECT_FALSE(IsFileQuarantined(test_file_, GURL(), GURL())); -} - -TEST_F(QuarantineMacTest, IsFileQuarantined_SourceUrlOnly) { - ASSERT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file_, source_url_, GURL(), std::string())); - EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, GURL())); - EXPECT_TRUE(IsFileQuarantined(test_file_, GURL(), GURL())); - EXPECT_TRUE(IsFileQuarantined(test_file_, GURL(), referrer_url_)); - EXPECT_FALSE(IsFileQuarantined(test_file_, referrer_url_, GURL())); -} - -TEST_F(QuarantineMacTest, IsFileQuarantined_FullMetadata) { - ASSERT_EQ( - QuarantineFileResult::OK, - QuarantineFile(test_file_, source_url_, referrer_url_, std::string())); - EXPECT_TRUE(IsFileQuarantined(test_file_, GURL(), GURL())); - EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, GURL())); - EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_)); - EXPECT_TRUE(IsFileQuarantined(test_file_, GURL(), referrer_url_)); - EXPECT_FALSE(IsFileQuarantined(test_file_, source_url_, source_url_)); - EXPECT_FALSE(IsFileQuarantined(test_file_, referrer_url_, referrer_url_)); -} - -TEST_F(QuarantineMacTest, NoWhereFromsKeyIfNoURLs) { - ASSERT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file_, GURL(), GURL(), std::string())); - - NSString* file_path = base::mac::FilePathToNSString(test_file_); - ASSERT_NE(nullptr, file_path); - base::ScopedCFTypeRef<MDItemRef> md_item( - MDItemCreate(kCFAllocatorDefault, base::mac::NSToCFCast(file_path))); - if (!md_item) { - // The quarantine code ignores it if adding origin metadata fails. If for - // some reason MDItemCreate fails (which it seems to do on the bots, not - // sure why) just stop on the test. - return; - } - - base::ScopedCFTypeRef<CFTypeRef> attr( - MDItemCopyAttribute(md_item, kMDItemWhereFroms)); - EXPECT_FALSE(attr); -} - -} // namespace -} // namespace download diff --git a/chromium/components/download/quarantine/quarantine_unittest.cc b/chromium/components/download/quarantine/quarantine_unittest.cc deleted file mode 100644 index a70fd27eb41..00000000000 --- a/chromium/components/download/quarantine/quarantine_unittest.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2016 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 "components/download/quarantine/quarantine.h" - -#include <iterator> -#include <string> - -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/stl_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" - -namespace download { - -namespace { - -const char kTestData[] = "It's okay to have a trailing nul."; -const char kInternetURL[] = "http://example.com/some-url"; -const char kInternetReferrerURL[] = "http://example.com/some-other-url"; -const char kTestGUID[] = "69f8621d-c46a-4e88-b915-1ce5415cb008"; - -} // namespace - -TEST(QuarantineTest, FileCanBeOpenedForReadAfterAnnotation) { - base::ScopedTempDir test_dir; - ASSERT_TRUE(test_dir.CreateUniqueTempDir()); - - base::FilePath test_file = test_dir.GetPath().AppendASCII("foo.class"); - ASSERT_EQ(static_cast<int>(base::size(kTestData)), - base::WriteFile(test_file, kTestData, base::size(kTestData))); - - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file, GURL(kInternetURL), - GURL(kInternetReferrerURL), kTestGUID)); - - std::string contents; - EXPECT_TRUE(base::ReadFileToString(test_file, &contents)); - EXPECT_EQ(std::string(std::begin(kTestData), std::end(kTestData)), contents); -} - -TEST(QuarantineTest, FileCanBeAnnotatedWithNoGUID) { - base::ScopedTempDir test_dir; - ASSERT_TRUE(test_dir.CreateUniqueTempDir()); - - base::FilePath test_file = test_dir.GetPath().AppendASCII("foo.class"); - ASSERT_EQ(static_cast<int>(base::size(kTestData)), - base::WriteFile(test_file, kTestData, base::size(kTestData))); - - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file, GURL(kInternetURL), - GURL(kInternetReferrerURL), std::string())); -} - -} // namespace download diff --git a/chromium/components/download/quarantine/quarantine_win.cc b/chromium/components/download/quarantine/quarantine_win.cc deleted file mode 100644 index eaf6370d144..00000000000 --- a/chromium/components/download/quarantine/quarantine_win.cc +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright 2016 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 "components/download/quarantine/quarantine.h" - -#include <windows.h> -#include <wrl/client.h> - -#include <cguid.h> -#include <objbase.h> -#include <shellapi.h> -#include <shlobj.h> -#include <shobjidl.h> -#include <wininet.h> - -#include <vector> - -#include "base/enterprise_util.h" -#include "base/feature_list.h" -#include "base/files/file_util.h" -#include "base/guid.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/scoped_blocking_call.h" -#include "base/win/scoped_handle.h" -#include "base/win/win_util.h" -#include "base/win/windows_version.h" -#include "components/download/quarantine/common_win.h" -#include "components/download/quarantine/quarantine_features_win.h" -#include "url/gurl.h" - -namespace download { -namespace { - -// Returns true for a valid |url| whose length does not exceed -// INTERNET_MAX_URL_LENGTH. -bool IsValidUrlForAttachmentServices(const GURL& url) { - return url.is_valid() && url.spec().size() <= INTERNET_MAX_URL_LENGTH; -} - -// Sets the Zone Identifier on the file to "Internet" (3). Returns true if the -// function succeeds, false otherwise. A failure is expected if alternate -// streams are not supported, like a file on a FAT32 filesystem. This function -// does not invoke Windows Attachment Execution Services. -// -// On Windows 10 or higher, the ReferrerUrl and HostUrl values are set according -// to the behavior of the IAttachmentExecute interface. -// -// |full_path| is the path to the downloaded file. -QuarantineFileResult SetInternetZoneIdentifierDirectly( - const base::FilePath& full_path, - const GURL& source_url, - const GURL& referrer_url) { - const DWORD kShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - std::wstring path = full_path.value() + kZoneIdentifierStreamSuffix; - base::win::ScopedHandle file(::CreateFile(path.c_str(), GENERIC_WRITE, kShare, - nullptr, OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, nullptr)); - if (!file.IsValid()) - return QuarantineFileResult::ANNOTATION_FAILED; - - static const char kReferrerUrlFormat[] = "ReferrerUrl=%s\r\n"; - static const char kHostUrlFormat[] = "HostUrl=%s\r\n"; - - std::string identifier = "[ZoneTransfer]\r\nZoneId=3\r\n"; - if (base::win::GetVersion() >= base::win::VERSION_WIN10) { - // Match what the InvokeAttachmentServices() function will output, including - // the order of the values. - if (IsValidUrlForAttachmentServices(referrer_url)) { - identifier.append( - base::StringPrintf(kReferrerUrlFormat, referrer_url.spec().c_str())); - } - identifier.append(base::StringPrintf( - kHostUrlFormat, IsValidUrlForAttachmentServices(source_url) - ? source_url.spec().c_str() - : "about:internet")); - } - - // Don't include trailing null in data written. - DWORD written = 0; - BOOL write_result = ::WriteFile(file.Get(), identifier.c_str(), - identifier.length(), &written, nullptr); - BOOL flush_result = FlushFileBuffers(file.Get()); - - return write_result && flush_result && written == identifier.length() - ? QuarantineFileResult::OK - : QuarantineFileResult::ANNOTATION_FAILED; -} - -// Maps a return code from an unsuccessful IAttachmentExecute::Save() call to a -// QuarantineFileResult. -// -// Typical return codes from IAttachmentExecute::Save(): -// S_OK : The file was okay. If any viruses were found, they were cleaned. -// E_FAIL : Virus infected. -// INET_E_SECURITY_PROBLEM : The file was blocked due to security policy. -// -// Any other return value indicates an unexpected error during the scan. -QuarantineFileResult FailedSaveResultToQuarantineResult(HRESULT result) { - switch (result) { - case INET_E_SECURITY_PROBLEM: // 0x800c000e - // This is returned if the download was blocked due to security - // restrictions. E.g. if the source URL was in the Restricted Sites zone - // and downloads are blocked on that zone, then the download would be - // deleted and this error code is returned. - return QuarantineFileResult::BLOCKED_BY_POLICY; - - case E_FAIL: // 0x80004005 - // Returned if an anti-virus product reports an infection in the - // downloaded file during IAE::Save(). - return QuarantineFileResult::VIRUS_INFECTED; - - default: - // Any other error that occurs during IAttachmentExecute::Save() likely - // indicates a problem with the security check, but not necessarily the - // download. This also includes cases where SUCCEEDED(result) is true. In - // the latter case we are likely dealing with a situation where the file - // is missing after a successful scan. See http://crbug.com/153212. - return QuarantineFileResult::SECURITY_CHECK_FAILED; - } -} - -// Invokes IAttachmentExecute::Save on CLSID_AttachmentServices to validate the -// downloaded file. The call may scan the file for viruses and if necessary, -// annotate it with evidence. As a result of the validation, the file may be -// deleted. See: http://msdn.microsoft.com/en-us/bb776299 -// -// IAE::Save() will delete the file if it was found to be blocked by local -// security policy or if it was found to be infected. The call may also delete -// the file due to other failures (http://crbug.com/153212). In these cases, -// |result| will contain the failure code. -// -// The return value is |false| iff the function fails to invoke -// IAttachmentExecute::Save(). If the function returns |true|, then the result -// of invoking IAttachmentExecute::Save() is stored in |result|. -// -// |full_path| : is the path to the downloaded file. This should be the final -// path of the download. Must be present. -// |source_url|: the source URL for the download. If empty, the source will -// be set to 'about:internet'. -// |referrer_url|: the referrer URL for the download. If empty, the referrer -// will not be set. -// |client_guid|: the GUID to be set in the IAttachmentExecute client slot. -// Used to identify the app to the system AV function. -// |result|: Receives the result of invoking IAttachmentExecute::Save(). -bool InvokeAttachmentServices(const base::FilePath& full_path, - const GURL& source_url, - const GURL& referrer_url, - const GUID& client_guid, - QuarantineFileResult* result) { - Microsoft::WRL::ComPtr<IAttachmentExecute> attachment_services; - HRESULT hr = ::CoCreateInstance(CLSID_AttachmentServices, nullptr, CLSCTX_ALL, - IID_PPV_ARGS(&attachment_services)); - - if (FAILED(hr)) { - // The thread must have COM initialized. - DCHECK_NE(CO_E_NOTINITIALIZED, hr); - return false; - } - - // Note that it is mandatory to check the return values from here on out. If - // setting one of the parameters fails, it could leave the object in a state - // where the final Save() call will also fail. - - hr = attachment_services->SetClientGuid(client_guid); - if (FAILED(hr)) - return false; - - hr = attachment_services->SetLocalPath(full_path.value().c_str()); - if (FAILED(hr)) - return false; - - // The source URL could be empty if it was not a valid URL, or was not HTTP/S, - // or the download was off-the-record. If so, use "about:internet" as a - // fallback URL. The latter is known to reliably map to the Internet zone. - // - // In addition, URLs that are longer than INTERNET_MAX_URL_LENGTH are also - // known to cause problems for URLMon. Hence also use "about:internet" in - // these cases. See http://crbug.com/601538. - hr = attachment_services->SetSource( - IsValidUrlForAttachmentServices(source_url) - ? base::UTF8ToWide(source_url.spec()).c_str() - : L"about:internet"); - if (FAILED(hr)) - return false; - - // Only set referrer if one is present and shorter than - // INTERNET_MAX_URL_LENGTH. Also, the source_url is authoritative for - // determining the relative danger of |full_path| so we don't consider it an - // error if we have to skip the |referrer_url|. - if (IsValidUrlForAttachmentServices(referrer_url)) { - hr = attachment_services->SetReferrer( - base::UTF8ToWide(referrer_url.spec()).c_str()); - if (FAILED(hr)) - return false; - } - - HRESULT save_result = S_OK; - { - // This method has been known to take longer than 10 seconds in some - // instances. - SCOPED_UMA_HISTOGRAM_LONG_TIMER("Download.AttachmentServices.Duration"); - save_result = attachment_services->Save(); - } - - // If the download file is missing after the call, then treat this as an - // interrupted download. - // - // If IAttachmentExecute::Save() failed, but the downloaded file is still - // around, then don't interrupt the download. Attachment Execution Services - // deletes the submitted file if the downloaded file is blocked by policy or - // if it was found to be infected. - // - // If the file is still there, then the error could be due to Windows - // Attachment Services not being available or some other error during the AES - // invocation. In either case, we don't surface the error to the user. - *result = base::PathExists(full_path) - ? QuarantineFileResult::OK - : FailedSaveResultToQuarantineResult(save_result); - - return true; -} - -} // namespace - -QuarantineFileResult QuarantineFile(const base::FilePath& file, - const GURL& source_url, - const GURL& referrer_url, - const std::string& client_guid) { - base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, - base::BlockingType::MAY_BLOCK); - - int64_t file_size = 0; - if (!base::PathExists(file) || !base::GetFileSize(file, &file_size)) - return QuarantineFileResult::FILE_MISSING; - - std::string braces_guid = "{" + client_guid + "}"; - GUID guid = GUID_NULL; - if (base::IsValidGUID(client_guid)) { - HRESULT hr = CLSIDFromString(base::UTF8ToUTF16(braces_guid).c_str(), &guid); - if (FAILED(hr)) - guid = GUID_NULL; - } - - if (file_size == 0 || IsEqualGUID(guid, GUID_NULL)) { - // Calling InvokeAttachmentServices on an empty file can result in the file - // being deleted. Also an anti-virus scan doesn't make a lot of sense to - // perform on an empty file. - return SetInternetZoneIdentifierDirectly(file, source_url, referrer_url); - } - - // Check if the attachment services should be invoked based on the experiment - // state. Not invoking the attachment services means that the Zone Identifier - // will always be set to 3 (Internet), regardless of URL zones configurations. - // - // Note: The attachment services must always be invoked on domain-joined - // machines. - // TODO(pmonette): Move the InvokeAttachmentServices() call to a utility - // process and remove the feature. - bool should_invoke_attachment_services = - base::IsMachineExternallyManaged() || - base::FeatureList::IsEnabled(kInvokeAttachmentServices); - - QuarantineFileResult attachment_services_result = QuarantineFileResult::OK; - if (should_invoke_attachment_services && - InvokeAttachmentServices(file, source_url, referrer_url, guid, - &attachment_services_result)) { - return attachment_services_result; - } - - return SetInternetZoneIdentifierDirectly(file, source_url, referrer_url); -} - -} // namespace download diff --git a/chromium/components/download/quarantine/quarantine_win_unittest.cc b/chromium/components/download/quarantine/quarantine_win_unittest.cc deleted file mode 100644 index 0c663a947c1..00000000000 --- a/chromium/components/download/quarantine/quarantine_win_unittest.cc +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright 2016 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 <windows.h> - -#include <wininet.h> - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/macros.h" -#include "base/strings/stringprintf.h" -#include "base/test/scoped_feature_list.h" -#include "base/test/test_file_util.h" -#include "base/test/test_reg_util_win.h" -#include "base/win/win_util.h" -#include "base/win/windows_version.h" -#include "components/download/quarantine/quarantine.h" -#include "components/download/quarantine/quarantine_features_win.h" -#include "net/base/filename_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" - -namespace download { - -namespace { - -const char kDummySourceUrl[] = "https://example.com/foo"; -const char kDummyReferrerUrl[] = "https://example.com/referrer"; -const char kDummyClientGuid[] = "A1B69307-8FA2-4B6F-9181-EA06051A48A7"; - -const char* const kUntrustedURLs[] = { - "http://example.com/foo", - "https://example.com/foo", - "ftp://example.com/foo", - "ftp://example.com:2121/foo", - "data:text/plain,Hello%20world", - "blob://example.com/126278b3-58f3-4b4a-a914-1d1185d634f6", - "about:internet", - ""}; - -// Creates a non-empty file at |file_path|. -bool CreateFile(const base::FilePath& file_path) { - constexpr char kTestData[] = "Hello world!"; - - return base::WriteFile(file_path, kTestData, base::size(kTestData)) == - static_cast<int>(base::size(kTestData)); -} - -base::FilePath GetZoneIdentifierStreamPath(const base::FilePath& file_path) { - const base::FilePath::CharType kMotwStreamSuffix[] = - FILE_PATH_LITERAL(":Zone.Identifier"); - - return base::FilePath(file_path.value() + kMotwStreamSuffix); -} - -// Reads the Zone.Identifier alternate data stream from |file_path| into -// |contents|. -bool GetZoneIdentifierStreamContents(const base::FilePath& file_path, - std::string* contents) { - DCHECK(contents); - return base::ReadFileToString(GetZoneIdentifierStreamPath(file_path), - contents); -} - -// Maps a domain and protocol to a zone. -class ScopedZoneForSite { - public: - enum ZoneIdentifierType : DWORD { - kMyComputer = 0, - kLocalIntranetZone = 1, - kTrustedSitesZone = 2, - kInternetZone = 3, - kRestrictedSitesZone = 4, - }; - - ScopedZoneForSite(const base::string16& domain, - const base::string16& protocol, - ZoneIdentifierType zone_identifier_type); - ~ScopedZoneForSite(); - - private: - base::string16 domain_; - base::string16 protocol_; - - DISALLOW_COPY_AND_ASSIGN(ScopedZoneForSite); -}; - -ScopedZoneForSite::ScopedZoneForSite(const base::string16& domain, - const base::string16& protocol, - ZoneIdentifierType zone_identifier_type) - : domain_(domain), protocol_(protocol) { - base::string16 registry_path = base::StringPrintf( - L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet " - L"Settings\\ZoneMap\\Domains\\%ls", - domain_.c_str()); - base::win::RegKey registry_key(HKEY_CURRENT_USER, registry_path.c_str(), - KEY_SET_VALUE); - - EXPECT_EQ(registry_key.WriteValue(protocol_.c_str(), zone_identifier_type), - ERROR_SUCCESS); -} - -ScopedZoneForSite::~ScopedZoneForSite() { - base::string16 registry_path = base::StringPrintf( - L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet " - L"Settings\\ZoneMap\\Domains\\%ls", - domain_.c_str()); - base::win::RegKey registry_key(HKEY_CURRENT_USER, registry_path.c_str(), - KEY_SET_VALUE); - registry_key.DeleteValue(protocol_.c_str()); -} - -// Sets the internet Zone.Identifier alternate data stream for |file_path|. -bool AddInternetZoneIdentifierDirectly(const base::FilePath& file_path) { - static const char kMotwForInternetZone[] = "[ZoneTransfer]\r\nZoneId=3\r\n"; - return base::WriteFile(GetZoneIdentifierStreamPath(file_path), - kMotwForInternetZone, - base::size(kMotwForInternetZone)) == - static_cast<int>(base::size(kMotwForInternetZone)); -} - -} // namespace - -class QuarantineWinTest : public ::testing::Test { - public: - QuarantineWinTest() = default; - ~QuarantineWinTest() override = default; - - void SetUp() override { - ASSERT_NO_FATAL_FAILURE( - registry_override_.OverrideRegistry(HKEY_CURRENT_USER)); - ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); - - scoped_zone_for_trusted_site_ = std::make_unique<ScopedZoneForSite>( - GetTrustedSite(), L"https", - ScopedZoneForSite::ZoneIdentifierType::kTrustedSitesZone); - scoped_zone_for_restricted_site_ = std::make_unique<ScopedZoneForSite>( - GetRestrictedSite(), L"https", - ScopedZoneForSite::ZoneIdentifierType::kRestrictedSitesZone); - } - - base::FilePath GetTempDir() { return scoped_temp_dir_.GetPath(); } - - const wchar_t* GetTrustedSite() { return L"thisisatrustedsite.com"; } - - const wchar_t* GetRestrictedSite() { return L"thisisarestrictedsite.com"; } - - private: - registry_util::RegistryOverrideManager registry_override_; - - base::ScopedTempDir scoped_temp_dir_; - - // Due to caching, these sites zone must be set for all tests, so that the - // order the tests are run does not matter. - std::unique_ptr<ScopedZoneForSite> scoped_zone_for_trusted_site_; - std::unique_ptr<ScopedZoneForSite> scoped_zone_for_restricted_site_; - - DISALLOW_COPY_AND_ASSIGN(QuarantineWinTest); -}; - -// If the file is missing, the QuarantineFile() call should return FILE_MISSING. -TEST_F(QuarantineWinTest, MissingFile) { - EXPECT_EQ(QuarantineFileResult::FILE_MISSING, - QuarantineFile(GetTempDir().AppendASCII("does-not-exist.exe"), - GURL(kDummySourceUrl), GURL(kDummyReferrerUrl), - kDummyClientGuid)); -} - -// On Windows systems, files downloaded from a local source are considered -// trustworthy. Hence they aren't annotated with source information. This test -// verifies this behavior since the other tests in this suite would pass with a -// false positive if local files are being annotated with the MOTW for the -// internet zone. -TEST_F(QuarantineWinTest, LocalFile_DependsOnLocalConfig) { - base::FilePath test_file = GetTempDir().AppendASCII("foo.exe"); - - const char* const kLocalSourceURLs[] = {"http://localhost/foo", - "file:///C:/some-local-dir/foo.exe"}; - - for (const char* source_url : kLocalSourceURLs) { - SCOPED_TRACE(::testing::Message() << "Trying URL " << source_url); - - ASSERT_TRUE(CreateFile(test_file)); - - EXPECT_EQ( - QuarantineFileResult::OK, - QuarantineFile(test_file, GURL(source_url), GURL(), kDummyClientGuid)); - - std::string zone_identifier; - GetZoneIdentifierStreamContents(test_file, &zone_identifier); - - // No zone identifier for local source. - EXPECT_TRUE(zone_identifier.empty()); - - ASSERT_TRUE(base::DeleteFile(test_file, false)); - } -} - -// A file downloaded from the internet should be annotated with .. something. -// The specific zone assigned to our dummy source URL depends on the local -// configuration. But no sane configuration should be treating the dummy URL as -// a trusted source for anything. -TEST_F(QuarantineWinTest, DownloadedFile_DependsOnLocalConfig) { - base::FilePath test_file = GetTempDir().AppendASCII("foo.exe"); - - for (const char* source_url : kUntrustedURLs) { - SCOPED_TRACE(::testing::Message() << "Trying URL " << source_url); - - ASSERT_TRUE(CreateFile(test_file)); - EXPECT_EQ( - QuarantineFileResult::OK, - QuarantineFile(test_file, GURL(source_url), GURL(), kDummyClientGuid)); - - std::string zone_identifier; - ASSERT_TRUE(GetZoneIdentifierStreamContents(test_file, &zone_identifier)); - - // The actual assigned zone could be anything and the contents of the zone - // identifier depends on the version of Windows. So only testing that there - // is a zone annotation. - EXPECT_FALSE(zone_identifier.empty()); - - ASSERT_TRUE(base::DeleteFile(test_file, false)); - } -} - -TEST_F(QuarantineWinTest, UnsafeReferrer_DependsOnLocalConfig) { - base::FilePath test_file = GetTempDir().AppendASCII("foo.exe"); - - std::vector<std::string> unsafe_referrers(std::begin(kUntrustedURLs), - std::end(kUntrustedURLs)); - - // Add one more test case. - std::string huge_referrer = "http://example.com/"; - huge_referrer.append(INTERNET_MAX_URL_LENGTH * 2, 'a'); - unsafe_referrers.push_back(huge_referrer); - - for (const auto referrer_url : unsafe_referrers) { - SCOPED_TRACE(::testing::Message() << "Trying URL " << referrer_url); - - ASSERT_TRUE(CreateFile(test_file)); - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file, GURL("http://example.com/good"), - GURL(referrer_url), kDummyClientGuid)); - - std::string zone_identifier; - ASSERT_TRUE(GetZoneIdentifierStreamContents(test_file, &zone_identifier)); - - // The actual assigned zone could be anything and the contents of the zone - // identifier depends on the version of Windows. So only testing that there - // is a zone annotation. - EXPECT_FALSE(zone_identifier.empty()); - - ASSERT_TRUE(base::DeleteFile(test_file, false)); - } -} - -// An empty source URL should result in a file that's treated the same as one -// downloaded from the internet. -TEST_F(QuarantineWinTest, EmptySource_DependsOnLocalConfig) { - base::FilePath test_file = GetTempDir().AppendASCII("foo.exe"); - ASSERT_TRUE(CreateFile(test_file)); - - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file, GURL(), GURL(), kDummyClientGuid)); - - std::string zone_identifier; - ASSERT_TRUE(GetZoneIdentifierStreamContents(test_file, &zone_identifier)); - - // The actual assigned zone could be anything and the contents of the zone - // identifier depends on the version of Windows. So only testing that there is - // a zone annotation. - EXPECT_FALSE(zone_identifier.empty()); -} - -// Empty files aren't passed to AVScanFile. They are instead marked manually. If -// the file is passed to AVScanFile, then there wouldn't be a MOTW attached to -// it and the test would fail. -TEST_F(QuarantineWinTest, EmptyFile) { - base::FilePath test_file = GetTempDir().AppendASCII("foo.exe"); - ASSERT_EQ(0, base::WriteFile(test_file, "", 0u)); - - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file, net::FilePathToFileURL(test_file), GURL(), - kDummyClientGuid)); - - std::string zone_identifier; - ASSERT_TRUE(GetZoneIdentifierStreamContents(test_file, &zone_identifier)); - - // The actual assigned zone could be anything and the contents of the zone - // identifier depends on the version of Windows. So only testing that there is - // a zone annotation. - EXPECT_FALSE(zone_identifier.empty()); -} - -// If there is no client GUID supplied to the QuarantineFile() call, then rather -// than invoking AVScanFile, the MOTW will be applied manually. If the file is -// passed to AVScanFile, then there wouldn't be a MOTW attached to it and the -// test would fail. -TEST_F(QuarantineWinTest, NoClientGuid) { - base::FilePath test_file = GetTempDir().AppendASCII("foo.exe"); - ASSERT_TRUE(CreateFile(test_file)); - - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file, net::FilePathToFileURL(test_file), GURL(), - std::string())); - - std::string zone_identifier; - ASSERT_TRUE(GetZoneIdentifierStreamContents(test_file, &zone_identifier)); - - // The actual assigned zone could be anything and the contents of the zone - // identifier depends on the version of Windows. So only testing that there is - // a zone annotation. - EXPECT_FALSE(zone_identifier.empty()); -} - -// URLs longer than INTERNET_MAX_URL_LENGTH are known to break URLMon. Such a -// URL, when used as a source URL shouldn't break QuarantineFile() which should -// mark the file as being from the internet zone as a safe fallback. -TEST_F(QuarantineWinTest, SuperLongURL) { - base::FilePath test_file = GetTempDir().AppendASCII("foo.exe"); - ASSERT_TRUE(CreateFile(test_file)); - - std::string source_url("http://example.com/"); - source_url.append(INTERNET_MAX_URL_LENGTH * 2, 'a'); - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file, GURL(source_url), GURL(), std::string())); - - std::string zone_identifier; - ASSERT_TRUE(GetZoneIdentifierStreamContents(test_file, &zone_identifier)); - - // The actual assigned zone could be anything and the contents of the zone - // identifier depends on the version of Windows. So only testing that there is - // a zone annotation. - EXPECT_FALSE(zone_identifier.empty()); -} - -TEST_F(QuarantineWinTest, TrustedSite) { - // Test file path and source URL. - base::FilePath test_file = GetTempDir().AppendASCII("good.exe"); - GURL source_url = GURL( - base::StringPrintf(L"https://%ls/folder/good.exe", GetTrustedSite())); - - ASSERT_TRUE(CreateFile(test_file)); - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file, source_url, GURL(), kDummyClientGuid)); - - // No zone identifier. - std::string zone_identifier; - EXPECT_FALSE(GetZoneIdentifierStreamContents(test_file, &zone_identifier)); -} - -TEST_F(QuarantineWinTest, RestrictedSite) { - // Test file path and source URL. - base::FilePath test_file = GetTempDir().AppendASCII("bad.exe"); - GURL source_url = GURL( - base::StringPrintf(L"https://%ls/folder/bad.exe", GetRestrictedSite())); - - ASSERT_TRUE(CreateFile(test_file)); - - // Files from a restricted site are deleted. - EXPECT_EQ(QuarantineFileResult::BLOCKED_BY_POLICY, - QuarantineFile(test_file, source_url, GURL(), kDummyClientGuid)); - - std::string zone_identifier; - EXPECT_FALSE(GetZoneIdentifierStreamContents(test_file, &zone_identifier)); -} - -TEST_F(QuarantineWinTest, TrustedSite_AlreadyQuarantined) { - // Test file path and source URL. - base::FilePath test_file = GetTempDir().AppendASCII("good.exe"); - GURL source_url = GURL( - base::StringPrintf(L"https://%ls/folder/good.exe", GetTrustedSite())); - - ASSERT_TRUE(CreateFile(test_file)); - // Ensure the file already contains a zone identifier. - ASSERT_TRUE(AddInternetZoneIdentifierDirectly(test_file)); - EXPECT_EQ(QuarantineFileResult::OK, - QuarantineFile(test_file, source_url, GURL(), kDummyClientGuid)); - - // The existing zone identifier was not removed. - std::string zone_identifier; - EXPECT_TRUE(GetZoneIdentifierStreamContents(test_file, &zone_identifier)); - - EXPECT_FALSE(zone_identifier.empty()); -} - -TEST_F(QuarantineWinTest, RestrictedSite_AlreadyQuarantined) { - // Test file path and source URL. - base::FilePath test_file = GetTempDir().AppendASCII("bad.exe"); - GURL source_url = GURL( - base::StringPrintf(L"https://%ls/folder/bad.exe", GetRestrictedSite())); - - ASSERT_TRUE(CreateFile(test_file)); - // Ensure the file already contains a zone identifier. - ASSERT_TRUE(AddInternetZoneIdentifierDirectly(test_file)); - - // Files from a restricted site are deleted. - EXPECT_EQ(QuarantineFileResult::BLOCKED_BY_POLICY, - QuarantineFile(test_file, source_url, GURL(), kDummyClientGuid)); - - std::string zone_identifier; - EXPECT_FALSE(GetZoneIdentifierStreamContents(test_file, &zone_identifier)); -} - -} // namespace download diff --git a/chromium/components/download/quarantine/test_support.cc b/chromium/components/download/quarantine/test_support.cc deleted file mode 100644 index 55ef0628d00..00000000000 --- a/chromium/components/download/quarantine/test_support.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 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 "components/download/quarantine/test_support.h" - -#include "build/build_config.h" - -#if !defined(OS_WIN) && !defined(OS_MACOSX) - -namespace download { - -bool IsFileQuarantined(const base::FilePath& file, - const GURL& source_url, - const GURL& referrer_url) { - return false; -} - -} // namespace download - -#endif // !WIN && !MAC diff --git a/chromium/components/download/quarantine/test_support.h b/chromium/components/download/quarantine/test_support.h deleted file mode 100644 index 38e601cdd27..00000000000 --- a/chromium/components/download/quarantine/test_support.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 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. - -#ifndef COMPONENTS_DOWNLOAD_QUARANTINE_TEST_SUPPORT_H_ -#define COMPONENTS_DOWNLOAD_QUARANTINE_TEST_SUPPORT_H_ - -#include <string> - -class GURL; - -namespace base { -class FilePath; -} - -namespace download { - -// Determine if a file has quarantine metadata attached to it. -// -// If |source_url| is non-empty, then the download source URL in -// quarantine metadata should match |source_url| exactly. The function returns -// |false| if there is a mismatch. If |source_url| is empty, then this function -// only checks for the existence of a download source URL in quarantine -// metadata. -// -// If |referrer_url| is valid, then the download referrer URL in quarantine -// metadata must match |referrer_url| exactly. The function returns |false| if -// there is a mismatch in the |referrer_url| even if the |source_url| matches. -// No referrer URL checks are performed if |referrer_url| is empty. -// -// If both |source_url| and |referrer_url| are empty, then the function returns -// true if any quarantine metadata is present for the file. -// -// **Note**: On Windows 8 or lower, this function only checks if the -// |ZoneIdentifier| metadata is present. |source_url| and |referrer_url| are -// ignored. Individual URLs are only stored as part of the mark-of-the-web since -// Windows 10. -bool IsFileQuarantined(const base::FilePath& file, - const GURL& source_url, - const GURL& referrer_url); - -} // namespace download - -#endif // COMPONENTS_DOWNLOAD_QUARANTINE_TEST_SUPPORT_H_ diff --git a/chromium/components/download/quarantine/test_support_mac.mm b/chromium/components/download/quarantine/test_support_mac.mm deleted file mode 100644 index 54369105c91..00000000000 --- a/chromium/components/download/quarantine/test_support_mac.mm +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018 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 "components/download/quarantine/test_support.h" - -#import <ApplicationServices/ApplicationServices.h> -#import <Foundation/Foundation.h> - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/mac/scoped_nsobject.h" -#include "base/strings/sys_string_conversions.h" -#include "base/threading/scoped_blocking_call.h" -#include "components/download/quarantine/common_mac.h" -#include "url/gurl.h" - -namespace download { - -bool IsFileQuarantined(const base::FilePath& file, - const GURL& expected_source_url, - const GURL& referrer_url) { - base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, - base::BlockingType::MAY_BLOCK); - - if (!base::PathExists(file)) - return false; - - base::scoped_nsobject<NSMutableDictionary> properties; - bool success = GetQuarantineProperties(file, &properties); - - if (!success || !properties) - return false; - - NSString* source_url = - [[properties valueForKey:(NSString*)kLSQuarantineDataURLKey] description]; - - if (!expected_source_url.is_valid()) - return [source_url length] > 0; - - if (![source_url isEqualToString:base::SysUTF8ToNSString( - expected_source_url.spec())]) { - return false; - } - - return !referrer_url.is_valid() || - [[[properties valueForKey:(NSString*)kLSQuarantineOriginURLKey] - description] - isEqualToString:base::SysUTF8ToNSString(referrer_url.spec())]; -} - -} // namespace download diff --git a/chromium/components/download/quarantine/test_support_win.cc b/chromium/components/download/quarantine/test_support_win.cc deleted file mode 100644 index 4220849c126..00000000000 --- a/chromium/components/download/quarantine/test_support_win.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018 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 "components/download/quarantine/test_support.h" - -#include <windows.h> - -#include <string> -#include <vector> - -#include "base/files/file_path.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_split.h" -#include "base/win/scoped_handle.h" -#include "components/download/quarantine/common_win.h" - -namespace download { - -namespace { - -bool ZoneIdentifierPresentForFile(const base::FilePath& path) { - const DWORD kShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - base::FilePath::StringType zone_identifier_path = - path.value() + kZoneIdentifierStreamSuffix; - base::win::ScopedHandle file( - ::CreateFile(zone_identifier_path.c_str(), GENERIC_READ, kShare, nullptr, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)); - if (!file.IsValid()) - return false; - - // The zone identifier contents is expected to be: - // "[ZoneTransfer]\r\nZoneId=3\r\n". The actual ZoneId can be different. A - // buffer of 32 bytes is sufficient for verifying the contents. - std::vector<char> zone_identifier_contents_buffer(32); - DWORD actual_length = 0; - if (!::ReadFile(file.Get(), &zone_identifier_contents_buffer.front(), - zone_identifier_contents_buffer.size(), &actual_length, - nullptr)) - return false; - zone_identifier_contents_buffer.resize(actual_length); - - std::string zone_identifier_contents(zone_identifier_contents_buffer.begin(), - zone_identifier_contents_buffer.end()); - - std::vector<base::StringPiece> lines = - base::SplitStringPiece(zone_identifier_contents, "\n", - base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - return lines.size() > 1 && lines[0] == "[ZoneTransfer]" && - lines[1].find("ZoneId=") == 0; -} - -} // namespace - -bool IsFileQuarantined(const base::FilePath& file, - const GURL& source_url, - const GURL& referrer_url) { - return ZoneIdentifierPresentForFile(file); -} - -} // namespace download |