diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-05-20 09:47:09 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-06-07 11:15:42 +0000 |
commit | 189d4fd8fad9e3c776873be51938cd31a42b6177 (patch) | |
tree | 6497caeff5e383937996768766ab3bb2081a40b2 /chromium/storage | |
parent | 8bc75099d364490b22f43a7ce366b366c08f4164 (diff) | |
download | qtwebengine-chromium-189d4fd8fad9e3c776873be51938cd31a42b6177.tar.gz |
BASELINE: Update Chromium to 90.0.4430.221
Change-Id: Iff4d9d18d2fcf1a576f3b1f453010f744a232920
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/storage')
100 files changed, 5390 insertions, 4088 deletions
diff --git a/chromium/storage/OWNERS b/chromium/storage/OWNERS index 919ed9bbf50..7ccd317ff28 100644 --- a/chromium/storage/OWNERS +++ b/chromium/storage/OWNERS @@ -1,3 +1,4 @@ +set noparent dmurph@chromium.org jsbell@chromium.org pwnall@chromium.org diff --git a/chromium/storage/browser/BUILD.gn b/chromium/storage/browser/BUILD.gn index 92d357a9636..bb9f5ff3184 100644 --- a/chromium/storage/browser/BUILD.gn +++ b/chromium/storage/browser/BUILD.gn @@ -182,8 +182,8 @@ component("browser") { "file_system/watcher_manager.h", "quota/client_usage_tracker.cc", "quota/client_usage_tracker.h", - "quota/padding_key.cc", - "quota/padding_key.h", + "quota/mojo_quota_client_wrapper.cc", + "quota/mojo_quota_client_wrapper.h", "quota/quota_callbacks.h", "quota/quota_client.h", "quota/quota_client_type.cc", @@ -197,6 +197,8 @@ component("browser") { "quota/quota_macros.h", "quota/quota_manager.cc", "quota/quota_manager.h", + "quota/quota_manager_impl.cc", + "quota/quota_manager_impl.h", "quota/quota_manager_proxy.cc", "quota/quota_manager_proxy.h", "quota/quota_override_handle.cc", @@ -209,6 +211,8 @@ component("browser") { "quota/quota_temporary_storage_evictor.h", "quota/special_storage_policy.cc", "quota/special_storage_policy.h", + "quota/storage_policy_observer.cc", + "quota/storage_policy_observer.h", "quota/usage_tracker.cc", "quota/usage_tracker.h", ] diff --git a/chromium/storage/browser/blob/blob_builder_from_stream_unittest.cc b/chromium/storage/browser/blob/blob_builder_from_stream_unittest.cc index 89df0f70f96..3db6bbb9926 100644 --- a/chromium/storage/browser/blob/blob_builder_from_stream_unittest.cc +++ b/chromium/storage/browser/blob/blob_builder_from_stream_unittest.cc @@ -91,7 +91,11 @@ class BlobBuilderFromStreamTestWithDelayedLimits std::unique_ptr<BlobDataHandle> BuildFromString( std::string data, bool initial_allocation_should_succeed = true) { - mojo::DataPipe pipe; + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + EXPECT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + base::RunLoop loop; std::unique_ptr<BlobDataHandle> result; uint64_t length_hint = GetLengthHint(data.length()); @@ -104,7 +108,7 @@ class BlobBuilderFromStreamTestWithDelayedLimits result = std::move(blob); loop.Quit(); })); - builder.Start(length_hint, std::move(pipe.consumer_handle), + builder.Start(length_hint, std::move(consumer_handle), mojo::NullAssociatedRemote()); // Make sure the initial memory allocation done by the builder matches the @@ -117,8 +121,8 @@ class BlobBuilderFromStreamTestWithDelayedLimits << ", disk_usage: " << context_->memory_controller().disk_usage(); } - mojo::BlockingCopyFromString(data, pipe.producer_handle); - pipe.producer_handle.reset(); + mojo::BlockingCopyFromString(data, producer_handle); + producer_handle.reset(); loop.Run(); EXPECT_EQ(&builder, finished_builder); @@ -195,7 +199,10 @@ class BlobBuilderFromStreamTest }; TEST_P(BlobBuilderFromStreamTest, CallbackCalledOnAbortBeforeDeletion) { - mojo::DataPipe pipe; + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); base::RunLoop loop; BlobBuilderFromStream* builder_ptr = nullptr; @@ -208,7 +215,7 @@ TEST_P(BlobBuilderFromStreamTest, CallbackCalledOnAbortBeforeDeletion) { loop.Quit(); })); builder_ptr = builder.get(); - builder->Start(GetLengthHint(16), std::move(pipe.consumer_handle), + builder->Start(GetLengthHint(16), std::move(consumer_handle), mojo::NullAssociatedRemote()); builder->Abort(); builder.reset(); @@ -364,7 +371,11 @@ TEST_P(BlobBuilderFromStreamTest, TooLargeForQuotaAndNoDisk) { TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuota) { const uint64_t kLengthHint = kTestBlobStorageMaxDiskSpace + kTestBlobStorageMaxBlobMemorySize + 1; - mojo::DataPipe pipe; + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + base::RunLoop loop; std::unique_ptr<BlobDataHandle> result; BlobBuilderFromStream builder( @@ -374,9 +385,9 @@ TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuota) { result = std::move(blob); loop.Quit(); })); - builder.Start(kLengthHint, std::move(pipe.consumer_handle), + builder.Start(kLengthHint, std::move(consumer_handle), mojo::NullAssociatedRemote()); - pipe.producer_handle.reset(); + producer_handle.reset(); loop.Run(); EXPECT_FALSE(result); @@ -388,7 +399,10 @@ TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuotaAndNoDisk) { context_->DisableFilePagingForTesting(); const uint64_t kLengthHint = kTestBlobStorageMaxBlobMemorySize + 1; - mojo::DataPipe pipe; + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); base::RunLoop loop; std::unique_ptr<BlobDataHandle> result; BlobBuilderFromStream builder( @@ -398,9 +412,9 @@ TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuotaAndNoDisk) { result = std::move(blob); loop.Quit(); })); - builder.Start(kLengthHint, std::move(pipe.consumer_handle), + builder.Start(kLengthHint, std::move(consumer_handle), mojo::NullAssociatedRemote()); - pipe.producer_handle.reset(); + producer_handle.reset(); loop.Run(); EXPECT_FALSE(result); @@ -418,7 +432,10 @@ TEST_P(BlobBuilderFromStreamTest, ProgressEvents) { &progress_client, progress_client_remote.BindNewEndpointAndPassDedicatedReceiver()); - mojo::DataPipe pipe; + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); base::RunLoop loop; std::unique_ptr<BlobDataHandle> result; BlobBuilderFromStream builder( @@ -428,10 +445,10 @@ TEST_P(BlobBuilderFromStreamTest, ProgressEvents) { result = std::move(blob); loop.Quit(); })); - builder.Start(GetLengthHint(kData.size()), std::move(pipe.consumer_handle), + builder.Start(GetLengthHint(kData.size()), std::move(consumer_handle), progress_client_remote.Unbind()); - mojo::BlockingCopyFromString(kData, pipe.producer_handle); - pipe.producer_handle.reset(); + mojo::BlockingCopyFromString(kData, producer_handle); + producer_handle.reset(); loop.Run(); progress_receiver.FlushForTesting(); @@ -453,7 +470,10 @@ TEST_F(BlobBuilderFromStreamTestWithDelayedLimits, LargeStream) { limits_.desired_max_disk_space = kDefaultMinPageFileSize * 2; limits_.effective_max_disk_space = kDefaultMinPageFileSize * 2; - mojo::DataPipe pipe; + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); base::RunLoop loop; std::unique_ptr<BlobDataHandle> result; BlobBuilderFromStream builder( @@ -463,12 +483,12 @@ TEST_F(BlobBuilderFromStreamTestWithDelayedLimits, LargeStream) { result = std::move(blob); loop.Quit(); })); - builder.Start(kData.size(), std::move(pipe.consumer_handle), + builder.Start(kData.size(), std::move(consumer_handle), mojo::NullAssociatedRemote()); context_->set_limits_for_testing(limits_); auto data_producer = - std::make_unique<mojo::DataPipeProducer>(std::move(pipe.producer_handle)); + std::make_unique<mojo::DataPipeProducer>(std::move(producer_handle)); auto* producer_ptr = data_producer.get(); producer_ptr->Write( std::make_unique<mojo::StringDataSource>( @@ -478,7 +498,6 @@ TEST_F(BlobBuilderFromStreamTestWithDelayedLimits, LargeStream) { base::DoNothing::Once<std::unique_ptr<mojo::DataPipeProducer>, MojoResult>(), std::move(data_producer))); - pipe.producer_handle.reset(); loop.Run(); ASSERT_TRUE(result); diff --git a/chromium/storage/browser/blob/blob_impl_unittest.cc b/chromium/storage/browser/blob/blob_impl_unittest.cc index 95f3908e98c..fcb524760b3 100644 --- a/chromium/storage/browser/blob/blob_impl_unittest.cc +++ b/chromium/storage/browser/blob/blob_impl_unittest.cc @@ -160,10 +160,14 @@ TEST_F(BlobImplTest, ReadAll) { MockBlobReaderClient client; mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client); - mojo::DataPipe pipe; - remote->ReadAll(std::move(pipe.producer_handle), + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + + remote->ReadAll(std::move(producer_handle), client_receiver.BindNewPipeAndPassRemote()); - std::string received = ReadDataPipe(std::move(pipe.consumer_handle)); + std::string received = ReadDataPipe(std::move(consumer_handle)); EXPECT_EQ(kContents, received); client_receiver.FlushForTesting(); @@ -184,9 +188,12 @@ TEST_F(BlobImplTest, ReadAll_WithoutClient) { mojo::Remote<blink::mojom::Blob> remote; BlobImpl::Create(std::move(handle), remote.BindNewPipeAndPassReceiver()); - mojo::DataPipe pipe; - remote->ReadAll(std::move(pipe.producer_handle), mojo::NullRemote()); - std::string received = ReadDataPipe(std::move(pipe.consumer_handle)); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + remote->ReadAll(std::move(producer_handle), mojo::NullRemote()); + std::string received = ReadDataPipe(std::move(consumer_handle)); EXPECT_EQ(kContents, received); } @@ -201,11 +208,14 @@ TEST_F(BlobImplTest, ReadAll_BrokenBlob) { MockBlobReaderClient client; mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client); - mojo::DataPipe pipe; - remote->ReadAll(std::move(pipe.producer_handle), + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + remote->ReadAll(std::move(producer_handle), client_receiver.BindNewPipeAndPassRemote()); - std::string received = ReadDataPipe(std::move(pipe.consumer_handle)); + std::string received = ReadDataPipe(std::move(consumer_handle)); EXPECT_EQ("", received); client_receiver.FlushForTesting(); @@ -226,11 +236,14 @@ TEST_F(BlobImplTest, ReadRange) { MockBlobReaderClient client; mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client); - mojo::DataPipe pipe; - remote->ReadRange(2, 5, std::move(pipe.producer_handle), + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + remote->ReadRange(2, 5, std::move(producer_handle), client_receiver.BindNewPipeAndPassRemote()); - std::string received = ReadDataPipe(std::move(pipe.consumer_handle)); + std::string received = ReadDataPipe(std::move(consumer_handle)); EXPECT_EQ(kContents.substr(2, 5), received); client_receiver.FlushForTesting(); @@ -251,10 +264,13 @@ TEST_F(BlobImplTest, ReadRange_WithoutClient) { mojo::Remote<blink::mojom::Blob> remote; BlobImpl::Create(std::move(handle), remote.BindNewPipeAndPassReceiver()); - mojo::DataPipe pipe; - remote->ReadRange(2, 5, std::move(pipe.producer_handle), mojo::NullRemote()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + remote->ReadRange(2, 5, std::move(producer_handle), mojo::NullRemote()); - std::string received = ReadDataPipe(std::move(pipe.consumer_handle)); + std::string received = ReadDataPipe(std::move(consumer_handle)); EXPECT_EQ(kContents.substr(2, 5), received); } @@ -269,11 +285,14 @@ TEST_F(BlobImplTest, ReadRange_TooLargeLength) { MockBlobReaderClient client; mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client); - mojo::DataPipe pipe; - remote->ReadRange(2, 15, std::move(pipe.producer_handle), + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + remote->ReadRange(2, 15, std::move(producer_handle), client_receiver.BindNewPipeAndPassRemote()); - std::string received = ReadDataPipe(std::move(pipe.consumer_handle)); + std::string received = ReadDataPipe(std::move(consumer_handle)); EXPECT_EQ(kContents.substr(2, 15), received); client_receiver.FlushForTesting(); @@ -297,12 +316,15 @@ TEST_F(BlobImplTest, ReadRange_UnboundedLength) { MockBlobReaderClient client; mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client); - mojo::DataPipe pipe; + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); remote->ReadRange(2, std::numeric_limits<uint64_t>::max(), - std::move(pipe.producer_handle), + std::move(producer_handle), client_receiver.BindNewPipeAndPassRemote()); - std::string received = ReadDataPipe(std::move(pipe.consumer_handle)); + std::string received = ReadDataPipe(std::move(consumer_handle)); EXPECT_EQ(kContents.substr(2, kContents.size()), received); client_receiver.FlushForTesting(); @@ -326,11 +348,14 @@ TEST_F(BlobImplTest, ReadRange_BrokenBlob) { MockBlobReaderClient client; mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client); - mojo::DataPipe pipe; - remote->ReadRange(2, 5, std::move(pipe.producer_handle), + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + remote->ReadRange(2, 5, std::move(producer_handle), client_receiver.BindNewPipeAndPassRemote()); - std::string received = ReadDataPipe(std::move(pipe.consumer_handle)); + std::string received = ReadDataPipe(std::move(consumer_handle)); EXPECT_EQ("", received); client_receiver.FlushForTesting(); @@ -352,11 +377,14 @@ TEST_F(BlobImplTest, ReadRange_InvalidRange) { mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client); base::RunLoop loop; - mojo::DataPipe pipe; - remote->ReadRange(15, 4, std::move(pipe.producer_handle), + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + remote->ReadRange(15, 4, std::move(producer_handle), client_receiver.BindNewPipeAndPassRemote()); - std::string received = ReadDataPipe(std::move(pipe.consumer_handle)); + std::string received = ReadDataPipe(std::move(consumer_handle)); EXPECT_EQ("", received); client_receiver.FlushForTesting(); diff --git a/chromium/storage/browser/blob/blob_reader.cc b/chromium/storage/browser/blob/blob_reader.cc index 9be28796b69..1adc28006a0 100644 --- a/chromium/storage/browser/blob/blob_reader.cc +++ b/chromium/storage/browser/blob/blob_reader.cc @@ -803,7 +803,7 @@ std::unique_ptr<network::DataPipeToSourceStream> BlobReader::CreateDataPipe( options.capacity_num_bytes = blink::BlobUtils::GetDataPipeCapacity(max_bytes_to_read); - MojoResult result = mojo::CreateDataPipe(&options, &producer, &consumer); + MojoResult result = mojo::CreateDataPipe(&options, producer, consumer); if (result != MOJO_RESULT_OK) return nullptr; diff --git a/chromium/storage/browser/blob/blob_reader_unittest.cc b/chromium/storage/browser/blob/blob_reader_unittest.cc index 1d56b6c7ac9..26a4973299f 100644 --- a/chromium/storage/browser/blob/blob_reader_unittest.cc +++ b/chromium/storage/browser/blob/blob_reader_unittest.cc @@ -673,7 +673,7 @@ TEST_F(BlobReaderTest, ReadableDataHandleSingle) { mojo::ScopedDataPipeProducerHandle producer; mojo::ScopedDataPipeConsumerHandle consumer; - MojoResult pipe_result = mojo::CreateDataPipe(nullptr, &producer, &consumer); + MojoResult pipe_result = mojo::CreateDataPipe(nullptr, producer, consumer); ASSERT_EQ(MOJO_RESULT_OK, pipe_result); int bytes_read = net::ERR_UNEXPECTED; @@ -720,7 +720,7 @@ TEST_F(BlobReaderTest, ReadableDataHandleSingleRange) { mojo::ScopedDataPipeProducerHandle producer; mojo::ScopedDataPipeConsumerHandle consumer; - MojoResult pipe_result = mojo::CreateDataPipe(nullptr, &producer, &consumer); + MojoResult pipe_result = mojo::CreateDataPipe(nullptr, producer, consumer); ASSERT_EQ(MOJO_RESULT_OK, pipe_result); int bytes_read = net::ERR_UNEXPECTED; diff --git a/chromium/storage/browser/blob/blob_registry_impl_unittest.cc b/chromium/storage/browser/blob/blob_registry_impl_unittest.cc index 69039f08d5a..64ed3dff9f3 100644 --- a/chromium/storage/browser/blob/blob_registry_impl_unittest.cc +++ b/chromium/storage/browser/blob/blob_registry_impl_unittest.cc @@ -1102,7 +1102,7 @@ TEST_F(BlobRegistryImplTest, RegisterFromStream) { mojo::ScopedDataPipeProducerHandle producer; mojo::ScopedDataPipeConsumerHandle consumer; - mojo::CreateDataPipe(nullptr, &producer, &consumer); + mojo::CreateDataPipe(nullptr, producer, consumer); blink::mojom::SerializedBlobPtr blob; base::RunLoop loop; registry_->RegisterFromStream( @@ -1142,7 +1142,7 @@ TEST_F(BlobRegistryImplTest, RegisterFromStream_NoDiskSpace) { mojo::ScopedDataPipeProducerHandle producer; mojo::ScopedDataPipeConsumerHandle consumer; - mojo::CreateDataPipe(nullptr, &producer, &consumer); + mojo::CreateDataPipe(nullptr, producer, consumer); blink::mojom::SerializedBlobPtr blob; base::RunLoop loop; registry_->RegisterFromStream( @@ -1161,11 +1161,20 @@ TEST_F(BlobRegistryImplTest, RegisterFromStream_NoDiskSpace) { } TEST_F(BlobRegistryImplTest, DestroyWithUnfinishedStream) { - mojo::DataPipe pipe1, pipe2; - registry_->RegisterFromStream("", "", 0, std::move(pipe1.consumer_handle), + mojo::ScopedDataPipeProducerHandle producer_handle1; + mojo::ScopedDataPipeConsumerHandle consumer_handle1; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle1, consumer_handle1), + MOJO_RESULT_OK); + + mojo::ScopedDataPipeProducerHandle producer_handle2; + mojo::ScopedDataPipeConsumerHandle consumer_handle2; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle2, consumer_handle2), + MOJO_RESULT_OK); + + registry_->RegisterFromStream("", "", 0, std::move(consumer_handle1), mojo::NullAssociatedRemote(), base::DoNothing()); - registry_->RegisterFromStream("", "", 0, std::move(pipe2.consumer_handle), + registry_->RegisterFromStream("", "", 0, std::move(consumer_handle2), mojo::NullAssociatedRemote(), base::DoNothing()); registry_.FlushForTesting(); diff --git a/chromium/storage/browser/blob/blob_storage_context_mojo_unittest.cc b/chromium/storage/browser/blob/blob_storage_context_mojo_unittest.cc index ea95526e8bd..effedb22b2d 100644 --- a/chromium/storage/browser/blob/blob_storage_context_mojo_unittest.cc +++ b/chromium/storage/browser/blob/blob_storage_context_mojo_unittest.cc @@ -150,8 +150,8 @@ TEST_F(BlobStorageContextMojoTest, BasicBlobCreation) { mojo::ScopedDataPipeProducerHandle data_pipe_producer; mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; - ASSERT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(nullptr, &data_pipe_producer, - &data_pipe_consumer)); + ASSERT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(nullptr, data_pipe_producer, + data_pipe_consumer)); blob->ReadAll(std::move(data_pipe_producer), mojo::NullRemote()); std::string received = ReadDataPipe(std::move(data_pipe_consumer)); EXPECT_EQ(std::string(kData), received); diff --git a/chromium/storage/browser/blob/blob_transport_strategy.cc b/chromium/storage/browser/blob/blob_transport_strategy.cc index eb660147922..05827b6748e 100644 --- a/chromium/storage/browser/blob/blob_transport_strategy.cc +++ b/chromium/storage/browser/blob/blob_transport_strategy.cc @@ -154,7 +154,7 @@ class DataPipeTransportStrategy : public BlobTransportStrategy { options.capacity_num_bytes = std::min(expected_source_size, limits_.max_shared_memory_size); MojoResult result = - CreateDataPipe(&options, &producer_handle, &consumer_handle_); + CreateDataPipe(&options, producer_handle, consumer_handle_); if (result != MOJO_RESULT_OK) { DVLOG(1) << "Unable to create data pipe for blob transfer."; std::move(result_callback_).Run(BlobStatus::ERR_OUT_OF_MEMORY); @@ -239,7 +239,7 @@ class DataPipeTransportStrategy : public BlobTransportStrategy { } } - const BlobStorageLimits& limits_; + const BlobStorageLimits limits_; base::circular_deque<base::OnceClosure> requests_; mojo::ScopedDataPipeConsumerHandle consumer_handle_; @@ -336,7 +336,7 @@ class FileTransportStrategy : public BlobTransportStrategy { std::move(result_callback_).Run(BlobStatus::DONE); } - const BlobStorageLimits& limits_; + const BlobStorageLimits limits_; // State used to assign bytes elements to individual files. // The index of the first file that still has available space. diff --git a/chromium/storage/browser/blob/blob_url_loader.cc b/chromium/storage/browser/blob/blob_url_loader.cc index aa5801ca5c2..072337a8d7c 100644 --- a/chromium/storage/browser/blob/blob_url_loader.cc +++ b/chromium/storage/browser/blob/blob_url_loader.cc @@ -155,7 +155,7 @@ void BlobURLLoader::Start(const std::string& method, options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; options.element_num_bytes = 1; options.capacity_num_bytes = network::kDataPipeDefaultAllocationSize; - if (mojo::CreateDataPipe(&options, &producer_handle, &consumer_handle) != + if (mojo::CreateDataPipe(&options, producer_handle, consumer_handle) != MOJO_RESULT_OK) { OnComplete(net::ERR_INSUFFICIENT_RESOURCES, 0); delete this; diff --git a/chromium/storage/browser/database/database_quota_client.cc b/chromium/storage/browser/database/database_quota_client.cc index 7f0c4424153..c297fb3e228 100644 --- a/chromium/storage/browser/database/database_quota_client.cc +++ b/chromium/storage/browser/database/database_quota_client.cc @@ -7,12 +7,15 @@ #include <stdint.h> #include <memory> +#include <string> #include <utility> #include <vector> #include "base/bind.h" #include "base/callback_helpers.h" #include "base/location.h" +#include "base/sequence_checker.h" +#include "base/sequenced_task_runner.h" #include "base/task_runner_util.h" #include "base/threading/sequenced_task_runner_handle.h" #include "net/base/completion_once_callback.h" @@ -38,43 +41,41 @@ int64_t GetOriginUsageOnDBThread(DatabaseTracker* db_tracker, return 0; } -void GetOriginsOnDBThread(DatabaseTracker* db_tracker, - std::vector<url::Origin>* origins_ptr) { +std::vector<url::Origin> GetOriginsOnDBThread(DatabaseTracker* db_tracker) { + std::vector<url::Origin> all_origins; std::vector<std::string> origin_identifiers; if (db_tracker->GetAllOriginIdentifiers(&origin_identifiers)) { + all_origins.reserve(origin_identifiers.size()); for (const auto& identifier : origin_identifiers) { - origins_ptr->push_back(GetOriginFromIdentifier(identifier)); + all_origins.push_back(GetOriginFromIdentifier(identifier)); } } + return all_origins; } -void GetOriginsForHostOnDBThread(DatabaseTracker* db_tracker, - std::vector<url::Origin>* origins_ptr, - const std::string& host) { +std::vector<url::Origin> GetOriginsForHostOnDBThread( + DatabaseTracker* db_tracker, + const std::string& host) { + std::vector<url::Origin> host_origins; + // In the vast majority of cases, this vector will end up with exactly one + // origin. The origin will be https://host or http://host. + host_origins.reserve(1); + std::vector<std::string> origin_identifiers; if (db_tracker->GetAllOriginIdentifiers(&origin_identifiers)) { for (const auto& identifier : origin_identifiers) { url::Origin origin = GetOriginFromIdentifier(identifier); if (host == origin.host()) - origins_ptr->push_back(std::move(origin)); + host_origins.push_back(std::move(origin)); } } + return host_origins; } -void DidGetQuotaClientOrigins(QuotaClient::GetOriginsForTypeCallback callback, - std::vector<url::Origin>* origins_ptr) { - std::move(callback).Run(*origins_ptr); -} - -void DidDeleteOriginData(base::SequencedTaskRunner* original_task_runner, - QuotaClient::DeleteOriginDataCallback callback, - int result) { - if (result == net::ERR_IO_PENDING) { - // The callback will be invoked via - // DatabaseTracker::ScheduleDatabasesForDeletion. - return; - } - +void DidDeleteOriginData( + scoped_refptr<base::SequencedTaskRunner> original_task_runner, + QuotaClient::DeleteOriginDataCallback callback, + int result) { blink::mojom::QuotaStatusCode status; if (result == net::OK) status = blink::mojom::QuotaStatusCode::kOk; @@ -89,25 +90,32 @@ void DidDeleteOriginData(base::SequencedTaskRunner* original_task_runner, DatabaseQuotaClient::DatabaseQuotaClient( scoped_refptr<DatabaseTracker> db_tracker) - : db_tracker_(std::move(db_tracker)) {} + : db_tracker_(std::move(db_tracker)) { + DCHECK(db_tracker_.get()); + + DETACH_FROM_SEQUENCE(sequence_checker_); +} DatabaseQuotaClient::~DatabaseQuotaClient() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!db_tracker_->task_runner()->RunsTasksInCurrentSequence()) { db_tracker_->task_runner()->ReleaseSoon(FROM_HERE, std::move(db_tracker_)); } } -void DatabaseQuotaClient::OnQuotaManagerDestroyed() {} +void DatabaseQuotaClient::OnQuotaManagerDestroyed() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} void DatabaseQuotaClient::GetOriginUsage(const url::Origin& origin, StorageType type, GetOriginUsageCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!callback.is_null()); - DCHECK(db_tracker_.get()); DCHECK_EQ(type, StorageType::kTemporary); - base::PostTaskAndReplyWithResult( - db_tracker_->task_runner(), FROM_HERE, + db_tracker_->task_runner()->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce(&GetOriginUsageOnDBThread, base::RetainedRef(db_tracker_), origin), std::move(callback)); @@ -116,65 +124,53 @@ void DatabaseQuotaClient::GetOriginUsage(const url::Origin& origin, void DatabaseQuotaClient::GetOriginsForType( StorageType type, GetOriginsForTypeCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!callback.is_null()); - DCHECK(db_tracker_.get()); DCHECK_EQ(type, StorageType::kTemporary); - auto* origins_ptr = new std::vector<url::Origin>(); - db_tracker_->task_runner()->PostTaskAndReply( + db_tracker_->task_runner()->PostTaskAndReplyWithResult( FROM_HERE, - base::BindOnce(&GetOriginsOnDBThread, base::RetainedRef(db_tracker_), - base::Unretained(origins_ptr)), - base::BindOnce(&DidGetQuotaClientOrigins, std::move(callback), - base::Owned(origins_ptr))); + base::BindOnce(&GetOriginsOnDBThread, base::RetainedRef(db_tracker_)), + std::move(callback)); } void DatabaseQuotaClient::GetOriginsForHost( StorageType type, const std::string& host, GetOriginsForHostCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!callback.is_null()); - DCHECK(db_tracker_.get()); DCHECK_EQ(type, StorageType::kTemporary); - auto* origins_ptr = new std::vector<url::Origin>(); - db_tracker_->task_runner()->PostTaskAndReply( + db_tracker_->task_runner()->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&GetOriginsForHostOnDBThread, - base::RetainedRef(db_tracker_), - base::Unretained(origins_ptr), host), - base::BindOnce(&DidGetQuotaClientOrigins, std::move(callback), - base::Owned(origins_ptr))); + base::RetainedRef(db_tracker_), host), + std::move(callback)); } void DatabaseQuotaClient::DeleteOriginData(const url::Origin& origin, StorageType type, DeleteOriginDataCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!callback.is_null()); - DCHECK(db_tracker_.get()); DCHECK_EQ(type, StorageType::kTemporary); - // DidDeleteOriginData() translates the net::Error response to a - // blink::mojom::QuotaStatusCode if necessary, and no-ops as appropriate if - // DatabaseTracker::ScheduleDatabasesForDeletion will also invoke the - // callback. - auto delete_callback = base::BindRepeating( - &DidDeleteOriginData, - base::RetainedRef(base::SequencedTaskRunnerHandle::Get()), - base::AdaptCallbackForRepeating(std::move(callback))); - - base::PostTaskAndReplyWithResult( - db_tracker_->task_runner(), FROM_HERE, + db_tracker_->task_runner()->PostTask( + FROM_HERE, base::BindOnce(&DatabaseTracker::DeleteDataForOrigin, db_tracker_, origin, - delete_callback), - net::CompletionOnceCallback(delete_callback)); + base::BindOnce(&DidDeleteOriginData, + base::SequencedTaskRunnerHandle::Get(), + std::move(callback)))); } void DatabaseQuotaClient::PerformStorageCleanup( blink::mojom::StorageType type, PerformStorageCleanupCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!callback.is_null()); DCHECK_EQ(type, StorageType::kTemporary); + std::move(callback).Run(); } diff --git a/chromium/storage/browser/database/database_quota_client.h b/chromium/storage/browser/database/database_quota_client.h index de235c1593a..f098ee618c9 100644 --- a/chromium/storage/browser/database/database_quota_client.h +++ b/chromium/storage/browser/database/database_quota_client.h @@ -11,7 +11,9 @@ #include "base/component_export.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/sequence_checker.h" #include "base/single_thread_task_runner.h" +#include "base/thread_annotations.h" #include "storage/browser/quota/quota_client.h" #include "storage/browser/quota/quota_client_type.h" #include "third_party/blink/public/mojom/quota/quota_types.mojom.h" @@ -29,6 +31,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) DatabaseQuotaClient public: explicit DatabaseQuotaClient(scoped_refptr<DatabaseTracker> tracker); + DatabaseQuotaClient(const DatabaseQuotaClient&) = delete; + DatabaseQuotaClient& operator=(const DatabaseQuotaClient&) = delete; + // QuotaClient method overrides void OnQuotaManagerDestroyed() override; void GetOriginUsage(const url::Origin& origin, @@ -48,9 +53,13 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) DatabaseQuotaClient private: ~DatabaseQuotaClient() override; - scoped_refptr<DatabaseTracker> db_tracker_; // only used on its sequence + SEQUENCE_CHECKER(sequence_checker_); - DISALLOW_COPY_AND_ASSIGN(DatabaseQuotaClient); + // The scoped_refptr is only be dereferenced on the QuotaClient's sequence. + // However, the DatabaseTracker it points to must only be used on the database + // sequence. + scoped_refptr<DatabaseTracker> db_tracker_ + GUARDED_BY_CONTEXT(sequence_checker_); }; } // namespace storage diff --git a/chromium/storage/browser/database/database_quota_client_unittest.cc b/chromium/storage/browser/database/database_quota_client_unittest.cc index 2b3c50443f6..b0c161b9aeb 100644 --- a/chromium/storage/browser/database/database_quota_client_unittest.cc +++ b/chromium/storage/browser/database/database_quota_client_unittest.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/containers/contains.h" #include "base/files/file_path.h" #include "base/location.h" #include "base/memory/scoped_refptr.h" @@ -69,17 +70,17 @@ class MockDatabaseTracker : public DatabaseTracker { return true; } - int DeleteDataForOrigin(const url::Origin& origin, - net::CompletionOnceCallback callback) override { + void DeleteDataForOrigin(const url::Origin& origin, + net::CompletionOnceCallback callback) override { ++delete_called_count_; if (async_delete()) { base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&MockDatabaseTracker::AsyncDeleteDataForOrigin, this, std::move(callback))); - return net::ERR_IO_PENDING; + return; } - return net::OK; + std::move(callback).Run(net::OK); } void AsyncDeleteDataForOrigin(net::CompletionOnceCallback callback) { @@ -107,8 +108,8 @@ class MockDatabaseTracker : public DatabaseTracker { } void AddMockDatabase(const base::string16& name, int size) { - EXPECT_TRUE(database_info_.find(name) == database_info_.end()); - database_info_[name].size = size; + EXPECT_FALSE(base::Contains(database_sizes_, name)); + database_sizes_[name] = size; total_size_ += size; } }; diff --git a/chromium/storage/browser/database/database_tracker.cc b/chromium/storage/browser/database/database_tracker.cc index fdf4b246607..cb40db94762 100644 --- a/chromium/storage/browser/database/database_tracker.cc +++ b/chromium/storage/browser/database/database_tracker.cc @@ -9,12 +9,14 @@ #include <algorithm> #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/files/file_enumerator.h" #include "base/files/file_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/task/post_task.h" #include "base/task/thread_pool.h" +#include "base/time/time.h" #include "net/base/net_errors.h" #include "sql/database.h" #include "sql/meta_table.h" @@ -34,7 +36,7 @@ namespace storage { const base::FilePath::CharType kDatabaseDirectoryName[] = FILE_PATH_LITERAL("databases"); -const base::FilePath::CharType kOffTheRecordDatabaseDirectoryName[] = +const base::FilePath::CharType kIncognitoDatabaseDirectoryName[] = FILE_PATH_LITERAL("databases-off-the-record"); const base::FilePath::CharType kTrackerDatabaseFileName[] = FILE_PATH_LITERAL("Databases.db"); @@ -55,44 +57,28 @@ OriginInfo::~OriginInfo() = default; void OriginInfo::GetAllDatabaseNames( std::vector<base::string16>* databases) const { - for (const auto& pair : database_info_) - databases->push_back(pair.first); + for (const auto& name_and_size : database_sizes_) + databases->push_back(name_and_size.first); } int64_t OriginInfo::GetDatabaseSize(const base::string16& database_name) const { - auto it = database_info_.find(database_name); - if (it != database_info_.end()) - return it->second.size; + auto it = database_sizes_.find(database_name); + if (it != database_sizes_.end()) + return it->second; return 0; } -base::string16 OriginInfo::GetDatabaseDescription( - const base::string16& database_name) const { - auto it = database_info_.find(database_name); - if (it != database_info_.end()) - return it->second.description; - return base::string16(); -} - -base::Time OriginInfo::GetDatabaseLastModified( - const base::string16& database_name) const { - auto it = database_info_.find(database_name); - if (it != database_info_.end()) - return it->second.last_modified; - return base::Time(); -} - OriginInfo::OriginInfo(const std::string& origin_identifier, int64_t total_size) : origin_identifier_(origin_identifier), total_size_(total_size) {} DatabaseTracker::DatabaseTracker(const base::FilePath& profile_path, - bool is_off_the_record, + bool is_incognito, SpecialStoragePolicy* special_storage_policy, QuotaManagerProxy* quota_manager_proxy) - : is_off_the_record_(is_off_the_record), + : is_incognito_(is_incognito), profile_path_(profile_path), - db_dir_(is_off_the_record_ - ? profile_path_.Append(kOffTheRecordDatabaseDirectoryName) + db_dir_(is_incognito_ + ? profile_path_.Append(kIncognitoDatabaseDirectoryName) : profile_path_.Append(kDatabaseDirectoryName)), db_(new sql::Database()), special_storage_policy_(special_storage_policy), @@ -101,7 +87,8 @@ DatabaseTracker::DatabaseTracker(const base::FilePath& profile_path, {base::MayBlock(), base::TaskPriority::USER_VISIBLE, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) { if (quota_manager_proxy) { - quota_manager_proxy->RegisterClient( + // TODO(crbug.com/1163048): Use mojo and switch to RegisterClient(). + quota_manager_proxy->RegisterLegacyClient( base::MakeRefCounted<DatabaseQuotaClient>(this), QuotaClientType::kDatabase, {blink::mojom::StorageType::kTemporary}); } @@ -126,7 +113,7 @@ void DatabaseTracker::DatabaseOpened(const std::string& origin_identifier, if (quota_manager_proxy_.get()) quota_manager_proxy_->NotifyStorageAccessed( GetOriginFromIdentifier(origin_identifier), - blink::mojom::StorageType::kTemporary); + blink::mojom::StorageType::kTemporary, base::Time::Now()); InsertOrUpdateDatabaseDetails(origin_identifier, database_name, database_description, estimated_size); @@ -162,7 +149,7 @@ void DatabaseTracker::DatabaseClosed(const std::string& origin_identifier, if (quota_manager_proxy_.get()) quota_manager_proxy_->NotifyStorageAccessed( GetOriginFromIdentifier(origin_identifier), - blink::mojom::StorageType::kTemporary); + blink::mojom::StorageType::kTemporary, base::Time::Now()); UpdateOpenDatabaseSizeAndNotify(origin_identifier, database_name); if (database_connections_.RemoveConnection(origin_identifier, database_name)) @@ -183,8 +170,7 @@ void DatabaseTracker::HandleSqliteError( // Note: the client-side filters out all but these two errors as // a small optimization, see WebDatabaseObserverImpl::HandleSqliteError. if (error == SQLITE_CORRUPT || error == SQLITE_NOTADB) { - DeleteDatabase(origin_identifier, database_name, - net::CompletionOnceCallback()); + DeleteDatabase(origin_identifier, database_name, base::DoNothing()); } } @@ -259,7 +245,7 @@ void DatabaseTracker::CloseTrackerDatabaseAndClearCaches() { DCHECK(task_runner_->RunsTasksInCurrentSequence()); ClearAllCachedOriginInfo(); - if (!is_off_the_record_) { + if (!is_incognito_) { meta_table_.reset(nullptr); databases_table_.reset(nullptr); db_->Close(); @@ -273,16 +259,16 @@ base::FilePath DatabaseTracker::GetOriginDirectory( base::string16 origin_directory; - if (!is_off_the_record_) { + if (!is_incognito_) { origin_directory = base::UTF8ToUTF16(origin_identifier); } else { - auto it = off_the_record_origin_directories_.find(origin_identifier); - if (it != off_the_record_origin_directories_.end()) { + auto it = incognito_origin_directories_.find(origin_identifier); + if (it != incognito_origin_directories_.end()) { origin_directory = it->second; } else { - origin_directory = base::NumberToString16( - off_the_record_origin_directories_generator_++); - off_the_record_origin_directories_[origin_identifier] = origin_directory; + origin_directory = + base::NumberToString16(incognito_origin_directories_generator_++); + incognito_origin_directories_[origin_identifier] = origin_directory; } } @@ -373,7 +359,8 @@ bool DatabaseTracker::DeleteClosedDatabase( if (quota_manager_proxy_.get() && db_file_size) quota_manager_proxy_->NotifyStorageModified( QuotaClientType::kDatabase, GetOriginFromIdentifier(origin_identifier), - blink::mojom::StorageType::kTemporary, -db_file_size); + blink::mojom::StorageType::kTemporary, -db_file_size, + base::Time::Now()); // Clean up the main database and invalidate the cached record. databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); @@ -427,18 +414,18 @@ bool DatabaseTracker::DeleteOrigin(const std::string& origin_identifier, base::DeletePathRecursively(origin_dir); base::DeletePathRecursively(new_origin_dir); // Might fail on windows. - if (is_off_the_record_) { - off_the_record_origin_directories_.erase(origin_identifier); + if (is_incognito_) { + incognito_origin_directories_.erase(origin_identifier); // TODO(jsbell): Consider alternate data structures to avoid this // linear scan. - for (auto it = off_the_record_file_handles_.begin(); - it != off_the_record_file_handles_.end();) { + for (auto it = incognito_file_handles_.begin(); + it != incognito_file_handles_.end();) { std::string id; if (DatabaseUtil::CrackVfsFileName(it->first, &id, nullptr, nullptr) && id == origin_identifier) { delete it->second; - it = off_the_record_file_handles_.erase(it); + it = incognito_file_handles_.erase(it); } else { ++it; } @@ -450,7 +437,8 @@ bool DatabaseTracker::DeleteOrigin(const std::string& origin_identifier, if (quota_manager_proxy_.get() && deleted_size) { quota_manager_proxy_->NotifyStorageModified( QuotaClientType::kDatabase, GetOriginFromIdentifier(origin_identifier), - blink::mojom::StorageType::kTemporary, -deleted_size); + blink::mojom::StorageType::kTemporary, -deleted_size, + base::Time::Now()); } return true; @@ -507,12 +495,11 @@ bool DatabaseTracker::LazyInit() { databases_table_.reset(new DatabasesTable(db_.get())); meta_table_.reset(new sql::MetaTable()); - is_initialized_ = - base::CreateDirectory(db_dir_) && - (db_->is_open() || - (is_off_the_record_ ? db_->OpenInMemory() - : db_->Open(kTrackerDatabaseFullPath))) && - UpgradeToCurrentVersion(); + is_initialized_ = base::CreateDirectory(db_dir_) && + (db_->is_open() || + (is_incognito_ ? db_->OpenInMemory() + : db_->Open(kTrackerDatabaseFullPath))) && + UpgradeToCurrentVersion(); if (!is_initialized_) { databases_table_.reset(nullptr); meta_table_.reset(nullptr); @@ -595,7 +582,6 @@ DatabaseTracker::CachedOriginInfo* DatabaseTracker::MaybeGetCachedOriginInfo( db_file_size = GetDBFileSize(origin_identifier, db.database_name); } origin_info.SetDatabaseSize(db.database_name, db_file_size); - origin_info.SetDatabaseDescription(db.database_name, db.description); base::FilePath path = GetFullDBFilePath(origin_identifier, db.database_name); @@ -603,8 +589,7 @@ DatabaseTracker::CachedOriginInfo* DatabaseTracker::MaybeGetCachedOriginInfo( // TODO(jsbell): Avoid duplicate base::GetFileInfo calls between this and // the GetDBFileSize() call above. if (base::GetFileInfo(path, &file_info)) { - origin_info.SetDatabaseLastModified(db.database_name, - file_info.last_modified); + origin_info.UpdateLastModified(file_info.last_modified); } } } @@ -634,7 +619,6 @@ int64_t DatabaseTracker::SeedOpenDatabaseInfo( CachedOriginInfo* info = MaybeGetCachedOriginInfo(origin_id, false); if (info) { info->SetDatabaseSize(name, size); - info->SetDatabaseDescription(name, description); } return size; } @@ -648,8 +632,6 @@ int64_t DatabaseTracker::UpdateOpenDatabaseInfoAndNotify( int64_t new_size = GetDBFileSize(origin_id, name); int64_t old_size = database_connections_.GetOpenDatabaseSize(origin_id, name); CachedOriginInfo* info = MaybeGetCachedOriginInfo(origin_id, false); - if (info && opt_description) - info->SetDatabaseDescription(name, *opt_description); if (old_size != new_size) { database_connections_.SetOpenDatabaseSize(origin_id, name, new_size); if (info) @@ -657,7 +639,8 @@ int64_t DatabaseTracker::UpdateOpenDatabaseInfoAndNotify( if (quota_manager_proxy_.get()) quota_manager_proxy_->NotifyStorageModified( QuotaClientType::kDatabase, GetOriginFromIdentifier(origin_id), - blink::mojom::StorageType::kTemporary, new_size - old_size); + blink::mojom::StorageType::kTemporary, new_size - old_size, + base::Time::Now()); for (auto& observer : observers_) observer.OnDatabaseSizeChanged(origin_id, name, new_size); } @@ -689,39 +672,46 @@ void DatabaseTracker::ScheduleDatabasesForDeletion( } } -int DatabaseTracker::DeleteDatabase(const std::string& origin_identifier, - const base::string16& database_name, - net::CompletionOnceCallback callback) { +void DatabaseTracker::DeleteDatabase(const std::string& origin_identifier, + const base::string16& database_name, + net::CompletionOnceCallback callback) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - if (!LazyInit()) - return net::ERR_FAILED; + DCHECK(!callback.is_null()); + if (!LazyInit()) { + std::move(callback).Run(net::ERR_FAILED); + return; + } if (database_connections_.IsDatabaseOpened(origin_identifier, database_name)) { - if (!callback.is_null()) { - DatabaseSet set; - set[origin_identifier].insert(database_name); - deletion_callbacks_.emplace_back(std::move(callback), set); - } + DatabaseSet set; + set[origin_identifier].insert(database_name); + deletion_callbacks_.emplace_back(std::move(callback), std::move(set)); ScheduleDatabaseForDeletion(origin_identifier, database_name); - return net::ERR_IO_PENDING; + return; } + DeleteClosedDatabase(origin_identifier, database_name); - return net::OK; + std::move(callback).Run(net::OK); } -int DatabaseTracker::DeleteDataModifiedSince( +void DatabaseTracker::DeleteDataModifiedSince( const base::Time& cutoff, net::CompletionOnceCallback callback) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - if (!LazyInit()) - return net::ERR_FAILED; - - DatabaseSet to_be_deleted; + DCHECK(!callback.is_null()); + if (!LazyInit()) { + std::move(callback).Run(net::ERR_FAILED); + return; + } std::vector<std::string> origins_identifiers; - if (!databases_table_->GetAllOriginIdentifiers(&origins_identifiers)) - return net::ERR_FAILED; + if (!databases_table_->GetAllOriginIdentifiers(&origins_identifiers)) { + std::move(callback).Run(net::ERR_FAILED); + return; + } + + DatabaseSet to_be_deleted; int rv = net::OK; for (const auto& origin : origins_identifiers) { if (special_storage_policy_.get() && @@ -732,8 +722,9 @@ int DatabaseTracker::DeleteDataModifiedSince( std::vector<DatabaseDetails> details; if (!databases_table_->GetAllDatabaseDetailsForOriginIdentifier(origin, - &details)) + &details)) { rv = net::ERR_FAILED; + } for (const DatabaseDetails& db : details) { base::FilePath db_file = GetFullDBFilePath(origin, db.database_name); base::File::Info file_info; @@ -742,110 +733,123 @@ int DatabaseTracker::DeleteDataModifiedSince( continue; // Check if the database is opened by any renderer. - if (database_connections_.IsDatabaseOpened(origin, db.database_name)) + if (database_connections_.IsDatabaseOpened(origin, db.database_name)) { to_be_deleted[origin].insert(db.database_name); - else + } else { DeleteClosedDatabase(origin, db.database_name); + } } } - if (rv != net::OK) - return rv; + if (rv != net::OK) { + DCHECK_EQ(rv, net::ERR_FAILED); + std::move(callback).Run(rv); + return; + } if (!to_be_deleted.empty()) { ScheduleDatabasesForDeletion(to_be_deleted, std::move(callback)); - return net::ERR_IO_PENDING; + return; } - return net::OK; + + std::move(callback).Run(net::OK); } -int DatabaseTracker::DeleteDataForOrigin(const url::Origin& origin, - net::CompletionOnceCallback callback) { +void DatabaseTracker::DeleteDataForOrigin( + const url::Origin& origin, + net::CompletionOnceCallback callback) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - if (!LazyInit()) - return net::ERR_FAILED; - - DatabaseSet to_be_deleted; + DCHECK(!callback.is_null()); + if (!LazyInit()) { + std::move(callback).Run(net::ERR_FAILED); + return; + } const std::string identifier = GetIdentifierFromOrigin(origin); std::vector<DatabaseDetails> details; if (!databases_table_->GetAllDatabaseDetailsForOriginIdentifier(identifier, - &details)) - return net::ERR_FAILED; - for (const auto& db : details) { + &details)) { + std::move(callback).Run(net::ERR_FAILED); + return; + } + + DatabaseSet to_be_deleted; + for (const DatabaseDetails& db : details) { // Check if the database is opened by any renderer. - if (database_connections_.IsDatabaseOpened(identifier, db.database_name)) + if (database_connections_.IsDatabaseOpened(identifier, db.database_name)) { to_be_deleted[identifier].insert(db.database_name); - else + } else { DeleteClosedDatabase(identifier, db.database_name); + } } if (!to_be_deleted.empty()) { ScheduleDatabasesForDeletion(to_be_deleted, std::move(callback)); - return net::ERR_IO_PENDING; + return; } - return net::OK; + + std::move(callback).Run(net::OK); } -const base::File* DatabaseTracker::GetOffTheRecordFile( +const base::File* DatabaseTracker::GetIncognitoFile( const base::string16& vfs_file_name) const { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK(is_off_the_record_); - auto it = off_the_record_file_handles_.find(vfs_file_name); - if (it != off_the_record_file_handles_.end()) + DCHECK(is_incognito_); + auto it = incognito_file_handles_.find(vfs_file_name); + if (it != incognito_file_handles_.end()) return it->second; return nullptr; } -const base::File* DatabaseTracker::SaveOffTheRecordFile( +const base::File* DatabaseTracker::SaveIncognitoFile( const base::string16& vfs_file_name, base::File file) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK(is_off_the_record_); + DCHECK(is_incognito_); if (!file.IsValid()) return nullptr; base::File* to_insert = new base::File(std::move(file)); - auto rv = off_the_record_file_handles_.insert( - std::make_pair(vfs_file_name, to_insert)); + auto rv = + incognito_file_handles_.insert(std::make_pair(vfs_file_name, to_insert)); DCHECK(rv.second); return rv.first->second; } -void DatabaseTracker::CloseOffTheRecordFileHandle( +void DatabaseTracker::CloseIncognitoFileHandle( const base::string16& vfs_file_name) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK(is_off_the_record_); - DCHECK(off_the_record_file_handles_.find(vfs_file_name) != - off_the_record_file_handles_.end()); + DCHECK(is_incognito_); + DCHECK(incognito_file_handles_.find(vfs_file_name) != + incognito_file_handles_.end()); - auto it = off_the_record_file_handles_.find(vfs_file_name); - if (it != off_the_record_file_handles_.end()) { + auto it = incognito_file_handles_.find(vfs_file_name); + if (it != incognito_file_handles_.end()) { delete it->second; - off_the_record_file_handles_.erase(it); + incognito_file_handles_.erase(it); } } -bool DatabaseTracker::HasSavedOffTheRecordFileHandle( +bool DatabaseTracker::HasSavedIncognitoFileHandle( const base::string16& vfs_file_name) const { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - return (off_the_record_file_handles_.find(vfs_file_name) != - off_the_record_file_handles_.end()); + return (incognito_file_handles_.find(vfs_file_name) != + incognito_file_handles_.end()); } -void DatabaseTracker::DeleteOffTheRecordDBDirectory() { +void DatabaseTracker::DeleteIncognitoDBDirectory() { DCHECK(task_runner_->RunsTasksInCurrentSequence()); is_initialized_ = false; - for (auto& pair : off_the_record_file_handles_) + for (auto& pair : incognito_file_handles_) delete pair.second; - base::FilePath off_the_record_db_dir = - profile_path_.Append(kOffTheRecordDatabaseDirectoryName); - if (base::DirectoryExists(off_the_record_db_dir)) - base::DeletePathRecursively(off_the_record_db_dir); + base::FilePath incognito_db_dir = + profile_path_.Append(kIncognitoDatabaseDirectoryName); + if (base::DirectoryExists(incognito_db_dir)) + base::DeletePathRecursively(incognito_db_dir); } void DatabaseTracker::ClearSessionOnlyOrigins() { @@ -893,8 +897,8 @@ void DatabaseTracker::Shutdown() { return; } shutting_down_ = true; - if (is_off_the_record_) - DeleteOffTheRecordDBDirectory(); + if (is_incognito_) + DeleteIncognitoDBDirectory(); else if (!force_keep_session_state_) ClearSessionOnlyOrigins(); CloseTrackerDatabaseAndClearCaches(); diff --git a/chromium/storage/browser/database/database_tracker.h b/chromium/storage/browser/database/database_tracker.h index 1898fac9d34..e1e34ba48a3 100644 --- a/chromium/storage/browser/database/database_tracker.h +++ b/chromium/storage/browser/database/database_tracker.h @@ -57,22 +57,14 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) OriginInfo { base::Time LastModified() const { return last_modified_; } void GetAllDatabaseNames(std::vector<base::string16>* databases) const; int64_t GetDatabaseSize(const base::string16& database_name) const; - base::string16 GetDatabaseDescription( - const base::string16& database_name) const; - base::Time GetDatabaseLastModified(const base::string16& database_name) const; protected: - struct DBInfo { - base::string16 description; - int64_t size; - base::Time last_modified; - }; OriginInfo(const std::string& origin_identifier, int64_t total_size); std::string origin_identifier_; int64_t total_size_; base::Time last_modified_; - std::map<base::string16, DBInfo> database_info_; + std::map<base::string16, int64_t> database_sizes_; }; // This class manages the main database and keeps track of open databases. @@ -98,7 +90,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) DatabaseTracker }; DatabaseTracker(const base::FilePath& profile_path, - bool is_off_the_record, + bool is_incognito, SpecialStoragePolicy* special_storage_policy, QuotaManagerProxy* quota_manager_proxy); @@ -141,41 +133,46 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) DatabaseTracker bool IsDatabaseScheduledForDeletion(const std::string& origin_identifier, const base::string16& database_name); - // Deletes a single database. Returns net::OK on success, net::FAILED on - // failure, or net::ERR_IO_PENDING and |callback| is invoked upon completion, - // if non-null. - int DeleteDatabase(const std::string& origin_identifier, - const base::string16& database_name, - net::CompletionOnceCallback callback); - - // Delete any databases that have been touched since the cutoff date that's - // supplied, omitting any that match IDs within |protected_origins|. - // Returns net::OK on success, net::FAILED if not all databases could be - // deleted, and net::ERR_IO_PENDING and |callback| is invoked upon completion, - // if non-null. Protected origins, according the the SpecialStoragePolicy, - // are not deleted by this method. - int DeleteDataModifiedSince(const base::Time& cutoff, - net::CompletionOnceCallback callback); - - // Delete all databases that belong to the given origin. Returns net::OK on - // success, net::FAILED if not all databases could be deleted, and - // net::ERR_IO_PENDING and |callback| is invoked upon completion, if non-null. - // virtual for unit testing only - virtual int DeleteDataForOrigin(const url::Origin& origin, - net::CompletionOnceCallback callback); - - bool IsOffTheRecordProfile() const { return is_off_the_record_; } - - const base::File* GetOffTheRecordFile( - const base::string16& vfs_file_path) const; - const base::File* SaveOffTheRecordFile(const base::string16& vfs_file_path, - base::File file); - void CloseOffTheRecordFileHandle(const base::string16& vfs_file_path); - bool HasSavedOffTheRecordFileHandle( - const base::string16& vfs_file_path) const; + // Deletes a single database. + // + // `callback` must be non-null, and is invoked upon completion with a + // net::Error, which will most likely be net::OK or net::FAILED. `callback` + // may be called before this method returns. + void DeleteDatabase(const std::string& origin_identifier, + const base::string16& database_name, + net::CompletionOnceCallback callback); + + // Deletes databases touched since `cutoff`. + // + // Does not delete databases belonging to origins designated as protected by + // the SpecialStoragePolicy passed to the DatabaseTracker constructor. + // + // `callback` must must be non-null, and is invoked upon completion with a + // net::Error. The status will be net::OK on success, or net::FAILED if not + // all databases could be deleted. `callback` may be called before this method + // returns. + void DeleteDataModifiedSince(const base::Time& cutoff, + net::CompletionOnceCallback callback); + + // Deletes all databases that belong to the given origin. + // + // `callback` must must be non-null, and is invoked upon completion with a + // net::Error. The status will be net::OK on success, or net::FAILED if not + // all databases could be deleted. `callback` may be called before this method + // returns. + virtual void DeleteDataForOrigin(const url::Origin& origin, + net::CompletionOnceCallback callback); + + bool IsIncognitoProfile() const { return is_incognito_; } + + const base::File* GetIncognitoFile(const base::string16& vfs_file_path) const; + const base::File* SaveIncognitoFile(const base::string16& vfs_file_path, + base::File file); + void CloseIncognitoFileHandle(const base::string16& vfs_file_path); + bool HasSavedIncognitoFileHandle(const base::string16& vfs_file_path) const; // Shutdown the database tracker, deleting database files if the tracker is - // used for an OffTheRecord profile. + // used for an Incognito profile. void Shutdown(); // Disables the exit-time deletion of session-only data. void SetForceKeepSessionState(); @@ -197,20 +194,18 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) DatabaseTracker } void SetDatabaseSize(const base::string16& database_name, int64_t new_size) { - int64_t old_size = 0; - if (database_info_.find(database_name) != database_info_.end()) - old_size = database_info_[database_name].size; - database_info_[database_name].size = new_size; + // If the name does not exist in the map, operator[] creates a new entry + // with a default-constructed value. The default-constructed value for + // int64_t is zero (0), which is exactly what we want `old_size` to be set + // to in this case. + int64_t& database_size = database_sizes_[database_name]; + int64_t old_size = database_size; + + database_size = new_size; if (new_size != old_size) total_size_ += new_size - old_size; } - void SetDatabaseDescription(const base::string16& database_name, - const base::string16& description) { - database_info_[database_name].description = description; - } - void SetDatabaseLastModified(const base::string16& database_name, - const base::Time& last_modified) { - database_info_[database_name].last_modified = last_modified; + void UpdateLastModified(base::Time last_modified) { if (last_modified > last_modified_) last_modified_ = last_modified; } @@ -219,9 +214,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) DatabaseTracker // virtual for unit-testing only. virtual ~DatabaseTracker(); - // Deletes the directory that stores all DBs in OffTheRecord mode, if it + // Deletes the directory that stores all DBs in Incognito mode, if it // exists. - void DeleteOffTheRecordDBDirectory(); + void DeleteIncognitoDBDirectory(); // Deletes session-only databases. Blocks databases from being created/opened. void ClearSessionOnlyOrigins(); @@ -279,7 +274,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) DatabaseTracker base::FilePath GetOriginDirectory(const std::string& origin_identifier); bool is_initialized_ = false; - const bool is_off_the_record_; + const bool is_incognito_; bool force_keep_session_state_ = false; bool shutting_down_ = false; const base::FilePath profile_path_; @@ -312,20 +307,20 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) DatabaseTracker // The database tracker thread we're supposed to run file IO on. scoped_refptr<base::SequencedTaskRunner> task_runner_; - // When in OffTheRecord mode, store a DELETE_ON_CLOSE handle to each - // main DB and journal file that was accessed. When the OffTheRecord profile + // When in Incognito mode, store a DELETE_ON_CLOSE handle to each + // main DB and journal file that was accessed. When the Incognito profile // goes away (or when the browser crashes), all these handles will be // closed, and the files will be deleted. - std::map<base::string16, base::File*> off_the_record_file_handles_; + std::map<base::string16, base::File*> incognito_file_handles_; - // In a non-OffTheRecord profile, all DBs in an origin are stored in a - // directory named after the origin. In an OffTheRecord profile though, we do + // In a non-Incognito profile, all DBs in an origin are stored in a + // directory named after the origin. In an Incognito profile though, we do // not want the directory structure to reveal the origins visited by the user // (in case the browser process crashes and those directories are not // deleted). So we use this map to assign directory names that do not reveal // this information. - std::map<std::string, base::string16> off_the_record_origin_directories_; - int off_the_record_origin_directories_generator_ = 0; + std::map<std::string, base::string16> incognito_origin_directories_; + int incognito_origin_directories_generator_ = 0; FRIEND_TEST_ALL_PREFIXES(DatabaseTracker, TestHelper); }; diff --git a/chromium/storage/browser/database/database_tracker_unittest.cc b/chromium/storage/browser/database/database_tracker_unittest.cc index 04c31e2b0d1..03a534a78c2 100644 --- a/chromium/storage/browser/database/database_tracker_unittest.cc +++ b/chromium/storage/browser/database/database_tracker_unittest.cc @@ -13,6 +13,8 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/ptr_util.h" +#include "base/memory/scoped_refptr.h" +#include "base/notreached.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "base/test/bind.h" @@ -99,9 +101,10 @@ void CheckNotificationReceived(TestObserver* observer, class TestQuotaManagerProxy : public QuotaManagerProxy { public: TestQuotaManagerProxy() - : QuotaManagerProxy(nullptr, nullptr), registered_client_(nullptr) {} + : QuotaManagerProxy(nullptr, base::SequencedTaskRunnerHandle::Get()), + registered_client_(nullptr) {} - void RegisterClient( + void RegisterLegacyClient( scoped_refptr<QuotaClient> client, QuotaClientType client_type, const std::vector<blink::mojom::StorageType>& storage_types) override { @@ -109,20 +112,34 @@ class TestQuotaManagerProxy : public QuotaManagerProxy { registered_client_ = client; } + void RegisterClient( + mojo::PendingRemote<mojom::QuotaClient> client, + QuotaClientType client_type, + const std::vector<blink::mojom::StorageType>& storage_types) override { + NOTREACHED(); + } + void NotifyStorageAccessed(const url::Origin& origin, - blink::mojom::StorageType type) override { + blink::mojom::StorageType type, + base::Time access_time) override { EXPECT_EQ(blink::mojom::StorageType::kTemporary, type); accesses_[origin] += 1; } - void NotifyStorageModified(QuotaClientType client_id, - const url::Origin& origin, - blink::mojom::StorageType type, - int64_t delta) override { + void NotifyStorageModified( + QuotaClientType client_id, + const url::Origin& origin, + blink::mojom::StorageType type, + int64_t delta, + base::Time modification_time, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceClosure callback) override { EXPECT_EQ(QuotaClientType::kDatabase, client_id); EXPECT_EQ(blink::mojom::StorageType::kTemporary, type); modifications_[origin].first += 1; modifications_[origin].second += delta; + if (callback) + callback_task_runner->PostTask(FROM_HERE, std::move(callback)); } // Not needed for our tests. @@ -132,10 +149,11 @@ class TestQuotaManagerProxy : public QuotaManagerProxy { const url::Origin& origin, blink::mojom::StorageType type, bool enabled) override {} - void GetUsageAndQuota(base::SequencedTaskRunner* original_task_runner, - const url::Origin& origin, - blink::mojom::StorageType type, - UsageAndQuotaCallback callback) override {} + void GetUsageAndQuota( + const url::Origin& origin, + blink::mojom::StorageType type, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + UsageAndQuotaCallback callback) override {} void SimulateQuotaManagerDestroyed() { if (registered_client_) { @@ -240,17 +258,15 @@ class DatabaseTracker_TestHelper_Test { // Delete db1. Should also delete origin1. TestObserver observer; tracker->AddObserver(&observer); - net::TestCompletionCallback callback1; - int result = - tracker->DeleteDatabase(kOrigin1, kDB1, callback1.callback()); - EXPECT_EQ(net::ERR_IO_PENDING, result); - ASSERT_FALSE(callback1.have_result()); + net::TestCompletionCallback delete_database_callback; + tracker->DeleteDatabase(kOrigin1, kDB1, + delete_database_callback.callback()); + EXPECT_FALSE(delete_database_callback.have_result()); EXPECT_TRUE(observer.DidReceiveNewNotification()); EXPECT_EQ(kOrigin1, observer.GetNotificationOriginIdentifier()); EXPECT_EQ(kDB1, observer.GetNotificationDatabaseName()); tracker->DatabaseClosed(kOrigin1, kDB1); - result = callback1.GetResult(result); - EXPECT_EQ(net::OK, result); + EXPECT_EQ(net::OK, delete_database_callback.WaitForResult()); EXPECT_FALSE(base::PathExists(tracker->GetOriginDirectory(kOrigin1))); // Recreate db1. @@ -278,16 +294,15 @@ class DatabaseTracker_TestHelper_Test { base::Time yesterday = base::Time::Now(); yesterday -= base::TimeDelta::FromDays(1); - net::TestCompletionCallback callback2; - result = - tracker->DeleteDataModifiedSince(yesterday, callback2.callback()); - EXPECT_EQ(net::ERR_IO_PENDING, result); - ASSERT_FALSE(callback2.have_result()); + net::TestCompletionCallback delete_data_modified_since_callback; + tracker->DeleteDataModifiedSince( + yesterday, delete_data_modified_since_callback.callback()); + EXPECT_FALSE(delete_data_modified_since_callback.have_result()); EXPECT_TRUE(observer.DidReceiveNewNotification()); tracker->DatabaseClosed(kOrigin1, kDB1); tracker->DatabaseClosed(kOrigin2, kDB2); - result = callback2.GetResult(result); - EXPECT_EQ(net::OK, result); + EXPECT_EQ(net::OK, + delete_data_modified_since_callback.WaitForResult()); EXPECT_FALSE(base::PathExists(tracker->GetOriginDirectory(kOrigin1))); EXPECT_TRUE( base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2))); @@ -506,9 +521,11 @@ class DatabaseTracker_TestHelper_Test { tracker->DatabaseClosed(kOriginId, kName); EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin)); - EXPECT_EQ(net::OK, - tracker->DeleteDatabase(kOriginId, kName, - net::CompletionOnceCallback())); + net::TestCompletionCallback delete_database_callback; + tracker->DeleteDatabase(kOriginId, kName, + delete_database_callback.callback()); + EXPECT_TRUE(delete_database_callback.have_result()); + EXPECT_EQ(net::OK, delete_database_callback.WaitForResult()); EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, -100)); test_quota_proxy->reset(); @@ -534,9 +551,10 @@ class DatabaseTracker_TestHelper_Test { EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 100)); test_quota_proxy->reset(); - EXPECT_EQ(net::ERR_IO_PENDING, - tracker->DeleteDatabase(kOriginId, kName, - net::CompletionOnceCallback())); + net::TestCompletionCallback delete_database_callback2; + tracker->DeleteDatabase(kOriginId, kName, + delete_database_callback2.callback()); + EXPECT_FALSE(delete_database_callback2.have_result()); EXPECT_FALSE( test_quota_proxy->WasModificationNotified(kOrigin, -100)); EXPECT_TRUE(base::PathExists(tracker->GetOriginDirectory(kOriginId))); @@ -546,6 +564,8 @@ class DatabaseTracker_TestHelper_Test { EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, -100)); EXPECT_FALSE( base::PathExists(tracker->GetOriginDirectory(kOriginId))); + EXPECT_TRUE(delete_database_callback2.have_result()); + EXPECT_EQ(net::OK, delete_database_callback2.WaitForResult()); test_quota_proxy->reset(); // Create a database and up the file size without telling @@ -799,7 +819,6 @@ class DatabaseTracker_TestHelper_Test { tracker->DatabaseModified(kOriginId, kEmptyName); EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos)); EXPECT_EQ(1u, infos.size()); - EXPECT_EQ(kDescription, infos[0].GetDatabaseDescription(kEmptyName)); EXPECT_FALSE( tracker->GetFullDBFilePath(kOriginId, kEmptyName).empty()); tracker->DatabaseOpened(kOriginId, kEmptyName, kChangedDescription, 0, @@ -807,15 +826,15 @@ class DatabaseTracker_TestHelper_Test { infos.clear(); EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos)); EXPECT_EQ(1u, infos.size()); - EXPECT_EQ(kChangedDescription, - infos[0].GetDatabaseDescription(kEmptyName)); tracker->DatabaseClosed(kOriginId, kEmptyName); tracker->DatabaseClosed(kOriginId, kEmptyName); // Deleting it should return to the initial state. - EXPECT_EQ(net::OK, - tracker->DeleteDatabase(kOriginId, kEmptyName, - net::CompletionOnceCallback())); + net::TestCompletionCallback delete_database_callback; + tracker->DeleteDatabase(kOriginId, kEmptyName, + delete_database_callback.callback()); + EXPECT_TRUE(delete_database_callback.have_result()); + EXPECT_EQ(net::OK, delete_database_callback.WaitForResult()); infos.clear(); EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos)); EXPECT_TRUE(infos.empty()); diff --git a/chromium/storage/browser/file_system/README.md b/chromium/storage/browser/file_system/README.md index 1e498f0f2e1..53460ee93ef 100644 --- a/chromium/storage/browser/file_system/README.md +++ b/chromium/storage/browser/file_system/README.md @@ -50,7 +50,7 @@ specification](https://dev.w3.org/2009/dap/file-system/file-dir-sys.html). There are two flavors of this, "temporary" and "persistent". This same file system (or at least the "temporary" version) is also exposed via -the new Native File System API. +the new File System Access API. ### Isolated File Systems @@ -58,7 +58,7 @@ Isolated file systems generally are used to expose files from other file system types to the web for the [Files and Directory Entries API](https://wicg.github.io/entries-api/), either via Drag&Drop or `<input type=file>`. They are also used for the (deprecated) [Chrome Apps chrome.fileSystem API](https://developer.chrome.com/apps/fileSystem), -and the new [Native File System API](http://wicg.github.io/native-file-system/). +and the new [File System Access API](http://wicg.github.io/file-system-access/). # Interesting Classes @@ -143,6 +143,6 @@ Today reference counts are increased/decreased implicitly by granting access to certain file systems to certain renderer processes (i.e. `content::ChildProcessSecurityPolicyImpl` calls `AddReference` when permission is granted, and call `RemoveReference` when the process is destroyed -on all the file systems that renderer has access to). The Native File System API +on all the file systems that renderer has access to). The File System Access API will introduce its own way of adding and removing references to these file systems. diff --git a/chromium/storage/browser/file_system/async_file_util.h b/chromium/storage/browser/file_system/async_file_util.h index 8528fadf41f..a5ef84d7783 100644 --- a/chromium/storage/browser/file_system/async_file_util.h +++ b/chromium/storage/browser/file_system/async_file_util.h @@ -79,9 +79,9 @@ class AsyncFileUtil { using CopyOrMoveOption = FileSystemOperation::CopyOrMoveOption; using GetMetadataField = FileSystemOperation::GetMetadataField; - // Creates an AsyncFileUtil instance which performs file operations on - // local native file system. The created instance assumes - // FileSystemURL::path() has the target platform path. + // Creates an AsyncFileUtil instance which performs file operations on local + // file system. The created instance assumes FileSystemURL::path() has the + // target platform path. COMPONENT_EXPORT(STORAGE_BROWSER) static AsyncFileUtil* CreateForLocalFileSystem(); diff --git a/chromium/storage/browser/file_system/async_file_util_adapter.cc b/chromium/storage/browser/file_system/async_file_util_adapter.cc index 8dfc2cf5c39..a28d6f5760f 100644 --- a/chromium/storage/browser/file_system/async_file_util_adapter.cc +++ b/chromium/storage/browser/file_system/async_file_util_adapter.cc @@ -162,8 +162,9 @@ void RunCreateOrOpenCallback(FileSystemOperationContext* context, } // namespace -AsyncFileUtilAdapter::AsyncFileUtilAdapter(FileSystemFileUtil* sync_file_util) - : sync_file_util_(sync_file_util) { +AsyncFileUtilAdapter::AsyncFileUtilAdapter( + std::unique_ptr<FileSystemFileUtil> sync_file_util) + : sync_file_util_(std::move(sync_file_util)) { DCHECK(sync_file_util_.get()); } diff --git a/chromium/storage/browser/file_system/async_file_util_adapter.h b/chromium/storage/browser/file_system/async_file_util_adapter.h index ad4abec2b1c..441533a04e4 100644 --- a/chromium/storage/browser/file_system/async_file_util_adapter.h +++ b/chromium/storage/browser/file_system/async_file_util_adapter.h @@ -31,11 +31,8 @@ class FileSystemFileUtil; class COMPONENT_EXPORT(STORAGE_BROWSER) AsyncFileUtilAdapter : public AsyncFileUtil { public: - // Creates a new AsyncFileUtil for |sync_file_util|. This takes the - // ownership of |sync_file_util|. (This doesn't take std::unique_ptr<> just - // to save extra base::WrapUnique; in all use cases a new fresh FileUtil is - // created only for this adapter.) - explicit AsyncFileUtilAdapter(FileSystemFileUtil* sync_file_util); + explicit AsyncFileUtilAdapter( + std::unique_ptr<FileSystemFileUtil> sync_file_util); ~AsyncFileUtilAdapter() override; diff --git a/chromium/storage/browser/file_system/copy_or_move_file_validator_unittest.cc b/chromium/storage/browser/file_system/copy_or_move_file_validator_unittest.cc index f8918394c2a..d0dbaf018b7 100644 --- a/chromium/storage/browser/file_system/copy_or_move_file_validator_unittest.cc +++ b/chromium/storage/browser/file_system/copy_or_move_file_validator_unittest.cc @@ -269,8 +269,7 @@ TEST(CopyOrMoveFileValidatorTest, AcceptAll) { CopyOrMoveFileValidatorTestHelper helper("http://foo", kNoValidatorType, kWithValidatorType); helper.SetUp(); - std::unique_ptr<CopyOrMoveFileValidatorFactory> factory( - new TestCopyOrMoveFileValidatorFactory(VALID)); + auto factory = std::make_unique<TestCopyOrMoveFileValidatorFactory>(VALID); helper.SetMediaCopyOrMoveFileValidatorFactory(std::move(factory)); helper.CopyTest(base::File::FILE_OK); @@ -281,8 +280,8 @@ TEST(CopyOrMoveFileValidatorTest, AcceptNone) { CopyOrMoveFileValidatorTestHelper helper("http://foo", kNoValidatorType, kWithValidatorType); helper.SetUp(); - std::unique_ptr<CopyOrMoveFileValidatorFactory> factory( - new TestCopyOrMoveFileValidatorFactory(PRE_WRITE_INVALID)); + auto factory = + std::make_unique<TestCopyOrMoveFileValidatorFactory>(PRE_WRITE_INVALID); helper.SetMediaCopyOrMoveFileValidatorFactory(std::move(factory)); helper.CopyTest(base::File::FILE_ERROR_SECURITY); @@ -294,12 +293,12 @@ TEST(CopyOrMoveFileValidatorTest, OverrideValidator) { CopyOrMoveFileValidatorTestHelper helper("http://foo", kNoValidatorType, kWithValidatorType); helper.SetUp(); - std::unique_ptr<CopyOrMoveFileValidatorFactory> reject_factory( - new TestCopyOrMoveFileValidatorFactory(PRE_WRITE_INVALID)); + auto reject_factory = + std::make_unique<TestCopyOrMoveFileValidatorFactory>(PRE_WRITE_INVALID); helper.SetMediaCopyOrMoveFileValidatorFactory(std::move(reject_factory)); - std::unique_ptr<CopyOrMoveFileValidatorFactory> accept_factory( - new TestCopyOrMoveFileValidatorFactory(VALID)); + auto accept_factory = + std::make_unique<TestCopyOrMoveFileValidatorFactory>(VALID); helper.SetMediaCopyOrMoveFileValidatorFactory(std::move(accept_factory)); helper.CopyTest(base::File::FILE_ERROR_SECURITY); @@ -310,8 +309,8 @@ TEST(CopyOrMoveFileValidatorTest, RejectPostWrite) { CopyOrMoveFileValidatorTestHelper helper("http://foo", kNoValidatorType, kWithValidatorType); helper.SetUp(); - std::unique_ptr<CopyOrMoveFileValidatorFactory> factory( - new TestCopyOrMoveFileValidatorFactory(POST_WRITE_INVALID)); + auto factory = + std::make_unique<TestCopyOrMoveFileValidatorFactory>(POST_WRITE_INVALID); helper.SetMediaCopyOrMoveFileValidatorFactory(std::move(factory)); helper.CopyTest(base::File::FILE_ERROR_SECURITY); diff --git a/chromium/storage/browser/file_system/copy_or_move_operation_delegate.cc b/chromium/storage/browser/file_system/copy_or_move_operation_delegate.cc index 91e146d3faf..86cbd036681 100644 --- a/chromium/storage/browser/file_system/copy_or_move_operation_delegate.cc +++ b/chromium/storage/browser/file_system/copy_or_move_operation_delegate.cc @@ -499,12 +499,13 @@ class StreamCopyOrMoveImpl NotifyOnStartUpdate(dest_url_); DCHECK(!copy_helper_); - copy_helper_.reset(new CopyOrMoveOperationDelegate::StreamCopyHelper( - std::move(reader_), std::move(writer_), - dest_url_.mount_option().flush_policy(), kReadBufferSize, - file_progress_callback_, - base::TimeDelta::FromMilliseconds( - kMinProgressCallbackInvocationSpanInMilliseconds))); + copy_helper_ = + std::make_unique<CopyOrMoveOperationDelegate::StreamCopyHelper>( + std::move(reader_), std::move(writer_), + dest_url_.mount_option().flush_policy(), kReadBufferSize, + file_progress_callback_, + base::TimeDelta::FromMilliseconds( + kMinProgressCallbackInvocationSpanInMilliseconds)); copy_helper_->Run(base::BindOnce(&StreamCopyOrMoveImpl::RunAfterStreamCopy, weak_factory_.GetWeakPtr(), std::move(callback), last_modified)); diff --git a/chromium/storage/browser/file_system/copy_or_move_operation_delegate_unittest.cc b/chromium/storage/browser/file_system/copy_or_move_operation_delegate_unittest.cc index 7d8aa80bc98..edaf1d252f9 100644 --- a/chromium/storage/browser/file_system/copy_or_move_operation_delegate_unittest.cc +++ b/chromium/storage/browser/file_system/copy_or_move_operation_delegate_unittest.cc @@ -12,14 +12,15 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/containers/contains.h" #include "base/containers/queue.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/location.h" #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" -#include "base/stl_util.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "components/services/filesystem/public/mojom/types.mojom.h" @@ -191,12 +192,12 @@ class CopyOrMoveOperationTestHelper { bool init_copy_or_move_validator) { ASSERT_TRUE(base_.CreateUniqueTempDir()); base::FilePath base_dir = base_.GetPath(); - quota_manager_ = - new MockQuotaManager(false /* is_incognito */, base_dir, - base::ThreadTaskRunnerHandle::Get().get(), - nullptr /* special storage policy */); - quota_manager_proxy_ = new MockQuotaManagerProxy( - quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get()); + quota_manager_ = base::MakeRefCounted<MockQuotaManager>( + false /* is_incognito */, base_dir, + base::ThreadTaskRunnerHandle::Get().get(), + nullptr /* special storage policy */); + quota_manager_proxy_ = base::MakeRefCounted<MockQuotaManagerProxy>( + quota_manager_.get(), base::ThreadTaskRunnerHandle::Get()); file_system_context_ = CreateFileSystemContextForTesting(quota_manager_proxy_.get(), base_dir); @@ -210,8 +211,7 @@ class CopyOrMoveOperationTestHelper { if (dest_type_ == kFileSystemTypeTest) { TestFileSystemBackend* test_backend = static_cast<TestFileSystemBackend*>(backend); - std::unique_ptr<CopyOrMoveFileValidatorFactory> factory( - new TestValidatorFactory); + auto factory = std::make_unique<TestValidatorFactory>(); test_backend->set_require_copy_or_move_validator( require_copy_or_move_validator); if (init_copy_or_move_validator) diff --git a/chromium/storage/browser/file_system/external_mount_points.cc b/chromium/storage/browser/file_system/external_mount_points.cc index 0c2e804bcce..985a050e1eb 100644 --- a/chromium/storage/browser/file_system/external_mount_points.cc +++ b/chromium/storage/browser/file_system/external_mount_points.cc @@ -36,7 +36,7 @@ base::FilePath NormalizeFilePath(const base::FilePath& path) { } bool IsOverlappingMountPathForbidden(FileSystemType type) { - return type != kFileSystemTypeNativeMedia && + return type != kFileSystemTypeLocalMedia && type != kFileSystemTypeDeviceMedia; } @@ -115,7 +115,7 @@ bool ExternalMountPoints::RegisterFileSystem( bool ExternalMountPoints::HandlesFileSystemMountType( FileSystemType type) const { return type == kFileSystemTypeExternal || - type == kFileSystemTypeNativeForPlatformApp; + type == kFileSystemTypeLocalForPlatformApp; } bool ExternalMountPoints::RevokeFileSystem(const std::string& mount_name) { @@ -264,7 +264,7 @@ FileSystemURL ExternalMountPoints::CrackFileSystemURL( return FileSystemURL(); base::FilePath virtual_path = url.path(); - if (url.type() == kFileSystemTypeNativeForPlatformApp) { + if (url.type() == kFileSystemTypeLocalForPlatformApp) { #if BUILDFLAG(IS_CHROMEOS_ASH) // On Chrome OS, find a mount point and virtual path for the external fs. if (!GetVirtualPath(url.path(), &virtual_path)) @@ -272,7 +272,7 @@ FileSystemURL ExternalMountPoints::CrackFileSystemURL( #else // On other OS, it is simply a native local path. return FileSystemURL(url.origin(), url.mount_type(), url.virtual_path(), - url.mount_filesystem_id(), kFileSystemTypeNativeLocal, + url.mount_filesystem_id(), kFileSystemTypeLocal, url.path(), url.filesystem_id(), url.mount_option()); #endif } diff --git a/chromium/storage/browser/file_system/external_mount_points_unittest.cc b/chromium/storage/browser/file_system/external_mount_points_unittest.cc index 7911a420b6f..e3df98989c5 100644 --- a/chromium/storage/browser/file_system/external_mount_points_unittest.cc +++ b/chromium/storage/browser/file_system/external_mount_points_unittest.cc @@ -111,9 +111,9 @@ TEST(ExternalMountPointsTest, AddMountPoint) { // Test adding mount points. for (const auto& test : kTestCases) { EXPECT_EQ(test.success, - mount_points->RegisterFileSystem( - test.name, kFileSystemTypeNativeLocal, - FileSystemMountOption(), base::FilePath(test.path))) + mount_points->RegisterFileSystem(test.name, kFileSystemTypeLocal, + FileSystemMountOption(), + base::FilePath(test.path))) << "Adding mount point: " << test.name << " with path " << test.path; } @@ -135,25 +135,25 @@ TEST(ExternalMountPointsTest, GetVirtualPath) { scoped_refptr<ExternalMountPoints> mount_points = ExternalMountPoints::CreateRefCounted(); - mount_points->RegisterFileSystem("c", kFileSystemTypeNativeLocal, + mount_points->RegisterFileSystem("c", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/a/b/c"))); // Note that "/a/b/c" < "/a/b/c(1)" < "/a/b/c/". - mount_points->RegisterFileSystem("c(1)", kFileSystemTypeNativeLocal, + mount_points->RegisterFileSystem("c(1)", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/a/b/c(1)"))); - mount_points->RegisterFileSystem("x", kFileSystemTypeNativeLocal, + mount_points->RegisterFileSystem("x", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/z/y/x"))); - mount_points->RegisterFileSystem("o", kFileSystemTypeNativeLocal, + mount_points->RegisterFileSystem("o", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/m/n/o"))); // A mount point whose name does not match its path base name. - mount_points->RegisterFileSystem("mount", kFileSystemTypeNativeLocal, + mount_points->RegisterFileSystem("mount", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/root/foo"))); // A mount point with an empty path. - mount_points->RegisterFileSystem("empty_path", kFileSystemTypeNativeLocal, + mount_points->RegisterFileSystem("empty_path", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath()); struct TestCase { @@ -246,10 +246,9 @@ TEST(ExternalMountPointsTest, HandlesFileSystemMountType) { mount_points->HandlesFileSystemMountType(kFileSystemTypePersistent)); EXPECT_FALSE(mount_points->HandlesFileSystemMountType(kFileSystemTypeTest)); // Not even if it's external subtype. + EXPECT_FALSE(mount_points->HandlesFileSystemMountType(kFileSystemTypeLocal)); EXPECT_FALSE( - mount_points->HandlesFileSystemMountType(kFileSystemTypeNativeLocal)); - EXPECT_FALSE(mount_points->HandlesFileSystemMountType( - kFileSystemTypeRestrictedNativeLocal)); + mount_points->HandlesFileSystemMountType(kFileSystemTypeRestrictedLocal)); EXPECT_FALSE( mount_points->HandlesFileSystemMountType(kFileSystemTypeDriveFs)); EXPECT_FALSE( @@ -263,7 +262,7 @@ TEST(ExternalMountPointsTest, CreateCrackedFileSystemURL) { const url::Origin kTestOrigin = url::Origin::Create(GURL("http://chromium.org")); - mount_points->RegisterFileSystem("c", kFileSystemTypeNativeLocal, + mount_points->RegisterFileSystem("c", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/a/b/c"))); mount_points->RegisterFileSystem("c(1)", kFileSystemTypeDriveFs, @@ -286,7 +285,7 @@ TEST(ExternalMountPointsTest, CreateCrackedFileSystemURL) { // Try native local which is not cracked. FileSystemURL native_local = mount_points->CreateCrackedFileSystemURL( - kTestOrigin, kFileSystemTypeNativeLocal, base::FilePath(FPL("c"))); + kTestOrigin, kFileSystemTypeLocal, base::FilePath(FPL("c"))); EXPECT_FALSE(native_local.is_valid()); struct TestCase { @@ -298,8 +297,7 @@ TEST(ExternalMountPointsTest, CreateCrackedFileSystemURL) { }; const TestCase kTestCases[] = { - {FPL("c/d/e"), true, kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"), - "c"}, + {FPL("c/d/e"), true, kFileSystemTypeLocal, DRIVE FPL("/a/b/c/d/e"), "c"}, {FPL("c(1)/d/e"), true, kFileSystemTypeDriveFs, DRIVE FPL("/a/b/c(1)/d/e"), "c(1)"}, {FPL("c(1)"), true, kFileSystemTypeDriveFs, DRIVE FPL("/a/b/c(1)"), "c(1)"}, @@ -320,8 +318,7 @@ TEST(ExternalMountPointsTest, CreateCrackedFileSystemURL) { {FPL("c/d/../e"), false, kFileSystemTypeUnknown, FPL(""), ""}, {FPL("/empty_path/a/../b"), false, kFileSystemTypeUnknown, FPL(""), ""}, #if defined(FILE_PATH_USES_WIN_SEPARATORS) - {FPL("c/d\\e"), true, kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"), - "c"}, + {FPL("c/d\\e"), true, kFileSystemTypeLocal, DRIVE FPL("/a/b/c/d/e"), "c"}, {FPL("mount\\a\\b"), true, kFileSystemTypeDriveFs, DRIVE FPL("/root/a/b"), "mount"}, #endif @@ -361,7 +358,7 @@ TEST(ExternalMountPointsTest, CrackVirtualPath) { const GURL kTestOrigin("http://chromium.org"); - mount_points->RegisterFileSystem("c", kFileSystemTypeNativeLocal, + mount_points->RegisterFileSystem("c", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/a/b/c"))); mount_points->RegisterFileSystem("c(1)", kFileSystemTypeDriveFs, @@ -382,8 +379,7 @@ TEST(ExternalMountPointsTest, CrackVirtualPath) { }; const TestCase kTestCases[] = { - {FPL("c/d/e"), true, kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"), - "c"}, + {FPL("c/d/e"), true, kFileSystemTypeLocal, DRIVE FPL("/a/b/c/d/e"), "c"}, {FPL("c(1)/d/e"), true, kFileSystemTypeDriveFs, DRIVE FPL("/a/b/c(1)/d/e"), "c(1)"}, {FPL("c(1)"), true, kFileSystemTypeDriveFs, DRIVE FPL("/a/b/c(1)"), "c(1)"}, @@ -404,8 +400,7 @@ TEST(ExternalMountPointsTest, CrackVirtualPath) { {FPL("c/d/../e"), false, kFileSystemTypeUnknown, FPL(""), ""}, {FPL("/empty_path/a/../b"), false, kFileSystemTypeUnknown, FPL(""), ""}, #if defined(FILE_PATH_USES_WIN_SEPARATORS) - {FPL("c/d\\e"), true, kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"), - "c"}, + {FPL("c/d\\e"), true, kFileSystemTypeLocal, DRIVE FPL("/a/b/c/d/e"), "c"}, {FPL("mount\\a\\b"), true, kFileSystemTypeDriveFs, DRIVE FPL("/root/a/b"), "mount"}, #endif @@ -445,11 +440,11 @@ TEST(ExternalMountPointsTest, MountOption) { ExternalMountPoints::CreateRefCounted(); mount_points->RegisterFileSystem( - "nosync", kFileSystemTypeNativeLocal, + "nosync", kFileSystemTypeLocal, FileSystemMountOption(FlushPolicy::NO_FLUSH_ON_COMPLETION), base::FilePath(DRIVE FPL("/nosync"))); mount_points->RegisterFileSystem( - "sync", kFileSystemTypeNativeLocal, + "sync", kFileSystemTypeLocal, FileSystemMountOption(FlushPolicy::FLUSH_ON_COMPLETION), base::FilePath(DRIVE FPL("/sync"))); diff --git a/chromium/storage/browser/file_system/file_stream_writer_test.h b/chromium/storage/browser/file_system/file_stream_writer_test.h index e8cf2d74dc5..eb84a69b7a6 100644 --- a/chromium/storage/browser/file_system/file_stream_writer_test.h +++ b/chromium/storage/browser/file_system/file_stream_writer_test.h @@ -41,8 +41,8 @@ class FileStreamWriterTest : public testing::Test { static void NeverCalled(int unused) { ADD_FAILURE(); } private: - base::test::SingleThreadTaskEnvironment task_environment_{ - base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::MainThreadType::IO}; }; template <class SubClass> @@ -109,11 +109,11 @@ TYPED_TEST_P(FileStreamWriterTypedTest, WriteAfterEnd) { std::unique_ptr<FileStreamWriter> writer( this->CreateWriter(std::string(this->kTestFileName), 7)); - EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, - WriteStringToWriter(writer.get(), "xxx")); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx")); EXPECT_TRUE(this->FilePathExists(std::string(this->kTestFileName))); - EXPECT_EQ("foobar", this->GetFileContent(std::string(this->kTestFileName))); + EXPECT_EQ(std::string("foobar\0xxx", 10), + this->GetFileContent(std::string(this->kTestFileName))); } TYPED_TEST_P(FileStreamWriterTypedTest, WriteFailForNonexistingFile) { diff --git a/chromium/storage/browser/file_system/file_system_context.cc b/chromium/storage/browser/file_system/file_system_context.cc index 553455ce2f5..e249cc7e780 100644 --- a/chromium/storage/browser/file_system/file_system_context.cc +++ b/chromium/storage/browser/file_system/file_system_context.cc @@ -12,13 +12,13 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/containers/contains.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" -#include "base/stl_util.h" #include "base/task_runner_util.h" #include "base/threading/thread_task_runner_handle.h" -#include "base/util/type_safety/pass_key.h" +#include "base/types/pass_key.h" #include "net/url_request/url_request.h" #include "storage/browser/file_system/copy_or_move_file_validator.h" #include "storage/browser/file_system/external_mount_points.h" @@ -90,8 +90,8 @@ int FileSystemContext::GetPermissionPolicy(FileSystemType type) { case kFileSystemTypeSyncable: return FILE_PERMISSION_SANDBOX; - case kFileSystemTypeNativeForPlatformApp: - case kFileSystemTypeNativeLocal: + case kFileSystemTypeLocalForPlatformApp: + case kFileSystemTypeLocal: case kFileSystemTypeCloudDevice: case kFileSystemTypeProvided: case kFileSystemTypeDeviceMediaAsFileStorage: @@ -101,11 +101,11 @@ int FileSystemContext::GetPermissionPolicy(FileSystemType type) { case kFileSystemTypeSmbFs: return FILE_PERMISSION_USE_FILE_PERMISSION; - case kFileSystemTypeRestrictedNativeLocal: + case kFileSystemTypeRestrictedLocal: return FILE_PERMISSION_READ_ONLY | FILE_PERMISSION_USE_FILE_PERMISSION; case kFileSystemTypeDeviceMedia: - case kFileSystemTypeNativeMedia: + case kFileSystemTypeLocalMedia: return FILE_PERMISSION_USE_FILE_PERMISSION; // Following types are only accessed via IsolatedFileSystem, and @@ -150,28 +150,29 @@ FileSystemContext::FileSystemContext( io_task_runner_(io_task_runner), default_file_task_runner_(file_task_runner), quota_manager_proxy_(quota_manager_proxy), - sandbox_delegate_( - new SandboxFileSystemBackendDelegate(quota_manager_proxy, - file_task_runner, - partition_path, - special_storage_policy, - options, - env_override_.get())), - sandbox_backend_(new SandboxFileSystemBackend(sandbox_delegate_.get())), - plugin_private_backend_( - new PluginPrivateFileSystemBackend(file_task_runner, - partition_path, - special_storage_policy, - options, - env_override_.get())), + sandbox_delegate_(std::make_unique<SandboxFileSystemBackendDelegate>( + quota_manager_proxy, + file_task_runner, + partition_path, + special_storage_policy, + options, + env_override_.get())), + sandbox_backend_( + std::make_unique<SandboxFileSystemBackend>(sandbox_delegate_.get())), + plugin_private_backend_(std::make_unique<PluginPrivateFileSystemBackend>( + file_task_runner, + partition_path, + special_storage_policy, + options, + env_override_.get())), additional_backends_(std::move(additional_backends)), auto_mount_handlers_(auto_mount_handlers), external_mount_points_(external_mount_points), partition_path_(partition_path), is_incognito_(options.is_incognito()), - operation_runner_( - new FileSystemOperationRunner(util::PassKey<FileSystemContext>(), - this)) { + operation_runner_(std::make_unique<FileSystemOperationRunner>( + base::PassKey<FileSystemContext>(), + this)) { RegisterBackend(sandbox_backend_.get()); RegisterBackend(plugin_private_backend_.get()); @@ -179,18 +180,19 @@ FileSystemContext::FileSystemContext( RegisterBackend(backend.get()); // If the embedder's additional backends already provide support for - // kFileSystemTypeNativeLocal and kFileSystemTypeNativeForPlatformApp then + // kFileSystemTypeLocal and kFileSystemTypeLocalForPlatformApp then // IsolatedFileSystemBackend does not need to handle them. For example, on // Chrome OS the additional backend chromeos::FileSystemBackend handles these // types. - isolated_backend_.reset(new IsolatedFileSystemBackend( - !base::Contains(backend_map_, kFileSystemTypeNativeLocal), - !base::Contains(backend_map_, kFileSystemTypeNativeForPlatformApp))); + isolated_backend_ = std::make_unique<IsolatedFileSystemBackend>( + !base::Contains(backend_map_, kFileSystemTypeLocal), + !base::Contains(backend_map_, kFileSystemTypeLocalForPlatformApp)); RegisterBackend(isolated_backend_.get()); if (quota_manager_proxy) { // Quota client assumes all backends have registered. - quota_manager_proxy->RegisterClient( + // TODO(crbug.com/1163048): Use mojo and switch to RegisterClient(). + quota_manager_proxy->RegisterLegacyClient( base::MakeRefCounted<FileSystemQuotaClient>(this), QuotaClientType::kFileSystem, QuotaManagedStorageTypes()); } @@ -458,13 +460,13 @@ std::unique_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter( std::unique_ptr<FileSystemOperationRunner> FileSystemContext::CreateFileSystemOperationRunner() { return std::make_unique<FileSystemOperationRunner>( - util::PassKey<FileSystemContext>(), this); + base::PassKey<FileSystemContext>(), this); } base::SequenceBound<FileSystemOperationRunner> FileSystemContext::CreateSequenceBoundFileSystemOperationRunner() { return base::SequenceBound<FileSystemOperationRunner>( - io_task_runner_, util::PassKey<FileSystemContext>(), + io_task_runner_, base::PassKey<FileSystemContext>(), base::WrapRefCounted(this)); } @@ -639,12 +641,8 @@ void FileSystemContext::DidOpenFileSystemForResolveURL( DCHECK(result); } - // TODO(mtomasz): Not all fields should be required for ResolveURL. operation_runner()->GetMetadata( - url, - FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY | - FileSystemOperation::GET_METADATA_FIELD_SIZE | - FileSystemOperation::GET_METADATA_FIELD_LAST_MODIFIED, + url, FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY, base::BindOnce(&DidGetMetadataForResolveURL, path, std::move(callback), info)); } diff --git a/chromium/storage/browser/file_system/file_system_context_unittest.cc b/chromium/storage/browser/file_system/file_system_context_unittest.cc index 2cc657c177f..76aa905b046 100644 --- a/chromium/storage/browser/file_system/file_system_context_unittest.cc +++ b/chromium/storage/browser/file_system/file_system_context_unittest.cc @@ -8,6 +8,7 @@ #include "base/files/scoped_temp_dir.h" #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/test/task_environment.h" @@ -51,9 +52,9 @@ class FileSystemContextTest : public testing::Test { void SetUp() override { ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - storage_policy_ = new MockSpecialStoragePolicy(); + storage_policy_ = base::MakeRefCounted<MockSpecialStoragePolicy>(); - mock_quota_manager_ = new MockQuotaManager( + mock_quota_manager_ = base::MakeRefCounted<MockQuotaManager>( false /* is_incognito */, data_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get(), storage_policy_.get()); } @@ -106,12 +107,12 @@ TEST_F(FileSystemContextTest, NullExternalMountPoints) { std::string isolated_name = "root"; IsolatedContext::ScopedFSHandle isolated_fs = IsolatedContext::GetInstance()->RegisterFileSystemForPath( - kFileSystemTypeNativeLocal, std::string(), + kFileSystemTypeLocal, std::string(), base::FilePath(DRIVE FPL("/test/isolated/root")), &isolated_name); std::string isolated_id = isolated_fs.id(); // Register system external mount point. ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( - "system", kFileSystemTypeNativeLocal, FileSystemMountOption(), + "system", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/test/sys/")))); FileSystemURL cracked_isolated = file_system_context->CrackURL( @@ -119,7 +120,7 @@ TEST_F(FileSystemContextTest, NullExternalMountPoints) { ExpectFileSystemURLMatches( cracked_isolated, GURL(kTestOrigin), kFileSystemTypeIsolated, - kFileSystemTypeNativeLocal, + kFileSystemTypeLocal, base::FilePath(DRIVE FPL("/test/isolated/root/file")) .NormalizePathSeparators(), base::FilePath::FromUTF8Unsafe(isolated_id) @@ -132,7 +133,7 @@ TEST_F(FileSystemContextTest, NullExternalMountPoints) { ExpectFileSystemURLMatches( cracked_external, GURL(kTestOrigin), kFileSystemTypeExternal, - kFileSystemTypeNativeLocal, + kFileSystemTypeLocal, base::FilePath(DRIVE FPL("/test/sys/root/file")) .NormalizePathSeparators(), base::FilePath(FPL("system/root/file")).NormalizePathSeparators(), @@ -149,7 +150,7 @@ TEST_F(FileSystemContextTest, FileSystemContextKeepsMountPointsAlive) { // Register system external mount point. ASSERT_TRUE(mount_points->RegisterFileSystem( - "system", kFileSystemTypeNativeLocal, FileSystemMountOption(), + "system", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/test/sys/")))); scoped_refptr<FileSystemContext> file_system_context( @@ -165,7 +166,7 @@ TEST_F(FileSystemContextTest, FileSystemContextKeepsMountPointsAlive) { ExpectFileSystemURLMatches( cracked_external, GURL(kTestOrigin), kFileSystemTypeExternal, - kFileSystemTypeNativeLocal, + kFileSystemTypeLocal, base::FilePath(DRIVE FPL("/test/sys/root/file")) .NormalizePathSeparators(), base::FilePath(FPL("system/root/file")).NormalizePathSeparators(), @@ -185,27 +186,27 @@ TEST_F(FileSystemContextTest, CrackFileSystemURL) { std::string isolated_file_system_name = "root"; IsolatedContext::ScopedFSHandle isolated_fs = IsolatedContext::GetInstance()->RegisterFileSystemForPath( - kFileSystemTypeNativeLocal, std::string(), + kFileSystemTypeLocal, std::string(), base::FilePath(DRIVE FPL("/test/isolated/root")), &isolated_file_system_name); const std::string kIsolatedFileSystemID = isolated_fs.id(); // Register system external mount point. ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( - "system", kFileSystemTypeNativeLocal, FileSystemMountOption(), + "system", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/test/sys/")))); ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( - "ext", kFileSystemTypeNativeLocal, FileSystemMountOption(), + "ext", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/test/ext")))); // Register a system external mount point with the same name/id as the // registered isolated mount point. ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( - kIsolatedFileSystemID, kFileSystemTypeRestrictedNativeLocal, + kIsolatedFileSystemID, kFileSystemTypeRestrictedLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/test/system/isolated")))); // Add a mount points with the same name as a system mount point to // FileSystemContext's external mount points. ASSERT_TRUE(external_mount_points->RegisterFileSystem( - "ext", kFileSystemTypeNativeLocal, FileSystemMountOption(), + "ext", kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath(DRIVE FPL("/test/local/ext/")))); const GURL kTestOrigin = GURL("http://chromium.org/"); @@ -238,18 +239,17 @@ TEST_F(FileSystemContextTest, CrackFileSystemURL) { }, // Should be cracked by isolated mount points: {kIsolatedFileSystemID, "isolated", true /* is_valid */, - kFileSystemTypeIsolated, kFileSystemTypeNativeLocal, + kFileSystemTypeIsolated, kFileSystemTypeLocal, DRIVE FPL("/test/isolated/root/file"), kIsolatedFileSystemID}, // Should be cracked by system mount points: {"system", "external", true /* is_valid */, kFileSystemTypeExternal, - kFileSystemTypeNativeLocal, DRIVE FPL("/test/sys/root/file"), "system"}, + kFileSystemTypeLocal, DRIVE FPL("/test/sys/root/file"), "system"}, {kIsolatedFileSystemID, "external", true /* is_valid */, - kFileSystemTypeExternal, kFileSystemTypeRestrictedNativeLocal, + kFileSystemTypeExternal, kFileSystemTypeRestrictedLocal, DRIVE FPL("/test/system/isolated/root/file"), kIsolatedFileSystemID}, // Should be cracked by FileSystemContext's ExternalMountPoints. {"ext", "external", true /* is_valid */, kFileSystemTypeExternal, - kFileSystemTypeNativeLocal, DRIVE FPL("/test/local/ext/root/file"), - "ext"}, + kFileSystemTypeLocal, DRIVE FPL("/test/local/ext/root/file"), "ext"}, // Test for invalid filesystem url (made invalid by adding invalid // filesystem type). {"sytem", "external", false /* is_valid */, @@ -309,7 +309,7 @@ TEST_F(FileSystemContextTest, CanServeURLRequest) { std::string isolated_fs_name = "root"; IsolatedContext::ScopedFSHandle isolated_fs = IsolatedContext::GetInstance()->RegisterFileSystemForPath( - kFileSystemTypeNativeLocal, std::string(), + kFileSystemTypeLocal, std::string(), base::FilePath(DRIVE FPL("/test/isolated/root")), &isolated_fs_name); std::string isolated_fs_id = isolated_fs.id(); cracked_url = @@ -320,7 +320,7 @@ TEST_F(FileSystemContextTest, CanServeURLRequest) { // A request for an external mount point should be served. const std::string kExternalMountName = "ext_mount"; ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( - kExternalMountName, kFileSystemTypeNativeLocal, FileSystemMountOption(), + kExternalMountName, kFileSystemTypeLocal, FileSystemMountOption(), base::FilePath())); cracked_url = context->CrackURL(CreateRawFileSystemURL("external", kExternalMountName)); @@ -347,10 +347,9 @@ TEST_F(FileSystemContextTest, IsolatedFileSystemsTypesHandled) { file_system_context->GetFileSystemBackend(kFileSystemTypeDragged)); EXPECT_TRUE(file_system_context->GetFileSystemBackend( kFileSystemTypeForTransientFile)); - EXPECT_TRUE( - file_system_context->GetFileSystemBackend(kFileSystemTypeNativeLocal)); + EXPECT_TRUE(file_system_context->GetFileSystemBackend(kFileSystemTypeLocal)); EXPECT_TRUE(file_system_context->GetFileSystemBackend( - kFileSystemTypeNativeForPlatformApp)); + kFileSystemTypeLocalForPlatformApp)); } } // namespace diff --git a/chromium/storage/browser/file_system/file_system_operation_impl.cc b/chromium/storage/browser/file_system/file_system_operation_impl.cc index 1c00df8b879..a897d6f4c73 100644 --- a/chromium/storage/browser/file_system/file_system_operation_impl.cc +++ b/chromium/storage/browser/file_system/file_system_operation_impl.cc @@ -15,6 +15,7 @@ #include "base/callback_helpers.h" #include "base/single_thread_task_runner.h" #include "base/strings/utf_string_conversions.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "base/time/time.h" #include "net/base/escape.h" #include "net/url_request/url_request.h" @@ -104,12 +105,12 @@ void FileSystemOperationImpl::Copy( DCHECK(SetPendingOperationType(kOperationCopy)); DCHECK(!recursive_operation_delegate_); - recursive_operation_delegate_.reset(new CopyOrMoveOperationDelegate( + recursive_operation_delegate_ = std::make_unique<CopyOrMoveOperationDelegate>( file_system_context(), src_url, dest_url, CopyOrMoveOperationDelegate::OPERATION_COPY, option, error_behavior, progress_callback, base::BindOnce(&FileSystemOperationImpl::DidFinishOperation, - weak_factory_.GetWeakPtr(), std::move(callback)))); + weak_factory_.GetWeakPtr(), std::move(callback))); recursive_operation_delegate_->RunRecursively(); } @@ -119,12 +120,12 @@ void FileSystemOperationImpl::Move(const FileSystemURL& src_url, StatusCallback callback) { DCHECK(SetPendingOperationType(kOperationMove)); DCHECK(!recursive_operation_delegate_); - recursive_operation_delegate_.reset(new CopyOrMoveOperationDelegate( + recursive_operation_delegate_ = std::make_unique<CopyOrMoveOperationDelegate>( file_system_context(), src_url, dest_url, CopyOrMoveOperationDelegate::OPERATION_MOVE, option, ERROR_BEHAVIOR_ABORT, FileSystemOperation::CopyProgressCallback(), base::BindOnce(&FileSystemOperationImpl::DidFinishOperation, - weak_factory_.GetWeakPtr(), std::move(callback)))); + weak_factory_.GetWeakPtr(), std::move(callback))); recursive_operation_delegate_->RunRecursively(); } @@ -178,10 +179,10 @@ void FileSystemOperationImpl::Remove(const FileSystemURL& url, return; } - recursive_operation_delegate_.reset(new RemoveOperationDelegate( + recursive_operation_delegate_ = std::make_unique<RemoveOperationDelegate>( file_system_context(), url, base::BindOnce(&FileSystemOperationImpl::DidFinishOperation, - weak_factory_.GetWeakPtr(), std::move(callback)))); + weak_factory_.GetWeakPtr(), std::move(callback))); recursive_operation_delegate_->Run(); } @@ -414,10 +415,9 @@ void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask( } DCHECK(quota_manager_proxy); - DCHECK(quota_manager_proxy->quota_manager()); - quota_manager_proxy->quota_manager()->GetUsageAndQuota( - url::Origin::Create(url.origin().GetURL()), - FileSystemTypeToQuotaStorageType(url.type()), + quota_manager_proxy->GetUsageAndQuota( + url.origin(), FileSystemTypeToQuotaStorageType(url.type()), + base::SequencedTaskRunnerHandle::Get(), base::BindOnce(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask, weak_ptr_, std::move(task), std::move(error_callback))); } @@ -567,10 +567,10 @@ void FileSystemOperationImpl::DidDeleteRecursively(const FileSystemURL& url, if (rv == base::File::FILE_ERROR_INVALID_OPERATION) { // Recursive removal is not supported on this platform. DCHECK(!recursive_operation_delegate_); - recursive_operation_delegate_.reset(new RemoveOperationDelegate( + recursive_operation_delegate_ = std::make_unique<RemoveOperationDelegate>( file_system_context(), url, base::BindOnce(&FileSystemOperationImpl::DidFinishOperation, weak_ptr_, - std::move(callback)))); + std::move(callback))); recursive_operation_delegate_->RunRecursively(); return; } diff --git a/chromium/storage/browser/file_system/file_system_operation_impl.h b/chromium/storage/browser/file_system/file_system_operation_impl.h index 425b0aad9c7..c096e2a63df 100644 --- a/chromium/storage/browser/file_system/file_system_operation_impl.h +++ b/chromium/storage/browser/file_system/file_system_operation_impl.h @@ -105,14 +105,15 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) FileSystemOperationImpl return file_system_context_.get(); } - private: - friend class FileSystemOperation; - + protected: FileSystemOperationImpl( const FileSystemURL& url, FileSystemContext* file_system_context, std::unique_ptr<FileSystemOperationContext> operation_context); + private: + friend class FileSystemOperation; + // Queries the quota and usage and then runs the given |task|. // If an error occurs during the quota query it runs |error_callback| instead. void GetUsageAndQuotaThenRunTask(const FileSystemURL& url, diff --git a/chromium/storage/browser/file_system/file_system_operation_impl_unittest.cc b/chromium/storage/browser/file_system/file_system_operation_impl_unittest.cc index d1eb56cdc3c..8ba68dfa6d7 100644 --- a/chromium/storage/browser/file_system/file_system_operation_impl_unittest.cc +++ b/chromium/storage/browser/file_system/file_system_operation_impl_unittest.cc @@ -16,6 +16,7 @@ #include "base/files/scoped_temp_dir.h" #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/run_loop.h" #include "base/stl_util.h" @@ -56,12 +57,12 @@ class FileSystemOperationImplTest : public testing::Test { update_observers_ = MockFileUpdateObserver::CreateList(&update_observer_); base::FilePath base_dir = base_.GetPath().AppendASCII("filesystem"); - quota_manager_ = - new MockQuotaManager(false /* is_incognito */, base_dir, - base::ThreadTaskRunnerHandle::Get().get(), - nullptr /* special storage policy */); - quota_manager_proxy_ = new MockQuotaManagerProxy( - quota_manager(), base::ThreadTaskRunnerHandle::Get().get()); + quota_manager_ = base::MakeRefCounted<MockQuotaManager>( + /* is_incognito= */ false, base_dir, + base::ThreadTaskRunnerHandle::Get().get(), + /* special storage policy= */ nullptr); + quota_manager_proxy_ = base::MakeRefCounted<MockQuotaManagerProxy>( + quota_manager(), base::ThreadTaskRunnerHandle::Get()); sandbox_file_system_.SetUp(base_dir, quota_manager_proxy_.get()); sandbox_file_system_.AddFileChangeObserver(&change_observer_); sandbox_file_system_.AddFileUpdateObserver(&update_observer_); @@ -103,11 +104,10 @@ class FileSystemOperationImplTest : public testing::Test { MockFileChangeObserver* change_observer() { return &change_observer_; } std::unique_ptr<FileSystemOperationContext> NewContext() { - FileSystemOperationContext* context = - sandbox_file_system_.NewOperationContext(); + auto context = sandbox_file_system_.NewOperationContext(); // Grant enough quota for all test cases. context->set_allowed_bytes_growth(1000000); - return base::WrapUnique(context); + return context; } FileSystemURL URLForPath(const std::string& path) const { diff --git a/chromium/storage/browser/file_system/file_system_operation_impl_write_unittest.cc b/chromium/storage/browser/file_system/file_system_operation_impl_write_unittest.cc index f8a3c40465a..987ff4fd608 100644 --- a/chromium/storage/browser/file_system/file_system_operation_impl_write_unittest.cc +++ b/chromium/storage/browser/file_system/file_system_operation_impl_write_unittest.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/run_loop.h" #include "base/test/task_environment.h" @@ -59,15 +60,15 @@ class FileSystemOperationImplWriteTest : public testing::Test { void SetUp() override { ASSERT_TRUE(dir_.CreateUniqueTempDir()); - quota_manager_ = - new MockQuotaManager(false /* is_incognito */, dir_.GetPath(), - base::ThreadTaskRunnerHandle::Get().get(), - nullptr /* special storage policy */); + quota_manager_ = base::MakeRefCounted<MockQuotaManager>( + /* is_incognito= */ false, dir_.GetPath(), + base::ThreadTaskRunnerHandle::Get().get(), + /* special storage policy= */ nullptr); virtual_path_ = base::FilePath(FILE_PATH_LITERAL("temporary file")); file_system_context_ = CreateFileSystemContextForTesting( quota_manager_->proxy(), dir_.GetPath()); - blob_storage_context_.reset(new BlobStorageContext); + blob_storage_context_ = std::make_unique<BlobStorageContext>(); file_system_context_->operation_runner()->CreateFile( URLForPath(virtual_path_), true /* exclusive */, diff --git a/chromium/storage/browser/file_system/file_system_operation_runner.cc b/chromium/storage/browser/file_system/file_system_operation_runner.cc index 3e99ddc30c5..7c186bdee47 100644 --- a/chromium/storage/browser/file_system/file_system_operation_runner.cc +++ b/chromium/storage/browser/file_system/file_system_operation_runner.cc @@ -12,9 +12,9 @@ #include "base/auto_reset.h" #include "base/bind.h" +#include "base/containers/contains.h" #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" #include "net/url_request/url_request_context.h" #include "storage/browser/blob/shareable_file_reference.h" @@ -28,12 +28,12 @@ namespace storage { using OperationID = FileSystemOperationRunner::OperationID; FileSystemOperationRunner::FileSystemOperationRunner( - util::PassKey<FileSystemContext>, + base::PassKey<FileSystemContext>, const scoped_refptr<FileSystemContext>& file_system_context) : FileSystemOperationRunner(file_system_context.get()) {} FileSystemOperationRunner::FileSystemOperationRunner( - util::PassKey<FileSystemContext>, + base::PassKey<FileSystemContext>, FileSystemContext* file_system_context) : FileSystemOperationRunner(file_system_context) {} @@ -267,8 +267,8 @@ OperationID FileSystemOperationRunner::Write( return id; } - std::unique_ptr<FileWriterDelegate> writer_delegate(new FileWriterDelegate( - std::move(writer), url.mount_option().flush_policy())); + auto writer_delegate = std::make_unique<FileWriterDelegate>( + std::move(writer), url.mount_option().flush_policy()); std::unique_ptr<BlobReader> blob_reader; if (blob) @@ -306,8 +306,8 @@ OperationID FileSystemOperationRunner::WriteStream( return id; } - std::unique_ptr<FileWriterDelegate> writer_delegate(new FileWriterDelegate( - std::move(writer), url.mount_option().flush_policy())); + auto writer_delegate = std::make_unique<FileWriterDelegate>( + std::move(writer), url.mount_option().flush_policy()); PrepareForWrite(id, url); operation_raw->Write(url, std::move(writer_delegate), std::move(data_pipe), diff --git a/chromium/storage/browser/file_system/file_system_operation_runner.h b/chromium/storage/browser/file_system/file_system_operation_runner.h index a5ec0e63d79..661cbe0b99e 100644 --- a/chromium/storage/browser/file_system/file_system_operation_runner.h +++ b/chromium/storage/browser/file_system/file_system_operation_runner.h @@ -16,7 +16,7 @@ #include "base/containers/id_map.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "base/util/type_safety/pass_key.h" +#include "base/types/pass_key.h" #include "components/services/filesystem/public/mojom/types.mojom.h" #include "storage/browser/blob/blob_data_handle.h" #include "storage/browser/file_system/file_system_operation.h" @@ -54,9 +54,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) FileSystemOperationRunner { // |file_system_context| is stored as a raw pointer. The caller must ensure // that |file_system_context| outlives the new instance. FileSystemOperationRunner( - util::PassKey<FileSystemContext>, + base::PassKey<FileSystemContext>, const scoped_refptr<FileSystemContext>& file_system_context); - FileSystemOperationRunner(util::PassKey<FileSystemContext>, + FileSystemOperationRunner(base::PassKey<FileSystemContext>, FileSystemContext* file_system_context); virtual ~FileSystemOperationRunner(); diff --git a/chromium/storage/browser/file_system/file_system_quota_client.cc b/chromium/storage/browser/file_system/file_system_quota_client.cc index e9f3f9bfc05..ee87ffb244d 100644 --- a/chromium/storage/browser/file_system/file_system_quota_client.cc +++ b/chromium/storage/browser/file_system/file_system_quota_client.cc @@ -29,35 +29,29 @@ namespace storage { namespace { -void GetOriginsForTypeOnFileTaskRunner(FileSystemContext* context, - StorageType storage_type, - std::vector<url::Origin>* origins_ptr) { +std::vector<url::Origin> GetOriginsForTypeOnFileTaskRunner( + FileSystemContext* context, + StorageType storage_type) { FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type); DCHECK(type != kFileSystemTypeUnknown); FileSystemQuotaUtil* quota_util = context->GetQuotaUtil(type); if (!quota_util) - return; - *origins_ptr = quota_util->GetOriginsForTypeOnFileTaskRunner(type); + return {}; + return quota_util->GetOriginsForTypeOnFileTaskRunner(type); } -void GetOriginsForHostOnFileTaskRunner(FileSystemContext* context, - StorageType storage_type, - const std::string& host, - std::vector<url::Origin>* origins_ptr) { +std::vector<url::Origin> GetOriginsForHostOnFileTaskRunner( + FileSystemContext* context, + StorageType storage_type, + const std::string& host) { FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type); DCHECK(type != kFileSystemTypeUnknown); FileSystemQuotaUtil* quota_util = context->GetQuotaUtil(type); if (!quota_util) - return; - *origins_ptr = quota_util->GetOriginsForHostOnFileTaskRunner(type, host); -} - -void DidGetFileSystemQuotaClientOrigins( - QuotaClient::GetOriginsForTypeCallback callback, - std::vector<url::Origin>* origins_ptr) { - std::move(callback).Run(*origins_ptr); + return {}; + return quota_util->GetOriginsForHostOnFileTaskRunner(type, host); } blink::mojom::QuotaStatusCode DeleteOriginOnFileTaskRunner( @@ -120,14 +114,11 @@ void FileSystemQuotaClient::GetOriginsForType( GetOriginsForTypeCallback callback) { DCHECK(!callback.is_null()); - auto* origins_ptr = new std::vector<url::Origin>(); - file_task_runner()->PostTaskAndReply( + file_task_runner()->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&GetOriginsForTypeOnFileTaskRunner, - base::RetainedRef(file_system_context_), storage_type, - base::Unretained(origins_ptr)), - base::BindOnce(&DidGetFileSystemQuotaClientOrigins, std::move(callback), - base::Owned(origins_ptr))); + base::RetainedRef(file_system_context_), storage_type), + std::move(callback)); } void FileSystemQuotaClient::GetOriginsForHost( @@ -136,14 +127,12 @@ void FileSystemQuotaClient::GetOriginsForHost( GetOriginsForHostCallback callback) { DCHECK(!callback.is_null()); - auto* origins_ptr = new std::vector<url::Origin>(); - file_task_runner()->PostTaskAndReply( + file_task_runner()->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&GetOriginsForHostOnFileTaskRunner, base::RetainedRef(file_system_context_), storage_type, - host, base::Unretained(origins_ptr)), - base::BindOnce(&DidGetFileSystemQuotaClientOrigins, std::move(callback), - base::Owned(origins_ptr))); + host), + std::move(callback)); } void FileSystemQuotaClient::DeleteOriginData( diff --git a/chromium/storage/browser/file_system/file_system_usage_cache.cc b/chromium/storage/browser/file_system/file_system_usage_cache.cc index ca742a0892d..3fb3e21957f 100644 --- a/chromium/storage/browser/file_system/file_system_usage_cache.cc +++ b/chromium/storage/browser/file_system/file_system_usage_cache.cc @@ -11,9 +11,9 @@ #include <utility> #include "base/bind.h" +#include "base/containers/contains.h" #include "base/files/file_util.h" #include "base/pickle.h" -#include "base/stl_util.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" diff --git a/chromium/storage/browser/file_system/isolated_context_unittest.cc b/chromium/storage/browser/file_system/isolated_context_unittest.cc index 2c28720f820..b8544bdb494 100644 --- a/chromium/storage/browser/file_system/isolated_context_unittest.cc +++ b/chromium/storage/browser/file_system/isolated_context_unittest.cc @@ -119,7 +119,7 @@ TEST_F(IsolatedContextTest, RegisterAndRevokeTest) { IsolatedContext::ScopedFSHandle fs2 = isolated_context()->RegisterFileSystemForPath( - kFileSystemTypeNativeLocal, std::string(), + kFileSystemTypeLocal, std::string(), base::FilePath(DRIVE FPL("/foo")), nullptr); // Make sure the GetDraggedFileInfo returns false for both ones. @@ -133,13 +133,13 @@ TEST_F(IsolatedContextTest, RegisterAndRevokeTest) { // Try registering three more file systems for the same path as id2. IsolatedContext::ScopedFSHandle fs3 = isolated_context()->RegisterFileSystemForPath( - kFileSystemTypeNativeLocal, std::string(), path, nullptr); + kFileSystemTypeLocal, std::string(), path, nullptr); IsolatedContext::ScopedFSHandle fs4 = isolated_context()->RegisterFileSystemForPath( - kFileSystemTypeNativeLocal, std::string(), path, nullptr); + kFileSystemTypeLocal, std::string(), path, nullptr); IsolatedContext::ScopedFSHandle fs5 = isolated_context()->RegisterFileSystemForPath( - kFileSystemTypeNativeLocal, std::string(), path, nullptr); + kFileSystemTypeLocal, std::string(), path, nullptr); // Remove file system for id4. fs4 = IsolatedContext::ScopedFSHandle(); @@ -317,12 +317,12 @@ TEST_F(IsolatedContextTest, CanHandleURL) { EXPECT_FALSE( isolated_context()->HandlesFileSystemMountType(kFileSystemTypeTest)); // Not even if it's isolated subtype. - EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( - kFileSystemTypeNativeLocal)); + EXPECT_FALSE( + isolated_context()->HandlesFileSystemMountType(kFileSystemTypeLocal)); EXPECT_FALSE( isolated_context()->HandlesFileSystemMountType(kFileSystemTypeDragged)); EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( - kFileSystemTypeNativeMedia)); + kFileSystemTypeLocalMedia)); EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType( kFileSystemTypeDeviceMedia)); } diff --git a/chromium/storage/browser/file_system/isolated_file_system_backend.cc b/chromium/storage/browser/file_system/isolated_file_system_backend.cc index ef345ee9b3e..49e2e3dca7a 100644 --- a/chromium/storage/browser/file_system/isolated_file_system_backend.cc +++ b/chromium/storage/browser/file_system/isolated_file_system_backend.cc @@ -38,9 +38,12 @@ IsolatedFileSystemBackend::IsolatedFileSystemBackend( bool use_for_type_platform_app) : use_for_type_native_local_(use_for_type_native_local), use_for_type_platform_app_(use_for_type_platform_app), - isolated_file_util_(new AsyncFileUtilAdapter(new LocalFileUtil())), - dragged_file_util_(new AsyncFileUtilAdapter(new DraggedFileUtil())), - transient_file_util_(new AsyncFileUtilAdapter(new TransientFileUtil())) {} + isolated_file_util_(std::make_unique<AsyncFileUtilAdapter>( + std::make_unique<LocalFileUtil>())), + dragged_file_util_(std::make_unique<AsyncFileUtilAdapter>( + std::make_unique<DraggedFileUtil>())), + transient_file_util_(std::make_unique<AsyncFileUtilAdapter>( + std::make_unique<TransientFileUtil>())) {} IsolatedFileSystemBackend::~IsolatedFileSystemBackend() = default; @@ -50,9 +53,9 @@ bool IsolatedFileSystemBackend::CanHandleType(FileSystemType type) const { case kFileSystemTypeDragged: case kFileSystemTypeForTransientFile: return true; - case kFileSystemTypeNativeLocal: + case kFileSystemTypeLocal: return use_for_type_native_local_; - case kFileSystemTypeNativeForPlatformApp: + case kFileSystemTypeLocalForPlatformApp: return use_for_type_platform_app_; default: return false; @@ -73,7 +76,7 @@ void IsolatedFileSystemBackend::ResolveURL(const FileSystemURL& url, AsyncFileUtil* IsolatedFileSystemBackend::GetAsyncFileUtil( FileSystemType type) { switch (type) { - case kFileSystemTypeNativeLocal: + case kFileSystemTypeLocal: return isolated_file_util_.get(); case kFileSystemTypeDragged: return dragged_file_util_.get(); @@ -114,7 +117,7 @@ bool IsolatedFileSystemBackend::SupportsStreaming( bool IsolatedFileSystemBackend::HasInplaceCopyImplementation( FileSystemType type) const { - DCHECK(type == kFileSystemTypeNativeLocal || type == kFileSystemTypeDragged || + DCHECK(type == kFileSystemTypeLocal || type == kFileSystemTypeDragged || type == kFileSystemTypeForTransientFile); return false; } diff --git a/chromium/storage/browser/file_system/local_file_stream_reader.cc b/chromium/storage/browser/file_system/local_file_stream_reader.cc index efd958857eb..89db58c714d 100644 --- a/chromium/storage/browser/file_system/local_file_stream_reader.cc +++ b/chromium/storage/browser/file_system/local_file_stream_reader.cc @@ -6,6 +6,7 @@ #include <stdint.h> +#include <memory> #include <utility> #include "base/bind.h" @@ -107,7 +108,7 @@ void LocalFileStreamReader::DidVerifyForOpen( return; } - stream_impl_.reset(new net::FileStream(task_runner_)); + stream_impl_ = std::make_unique<net::FileStream>(task_runner_); callback_ = std::move(callback); const int result = stream_impl_->Open( file_path_, kOpenFlagsForRead, diff --git a/chromium/storage/browser/file_system/local_file_stream_writer.cc b/chromium/storage/browser/file_system/local_file_stream_writer.cc index 5ef1c708372..abb82703cf4 100644 --- a/chromium/storage/browser/file_system/local_file_stream_writer.cc +++ b/chromium/storage/browser/file_system/local_file_stream_writer.cc @@ -6,6 +6,8 @@ #include <stdint.h> +#include <memory> + #include "base/bind.h" #include "base/memory/ptr_util.h" #include "net/base/file_stream.h" @@ -103,7 +105,7 @@ int LocalFileStreamWriter::InitiateOpen(base::OnceClosure main_operation) { DCHECK(has_pending_operation_); DCHECK(!stream_impl_.get()); - stream_impl_.reset(new net::FileStream(task_runner_)); + stream_impl_ = std::make_unique<net::FileStream>(task_runner_); int open_flags = 0; switch (open_or_create_) { @@ -139,46 +141,19 @@ void LocalFileStreamWriter::DidOpen(base::OnceClosure main_operation, return; } - auto file_info = std::make_unique<base::File::Info>(); - base::File::Info* raw_file_info = file_info.get(); - result = stream_impl_->GetFileInfo( - raw_file_info, - base::BindOnce(&LocalFileStreamWriter::InitiateSeek, - weak_factory_.GetWeakPtr(), std::move(main_operation), - std::move(file_info))); - DCHECK_NE(result, net::OK); - if (result != net::ERR_IO_PENDING) { - has_pending_operation_ = false; - std::move(write_callback_).Run(result); - } + InitiateSeek(std::move(main_operation)); } -void LocalFileStreamWriter::InitiateSeek( - base::OnceClosure main_operation, - std::unique_ptr<base::File::Info> file_info, - int file_info_result) { +void LocalFileStreamWriter::InitiateSeek(base::OnceClosure main_operation) { DCHECK(has_pending_operation_); DCHECK(stream_impl_.get()); - if (file_info_result != net::OK) { - has_pending_operation_ = false; - std::move(write_callback_).Run(file_info_result); - return; - } - if (initial_offset_ == 0) { // No need to seek. std::move(main_operation).Run(); return; } - if (initial_offset_ > file_info->size) { - // We should not be writing past the end of the file. - has_pending_operation_ = false; - std::move(write_callback_).Run(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); - return; - } - int result = stream_impl_->Seek( initial_offset_, base::BindOnce(&LocalFileStreamWriter::DidSeek, diff --git a/chromium/storage/browser/file_system/local_file_stream_writer.h b/chromium/storage/browser/file_system/local_file_stream_writer.h index df2b4d1dff2..ed51d9c4fb7 100644 --- a/chromium/storage/browser/file_system/local_file_stream_writer.h +++ b/chromium/storage/browser/file_system/local_file_stream_writer.h @@ -13,7 +13,6 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/component_export.h" -#include "base/files/file.h" #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" @@ -55,9 +54,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) LocalFileStreamWriter // Seeks to |initial_offset_| and proceeds to |main_operation| if it succeeds. // If failed, the error code is returned by calling |error_callback|. - void InitiateSeek(base::OnceClosure main_operation, - std::unique_ptr<base::File::Info> file_info, - int file_info_result); + void InitiateSeek(base::OnceClosure main_operation); void DidSeek(base::OnceClosure main_operation, int64_t result); // Passed as the |main_operation| of InitiateOpen() function. diff --git a/chromium/storage/browser/file_system/local_file_util.cc b/chromium/storage/browser/file_system/local_file_util.cc index 807079a6732..771e750b2c7 100644 --- a/chromium/storage/browser/file_system/local_file_util.cc +++ b/chromium/storage/browser/file_system/local_file_util.cc @@ -23,7 +23,7 @@ namespace storage { AsyncFileUtil* AsyncFileUtil::CreateForLocalFileSystem() { - return new AsyncFileUtilAdapter(new LocalFileUtil()); + return new AsyncFileUtilAdapter(std::make_unique<LocalFileUtil>()); } class LocalFileUtil::LocalFileEnumerator @@ -136,7 +136,7 @@ LocalFileUtil::CreateFileEnumerator(FileSystemOperationContext* context, bool recursive) { base::FilePath file_path; if (GetLocalFilePath(context, root_url, &file_path) != base::File::FILE_OK) { - return base::WrapUnique(new EmptyFileEnumerator); + return std::make_unique<EmptyFileEnumerator>(); } return std::make_unique<LocalFileEnumerator>( this, file_path, root_url.path(), recursive, diff --git a/chromium/storage/browser/file_system/local_file_util_unittest.cc b/chromium/storage/browser/file_system/local_file_util_unittest.cc index 33e73e39238..c9e1b6d484b 100644 --- a/chromium/storage/browser/file_system/local_file_util_unittest.cc +++ b/chromium/storage/browser/file_system/local_file_util_unittest.cc @@ -54,9 +54,9 @@ class LocalFileUtilTest : public testing::Test { } protected: - FileSystemOperationContext* NewContext() { - FileSystemOperationContext* context = - new FileSystemOperationContext(file_system_context_.get()); + std::unique_ptr<FileSystemOperationContext> NewContext() { + auto context = std::make_unique<FileSystemOperationContext>( + file_system_context_.get()); context->set_update_observers( *file_system_context_->GetUpdateObservers(kFileSystemType)); return context; @@ -230,7 +230,7 @@ TEST_F(LocalFileUtilTest, Truncate) { std::unique_ptr<FileSystemOperationContext> context; - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ(base::File::FILE_OK, file_util()->Truncate(context.get(), CreateURL(file_name), 1020)); @@ -247,7 +247,7 @@ TEST_F(LocalFileUtilTest, CopyFile) { ASSERT_TRUE(created); std::unique_ptr<FileSystemOperationContext> context; - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ(base::File::FILE_OK, file_util()->Truncate(context.get(), CreateURL(from_file), 1020)); @@ -259,7 +259,7 @@ TEST_F(LocalFileUtilTest, CopyFile) { AsyncFileTestHelper::Copy(file_system_context(), CreateURL(from_file), CreateURL(to_file1))); - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ( base::File::FILE_OK, AsyncFileTestHelper::Copy(file_system_context(), CreateURL(from_file), @@ -281,14 +281,14 @@ TEST_F(LocalFileUtilTest, CopyDirectory) { bool created; std::unique_ptr<FileSystemOperationContext> context; - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ(base::File::FILE_OK, file_util()->CreateDirectory(context.get(), CreateURL(from_dir), false, false)); ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(from_file, &created)); ASSERT_TRUE(created); - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ(base::File::FILE_OK, file_util()->Truncate(context.get(), CreateURL(from_file), 1020)); @@ -297,7 +297,7 @@ TEST_F(LocalFileUtilTest, CopyDirectory) { EXPECT_EQ(1020, GetSize(from_file)); EXPECT_FALSE(DirectoryExists(to_dir)); - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ(base::File::FILE_OK, AsyncFileTestHelper::Copy(file_system_context(), CreateURL(from_dir), CreateURL(to_dir))); @@ -318,14 +318,14 @@ TEST_F(LocalFileUtilTest, MoveFile) { ASSERT_TRUE(created); std::unique_ptr<FileSystemOperationContext> context; - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ(base::File::FILE_OK, file_util()->Truncate(context.get(), CreateURL(from_file), 1020)); EXPECT_TRUE(FileExists(from_file)); EXPECT_EQ(1020, GetSize(from_file)); - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ(base::File::FILE_OK, AsyncFileTestHelper::Move( file_system_context(), CreateURL(from_file), CreateURL(to_file))); @@ -343,14 +343,14 @@ TEST_F(LocalFileUtilTest, MoveDirectory) { bool created; std::unique_ptr<FileSystemOperationContext> context; - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ(base::File::FILE_OK, file_util()->CreateDirectory(context.get(), CreateURL(from_dir), false, false)); ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(from_file, &created)); ASSERT_TRUE(created); - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ(base::File::FILE_OK, file_util()->Truncate(context.get(), CreateURL(from_file), 1020)); @@ -359,7 +359,7 @@ TEST_F(LocalFileUtilTest, MoveDirectory) { EXPECT_EQ(1020, GetSize(from_file)); EXPECT_FALSE(DirectoryExists(to_dir)); - context.reset(NewContext()); + context = NewContext(); ASSERT_EQ(base::File::FILE_OK, AsyncFileTestHelper::Move(file_system_context(), CreateURL(from_dir), CreateURL(to_dir))); diff --git a/chromium/storage/browser/file_system/obfuscated_file_util.cc b/chromium/storage/browser/file_system/obfuscated_file_util.cc index 78b74678b00..f2ad76f4342 100644 --- a/chromium/storage/browser/file_system/obfuscated_file_util.cc +++ b/chromium/storage/browser/file_system/obfuscated_file_util.cc @@ -819,7 +819,7 @@ ObfuscatedFileUtil::CreateFileEnumerator(FileSystemOperationContext* context, DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); SandboxDirectoryDatabase* db = GetDirectoryDatabase(root_url, false); if (!db) { - return base::WrapUnique(new EmptyFileEnumerator); + return std::make_unique<EmptyFileEnumerator>(); } return std::make_unique<ObfuscatedFileEnumerator>( db, context, this, root_url, recursive); @@ -1348,18 +1348,19 @@ bool ObfuscatedFileUtil::InitOriginDatabase(const url::Origin& origin_hint, } } - SandboxPrioritizedOriginDatabase* prioritized_origin_database = - new SandboxPrioritizedOriginDatabase(file_system_directory_, - env_override_); - origin_database_.reset(prioritized_origin_database); + std::unique_ptr<SandboxPrioritizedOriginDatabase> + prioritized_origin_database = + std::make_unique<SandboxPrioritizedOriginDatabase>( + file_system_directory_, env_override_); - if (!origin_hint.opaque() || !HasIsolatedStorage(origin_hint)) - return true; - - const std::string isolated_origin_string = - GetIdentifierFromOrigin(origin_hint); + if (origin_hint.opaque() && HasIsolatedStorage(origin_hint)) { + const std::string isolated_origin_string = + GetIdentifierFromOrigin(origin_hint); + prioritized_origin_database->InitializePrimaryOrigin( + isolated_origin_string); + } - prioritized_origin_database->InitializePrimaryOrigin(isolated_origin_string); + origin_database_ = std::move(prioritized_origin_database); return true; } diff --git a/chromium/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc b/chromium/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc index ec25aacf576..20b000b6cae 100644 --- a/chromium/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc +++ b/chromium/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc @@ -8,10 +8,33 @@ #include "base/files/file_util.h" #include "base/numerics/checked_math.h" +#include "base/system/sys_info.h" #include "build/build_config.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" +namespace { + +// We are giving a relatively large quota to in-memory filesystem (see +// quota_settings.cc), but we do not allocate this memory beforehand for the +// filesystem. Therefore, specially on low-end devices, a website can get to a +// state where it has not used all its quota, but there is no more memory +// available and memory allocation fails and results in Chrome crash. +// By checking for availability of memory before allocating it, we reduce the +// crash possibility. +// Note that quota assignment is the same for on-disk filesystem and the +// assigned quota is not guaranteed to be allocatable later. +bool IsMemoryAvailable(int64_t required_memory) { +#if defined(OS_FUCHSIA) + // This function is not implemented on FUCHSIA, yet. (crbug.com/986608) + return true; +#else + return base::SysInfo::AmountOfAvailablePhysicalMemory() >= required_memory; +#endif +} + +} // namespace + namespace storage { // Struct keeping one entry of the directory tree. @@ -305,6 +328,12 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::Truncate( if (!dp || !dp->entry || dp->entry->type != Entry::kFile) return base::File::FILE_ERROR_NOT_FOUND; + // Fail if enough memory is not available. + if (static_cast<size_t>(length) > dp->entry->file_content.capacity() && + !IsMemoryAvailable(length)) { + return base::File::FILE_ERROR_NO_SPACE; + } + dp->entry->file_content.resize(length); return base::File::FILE_OK; } @@ -356,9 +385,13 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFile( case NativeFileUtil::CopyOrMoveMode::COPY_NOSYNC: case NativeFileUtil::CopyOrMoveMode::COPY_SYNC: DCHECK(!src_is_directory); + // Fail if enough memory is not available. + if (!IsMemoryAvailable(src_dp->entry->file_content.size())) + return base::File::FILE_ERROR_NO_SPACE; if (!CopyOrMoveFileInternal(*src_dp, *dest_dp, false)) return base::File::FILE_ERROR_FAILED; break; + case NativeFileUtil::CopyOrMoveMode::MOVE: if (src_is_directory) { if (!MoveDirectoryInternal(*src_dp, *dest_dp)) @@ -485,7 +518,7 @@ int ObfuscatedFileUtilMemoryDelegate::WriteFile( size_t offset_u = static_cast<size_t>(offset); // Fail if |offset| or |buf_len| not valid. - if (offset < 0 || buf_len < 0 || offset_u > dp->entry->file_content.size()) + if (offset < 0 || buf_len < 0) return net::ERR_REQUEST_RANGE_NOT_SATISFIABLE; // Fail if result doesn't fit in a std::vector. @@ -493,6 +526,12 @@ int ObfuscatedFileUtilMemoryDelegate::WriteFile( static_cast<size_t>(buf_len)) return net::ERR_REQUEST_RANGE_NOT_SATISFIABLE; + // Fail if enough memory is not available. + if (offset_u + buf_len > dp->entry->file_content.capacity() && + !IsMemoryAvailable(offset_u + buf_len)) { + return net::ERR_FILE_NO_SPACE; + } + if (offset_u == dp->entry->file_content.size()) { dp->entry->file_content.insert(dp->entry->file_content.end(), buf->data(), buf->data() + buf_len); @@ -500,6 +539,8 @@ int ObfuscatedFileUtilMemoryDelegate::WriteFile( if (offset_u + buf_len > dp->entry->file_content.size()) dp->entry->file_content.resize(offset_u + buf_len); + // if |offset_u| is larger than the original file size, there will be null + // bytes between the end of the file and |offset_u|. memcpy(dp->entry->file_content.data() + offset, buf->data(), buf_len); } return buf_len; @@ -548,6 +589,10 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyInForeignFile( return base::File::FILE_ERROR_NO_SPACE; } + // Fail if enough memory is not available. + if (!IsMemoryAvailable(source_info.size)) + return base::File::FILE_ERROR_NO_SPACE; + // Create file. Entry* entry = &dest_dp->parent->directory_content .emplace(dest_dp->components.back(), Entry::kFile) diff --git a/chromium/storage/browser/file_system/obfuscated_file_util_unittest.cc b/chromium/storage/browser/file_system/obfuscated_file_util_unittest.cc index ce28f2a91ef..cd1d6cee87a 100644 --- a/chromium/storage/browser/file_system/obfuscated_file_util_unittest.cc +++ b/chromium/storage/browser/file_system/obfuscated_file_util_unittest.cc @@ -13,6 +13,7 @@ #include <vector> #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/files/file.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -50,13 +51,6 @@ #include "url/gurl.h" #include "url/origin.h" -// TODO(crbug.com/961068): Fix memory leaks in tests and re-enable on LSAN. -#ifdef LEAK_SANITIZER -#define MAYBE_TestQuotaOnTruncation DISABLED_TestQuotaOnTruncation -#else -#define MAYBE_TestQuotaOnTruncation TestQuotaOnTruncation -#endif - using url::Origin; namespace storage { @@ -174,7 +168,8 @@ class ObfuscatedFileUtilTest : public testing::Test, quota_manager_ = base::MakeRefCounted<QuotaManager>( is_incognito(), data_dir_.GetPath(), - base::ThreadTaskRunnerHandle::Get().get(), storage_policy_.get(), + base::ThreadTaskRunnerHandle::Get().get(), + /*quota_change_callback=*/base::DoNothing(), storage_policy_.get(), GetQuotaSettingsFunc()); QuotaSettings settings; settings.per_host_quota = 25 * 1024 * 1024; @@ -224,10 +219,10 @@ class ObfuscatedFileUtilTest : public testing::Test, return LimitedContext(std::numeric_limits<int64_t>::max()); } - FileSystemOperationContext* NewContext( + std::unique_ptr<FileSystemOperationContext> NewContext( SandboxFileSystemTestHelper* file_system) { change_observer()->ResetCount(); - FileSystemOperationContext* context; + std::unique_ptr<FileSystemOperationContext> context; if (file_system) context = file_system->NewOperationContext(); else @@ -387,7 +382,7 @@ class ObfuscatedFileUtilTest : public testing::Test, if (!is_incognito()) EXPECT_EQ(length, GetLocalFileSize(data_path)); EXPECT_EQ(length, GetPathSize(url)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->GetFileInfo(context.get(), url, &file_info1, &data_path)); EXPECT_EQ(data_path, local_path); @@ -400,12 +395,12 @@ class ObfuscatedFileUtilTest : public testing::Test, EXPECT_EQ(length, file_info1.size); EXPECT_LE(file_info0.last_modified, file_info1.last_modified); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->Truncate(context.get(), url, length * 2)); EXPECT_EQ(length * 2, GetPathSize(url)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->Truncate(context.get(), url, 0)); EXPECT_EQ(0, GetPathSize(url)); } @@ -417,7 +412,7 @@ class ObfuscatedFileUtilTest : public testing::Test, std::unique_ptr<FileSystemOperationContext> context; for (const auto& file : files) { bool created = true; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ( base::File::FILE_OK, ofu()->EnsureFileExists( @@ -425,7 +420,7 @@ class ObfuscatedFileUtilTest : public testing::Test, ASSERT_FALSE(created); } for (const auto& directory : directories) { - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_TRUE(DirectoryExists(FileSystemURLAppend(root_url, directory))); } } @@ -462,17 +457,17 @@ class ObfuscatedFileUtilTest : public testing::Test, std::unique_ptr<UsageVerifyHelper> AllowUsageIncrease( int64_t requested_growth) { int64_t usage = sandbox_file_system_.GetCachedOriginUsage(); - return std::unique_ptr<UsageVerifyHelper>(new UsageVerifyHelper( - LimitedContext(requested_growth), &sandbox_file_system_, - usage + requested_growth, this)); + return std::make_unique<UsageVerifyHelper>(LimitedContext(requested_growth), + &sandbox_file_system_, + usage + requested_growth, this); } std::unique_ptr<UsageVerifyHelper> DisallowUsageIncrease( int64_t requested_growth) { int64_t usage = sandbox_file_system_.GetCachedOriginUsage(); - return std::unique_ptr<UsageVerifyHelper>( - new UsageVerifyHelper(LimitedContext(requested_growth - 1), - &sandbox_file_system_, usage, this)); + return std::make_unique<UsageVerifyHelper>( + LimitedContext(requested_growth - 1), &sandbox_file_system_, usage, + this); } void FillTestDirectory(const FileSystemURL& root_url, @@ -495,7 +490,7 @@ class ObfuscatedFileUtilTest : public testing::Test, directories->insert(FILE_PATH_LITERAL("sixth")); for (const auto& file : *files) { bool created = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ( base::File::FILE_OK, ofu()->EnsureFileExists( @@ -505,7 +500,7 @@ class ObfuscatedFileUtilTest : public testing::Test, for (const auto& directory : *directories) { bool exclusive = true; bool recursive = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), FileSystemURLAppend(root_url, directory), @@ -521,7 +516,7 @@ class ObfuscatedFileUtilTest : public testing::Test, std::unique_ptr<FileSystemOperationContext> context; std::vector<filesystem::mojom::DirectoryEntry> entries; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, AsyncFileTestHelper::ReadDirectory(file_system_context(), root_url, &entries)); @@ -553,7 +548,7 @@ class ObfuscatedFileUtilTest : public testing::Test, EXPECT_TRUE(change_observer()->HasNoChange()); base::FilePath local_path; base::File::Info file_info; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->GetFileInfo(context.get(), url, &file_info, &local_path)); // We compare as time_t here to lower our resolution, to avoid false @@ -561,14 +556,14 @@ class ObfuscatedFileUtilTest : public testing::Test, // representation and back. EXPECT_EQ(file_info.last_modified.ToTimeT(), last_modified_time.ToTimeT()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); last_modified_time += base::TimeDelta::FromHours(1); last_access_time += base::TimeDelta::FromHours(14); EXPECT_EQ( base::File::FILE_OK, ofu()->Touch(context.get(), url, last_access_time, last_modified_time)); EXPECT_TRUE(change_observer()->HasNoChange()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->GetFileInfo(context.get(), url, &file_info, &local_path)); EXPECT_EQ(file_info.last_modified.ToTimeT(), last_modified_time.ToTimeT()); @@ -594,7 +589,7 @@ class ObfuscatedFileUtilTest : public testing::Test, std::unique_ptr<FileSystemOperationContext> context; if (overwrite) { - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); bool created = false; EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), dest_url, &created)); @@ -609,14 +604,14 @@ class ObfuscatedFileUtilTest : public testing::Test, ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()); if (!overwrite) { // Verify that file creation requires sufficient quota for the path. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth(path_cost + src_file_length - 1); EXPECT_EQ( base::File::FILE_ERROR_NO_SPACE, ofu()->CopyInForeignFile(context.get(), src_file_path, dest_url)); } - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth(path_cost + src_file_length); EXPECT_EQ(base::File::FILE_OK, ofu()->CopyInForeignFile(context.get(), src_file_path, dest_url)); @@ -624,7 +619,7 @@ class ObfuscatedFileUtilTest : public testing::Test, EXPECT_TRUE(PathExists(dest_url)); EXPECT_FALSE(DirectoryExists(dest_url)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); base::File::Info file_info; base::FilePath data_path; EXPECT_EQ(base::File::FILE_OK, ofu()->GetFileInfo(context.get(), dest_url, @@ -647,7 +642,7 @@ class ObfuscatedFileUtilTest : public testing::Test, std::unique_ptr<FileSystemOperationContext> context(NewContext(nullptr)); base::FilePath data_path; base::File::Info file_info; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->GetFileInfo(context.get(), url, &file_info, &data_path)); EXPECT_TRUE(change_observer()->HasNoChange()); @@ -668,19 +663,19 @@ class ObfuscatedFileUtilTest : public testing::Test, const FileSystemURL dest_file_url( FileSystemURLAppendUTF8(dest_dir_url, "fuga")); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), src_dir_url, true, true)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), dest_dir_url, true, true)); bool created = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), src_file_url, &created)); if (overwrite) { - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ( base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), dest_file_url, &created)); @@ -688,7 +683,7 @@ class ObfuscatedFileUtilTest : public testing::Test, ClearTimestamp(src_dir_url); ClearTimestamp(dest_dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CopyOrMoveFile(context.get(), src_file_url, dest_file_url, FileSystemOperation::OPTION_NONE, copy)); @@ -822,7 +817,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) { ofu()->EnsureFileExists(context.get(), url, &created); EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, result); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->DeleteFile(context.get(), url)); @@ -831,13 +826,13 @@ TEST_P(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) { EXPECT_TRUE(change_observer()->HasNoChange()); // Verify that file creation requires sufficient quota for the path. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth( ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1); result = ofu()->EnsureFileExists(context.get(), url, &created); ASSERT_EQ(base::File::FILE_ERROR_NO_SPACE, result); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth( ObfuscatedFileUtil::ComputeFilePathCost(url.path())); created = false; @@ -848,7 +843,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) { CheckFile(url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); base::FilePath local_path; EXPECT_EQ(base::File::FILE_OK, ofu()->GetLocalFilePath(context.get(), url, &local_path)); @@ -857,7 +852,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) { // Verify that deleting a file isn't stopped by zero quota, and that it frees // up quote from its path. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth(0); EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteFile(context.get(), url)); EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count()); @@ -865,7 +860,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) { EXPECT_EQ(ObfuscatedFileUtil::ComputeFilePathCost(url.path()), context->allowed_bytes_growth()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); bool exclusive = true; bool recursive = true; FileSystemURL directory_url = CreateURLFromUTF8("series/of/directories"); @@ -876,7 +871,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) { // The oepration created 3 directories recursively. EXPECT_EQ(3, change_observer()->get_and_reset_create_directory_count()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); created = false; result = ofu()->EnsureFileExists(context.get(), url, &created); ASSERT_TRUE(created); @@ -885,13 +880,13 @@ TEST_P(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) { CheckFile(url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->GetLocalFilePath(context.get(), url, &local_path)); EXPECT_NE(is_incognito(), base::PathExists(local_path)); EXPECT_TRUE(PathExists(url)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteFile(context.get(), url)); EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count()); EXPECT_FALSE(PathExists(url)); @@ -908,24 +903,24 @@ TEST_P(ObfuscatedFileUtilTest, TestTruncate) { EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->Truncate(context.get(), url, 4)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url, &created)); ASSERT_TRUE(created); EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); base::FilePath local_path; EXPECT_EQ(base::File::FILE_OK, ofu()->GetLocalFilePath(context.get(), url, &local_path)); CheckFileSize(url, local_path, 0); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->Truncate(context.get(), url, 10)); EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); CheckFileSize(url, local_path, 10); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->Truncate(context.get(), url, 1)); EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); CheckFileSize(url, local_path, 1); @@ -937,7 +932,7 @@ TEST_P(ObfuscatedFileUtilTest, TestTruncate) { EXPECT_TRUE(change_observer()->HasNoChange()); } -TEST_P(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnTruncation) { +TEST_P(ObfuscatedFileUtilTest, TestQuotaOnTruncation) { bool created = false; FileSystemURL url = CreateURLFromUTF8("file"); @@ -989,7 +984,8 @@ TEST_P(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnTruncation) { EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->Truncate(LimitedContext(1234).get(), url, 1234)); } else { - EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteFile(NewContext(nullptr), url)); + EXPECT_EQ(base::File::FILE_OK, + ofu()->DeleteFile(NewContext(nullptr).get(), url)); } ASSERT_EQ(0, ComputeTotalFileSize()); } @@ -1003,7 +999,7 @@ TEST_P(ObfuscatedFileUtilTest, TestEnsureFileExists) { EXPECT_TRUE(change_observer()->HasNoChange()); // Verify that file creation requires sufficient quota for the path. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); url = CreateURLFromUTF8("test file"); created = false; context->set_allowed_bytes_growth( @@ -1013,7 +1009,7 @@ TEST_P(ObfuscatedFileUtilTest, TestEnsureFileExists) { ASSERT_FALSE(created); EXPECT_TRUE(change_observer()->HasNoChange()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth( ObfuscatedFileUtil::ComputeFilePathCost(url.path())); ASSERT_EQ(base::File::FILE_OK, @@ -1023,7 +1019,7 @@ TEST_P(ObfuscatedFileUtilTest, TestEnsureFileExists) { CheckFile(url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url, &created)); ASSERT_FALSE(created); @@ -1031,7 +1027,7 @@ TEST_P(ObfuscatedFileUtilTest, TestEnsureFileExists) { // Also test in a subdirectory. url = CreateURLFromUTF8("path/to/file.txt"); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); bool exclusive = true; bool recursive = true; EXPECT_EQ(base::File::FILE_OK, @@ -1040,7 +1036,7 @@ TEST_P(ObfuscatedFileUtilTest, TestEnsureFileExists) { // 2 directories: path/ and path/to. EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url, &created)); ASSERT_TRUE(created); @@ -1058,17 +1054,17 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryOps) { EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->CreateDirectory(context.get(), url, exclusive, recursive)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->DeleteDirectory(context.get(), url)); FileSystemURL root = CreateURLFromUTF8(std::string()); EXPECT_FALSE(DirectoryExists(url)); EXPECT_FALSE(PathExists(url)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_TRUE(ofu()->IsDirectoryEmpty(context.get(), root)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); exclusive = false; recursive = true; EXPECT_EQ(base::File::FILE_OK, @@ -1078,16 +1074,16 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryOps) { EXPECT_TRUE(DirectoryExists(url)); EXPECT_TRUE(PathExists(url)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_FALSE(ofu()->IsDirectoryEmpty(context.get(), root)); EXPECT_TRUE(DirectoryExists(FileSystemURLDirName(url))); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_FALSE( ofu()->IsDirectoryEmpty(context.get(), FileSystemURLDirName(url))); // Can't remove a non-empty directory. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_NOT_EMPTY, ofu()->DeleteDirectory(context.get(), FileSystemURLDirName(url))); EXPECT_TRUE(change_observer()->HasNoChange()); @@ -1101,21 +1097,21 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryOps) { EXPECT_FALSE(file_info.is_symbolic_link); // Same create again should succeed, since exclusive is false. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), url, exclusive, recursive)); EXPECT_TRUE(change_observer()->HasNoChange()); exclusive = true; recursive = true; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_EXISTS, ofu()->CreateDirectory(context.get(), url, exclusive, recursive)); EXPECT_TRUE(change_observer()->HasNoChange()); // Verify that deleting a directory isn't stopped by zero quota, and that it // frees up quota from its path. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth(0); EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteDirectory(context.get(), url)); EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count()); @@ -1127,7 +1123,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryOps) { EXPECT_FALSE(DirectoryExists(url)); EXPECT_FALSE(PathExists(url)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_TRUE(ofu()->IsDirectoryEmpty(context.get(), url)); EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->GetFileInfo(context.get(), url, &file_info, &local_path)); @@ -1135,14 +1131,14 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryOps) { // Verify that file creation requires sufficient quota for the path. exclusive = true; recursive = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth( ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1); EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, ofu()->CreateDirectory(context.get(), url, exclusive, recursive)); EXPECT_TRUE(change_observer()->HasNoChange()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth( ObfuscatedFileUtil::ComputeFilePathCost(url.path())); EXPECT_EQ(base::File::FILE_OK, @@ -1154,7 +1150,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryOps) { exclusive = true; recursive = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_EXISTS, ofu()->CreateDirectory(context.get(), url, exclusive, recursive)); EXPECT_TRUE(change_observer()->HasNoChange()); @@ -1162,7 +1158,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryOps) { exclusive = true; recursive = false; url = CreateURLFromUTF8("foo"); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_EXISTS, ofu()->CreateDirectory(context.get(), url, exclusive, recursive)); EXPECT_TRUE(change_observer()->HasNoChange()); @@ -1174,7 +1170,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryOps) { exclusive = true; recursive = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), url, exclusive, recursive)); EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count()); @@ -1184,7 +1180,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryOps) { exclusive = true; recursive = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_EXISTS, ofu()->CreateDirectory(context.get(), url, exclusive, recursive)); EXPECT_TRUE(change_observer()->HasNoChange()); @@ -1238,7 +1234,7 @@ TEST_P(ObfuscatedFileUtilTest, TestTouch) { ofu()->Touch(context.get(), url, last_access_time, last_modified_time)); // OK, now create it. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); bool created = false; ASSERT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url, &created)); @@ -1246,7 +1242,7 @@ TEST_P(ObfuscatedFileUtilTest, TestTouch) { TestTouchHelper(url, true); // Now test a directory: - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); bool exclusive = true; bool recursive = false; url = CreateURLFromUTF8("dir"); @@ -1283,7 +1279,7 @@ TEST_P(ObfuscatedFileUtilTest, TestPathQuotas) { path_cost += ObfuscatedFileUtil::ComputeFilePathCost(base::FilePath(component)); } - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth(1024); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), url, exclusive, recursive)); @@ -1301,7 +1297,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCopyOrMoveFileNotFound) { FileSystemOperation::OPTION_NONE, is_copy_not_move)); EXPECT_TRUE(change_observer()->HasNoChange()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); is_copy_not_move = true; EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->CopyOrMoveFile(context.get(), source_url, dest_url, @@ -1311,7 +1307,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCopyOrMoveFileNotFound) { source_url = CreateURLFromUTF8("dir/dir/file"); bool exclusive = true; bool recursive = true; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ( base::File::FILE_OK, ofu()->CreateDirectory(context.get(), FileSystemURLDirName(source_url), @@ -1323,7 +1319,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCopyOrMoveFileNotFound) { FileSystemOperation::OPTION_NONE, is_copy_not_move)); EXPECT_TRUE(change_observer()->HasNoChange()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); is_copy_not_move = true; EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->CopyOrMoveFile(context.get(), source_url, dest_url, @@ -1353,38 +1349,38 @@ TEST_P(ObfuscatedFileUtilTest, TestCopyOrMoveFileSuccess) { FileSystemURL source_url = CreateURLFromUTF8(test_case.source_path); FileSystemURL dest_url = CreateURLFromUTF8(test_case.dest_path); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ( base::File::FILE_OK, ofu()->CreateDirectory(context.get(), FileSystemURLDirName(source_url), exclusive, recursive)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ( base::File::FILE_OK, ofu()->CreateDirectory(context.get(), FileSystemURLDirName(dest_url), exclusive, recursive)); bool created = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), source_url, &created)); ASSERT_TRUE(created); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ(base::File::FILE_OK, ofu()->Truncate(context.get(), source_url, kSourceLength)); if (test_case.cause_overwrite) { - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); created = false; ASSERT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), dest_url, &created)); ASSERT_TRUE(created); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ(base::File::FILE_OK, ofu()->Truncate(context.get(), dest_url, kDestLength)); } - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CopyOrMoveFile(context.get(), source_url, dest_url, FileSystemOperation::OPTION_NONE, @@ -1393,7 +1389,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCopyOrMoveFileSuccess) { if (test_case.is_copy_not_move) { base::File::Info file_info; base::FilePath local_path; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->GetFileInfo(context.get(), source_url, &file_info, &local_path)); @@ -1403,7 +1399,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCopyOrMoveFileSuccess) { } else { base::File::Info file_info; base::FilePath local_path; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->GetFileInfo(context.get(), source_url, &file_info, &local_path)); @@ -1433,7 +1429,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCopyPathQuotas) { EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth( ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path())); EXPECT_EQ(base::File::FILE_OK, @@ -1441,7 +1437,7 @@ TEST_P(ObfuscatedFileUtilTest, TestCopyPathQuotas) { FileSystemOperation::OPTION_NONE, is_copy)); // Copy, with overwrite. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth(0); EXPECT_EQ(base::File::FILE_OK, ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, @@ -1458,14 +1454,14 @@ TEST_P(ObfuscatedFileUtilTest, TestMovePathQuotasWithRename) { bool is_copy = false; // Move, rename, no overwrite. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth( ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) - ObfuscatedFileUtil::ComputeFilePathCost(src_url.path()) - 1); EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth( ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) - ObfuscatedFileUtil::ComputeFilePathCost(src_url.path())); @@ -1473,12 +1469,12 @@ TEST_P(ObfuscatedFileUtilTest, TestMovePathQuotasWithRename) { ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), src_url, &created)); // Move, rename, with overwrite. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth(0); EXPECT_EQ(base::File::FILE_OK, ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, @@ -1495,7 +1491,7 @@ TEST_P(ObfuscatedFileUtilTest, TestMovePathQuotasWithoutRename) { bool exclusive = true; bool recursive = false; FileSystemURL dir_url = CreateURLFromUTF8("directory path"); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), dir_url, exclusive, recursive)); @@ -1504,7 +1500,7 @@ TEST_P(ObfuscatedFileUtilTest, TestMovePathQuotasWithoutRename) { bool is_copy = false; int64_t allowed_bytes_growth = -1000; // Over quota, this should still work. // Move, no rename, no overwrite. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth(allowed_bytes_growth); EXPECT_EQ(base::File::FILE_OK, ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, @@ -1512,10 +1508,10 @@ TEST_P(ObfuscatedFileUtilTest, TestMovePathQuotasWithoutRename) { EXPECT_EQ(allowed_bytes_growth, context->allowed_bytes_growth()); // Move, no rename, with overwrite. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); ASSERT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), src_url, &created)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); context->set_allowed_bytes_growth(allowed_bytes_growth); EXPECT_EQ(base::File::FILE_OK, ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, @@ -1698,13 +1694,13 @@ TEST_P(ObfuscatedFileUtilTest, TestInconsistency) { bool created = false; // Create a non-empty file. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), kPath1, &created)); EXPECT_TRUE(created); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->Truncate(context.get(), kPath1, 10)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->GetFileInfo(context.get(), kPath1, &file_info, &data_path)); EXPECT_EQ(10, file_info.size); @@ -1714,17 +1710,17 @@ TEST_P(ObfuscatedFileUtilTest, TestInconsistency) { // Try to get file info of broken file. EXPECT_FALSE(PathExists(kPath1)); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), kPath1, &created)); EXPECT_TRUE(created); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->GetFileInfo(context.get(), kPath1, &file_info, &data_path)); EXPECT_EQ(0, file_info.size); // Make another broken file to |kPath2|. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), kPath2, &created)); EXPECT_TRUE(created); @@ -1733,7 +1729,7 @@ TEST_P(ObfuscatedFileUtilTest, TestInconsistency) { ofu()->DestroyDirectoryDatabase(origin(), type_string()); // Repair broken |kPath1|. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->Touch(context.get(), kPath1, base::Time::Now(), base::Time::Now())); @@ -1742,14 +1738,14 @@ TEST_P(ObfuscatedFileUtilTest, TestInconsistency) { EXPECT_TRUE(created); // Copy from sound |kPath1| to broken |kPath2|. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ( base::File::FILE_OK, ofu()->CopyOrMoveFile(context.get(), kPath1, kPath2, FileSystemOperation::OPTION_NONE, true /* copy */)); ofu()->DestroyDirectoryDatabase(origin(), type_string()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); created = false; EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), kPath1, &created)); @@ -1767,7 +1763,7 @@ TEST_P(ObfuscatedFileUtilTest, TestIncompleteDirectoryReading) { for (const auto& path : kPath) { bool created = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), path, &created)); EXPECT_TRUE(created); @@ -1800,7 +1796,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { FileSystemURL url(FileSystemURLAppendUTF8(dir_url, "EnsureFileExists_file")); bool created = false; ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url, &created)); EXPECT_TRUE(created); @@ -1809,7 +1805,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { // non create case. created = true; ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url, &created)); EXPECT_FALSE(created); @@ -1817,12 +1813,12 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { // fail case. url = FileSystemURLAppendUTF8(dir_url, "EnsureFileExists_dir"); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), url, false, false)); ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_NOT_A_FILE, ofu()->EnsureFileExists(context.get(), url, &created)); EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); @@ -1830,7 +1826,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { // CreateOrOpen, create case. url = FileSystemURLAppendUTF8(dir_url, "CreateOrOpen_file"); ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); created = false; EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url, &created)); @@ -1839,7 +1835,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { // open case. ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url, &created)); EXPECT_FALSE(created); @@ -1847,7 +1843,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { // fail case ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url, &created)); EXPECT_FALSE(created); @@ -1858,7 +1854,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { url = FileSystemURLAppendUTF8(dir_url, "CreateDirectory_dir"); FileSystemURL subdir_url(FileSystemURLAppendUTF8(url, "subdir")); ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), subdir_url, true /* exclusive */, true /* recursive */)); @@ -1869,7 +1865,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { subdir_url = FileSystemURLAppendUTF8(url, "subdir2"); ClearTimestamp(dir_url); ClearTimestamp(url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), subdir_url, true /* exclusive */, true /* recursive */)); @@ -1879,7 +1875,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { // fail case. url = FileSystemURLAppendUTF8(dir_url, "CreateDirectory_dir"); ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_EXISTS, ofu()->CreateDirectory(context.get(), url, true /* exclusive */, true /* recursive */)); @@ -1897,7 +1893,7 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { EXPECT_TRUE(created); ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ( base::File::FILE_OK, ofu()->CopyInForeignFile(context.get(), foreign_src_file_path, url)); @@ -1915,19 +1911,19 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForDeletion) { // DeleteFile, delete case. FileSystemURL url = FileSystemURLAppendUTF8(dir_url, "DeleteFile_file"); bool created = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url, &created)); EXPECT_TRUE(created); ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteFile(context.get(), url)); EXPECT_NE(base::Time(), GetModifiedTime(dir_url)); // fail case. ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, ofu()->DeleteFile(context.get(), url)); EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); @@ -1935,27 +1931,27 @@ TEST_P(ObfuscatedFileUtilTest, TestDirectoryTimestampForDeletion) { // DeleteDirectory, fail case. url = FileSystemURLAppendUTF8(dir_url, "DeleteDirectory_dir"); FileSystemURL file_path(FileSystemURLAppendUTF8(url, "pakeratta")); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), url, true, true)); created = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), file_path, &created)); EXPECT_TRUE(created); ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_ERROR_NOT_EMPTY, ofu()->DeleteDirectory(context.get(), url)); EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); // delete case. - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteFile(context.get(), file_path)); ClearTimestamp(dir_url); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteDirectory(context.get(), url)); EXPECT_NE(base::Time(), GetModifiedTime(dir_url)); } @@ -1980,35 +1976,35 @@ TEST_P(ObfuscatedFileUtilTest, TestFileEnumeratorTimestamp) { ofu()->CreateDirectory(context.get(), dir, false, false)); bool created = false; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->EnsureFileExists(context.get(), url1, &created)); EXPECT_TRUE(created); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->CreateDirectory(context.get(), url2, false, false)); base::FilePath file_path; - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->GetLocalFilePath(context.get(), url1, &file_path)); EXPECT_FALSE(file_path.empty()); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); EXPECT_EQ(base::File::FILE_OK, ofu()->Touch(context.get(), url1, base::Time::Now() + base::TimeDelta::FromHours(1), base::Time())); - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); std::unique_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum = ofu()->CreateFileEnumerator(context.get(), dir, false); int count = 0; base::FilePath file_path_each; while (!(file_path_each = file_enum->Next()).empty()) { - context.reset(NewContext(nullptr)); + context = NewContext(nullptr); base::File::Info file_info; base::FilePath file_path; EXPECT_EQ( diff --git a/chromium/storage/browser/file_system/plugin_private_file_system_backend.cc b/chromium/storage/browser/file_system/plugin_private_file_system_backend.cc index 70975f6b46a..ee251a933b2 100644 --- a/chromium/storage/browser/file_system/plugin_private_file_system_backend.cc +++ b/chromium/storage/browser/file_system/plugin_private_file_system_backend.cc @@ -11,9 +11,9 @@ #include <utility> #include "base/bind.h" +#include "base/containers/contains.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" -#include "base/stl_util.h" #include "base/synchronization/lock.h" #include "base/task_runner_util.h" #include "base/threading/thread_task_runner_handle.h" @@ -103,11 +103,13 @@ PluginPrivateFileSystemBackend::PluginPrivateFileSystemBackend( base_path_(profile_path.Append(kFileSystemDirectory) .Append(kPluginPrivateDirectory)), plugin_map_(new FileSystemIDToPluginMap(file_task_runner)) { - file_util_ = std::make_unique<AsyncFileUtilAdapter>(new ObfuscatedFileUtil( - special_storage_policy, base_path_, env_override, - base::BindRepeating(&FileSystemIDToPluginMap::GetPluginIDForURL, - base::Owned(plugin_map_)), - std::set<std::string>(), nullptr, file_system_options.is_incognito())); + file_util_ = std::make_unique<AsyncFileUtilAdapter>( + std::make_unique<ObfuscatedFileUtil>( + special_storage_policy, base_path_, env_override, + base::BindRepeating(&FileSystemIDToPluginMap::GetPluginIDForURL, + base::Owned(plugin_map_)), + std::set<std::string>(), nullptr, + file_system_options.is_incognito())); } PluginPrivateFileSystemBackend::~PluginPrivateFileSystemBackend() { @@ -180,7 +182,7 @@ FileSystemOperation* PluginPrivateFileSystemBackend::CreateFileSystemOperation( FileSystemContext* context, base::File::Error* error_code) const { std::unique_ptr<FileSystemOperationContext> operation_context( - new FileSystemOperationContext(context)); + std::make_unique<FileSystemOperationContext>(context)); return FileSystemOperation::Create(url, context, std::move(operation_context)); } @@ -315,7 +317,7 @@ void PluginPrivateFileSystemBackend::GetOriginDetailsOnFileTaskRunner( "pluginprivate"); std::unique_ptr<FileSystemOperationContext> operation_context( - new FileSystemOperationContext(context)); + std::make_unique<FileSystemOperationContext>(context)); // Determine the available plugin private filesystem directories for this // origin. Currently the plugin private filesystem is only used by Encrypted diff --git a/chromium/storage/browser/file_system/quota/quota_backend_impl.cc b/chromium/storage/browser/file_system/quota/quota_backend_impl.cc index f5a5ddacfbf..9a3af570aca 100644 --- a/chromium/storage/browser/file_system/quota/quota_backend_impl.cc +++ b/chromium/storage/browser/file_system/quota/quota_backend_impl.cc @@ -47,7 +47,7 @@ void QuotaBackendImpl::ReserveQuota(const url::Origin& origin, } DCHECK(quota_manager_proxy_.get()); quota_manager_proxy_->GetUsageAndQuota( - file_task_runner_.get(), origin, FileSystemTypeToQuotaStorageType(type), + origin, FileSystemTypeToQuotaStorageType(type), file_task_runner_, base::BindOnce(&QuotaBackendImpl::DidGetUsageAndQuotaForReserveQuota, weak_ptr_factory_.GetWeakPtr(), QuotaReservationInfo(origin, type, delta), @@ -141,7 +141,8 @@ void QuotaBackendImpl::ReserveQuotaInternal(const QuotaReservationInfo& info) { DCHECK(quota_manager_proxy_.get()); quota_manager_proxy_->NotifyStorageModified( QuotaClientType::kFileSystem, info.origin, - FileSystemTypeToQuotaStorageType(info.type), info.delta); + FileSystemTypeToQuotaStorageType(info.type), info.delta, + base::Time::Now()); } base::File::Error QuotaBackendImpl::GetUsageCachePath( diff --git a/chromium/storage/browser/file_system/quota/quota_backend_impl_unittest.cc b/chromium/storage/browser/file_system/quota/quota_backend_impl_unittest.cc index e7e8c49e7f1..84dc3a1af71 100644 --- a/chromium/storage/browser/file_system/quota/quota_backend_impl_unittest.cc +++ b/chromium/storage/browser/file_system/quota/quota_backend_impl_unittest.cc @@ -11,9 +11,12 @@ #include <utility> #include "base/bind.h" +#include "base/check.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "base/run_loop.h" +#include "base/sequenced_task_runner.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "storage/browser/file_system/file_system_usage_cache.h" @@ -41,7 +44,7 @@ bool DidReserveQuota(bool accepted, class MockQuotaManagerProxy : public QuotaManagerProxy { public: MockQuotaManagerProxy() - : QuotaManagerProxy(nullptr, nullptr), + : QuotaManagerProxy(nullptr, base::ThreadTaskRunnerHandle::Get()), storage_modified_count_(0), usage_(0), quota_(0) {} @@ -54,19 +57,28 @@ class MockQuotaManagerProxy : public QuotaManagerProxy { blink::mojom::StorageType type, bool enabled) override {} - void NotifyStorageModified(QuotaClientType client_id, - const url::Origin& origin, - blink::mojom::StorageType type, - int64_t delta) override { + void NotifyStorageModified( + QuotaClientType client_id, + const url::Origin& origin, + blink::mojom::StorageType type, + int64_t delta, + base::Time modification_time, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceClosure callback) override { ++storage_modified_count_; usage_ += delta; ASSERT_LE(usage_, quota_); + if (callback) + callback_task_runner->PostTask(FROM_HERE, std::move(callback)); } - void GetUsageAndQuota(base::SequencedTaskRunner* original_task_runner, - const url::Origin& origin, - blink::mojom::StorageType type, - UsageAndQuotaCallback callback) override { + void GetUsageAndQuota( + const url::Origin& origin, + blink::mojom::StorageType type, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + UsageAndQuotaCallback callback) override { + DCHECK(callback_task_runner); + DCHECK(callback_task_runner->RunsTasksInCurrentSequence()); std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk, usage_, quota_); } @@ -93,7 +105,7 @@ class QuotaBackendImplTest : public testing::Test, public: QuotaBackendImplTest() : file_system_usage_cache_(is_incognito()), - quota_manager_proxy_(new MockQuotaManagerProxy) {} + quota_manager_proxy_(base::MakeRefCounted<MockQuotaManagerProxy>()) {} void SetUp() override { ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); diff --git a/chromium/storage/browser/file_system/quota/quota_reservation_manager_unittest.cc b/chromium/storage/browser/file_system/quota/quota_reservation_manager_unittest.cc index 3e5f74dc6ba..f588e185d30 100644 --- a/chromium/storage/browser/file_system/quota/quota_reservation_manager_unittest.cc +++ b/chromium/storage/browser/file_system/quota/quota_reservation_manager_unittest.cc @@ -188,9 +188,9 @@ class QuotaReservationManagerTest : public testing::Test { file_path_ = work_dir_.GetPath().Append(FILE_PATH_LITERAL("hoge")); SetFileSize(file_path_, kInitialFileSize); - std::unique_ptr<QuotaReservationManager::QuotaBackend> backend( - new FakeBackend); - reservation_manager_.reset(new QuotaReservationManager(std::move(backend))); + auto backend = std::make_unique<FakeBackend>(); + reservation_manager_ = + std::make_unique<QuotaReservationManager>(std::move(backend)); } void TearDown() override { reservation_manager_.reset(); } diff --git a/chromium/storage/browser/file_system/recursive_operation_delegate_unittest.cc b/chromium/storage/browser/file_system/recursive_operation_delegate_unittest.cc index 4195b5bb5ba..cd4c1f2e310 100644 --- a/chromium/storage/browser/file_system/recursive_operation_delegate_unittest.cc +++ b/chromium/storage/browser/file_system/recursive_operation_delegate_unittest.cc @@ -148,11 +148,10 @@ class RecursiveOperationDelegateTest : public testing::Test { void TearDown() override { sandbox_file_system_.TearDown(); } std::unique_ptr<FileSystemOperationContext> NewContext() { - FileSystemOperationContext* context = - sandbox_file_system_.NewOperationContext(); + auto context = sandbox_file_system_.NewOperationContext(); // Grant enough quota for all test cases. context->set_allowed_bytes_growth(1000000); - return base::WrapUnique(context); + return context; } FileSystemFileUtil* file_util() { return sandbox_file_system_.file_util(); } diff --git a/chromium/storage/browser/file_system/sandbox_directory_database_unittest.cc b/chromium/storage/browser/file_system/sandbox_directory_database_unittest.cc index 2f844339fab..e24f825f338 100644 --- a/chromium/storage/browser/file_system/sandbox_directory_database_unittest.cc +++ b/chromium/storage/browser/file_system/sandbox_directory_database_unittest.cc @@ -46,7 +46,7 @@ class SandboxDirectoryDatabaseTest : public testing::Test { // Call CloseDatabase() to avoid having multiple database instances for // single directory at once. CloseDatabase(); - db_.reset(new SandboxDirectoryDatabase(path(), nullptr)); + db_ = std::make_unique<SandboxDirectoryDatabase>(path(), nullptr); } void CloseDatabase() { db_.reset(); } @@ -98,7 +98,7 @@ class SandboxDirectoryDatabaseTest : public testing::Test { db_.reset(); ASSERT_TRUE(base::DeletePathRecursively(path())); ASSERT_TRUE(base::CreateDirectory(path())); - db_.reset(new SandboxDirectoryDatabase(path(), nullptr)); + db_ = std::make_unique<SandboxDirectoryDatabase>(path(), nullptr); } bool RepairDatabase() { diff --git a/chromium/storage/browser/file_system/sandbox_file_stream_writer.cc b/chromium/storage/browser/file_system/sandbox_file_stream_writer.cc index 2a09c4cf6ec..fcc3cfb2548 100644 --- a/chromium/storage/browser/file_system/sandbox_file_stream_writer.cc +++ b/chromium/storage/browser/file_system/sandbox_file_stream_writer.cc @@ -13,6 +13,7 @@ #include "base/bind.h" #include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" #include "storage/browser/file_system/file_observers.h" @@ -28,18 +29,18 @@ namespace storage { namespace { -// Adjust the |quota| value in overwriting case (i.e. |file_size| > 0 and -// |file_offset| < |file_size|) to make the remaining quota calculation easier. -// Specifically this widens the quota for overlapping range (so that we can -// simply compare written bytes against the adjusted quota). +// Adjust the |quota| value to make the remaining quota calculation easier. This +// allows us to simply compare written bytes against the adjusted quota. int64_t AdjustQuotaForOverlap(int64_t quota, int64_t file_offset, int64_t file_size) { - DCHECK_LE(file_offset, file_size); if (quota < 0) quota = 0; + // |overlap| can be negative if |file_offset| is past the end of the file. + // Negative |overlap| ensures null bytes between the end of the file and the + // |file_offset| are counted towards the file's quota. int64_t overlap = file_size - file_offset; - if (std::numeric_limits<int64_t>::max() - overlap > quota) + if (overlap < 0 || std::numeric_limits<int64_t>::max() - overlap > quota) quota += overlap; return quota; } @@ -102,8 +103,9 @@ int SandboxFileStreamWriter::WriteInternal(net::IOBuffer* buf, int buf_len) { DCHECK(total_bytes_written_ <= allowed_bytes_to_write_ || allowed_bytes_to_write_ < 0); if (total_bytes_written_ >= allowed_bytes_to_write_) { - has_pending_operation_ = false; - return net::ERR_FILE_NO_SPACE; + const int out_of_quota = net::ERR_FILE_NO_SPACE; + DidWrite(out_of_quota); + return out_of_quota; } if (buf_len > allowed_bytes_to_write_ - total_bytes_written_) @@ -139,11 +141,7 @@ void SandboxFileStreamWriter::DidCreateSnapshotFile( return; } file_size_ = file_info.size; - if (initial_offset_ > file_size_) { - // We should not be writing past the end of the file. - std::move(callback).Run(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); - return; - } + DCHECK(!file_writer_.get()); if (file_system_context_->is_incognito()) { @@ -177,9 +175,10 @@ void SandboxFileStreamWriter::DidCreateSnapshotFile( return; } - DCHECK(quota_manager_proxy->quota_manager()); - quota_manager_proxy->quota_manager()->GetUsageAndQuota( + DCHECK(quota_manager_proxy); + quota_manager_proxy->GetUsageAndQuota( url_.origin(), FileSystemTypeToQuotaStorageType(url_.type()), + base::SequencedTaskRunnerHandle::Get(), base::BindOnce(&SandboxFileStreamWriter::DidGetUsageAndQuota, weak_factory_.GetWeakPtr(), std::move(callback))); } @@ -226,17 +225,24 @@ void SandboxFileStreamWriter::DidWrite(int write_response) { if (write_response <= 0) { // TODO(crbug.com/1091792): Consider listening explicitly for out // of space errors instead of surfacing all write errors to quota. - file_system_context_->quota_manager_proxy()->NotifyWriteFailed( - url_.origin()); + QuotaManagerProxy* quota_manager_proxy = + file_system_context_->quota_manager_proxy(); + if (quota_manager_proxy) { + quota_manager_proxy->NotifyWriteFailed(url_.origin()); + } if (CancelIfRequested()) return; - std::move(write_callback_).Run(write_response); + if (write_callback_) + std::move(write_callback_).Run(write_response); return; } if (total_bytes_written_ + write_response + initial_offset_ > file_size_) { int overlapped = file_size_ - total_bytes_written_ - initial_offset_; - if (overlapped < 0) + // If writing past the end of a file, the distance seeked past the file + // needs to be accounted for. This adjustment should only be made for the + // first such write (when |total_bytes_written_| is 0). + if (overlapped < 0 && total_bytes_written_ != 0) overlapped = 0; observers_.Notify(&FileUpdateObserver::OnUpdate, url_, write_response - overlapped); diff --git a/chromium/storage/browser/file_system/sandbox_file_stream_writer.h b/chromium/storage/browser/file_system/sandbox_file_stream_writer.h index 3fc751e101e..ed4414b78a1 100644 --- a/chromium/storage/browser/file_system/sandbox_file_stream_writer.h +++ b/chromium/storage/browser/file_system/sandbox_file_stream_writer.h @@ -47,6 +47,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) SandboxFileStreamWriter private: // Performs quota calculation and calls file_writer_->Write(). + // Will either return synchronously, or run asynchronously and call + // |write_callback_|. int WriteInternal(net::IOBuffer* buf, int buf_len); // Callbacks that are chained for the first write. This eventually calls @@ -62,6 +64,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) SandboxFileStreamWriter int64_t quota); void DidInitializeForWrite(net::IOBuffer* buf, int buf_len, int init_status); + // Will call |write_callback_| if set, or return synchronously. void DidWrite(int write_response); void DidFlush(net::CompletionOnceCallback callback, int result); diff --git a/chromium/storage/browser/file_system/sandbox_file_stream_writer_unittest.cc b/chromium/storage/browser/file_system/sandbox_file_stream_writer_unittest.cc index f88ef6970f7..8678c9f22ca 100644 --- a/chromium/storage/browser/file_system/sandbox_file_stream_writer_unittest.cc +++ b/chromium/storage/browser/file_system/sandbox_file_stream_writer_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "storage/browser/file_system/sandbox_file_stream_writer.h" +#include "base/test/bind.h" #include "storage/browser/file_system/file_stream_writer_test.h" #include <stdint.h> @@ -22,6 +23,8 @@ #include "storage/browser/file_system/file_stream_writer.h" #include "storage/browser/file_system/file_system_context.h" #include "storage/browser/test/async_file_test_helper.h" +#include "storage/browser/test/mock_quota_manager_proxy.h" +#include "storage/browser/test/mock_special_storage_policy.h" #include "storage/browser/test/test_file_system_context.h" #include "storage/common/file_system/file_system_types.h" @@ -38,7 +41,14 @@ class SandboxFileStreamWriterTest : public FileStreamWriterTest { void SetUp() override { ASSERT_TRUE(dir_.CreateUniqueTempDir()); - file_system_context_ = CreateFileSystemContext(dir_); + quota_manager_ = base::MakeRefCounted<storage::MockQuotaManager>( + is_incognito(), dir_.GetPath(), base::ThreadTaskRunnerHandle::Get(), + nullptr); + quota_manager_proxy_ = base::MakeRefCounted<storage::MockQuotaManagerProxy>( + quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get()); + + file_system_context_ = + CreateFileSystemContext(quota_manager_proxy_.get(), dir_); file_system_context_->OpenFileSystem( url::Origin::Create(GURL(kURLOrigin)), kFileSystemTypeTemporary, @@ -47,20 +57,38 @@ class SandboxFileStreamWriterTest : public FileStreamWriterTest { base::File::Error result) { ASSERT_EQ(base::File::FILE_OK, result); })); + + SetQuota(1024 * 1024 * 100); base::RunLoop().RunUntilIdle(); } - void TearDown() override { base::RunLoop().RunUntilIdle(); } + void TearDown() override { + quota_manager_proxy_->SimulateQuotaManagerDestroyed(); + quota_manager_.reset(); + base::RunLoop().RunUntilIdle(); + } protected: base::ScopedTempDir dir_; scoped_refptr<FileSystemContext> file_system_context_; + scoped_refptr<MockQuotaManager> quota_manager_; + scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_; + + struct quota_usage_and_info { + blink::mojom::QuotaStatusCode status; + int64_t usage; + int64_t quota; + }; virtual FileSystemContext* CreateFileSystemContext( + QuotaManagerProxy* quota_manager_proxy, const base::ScopedTempDir& dir) { - return CreateFileSystemContextForTesting(nullptr, dir.GetPath()); + return CreateFileSystemContextForTesting(quota_manager_proxy, + dir.GetPath()); } + virtual bool is_incognito() { return false; } + FileSystemURL GetFileSystemURL(const std::string& file_name) { return file_system_context_->CreateCrackedFileSystemURL( url::Origin::Create(GURL(kURLOrigin)), kFileSystemTypeTemporary, @@ -107,8 +135,181 @@ class SandboxFileStreamWriterTest : public FileStreamWriterTest { return content; } + + std::unique_ptr<SandboxFileStreamWriter> CreateSandboxWriter( + const std::string& name, + int64_t offset) { + auto writer = std::make_unique<SandboxFileStreamWriter>( + file_system_context_.get(), GetFileSystemURL(name), offset, + *file_system_context_->GetUpdateObservers(kFileSystemTypeTemporary)); + return writer; + } + + quota_usage_and_info GetUsageAndQuotaSync() { + quota_usage_and_info info; + quota_manager_->GetUsageAndQuota( + url::Origin::Create(GURL(kURLOrigin)), + blink::mojom::StorageType::kTemporary, + base::BindLambdaForTesting([&](blink::mojom::QuotaStatusCode status, + int64_t usage, int64_t quota) { + info.status = status; + info.usage = usage; + info.quota = quota; + })); + return info; + } + + void SetQuota(int64_t quota) { + quota_manager_->SetQuota(url::Origin::Create(GURL(kURLOrigin)), + blink::mojom::StorageType::kTemporary, quota); + } + + int64_t GetFreeQuota() { + auto info = GetUsageAndQuotaSync(); + return info.quota - info.usage; + } + + void SetFreeQuota(int64_t free_quota) { + auto info = GetUsageAndQuotaSync(); + SetQuota(info.usage + free_quota); + } + + void Test_Quota_OK() { + std::string name = "file_a"; + EXPECT_TRUE(CreateFileWithContent(name, "foo")); + + SetFreeQuota(7); + std::unique_ptr<SandboxFileStreamWriter> writer( + CreateSandboxWriter(name, 3)); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx")); + writer.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(FilePathExists(name)); + EXPECT_EQ(std::string("fooxxx", 6), GetFileContent(name)); + EXPECT_EQ(GetFreeQuota(), 4); + } + + void Test_Quota_WritePastEnd() { + std::string name = "file_a"; + EXPECT_TRUE(CreateFileWithContent(name, "foo")); + + SetFreeQuota(6); + std::unique_ptr<SandboxFileStreamWriter> writer( + CreateSandboxWriter(name, 6)); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx")); + writer.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(FilePathExists(name)); + EXPECT_EQ(std::string("foo\0\0\0xxx", 9), GetFileContent(name)); + EXPECT_EQ(GetFreeQuota(), 0); + } + + void Test_Quota_NoSpace() { + std::string name = "file_a"; + EXPECT_TRUE(CreateFileWithContent(name, "foo")); + + SetFreeQuota(0); + std::unique_ptr<SandboxFileStreamWriter> writer( + CreateSandboxWriter(name, 3)); + EXPECT_EQ(net::ERR_FILE_NO_SPACE, WriteStringToWriter(writer.get(), "xxx")); + writer.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(FilePathExists(name)); + EXPECT_EQ(std::string("foo", 3), GetFileContent(name)); + EXPECT_EQ(GetFreeQuota(), 0); + } + + void Test_Quota_NoSpace_PartialWrite() { + std::string name = "file_a"; + EXPECT_TRUE(CreateFileWithContent(name, "foo")); + + SetFreeQuota(5); + std::unique_ptr<SandboxFileStreamWriter> writer( + CreateSandboxWriter(name, 6)); + EXPECT_EQ(net::ERR_FILE_NO_SPACE, WriteStringToWriter(writer.get(), "xxx")); + writer.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(FilePathExists(name)); + EXPECT_EQ(std::string("foo\0\0\0xx", 8), GetFileContent(name)); + EXPECT_EQ(GetFreeQuota(), 0); + } + + void Test_Quota_Negative() { + std::string name = "file_a"; + EXPECT_TRUE(CreateFileWithContent(name, "foo")); + + SetFreeQuota(-1); + std::unique_ptr<SandboxFileStreamWriter> writer( + CreateSandboxWriter(name, 3)); + EXPECT_EQ(net::ERR_FILE_NO_SPACE, WriteStringToWriter(writer.get(), "xxx")); + writer.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(FilePathExists(name)); + EXPECT_EQ(std::string("foo", 3), GetFileContent(name)); + EXPECT_EQ(GetFreeQuota(), -1); + } + + void Test_Quota_WritePastEndTwice_OK() { + std::string name = "file_a"; + EXPECT_TRUE(CreateFileWithContent(name, "foo")); + + SetFreeQuota(9); + std::unique_ptr<SandboxFileStreamWriter> writer( + CreateSandboxWriter(name, 6)); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx")); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "yyy")); + writer.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(FilePathExists(name)); + EXPECT_EQ(std::string("foo\0\0\0xxxyyy", 12), GetFileContent(name)); + EXPECT_EQ(GetFreeQuota(), 0); + } + + void Test_Quota_WritePastEndTwice_NoSpace() { + std::string name = "file_a"; + EXPECT_TRUE(CreateFileWithContent(name, "foo")); + + SetFreeQuota(7); + std::unique_ptr<SandboxFileStreamWriter> writer( + CreateSandboxWriter(name, 6)); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx")); + EXPECT_EQ(net::ERR_FILE_NO_SPACE, WriteStringToWriter(writer.get(), "yyy")); + writer.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(FilePathExists(name)); + EXPECT_EQ(std::string("foo\0\0\0xxxy", 10), GetFileContent(name)); + EXPECT_EQ(GetFreeQuota(), 0); + } }; +TEST_F(SandboxFileStreamWriterTest, Quota_OK) { + Test_Quota_OK(); +} + +TEST_F(SandboxFileStreamWriterTest, Quota_WritePastEnd) { + Test_Quota_WritePastEnd(); +} + +TEST_F(SandboxFileStreamWriterTest, Quota_NoSpace) { + Test_Quota_NoSpace(); +} + +TEST_F(SandboxFileStreamWriterTest, Quota_NoSpace_PartialWrite) { + Test_Quota_NoSpace_PartialWrite(); +} + +TEST_F(SandboxFileStreamWriterTest, Quota_Negative) { + Test_Quota_Negative(); +} + +TEST_F(SandboxFileStreamWriterTest, Quota_WritePastEndTwice_OK) { + Test_Quota_WritePastEndTwice_OK(); +} + +TEST_F(SandboxFileStreamWriterTest, Quota_WritePastEndTwice_NoSpace) { + Test_Quota_WritePastEndTwice_NoSpace(); +} + INSTANTIATE_TYPED_TEST_SUITE_P(Sandbox, FileStreamWriterTypedTest, SandboxFileStreamWriterTest); @@ -120,13 +321,45 @@ class SandboxFileStreamWriterIncognitoTest protected: FileSystemContext* CreateFileSystemContext( + QuotaManagerProxy* quota_manager_proxy, const base::ScopedTempDir& dir) override { return CreateIncognitoFileSystemContextForTesting( base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get(), nullptr, dir.GetPath()); + base::ThreadTaskRunnerHandle::Get(), quota_manager_proxy, + dir.GetPath()); } + + bool is_incognito() override { return true; } }; +TEST_F(SandboxFileStreamWriterIncognitoTest, Quota_OK) { + Test_Quota_OK(); +} + +TEST_F(SandboxFileStreamWriterIncognitoTest, Quota_WritePastEnd) { + Test_Quota_WritePastEnd(); +} + +TEST_F(SandboxFileStreamWriterIncognitoTest, Quota_NoSpace) { + Test_Quota_NoSpace(); +} + +TEST_F(SandboxFileStreamWriterIncognitoTest, Quota_NoSpace_PartialWrite) { + Test_Quota_NoSpace_PartialWrite(); +} + +TEST_F(SandboxFileStreamWriterIncognitoTest, Quota_Negative) { + Test_Quota_Negative(); +} + +TEST_F(SandboxFileStreamWriterIncognitoTest, Quota_WritePastEndTwice_OK) { + Test_Quota_WritePastEndTwice_OK(); +} + +TEST_F(SandboxFileStreamWriterIncognitoTest, Quota_WritePastEndTwice_NoSpace) { + Test_Quota_WritePastEndTwice_NoSpace(); +} + INSTANTIATE_TYPED_TEST_SUITE_P(SandboxIncognito, FileStreamWriterTypedTest, SandboxFileStreamWriterIncognitoTest); diff --git a/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate.cc b/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate.cc index 17a9a301c67..b6cd5b58fd0 100644 --- a/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate.cc +++ b/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate.cc @@ -13,11 +13,12 @@ #include "base/bind.h" #include "base/callback_helpers.h" #include "base/command_line.h" +#include "base/containers/contains.h" #include "base/files/file_util.h" #include "base/macros.h" #include "base/metrics/histogram_macros.h" -#include "base/stl_util.h" #include "base/task_runner_util.h" +#include "base/time/time.h" #include "storage/browser/file_system/async_file_util_adapter.h" #include "storage/browser/file_system/file_stream_reader.h" #include "storage/browser/file_system/file_system_context.h" @@ -113,17 +114,17 @@ class SandboxObfuscatedOriginEnumerator std::unique_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enum_; }; -void OpenSandboxFileSystemOnFileTaskRunner(ObfuscatedFileUtil* file_util, - const GURL& origin_url, - FileSystemType type, - OpenFileSystemMode mode, - base::File::Error* error_ptr) { - DCHECK(error_ptr); +base::File::Error OpenSandboxFileSystemOnFileTaskRunner( + ObfuscatedFileUtil* file_util, + const GURL& origin_url, + FileSystemType type, + OpenFileSystemMode mode) { const bool create = (mode == OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT); + base::File::Error error; file_util->GetDirectoryForOriginAndType( url::Origin::Create(origin_url), - SandboxFileSystemBackendDelegate::GetTypeString(type), create, error_ptr); - if (*error_ptr != base::File::FILE_OK) { + SandboxFileSystemBackendDelegate::GetTypeString(type), create, &error); + if (error != base::File::FILE_OK) { UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemLabel, kCreateDirectoryError, kFileSystemErrorMax); } else { @@ -132,18 +133,20 @@ void OpenSandboxFileSystemOnFileTaskRunner(ObfuscatedFileUtil* file_util, // The reference of file_util will be derefed on the FILE thread // when the storage of this callback gets deleted regardless of whether // this method is called or not. + + return error; } void DidOpenFileSystem( base::WeakPtr<SandboxFileSystemBackendDelegate> delegate, base::OnceClosure quota_callback, base::OnceCallback<void(base::File::Error error)> callback, - base::File::Error* error) { + base::File::Error error) { if (delegate) - delegate->CollectOpenFileSystemMetrics(*error); - if (*error == base::File::FILE_OK) + delegate->CollectOpenFileSystemMetrics(error); + if (error == base::File::FILE_OK) std::move(quota_callback).Run(); - std::move(callback).Run(*error); + std::move(callback).Run(error); } template <typename T> @@ -186,20 +189,22 @@ SandboxFileSystemBackendDelegate::SandboxFileSystemBackendDelegate( : file_task_runner_(file_task_runner), quota_manager_proxy_(quota_manager_proxy), sandbox_file_util_(std::make_unique<AsyncFileUtilAdapter>( - new ObfuscatedFileUtil(special_storage_policy, - profile_path.Append(kFileSystemDirectory), - env_override, - base::BindRepeating(&GetTypeStringForURL), - GetKnownTypeStrings(), - this, - file_system_options.is_incognito()))), + std::make_unique<ObfuscatedFileUtil>( + special_storage_policy, + profile_path.Append(kFileSystemDirectory), + env_override, + base::BindRepeating(&GetTypeStringForURL), + GetKnownTypeStrings(), + this, + file_system_options.is_incognito()))), file_system_usage_cache_(std::make_unique<FileSystemUsageCache>( file_system_options.is_incognito())), - quota_observer_(new SandboxQuotaObserver(quota_manager_proxy, - file_task_runner, - obfuscated_file_util(), - usage_cache())), - quota_reservation_manager_(new QuotaReservationManager( + quota_observer_( + std::make_unique<SandboxQuotaObserver>(quota_manager_proxy, + file_task_runner, + obfuscated_file_util(), + usage_cache())), + quota_reservation_manager_(std::make_unique<QuotaReservationManager>( std::make_unique<QuotaBackendImpl>(file_task_runner_.get(), obfuscated_file_util(), usage_cache(), @@ -270,19 +275,17 @@ void SandboxFileSystemBackendDelegate::OpenFileSystem( (quota_manager_proxy_.get()) ? base::BindOnce(&QuotaManagerProxy::NotifyStorageAccessed, quota_manager_proxy_, origin, - FileSystemTypeToQuotaStorageType(type)) + FileSystemTypeToQuotaStorageType(type), + base::Time::Now()) : base::DoNothing(); - base::File::Error* error_ptr = new base::File::Error; - file_task_runner_->PostTaskAndReply( + file_task_runner_->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&OpenSandboxFileSystemOnFileTaskRunner, - obfuscated_file_util(), origin.GetURL(), type, mode, - base::Unretained(error_ptr)), + obfuscated_file_util(), origin.GetURL(), type, mode), base::BindOnce(&DidOpenFileSystem, weak_factory_.GetWeakPtr(), std::move(quota_callback), - base::BindOnce(std::move(callback), root_url, name), - base::Owned(error_ptr))); + base::BindOnce(std::move(callback), root_url, name))); DETACH_FROM_THREAD(io_thread_checker_); is_filesystem_opened_ = true; @@ -302,8 +305,8 @@ SandboxFileSystemBackendDelegate::CreateFileSystemOperationContext( const ChangeObserverList* change_observers = GetChangeObservers(url.type()); DCHECK(update_observers); - std::unique_ptr<FileSystemOperationContext> operation_context( - new FileSystemOperationContext(context)); + auto operation_context = + std::make_unique<FileSystemOperationContext>(context); operation_context->set_update_observers(*update_observers); operation_context->set_change_observers( change_observers ? *change_observers : ChangeObserverList()); @@ -351,8 +354,8 @@ SandboxFileSystemBackendDelegate::DeleteOriginDataOnFileTaskRunner( origin, GetTypeString(type)); if (result && proxy && usage) { proxy->NotifyStorageModified(QuotaClientType::kFileSystem, origin, - FileSystemTypeToQuotaStorageType(type), - -usage); + FileSystemTypeToQuotaStorageType(type), -usage, + base::Time::Now()); } if (result) diff --git a/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate_unittest.cc b/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate_unittest.cc index 48a2098f1e2..b480ff2e4a6 100644 --- a/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate_unittest.cc +++ b/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate_unittest.cc @@ -37,7 +37,7 @@ class SandboxFileSystemBackendDelegateTest : public testing::Test { void SetUp() override { ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); quota_manager_proxy_ = base::MakeRefCounted<MockQuotaManagerProxy>( - nullptr, base::ThreadTaskRunnerHandle::Get().get()); + nullptr, base::ThreadTaskRunnerHandle::Get()); delegate_ = std::make_unique<SandboxFileSystemBackendDelegate>( quota_manager_proxy_.get(), base::ThreadTaskRunnerHandle::Get().get(), data_dir_.GetPath(), /*special_storage_policy=*/nullptr, diff --git a/chromium/storage/browser/file_system/sandbox_file_system_backend_unittest.cc b/chromium/storage/browser/file_system/sandbox_file_system_backend_unittest.cc index 7e029cb8f69..52d6d9f74d2 100644 --- a/chromium/storage/browser/file_system/sandbox_file_system_backend_unittest.cc +++ b/chromium/storage/browser/file_system/sandbox_file_system_backend_unittest.cc @@ -91,16 +91,16 @@ class SandboxFileSystemBackendTest void SetUpNewDelegate(const FileSystemOptions& options) { incognito_env_override_ = leveldb_chrome::NewMemEnv("FileSystem"); - delegate_.reset(new SandboxFileSystemBackendDelegate( + delegate_ = std::make_unique<SandboxFileSystemBackendDelegate>( nullptr /* quota_manager_proxy */, base::ThreadTaskRunnerHandle::Get().get(), data_dir_.GetPath(), nullptr /* special_storage_policy */, options, - options.is_in_memory() ? incognito_env_override_.get() : nullptr)); + options.is_in_memory() ? incognito_env_override_.get() : nullptr); } void SetUpNewBackend(const FileSystemOptions& options) { SetUpNewDelegate(options); - backend_.reset(new SandboxFileSystemBackend(delegate_.get())); + backend_ = std::make_unique<SandboxFileSystemBackend>(delegate_.get()); } SandboxFileSystemBackendDelegate::OriginEnumerator* CreateOriginEnumerator() diff --git a/chromium/storage/browser/file_system/sandbox_origin_database_unittest.cc b/chromium/storage/browser/file_system/sandbox_origin_database_unittest.cc index a52ab31858b..f9b1aa4e493 100644 --- a/chromium/storage/browser/file_system/sandbox_origin_database_unittest.cc +++ b/chromium/storage/browser/file_system/sandbox_origin_database_unittest.cc @@ -208,8 +208,7 @@ TEST(SandboxOriginDatabaseTest, DatabaseRecoveryTest) { "hoge.example.com", "fuga.example.com", }; - std::unique_ptr<SandboxOriginDatabase> database( - new SandboxOriginDatabase(kFSDir, nullptr)); + auto database = std::make_unique<SandboxOriginDatabase>(kFSDir, nullptr); for (size_t i = 0; i < base::size(kOrigins); ++i) { base::FilePath path; EXPECT_FALSE(database->HasOriginPath(kOrigins[i])); @@ -238,7 +237,7 @@ TEST(SandboxOriginDatabaseTest, DatabaseRecoveryTest) { CorruptDatabase(kDBDir, leveldb::kLogFile, -1, 1); base::FilePath path; - database.reset(new SandboxOriginDatabase(kFSDir, nullptr)); + database = std::make_unique<SandboxOriginDatabase>(kFSDir, nullptr); std::vector<SandboxOriginDatabase::OriginRecord> origins_in_db; EXPECT_TRUE(database->ListAllOrigins(&origins_in_db)); @@ -274,8 +273,7 @@ TEST(SandboxOriginDatabaseTest, DatabaseRecoveryForMissingDBFileTest) { const std::string kOrigin = "foo.example.com"; base::FilePath path; - std::unique_ptr<SandboxOriginDatabase> database( - new SandboxOriginDatabase(kFSDir, nullptr)); + auto database = std::make_unique<SandboxOriginDatabase>(kFSDir, nullptr); EXPECT_FALSE(database->HasOriginPath(kOrigin)); EXPECT_TRUE(database->GetPathForOrigin(kOrigin, &path)); EXPECT_FALSE(path.empty()); @@ -285,7 +283,7 @@ TEST(SandboxOriginDatabaseTest, DatabaseRecoveryForMissingDBFileTest) { DeleteDatabaseFile(kDBDir, file_type); - database.reset(new SandboxOriginDatabase(kFSDir, nullptr)); + database = std::make_unique<SandboxOriginDatabase>(kFSDir, nullptr); std::vector<SandboxOriginDatabase::OriginRecord> origins_in_db; EXPECT_TRUE(database->ListAllOrigins(&origins_in_db)); diff --git a/chromium/storage/browser/file_system/sandbox_prioritized_origin_database.cc b/chromium/storage/browser/file_system/sandbox_prioritized_origin_database.cc index 5c46bff3601..d364b549cde 100644 --- a/chromium/storage/browser/file_system/sandbox_prioritized_origin_database.cc +++ b/chromium/storage/browser/file_system/sandbox_prioritized_origin_database.cc @@ -4,6 +4,8 @@ #include "storage/browser/file_system/sandbox_prioritized_origin_database.h" +#include <memory> + #include "base/check.h" #include "base/files/file.h" #include "base/files/file_util.h" @@ -65,8 +67,10 @@ bool SandboxPrioritizedOriginDatabase::InitializePrimaryOrigin( if (!primary_origin_database_ && !is_in_memory) { if (!MaybeLoadPrimaryOrigin() && ResetPrimaryOrigin(origin)) { MaybeMigrateDatabase(origin); - primary_origin_database_.reset(new SandboxIsolatedOriginDatabase( - origin, file_system_directory_, base::FilePath(kPrimaryDirectory))); + primary_origin_database_ = + std::make_unique<SandboxIsolatedOriginDatabase>( + origin, file_system_directory_, + base::FilePath(kPrimaryDirectory)); return true; } } @@ -151,8 +155,8 @@ bool SandboxPrioritizedOriginDatabase::MaybeLoadPrimaryOrigin() { std::string saved_origin; if (!ReadPrimaryOriginFile(primary_origin_file_, &saved_origin)) return false; - primary_origin_database_.reset(new SandboxIsolatedOriginDatabase( - saved_origin, file_system_directory_, base::FilePath(kPrimaryDirectory))); + primary_origin_database_ = std::make_unique<SandboxIsolatedOriginDatabase>( + saved_origin, file_system_directory_, base::FilePath(kPrimaryDirectory)); return true; } @@ -207,8 +211,8 @@ void SandboxPrioritizedOriginDatabase::MaybeInitializeNonPrimaryDatabase( if (origin_database_) return; - origin_database_.reset( - new SandboxOriginDatabase(file_system_directory_, env_override_)); + origin_database_ = std::make_unique<SandboxOriginDatabase>( + file_system_directory_, env_override_); if (!create && !base::DirectoryExists(origin_database_->GetDatabasePath())) { origin_database_.reset(); return; diff --git a/chromium/storage/browser/file_system/sandbox_quota_observer.cc b/chromium/storage/browser/file_system/sandbox_quota_observer.cc index 894f56cbd3a..10b0616eb76 100644 --- a/chromium/storage/browser/file_system/sandbox_quota_observer.cc +++ b/chromium/storage/browser/file_system/sandbox_quota_observer.cc @@ -42,7 +42,7 @@ void SandboxQuotaObserver::OnUpdate(const FileSystemURL& url, int64_t delta) { if (quota_manager_proxy_.get()) { quota_manager_proxy_->NotifyStorageModified( QuotaClientType::kFileSystem, url.origin(), - FileSystemTypeToQuotaStorageType(url.type()), delta); + FileSystemTypeToQuotaStorageType(url.type()), delta, base::Time::Now()); } base::FilePath usage_file_path = GetUsageCachePath(url); @@ -78,7 +78,8 @@ void SandboxQuotaObserver::OnEndUpdate(const FileSystemURL& url) { void SandboxQuotaObserver::OnAccess(const FileSystemURL& url) { if (quota_manager_proxy_.get()) { quota_manager_proxy_->NotifyStorageAccessed( - url.origin(), FileSystemTypeToQuotaStorageType(url.type())); + url.origin(), FileSystemTypeToQuotaStorageType(url.type()), + base::Time::Now()); } } diff --git a/chromium/storage/browser/quota/README.md b/chromium/storage/browser/quota/README.md new file mode 100644 index 00000000000..b1f532f9ec6 --- /dev/null +++ b/chromium/storage/browser/quota/README.md @@ -0,0 +1,78 @@ +# Overview + +The quota system's primary role is to set and enforce limits on disk usage at +both the browser level, and at the origin level (see ./quota_settings.cc for +these limit values). The quota system manages disk usage only for certain web +platform storage APIs. + +In order for a storage backend to integrate with the quota system, it must +implement the QuotaClient interface. + +Most work on the quota system is currently done on the browser process' IO +thread. There are plans for quota to be moved to [the Storage +Service](https://docs.google.com/document/d/1v83XKVxnasgf2uNfb_Uc-rfhDa3-ynNP23yU2DWqshI/), +which will run on its own process on desktop platforms. + +# Key Components +## Interface +The quota system's interface is comprised of the following classes: + +### QuotaManagerImpl +The "heart" of the quota system. This class lives on the browser +process' IO thread, but is primarily accessed through QuotaManagerProxy, which +handles thread hops. In the future, QuotaManagerProxy will turn into +mojom::QuotaManager, and the quota system will be accessed exclusively via mojo. + +### QuotaClient +This interface must be implemented by any storage backend that wants to +integrate with the quota system. This is probably the most used interface from +outside of the quota system. + +### PaddingKey +Helpers for computing quota usage for opaque resources. Features that store +opaque resources (AppCache, Cache Storage) should use these helpers to avoid +leaking cross-origin information via the quota usage they report. + +### SpecialStoragePolicy +Hook that allows browser features (currently Extensions and Chrome Apps) to +change an origin's quota. + +## Implementation +The quota system's implementation is made up of the following components: + +### UsageTracker, ClientUsageTracker +QuotaManagerImpl helpers that distribute tasks (e.g. measure an origin's quota +usage) across QuotaClient instances, and cache results as needed. + +### QuotaDatabase +Stores persistent information in a per-profile SQLite database. Currently stores +a few bits of implementation details, and will likely be expanded to cover +Storage Buckets. The currently stored information is a usage count, +last-modified-time, and last-accessed-time for each origin (used to implement +LRU eviction on storage pressure, and Clear Site Data with a time filter), and +quota granted via the deprecated API +webkitStorageInfo.requestQuota(PERSISTENT,...). + +### QuotaTemporaryStorageEvictor +Handles eviction and records stats about eviction rounds. + +### QuotaTask +Implementation detail of QuotaManagerImpl. + +# Glossary + +## Storage Pressure +A device is said to be under storage pressure when it is close to capacity. +Storage pressure is used to signal a couple of behaviors in the quota system: + - Eviction + - The QuotaChange event + - Triggering storage pressure UI (implementation specific) + +## Eviction +This is the process by which the quota system cleans up app's data as disk usage +gets close to the disk's capacity. + +# Resources + - [Chrome Web Storage and Quota Concepts](https://docs.google.com/document/d/19QemRTdIxYaJ4gkHYf2WWBNPbpuZQDNMpUVf8dQxj4U/) + - In-depth description of the quota system that also explains related + concepts and legacy APIs that left a mark on quota. diff --git a/chromium/storage/browser/quota/client_usage_tracker.cc b/chromium/storage/browser/quota/client_usage_tracker.cc index f06c6a342ba..84b1cbc885c 100644 --- a/chromium/storage/browser/quota/client_usage_tracker.cc +++ b/chromium/storage/browser/quota/client_usage_tracker.cc @@ -8,8 +8,8 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/containers/contains.h" #include "base/metrics/histogram_macros.h" -#include "base/stl_util.h" namespace storage { diff --git a/chromium/storage/browser/quota/mojo_quota_client_wrapper.cc b/chromium/storage/browser/quota/mojo_quota_client_wrapper.cc new file mode 100644 index 00000000000..59ea75264d7 --- /dev/null +++ b/chromium/storage/browser/quota/mojo_quota_client_wrapper.cc @@ -0,0 +1,71 @@ +// Copyright 2021 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 "storage/browser/quota/mojo_quota_client_wrapper.h" + +#include <utility> + +#include "base/check.h" +#include "base/sequence_checker.h" +#include "components/services/storage/public/mojom/quota_client.mojom.h" +#include "url/origin.h" + +namespace storage { + +MojoQuotaClientWrapper::MojoQuotaClientWrapper( + mojom::QuotaClient* wrapped_client) + : wrapped_client_(wrapped_client) { + DCHECK(wrapped_client); + DETACH_FROM_SEQUENCE(sequence_checker_); +} + +MojoQuotaClientWrapper::~MojoQuotaClientWrapper() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +void MojoQuotaClientWrapper::GetOriginUsage(const url::Origin& origin, + blink::mojom::StorageType type, + GetOriginUsageCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + wrapped_client_->GetOriginUsage(origin, type, std::move(callback)); +} + +void MojoQuotaClientWrapper::GetOriginsForType( + blink::mojom::StorageType type, + GetOriginsForTypeCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + wrapped_client_->GetOriginsForType(type, std::move(callback)); +} + +void MojoQuotaClientWrapper::GetOriginsForHost( + blink::mojom::StorageType type, + const std::string& host, + GetOriginsForHostCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + wrapped_client_->GetOriginsForHost(type, host, std::move(callback)); +} + +void MojoQuotaClientWrapper::DeleteOriginData( + const url::Origin& origin, + blink::mojom::StorageType type, + DeleteOriginDataCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + wrapped_client_->DeleteOriginData(origin, type, std::move(callback)); +} + +void MojoQuotaClientWrapper::PerformStorageCleanup( + blink::mojom::StorageType type, + PerformStorageCleanupCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + wrapped_client_->PerformStorageCleanup(type, std::move(callback)); +} + +void MojoQuotaClientWrapper::OnQuotaManagerDestroyed() {} + +} // namespace storage diff --git a/chromium/storage/browser/quota/mojo_quota_client_wrapper.h b/chromium/storage/browser/quota/mojo_quota_client_wrapper.h new file mode 100644 index 00000000000..9b598ed47c0 --- /dev/null +++ b/chromium/storage/browser/quota/mojo_quota_client_wrapper.h @@ -0,0 +1,61 @@ +// Copyright 2021 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 STORAGE_BROWSER_QUOTA_MOJO_QUOTA_CLIENT_WRAPPER_H_ +#define STORAGE_BROWSER_QUOTA_MOJO_QUOTA_CLIENT_WRAPPER_H_ + +#include <string> + +#include "base/component_export.h" +#include "base/sequence_checker.h" +#include "base/thread_annotations.h" +#include "components/services/storage/public/mojom/quota_client.mojom.h" +#include "storage/browser/quota/quota_client.h" +#include "third_party/blink/public/mojom/quota/quota_types.mojom.h" + +namespace url { +class Origin; +} // namespace url + +namespace storage { + +// TODO(crbug.com/1163009): Remove this class after all QuotaClients have been +// mojofied. +class COMPONENT_EXPORT(STORAGE_BROWSER) MojoQuotaClientWrapper + : public storage::QuotaClient { + public: + // `wrapped_client` must outlive this instance. + explicit MojoQuotaClientWrapper(storage::mojom::QuotaClient* wrapped_client); + + MojoQuotaClientWrapper(const MojoQuotaClientWrapper&) = delete; + MojoQuotaClientWrapper& operator=(const MojoQuotaClientWrapper&) = delete; + + // QuotaClient. + void GetOriginUsage(const url::Origin& origin, + blink::mojom::StorageType type, + GetOriginUsageCallback callback) override; + void GetOriginsForType(blink::mojom::StorageType type, + GetOriginsForTypeCallback callback) override; + void GetOriginsForHost(blink::mojom::StorageType type, + const std::string& host, + GetOriginsForHostCallback callback) override; + void DeleteOriginData(const url::Origin& origin, + blink::mojom::StorageType type, + DeleteOriginDataCallback callback) override; + void PerformStorageCleanup(blink::mojom::StorageType type, + PerformStorageCleanupCallback callback) override; + void OnQuotaManagerDestroyed() override; + + private: + ~MojoQuotaClientWrapper() override; + + SEQUENCE_CHECKER(sequence_checker_); + + storage::mojom::QuotaClient* const wrapped_client_ + GUARDED_BY_CONTEXT(sequence_checker_); +}; + +} // namespace storage + +#endif // STORAGE_BROWSER_QUOTA_MOJO_QUOTA_CLIENT_WRAPPER_H_ diff --git a/chromium/storage/browser/quota/padding_key.h b/chromium/storage/browser/quota/padding_key.h deleted file mode 100644 index 4b5978a82e7..00000000000 --- a/chromium/storage/browser/quota/padding_key.h +++ /dev/null @@ -1,70 +0,0 @@ -// 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 STORAGE_BROWSER_QUOTA_PADDING_KEY_H_ -#define STORAGE_BROWSER_QUOTA_PADDING_KEY_H_ - -#include <memory> -#include <string> - -#include "base/component_export.h" -#include "crypto/symmetric_key.h" -#include "url/gurl.h" - -namespace storage { - -COMPONENT_EXPORT(STORAGE_BROWSER) -const crypto::SymmetricKey* GetDefaultPaddingKey(); - -// Returns a copy of the default key used to calculate padding sizes. -// -// The default padding key is a singleton object whose value is randomly -// generated the first time it is requested on every browser startup. In -// CacheStorage, when a cache does not have a padding key, it is assigned the -// current default key. -COMPONENT_EXPORT(STORAGE_BROWSER) -std::unique_ptr<crypto::SymmetricKey> CopyDefaultPaddingKey(); - -// Builds a key whose value is the given string. -// -// May return null if deserializing fails (e.g. if the raw key is the wrong -// size). -COMPONENT_EXPORT(STORAGE_BROWSER) -std::unique_ptr<crypto::SymmetricKey> DeserializePaddingKey( - const std::string& raw_key); - -// Gets the raw value of the default padding key. -// -// Each cache stores the raw value of the key that should be used when -// calculating its padding size. -COMPONENT_EXPORT(STORAGE_BROWSER) -std::string SerializeDefaultPaddingKey(); - -// Resets the default key to a random value. -// -// Simulating a key change across a browser restart lets us test that padding -// calculations are using the appropriate key. -COMPONENT_EXPORT(STORAGE_BROWSER) -void ResetPaddingKeyForTesting(); - -// Computes the padding size for a resource. -// -// For AppCache, which does not support storing metadata for a resource, -// |has_metadata| will always be false. -// -// For CacheStorage, the padding size of an entry depends on whether it contains -// metadata (a.k.a. "side data") and if the response was loaded with -// credentials. If metadata is added to the entry, the entry must be assigned a -// new padding size. Otherwise, the growth in the entry's size would leak the -// exact size of the added metadata. -COMPONENT_EXPORT(STORAGE_BROWSER) -int64_t ComputeResponsePadding(const std::string& response_url, - const crypto::SymmetricKey* padding_key, - bool has_metadata, - bool loaded_with_credentials, - const std::string& request_method); - -} // namespace storage - -#endif // STORAGE_BROWSER_QUOTA_PADDING_KEY_H_ diff --git a/chromium/storage/browser/quota/quota_callbacks.h b/chromium/storage/browser/quota/quota_callbacks.h index 4716375a327..f530e4628cb 100644 --- a/chromium/storage/browser/quota/quota_callbacks.h +++ b/chromium/storage/browser/quota/quota_callbacks.h @@ -14,9 +14,8 @@ #include <vector> #include "base/callback.h" +#include "base/containers/contains.h" #include "base/optional.h" -#include "base/stl_util.h" -#include "storage/browser/quota/quota_client.h" #include "third_party/blink/public/mojom/quota/quota_types.mojom-forward.h" namespace url { diff --git a/chromium/storage/browser/quota/quota_client.h b/chromium/storage/browser/quota/quota_client.h index bd4de1d8e59..9d86a42d497 100644 --- a/chromium/storage/browser/quota/quota_client.h +++ b/chromium/storage/browser/quota/quota_client.h @@ -12,76 +12,35 @@ #include "base/callback.h" #include "base/component_export.h" +#include "components/services/storage/public/mojom/quota_client.mojom.h" #include "storage/browser/quota/quota_client_type.h" -#include "third_party/blink/public/mojom/quota/quota_types.mojom-forward.h" +#include "third_party/blink/public/mojom/quota/quota_types.mojom.h" #include "url/origin.h" namespace storage { -// Interface between each storage API and the quota manager. +// Interface between the legacy quota clients and the QuotaManager. // -// Each storage API must register an implementation of this interface with -// the quota manager, by calling QuotaManager::RegisterClient(). +// Implementations of this class will be transitioned to inherit from +// storage::mojom::QuotaClient and talk to the QuotaManager via mojo. // -// All the methods will be called on the IO thread in the browser. +// This inherits from storage::mojom::QuotaClient so that MockQuotaClient +// instances can be passed to QuotaManger::RegisterLegacyClient(), +// as well as used via mojo with QuotaManager::RegisterClient(). // -// When AppCache is deleted, this can become a std::unique_ptr instead -// of refcounted, and owned by the QuotaManager. +// TODO(crbug.com/1163009): Remove this class after all QuotaClients have +// been mojofied. class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaClient - : public base::RefCountedThreadSafe<QuotaClient> { + : public base::RefCountedThreadSafe<QuotaClient>, + public storage::mojom::QuotaClient { public: - // The callback aliases precisely follow mojo conventions, because this - // abstract class will become a mojo interface soon. See crbug.com/1016065. - using GetOriginUsageCallback = base::OnceCallback<void(int64_t usage)>; - using GetOriginsForTypeCallback = - base::OnceCallback<void(const std::vector<url::Origin>& origins)>; - using GetOriginsForHostCallback = - base::OnceCallback<void(const std::vector<url::Origin>& origins)>; - using DeleteOriginDataCallback = - base::OnceCallback<void(blink::mojom::QuotaStatusCode status)>; - using PerformStorageCleanupCallback = base::OnceClosure; - // Called when the QuotaManager is destroyed. virtual void OnQuotaManagerDestroyed() = 0; - // Called by the QuotaManager. - // Gets the amount of data stored in the storage specified by - // |origin| and |type|. - // Note it is safe to fire the callback after the QuotaClient is destructed. - virtual void GetOriginUsage(const url::Origin& origin, - blink::mojom::StorageType type, - GetOriginUsageCallback callback) = 0; - - // Called by the QuotaManager. - // Returns a list of origins that has data in the |type| storage. - // Note it is safe to fire the callback after the QuotaClient is destructed. - virtual void GetOriginsForType(blink::mojom::StorageType type, - GetOriginsForTypeCallback callback) = 0; - - // Called by the QuotaManager. - // Returns a list of origins that match the |host|. - // Note it is safe to fire the callback after the QuotaClient is destructed. - virtual void GetOriginsForHost(blink::mojom::StorageType type, - const std::string& host, - GetOriginsForHostCallback callback) = 0; - - // Called by the QuotaManager. - // Note it is safe to fire the callback after the QuotaClient is destructed. - virtual void DeleteOriginData(const url::Origin& origin, - blink::mojom::StorageType type, - DeleteOriginDataCallback callback) = 0; - - // Called by the QuotaManager. - // Gives the QuotaClient an opportunity to perform a cleanup step after major - // deletions. - virtual void PerformStorageCleanup( - blink::mojom::StorageType type, - PerformStorageCleanupCallback callback) = 0; - protected: friend class RefCountedThreadSafe<QuotaClient>; - virtual ~QuotaClient() = default; + ~QuotaClient() override = default; }; } // namespace storage diff --git a/chromium/storage/browser/quota/quota_client_type.cc b/chromium/storage/browser/quota/quota_client_type.cc index a2daaa23b5e..f74f7236f29 100644 --- a/chromium/storage/browser/quota/quota_client_type.cc +++ b/chromium/storage/browser/quota/quota_client_type.cc @@ -17,6 +17,7 @@ const QuotaClientTypes& AllQuotaClientTypes() { QuotaClientType::kServiceWorkerCache, QuotaClientType::kServiceWorker, QuotaClientType::kBackgroundFetch, + QuotaClientType::kNativeIO, }}; return *all; } diff --git a/chromium/storage/browser/quota/quota_client_type.h b/chromium/storage/browser/quota/quota_client_type.h index f3c62f8eb5d..c69f2b36df0 100644 --- a/chromium/storage/browser/quota/quota_client_type.h +++ b/chromium/storage/browser/quota/quota_client_type.h @@ -22,6 +22,7 @@ enum class QuotaClientType { kServiceWorker = 5, kBackgroundFetch = 6, kAppcache = 7, + kNativeIO = 8, }; // Set of QuotaClientType values. diff --git a/chromium/storage/browser/quota/quota_database.cc b/chromium/storage/browser/quota/quota_database.cc index 7322359b68c..6033663757b 100644 --- a/chromium/storage/browser/quota/quota_database.cc +++ b/chromium/storage/browser/quota/quota_database.cc @@ -12,9 +12,9 @@ #include "base/auto_reset.h" #include "base/bind.h" +#include "base/containers/contains.h" #include "base/files/file_util.h" #include "base/metrics/histogram_macros.h" -#include "base/stl_util.h" #include "sql/database.h" #include "sql/meta_table.h" #include "sql/statement.h" @@ -38,21 +38,10 @@ const char kOriginInfoTable[] = "OriginInfoTable"; const char kEvictionInfoTable[] = "EvictionInfoTable"; const char kIsOriginTableBootstrapped[] = "IsOriginTableBootstrapped"; -bool VerifyValidQuotaConfig(const char* key) { - return (key != nullptr && - (!strcmp(key, QuotaDatabase::kDesiredAvailableSpaceKey) || - !strcmp(key, QuotaDatabase::kTemporaryQuotaOverrideKey))); -} - const int kCommitIntervalMs = 30000; } // anonymous namespace -// static -const char QuotaDatabase::kDesiredAvailableSpaceKey[] = "DesiredAvailableSpace"; -const char QuotaDatabase::kTemporaryQuotaOverrideKey[] = - "TemporaryQuotaOverride"; - const QuotaDatabase::TableSchema QuotaDatabase::kTables[] = { {kHostQuotaTable, "(host TEXT NOT NULL," @@ -139,12 +128,6 @@ QuotaDatabase::~QuotaDatabase() { } } -void QuotaDatabase::CloseDatabase() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - meta_table_.reset(); - db_.reset(); -} - bool QuotaDatabase::GetHostQuota(const std::string& host, StorageType type, int64_t* quota) { @@ -417,22 +400,6 @@ bool QuotaDatabase::DeleteOriginInfo(const url::Origin& origin, return true; } -bool QuotaDatabase::GetQuotaConfigValue(const char* key, int64_t* value) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!LazyOpen(false)) - return false; - DCHECK(VerifyValidQuotaConfig(key)); - return meta_table_->GetValue(key, value); -} - -bool QuotaDatabase::SetQuotaConfigValue(const char* key, int64_t value) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!LazyOpen(true)) - return false; - DCHECK(VerifyValidQuotaConfig(key)); - return meta_table_->SetValue(key, value); -} - bool QuotaDatabase::GetLRUOrigin(StorageType type, const std::set<url::Origin>& exceptions, SpecialStoragePolicy* special_storage_policy, diff --git a/chromium/storage/browser/quota/quota_database.h b/chromium/storage/browser/quota/quota_database.h index 64cbe16ec6e..816b4002417 100644 --- a/chromium/storage/browser/quota/quota_database.h +++ b/chromium/storage/browser/quota/quota_database.h @@ -33,10 +33,9 @@ class SpecialStoragePolicy; // Stores all origin scoped quota managed data and metadata. // -// Instances are owned by QuotaManager. There is one instance per QuotaManager -// instance. -// All the methods of this class, except the constructor, must called on the DB -// thread. +// Instances are owned by QuotaManagerImpl. There is one instance per +// QuotaManagerImpl instance. All the methods of this class, except the +// constructor, must called on the DB thread. class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase { public: struct COMPONENT_EXPORT(STORAGE_BROWSER) OriginInfoTableEntry { @@ -53,16 +52,10 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase { base::Time last_modified_time; }; - // Constants for {Get,Set}QuotaConfigValue keys. - static const char kDesiredAvailableSpaceKey[]; - static const char kTemporaryQuotaOverrideKey[]; - // If 'path' is empty, an in memory database will be used. explicit QuotaDatabase(const base::FilePath& path); ~QuotaDatabase(); - void CloseDatabase(); - // Returns whether the record could be found. bool GetHostQuota(const std::string& host, blink::mojom::StorageType type, @@ -111,9 +104,6 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase { bool DeleteOriginInfo(const url::Origin& origin, blink::mojom::StorageType type); - bool GetQuotaConfigValue(const char* key, int64_t* value); - bool SetQuotaConfigValue(const char* key, int64_t value); - // Sets |origin| to the least recently used origin of origins not included // in |exceptions| and not granted the special unlimited storage right. // It returns false when it failed in accessing the database. @@ -207,7 +197,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase { static base::Time TimeFromSqlValue(int64_t time); static int64_t TimeToSqlValue(const base::Time& time); - base::FilePath db_file_path_; + const base::FilePath db_file_path_; std::unique_ptr<sql::Database> db_; std::unique_ptr<sql::MetaTable> meta_table_; @@ -217,7 +207,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase { base::OneShotTimer timer_; friend class QuotaDatabaseTest; - friend class QuotaManager; + friend class QuotaManagerImpl; static const TableSchema kTables[]; static const IndexSchema kIndexes[]; diff --git a/chromium/storage/browser/quota/quota_database_unittest.cc b/chromium/storage/browser/quota/quota_database_unittest.cc index 0310fda6708..f4c8bd611bc 100644 --- a/chromium/storage/browser/quota/quota_database_unittest.cc +++ b/chromium/storage/browser/quota/quota_database_unittest.cc @@ -116,36 +116,6 @@ class QuotaDatabaseTest : public testing::Test { EXPECT_FALSE(db.GetHostQuota(kHost, kPersistent, "a)); } - void GlobalQuota(const base::FilePath& kDbFile) { - QuotaDatabase db(kDbFile); - ASSERT_TRUE(db.LazyOpen(true)); - - const char* kTempQuotaKey = QuotaDatabase::kTemporaryQuotaOverrideKey; - const char* kAvailSpaceKey = QuotaDatabase::kDesiredAvailableSpaceKey; - - int64_t value = 0; - const int64_t kValue1 = 456; - const int64_t kValue2 = 123000; - EXPECT_FALSE(db.GetQuotaConfigValue(kTempQuotaKey, &value)); - EXPECT_FALSE(db.GetQuotaConfigValue(kAvailSpaceKey, &value)); - - EXPECT_TRUE(db.SetQuotaConfigValue(kTempQuotaKey, kValue1)); - EXPECT_TRUE(db.GetQuotaConfigValue(kTempQuotaKey, &value)); - EXPECT_EQ(kValue1, value); - - EXPECT_TRUE(db.SetQuotaConfigValue(kTempQuotaKey, kValue2)); - EXPECT_TRUE(db.GetQuotaConfigValue(kTempQuotaKey, &value)); - EXPECT_EQ(kValue2, value); - - EXPECT_TRUE(db.SetQuotaConfigValue(kAvailSpaceKey, kValue1)); - EXPECT_TRUE(db.GetQuotaConfigValue(kAvailSpaceKey, &value)); - EXPECT_EQ(kValue1, value); - - EXPECT_TRUE(db.SetQuotaConfigValue(kAvailSpaceKey, kValue2)); - EXPECT_TRUE(db.GetQuotaConfigValue(kAvailSpaceKey, &value)); - EXPECT_EQ(kValue2, value); - } - void OriginLastAccessTimeLRU(const base::FilePath& kDbFile) { QuotaDatabase db(kDbFile); ASSERT_TRUE(db.LazyOpen(true)); @@ -622,14 +592,6 @@ TEST_F(QuotaDatabaseTest, HostQuota) { HostQuota(base::FilePath()); } -TEST_F(QuotaDatabaseTest, GlobalQuota) { - base::ScopedTempDir data_dir; - ASSERT_TRUE(data_dir.CreateUniqueTempDir()); - const base::FilePath kDbFile = data_dir.GetPath().AppendASCII(kDBFileName); - GlobalQuota(kDbFile); - GlobalQuota(base::FilePath()); -} - TEST_F(QuotaDatabaseTest, OriginLastAccessTimeLRU) { base::ScopedTempDir data_dir; ASSERT_TRUE(data_dir.CreateUniqueTempDir()); diff --git a/chromium/storage/browser/quota/quota_features.cc b/chromium/storage/browser/quota/quota_features.cc index b638a01437b..cfb874fbd0f 100644 --- a/chromium/storage/browser/quota/quota_features.cc +++ b/chromium/storage/browser/quota/quota_features.cc @@ -8,9 +8,35 @@ namespace storage { namespace features { +namespace { +constexpr int64_t kMBytes = 1024 * 1024; +} // namespace + // Enables Storage Pressure Event. const base::Feature kStoragePressureEvent{"StoragePressureEvent", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables customized storage quota settings for embedders. +const base::Feature kStorageQuotaSettings{"StorageQuotaSettings", + base::FEATURE_DISABLED_BY_DEFAULT}; +constexpr base::FeatureParam<double> kMustRemainAvailableBytes{ + &kStorageQuotaSettings, "MustRemainAvailableBytes", 1024 * kMBytes /* 1GB */ +}; +constexpr base::FeatureParam<double> kMustRemainAvailableRatio{ + &kStorageQuotaSettings, "MustRemainAvailableRatio", 0.01 /* 1% */ +}; +constexpr base::FeatureParam<double> kPoolSizeBytes{&kStorageQuotaSettings, + "PoolSizeBytes", 0}; +constexpr base::FeatureParam<double> kPoolSizeRatio{ + &kStorageQuotaSettings, "PoolSizeRatio", 0.8 /* 80% */ +}; +constexpr base::FeatureParam<double> kShouldRemainAvailableBytes{ + &kStorageQuotaSettings, "ShouldRemainAvailableBytes", + 2048 * kMBytes /* 2GB */ +}; +constexpr base::FeatureParam<double> kShouldRemainAvailableRatio{ + &kStorageQuotaSettings, "ShouldRemainAvailableRatio", 0.1 /* 10% */ +}; + } // namespace features } // namespace storage diff --git a/chromium/storage/browser/quota/quota_features.h b/chromium/storage/browser/quota/quota_features.h index 2203dd63248..6bf775fdfdb 100644 --- a/chromium/storage/browser/quota/quota_features.h +++ b/chromium/storage/browser/quota/quota_features.h @@ -16,6 +16,15 @@ namespace features { COMPONENT_EXPORT(STORAGE_BROWSER) extern const base::Feature kStoragePressureEvent; +COMPONENT_EXPORT(STORAGE_BROWSER) +extern const base::Feature kStorageQuotaSettings; +extern const base::FeatureParam<double> kMustRemainAvailableBytes; +extern const base::FeatureParam<double> kMustRemainAvailableRatio; +extern const base::FeatureParam<double> kPoolSizeBytes; +extern const base::FeatureParam<double> kPoolSizeRatio; +extern const base::FeatureParam<double> kShouldRemainAvailableBytes; +extern const base::FeatureParam<double> kShouldRemainAvailableRatio; + } // namespace features } // namespace storage diff --git a/chromium/storage/browser/quota/quota_manager.cc b/chromium/storage/browser/quota/quota_manager.cc index 4b1d43e3ff6..739f981d98e 100644 --- a/chromium/storage/browser/quota/quota_manager.cc +++ b/chromium/storage/browser/quota/quota_manager.cc @@ -1,1968 +1,27 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2021 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 "storage/browser/quota/quota_manager.h" -#include <stddef.h> -#include <stdint.h> - -#include <algorithm> -#include <functional> -#include <limits> -#include <memory> #include <utility> -#include "base/barrier_closure.h" -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/macros.h" -#include "base/metrics/histogram_macros.h" -#include "base/numerics/safe_conversions.h" -#include "base/rand_util.h" -#include "base/sequence_checker.h" -#include "base/sequenced_task_runner.h" -#include "base/single_thread_task_runner.h" -#include "base/strings/string_number_conversions.h" -#include "base/system/sys_info.h" -#include "base/task/post_task.h" -#include "base/task/thread_pool.h" -#include "base/task_runner_util.h" -#include "base/threading/thread_restrictions.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/time/time.h" -#include "base/trace_event/trace_event.h" -#include "storage/browser/quota/client_usage_tracker.h" -#include "storage/browser/quota/quota_client_type.h" -#include "storage/browser/quota/quota_features.h" -#include "storage/browser/quota/quota_macros.h" -#include "storage/browser/quota/quota_manager_proxy.h" -#include "storage/browser/quota/quota_override_handle.h" -#include "storage/browser/quota/quota_temporary_storage_evictor.h" -#include "storage/browser/quota/usage_tracker.h" -#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h" - -using blink::mojom::StorageType; - namespace storage { -namespace { - -constexpr int64_t kReportHistogramInterval = 60 * 60 * 1000; // 1 hour - -// Take action on write errors if there is <= 2% disk space -// available. -constexpr double kStoragePressureThresholdRatio = 0.02; - -// Limit how frequently QuotaManager polls for free disk space when -// only using that information to identify storage pressure. -constexpr base::TimeDelta kStoragePressureCheckDiskStatsInterval = - base::TimeDelta::FromMinutes(5); - -// Modifies a given value by a uniformly random amount from -// -percent to +percent. -int64_t RandomizeByPercent(int64_t value, int percent) { - double random_percent = (base::RandDouble() - 0.5) * percent * 2; - return value * (1 + (random_percent / 100.0)); -} -} // namespace - -// Heuristics: assuming average cloud server allows a few Gigs storage -// on the server side and the storage needs to be shared for user data -// and by multiple apps. -int64_t QuotaManager::kSyncableStorageDefaultHostQuota = 500 * kMBytes; - -namespace { - -bool IsSupportedType(StorageType type) { - return type == StorageType::kTemporary || type == StorageType::kPersistent || - type == StorageType::kSyncable; -} - -bool IsSupportedIncognitoType(StorageType type) { - return type == StorageType::kTemporary || type == StorageType::kPersistent; -} - -bool GetPersistentHostQuotaOnDBThread(const std::string& host, - int64_t* quota, - QuotaDatabase* database) { - DCHECK(database); - database->GetHostQuota(host, StorageType::kPersistent, quota); - return true; -} - -bool SetPersistentHostQuotaOnDBThread(const std::string& host, - int64_t* new_quota, - QuotaDatabase* database) { - DCHECK(database); - if (database->SetHostQuota(host, StorageType::kPersistent, *new_quota)) - return true; - *new_quota = 0; - return false; -} - -bool GetLRUOriginOnDBThread(StorageType type, - const std::set<url::Origin>& exceptions, - SpecialStoragePolicy* policy, - base::Optional<url::Origin>* origin, - QuotaDatabase* database) { - DCHECK(database); - database->GetLRUOrigin(type, exceptions, policy, origin); - return true; -} - -bool DeleteOriginInfoOnDBThread(const url::Origin& origin, - StorageType type, - bool is_eviction, - QuotaDatabase* database) { - DCHECK(database); - - base::Time now = base::Time::Now(); - - if (is_eviction) { - QuotaDatabase::OriginInfoTableEntry entry; - database->GetOriginInfo(origin, type, &entry); - UMA_HISTOGRAM_COUNTS_1M(QuotaManager::kEvictedOriginAccessedCountHistogram, - entry.used_count); - UMA_HISTOGRAM_COUNTS_1000( - QuotaManager::kEvictedOriginDaysSinceAccessHistogram, - (now - entry.last_access_time).InDays()); - } - - if (!database->DeleteOriginInfo(origin, type)) - return false; - - // If the deletion is not due to an eviction, delete the entry in the eviction - // table as well due to privacy concerns. - if (!is_eviction) - return database->DeleteOriginLastEvictionTime(origin, type); - - base::Time last_eviction_time; - database->GetOriginLastEvictionTime(origin, type, &last_eviction_time); - - if (last_eviction_time != base::Time()) { - UMA_HISTOGRAM_COUNTS_1000( - QuotaManager::kDaysBetweenRepeatedOriginEvictionsHistogram, - (now - last_eviction_time).InDays()); - } - - return database->SetOriginLastEvictionTime(origin, type, now); -} - -bool BootstrapDatabaseOnDBThread(std::set<url::Origin> origins, - QuotaDatabase* database) { - DCHECK(database); - if (database->IsOriginDatabaseBootstrapped()) - return true; - - // Register existing origins with 0 last time access. - if (database->RegisterInitialOriginInfo(origins, StorageType::kTemporary)) { - database->SetOriginDatabaseBootstrapped(true); - return true; - } - return false; -} - -bool UpdateAccessTimeOnDBThread(const url::Origin& origin, - StorageType type, - base::Time accessed_time, - QuotaDatabase* database) { - DCHECK(database); - return database->SetOriginLastAccessTime(origin, type, accessed_time); -} - -bool UpdateModifiedTimeOnDBThread(const url::Origin& origin, - StorageType type, - base::Time modified_time, - QuotaDatabase* database) { - DCHECK(database); - return database->SetOriginLastModifiedTime(origin, type, modified_time); -} - -void DidGetUsageAndQuotaStripBreakdown( - QuotaManager::UsageAndQuotaCallback callback, - blink::mojom::QuotaStatusCode status, - int64_t usage, - int64_t quota, - blink::mojom::UsageBreakdownPtr usage_breakdown) { - std::move(callback).Run(status, usage, quota); -} - -void DidGetUsageAndQuotaStripOverride( - QuotaManager::UsageAndQuotaWithBreakdownCallback callback, - blink::mojom::QuotaStatusCode status, - int64_t usage, - int64_t quota, - bool is_override_enabled, - blink::mojom::UsageBreakdownPtr usage_breakdown) { - std::move(callback).Run(status, usage, quota, std::move(usage_breakdown)); -} - -} // namespace - -constexpr int64_t QuotaManager::kGBytes; -constexpr int64_t QuotaManager::kNoLimit; -constexpr int64_t QuotaManager::kPerHostPersistentQuotaLimit; -constexpr int QuotaManager::kEvictionIntervalInMilliSeconds; -constexpr int QuotaManager::kThresholdOfErrorsToBeDenylisted; -constexpr int QuotaManager::kThresholdRandomizationPercent; -constexpr char QuotaManager::kDatabaseName[]; -constexpr char QuotaManager::kDaysBetweenRepeatedOriginEvictionsHistogram[]; -constexpr char QuotaManager::kEvictedOriginAccessedCountHistogram[]; -constexpr char QuotaManager::kEvictedOriginDaysSinceAccessHistogram[]; - -QuotaManager::QuotaOverride::QuotaOverride() = default; -QuotaManager::QuotaOverride::~QuotaOverride() = default; - -class QuotaManager::UsageAndQuotaInfoGatherer : public QuotaTask { - public: - UsageAndQuotaInfoGatherer(QuotaManager* manager, - const url::Origin& origin, - StorageType type, - bool is_unlimited, - bool is_session_only, - bool is_incognito, - base::Optional<int64_t> quota_override_size, - UsageAndQuotaForDevtoolsCallback callback) - : QuotaTask(manager), - origin_(origin), - callback_(std::move(callback)), - type_(type), - is_unlimited_(is_unlimited), - is_session_only_(is_session_only), - is_incognito_(is_incognito), - is_override_enabled_(quota_override_size.has_value()), - quota_override_size_(quota_override_size) {} - - protected: - void Run() override { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // Start the async process of gathering the info we need. - // Gather 4 pieces of info before computing an answer: - // settings, device_storage_capacity, host_usage, and host_quota. - base::RepeatingClosure barrier = base::BarrierClosure( - 4, base::BindOnce(&UsageAndQuotaInfoGatherer::OnBarrierComplete, - weak_factory_.GetWeakPtr())); - - const std::string& host = origin_.host(); - - manager()->GetQuotaSettings( - base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotSettings, - weak_factory_.GetWeakPtr(), barrier)); - manager()->GetStorageCapacity( - base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotCapacity, - weak_factory_.GetWeakPtr(), barrier)); - manager()->GetHostUsageWithBreakdown( - host, type_, - base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotHostUsage, - weak_factory_.GetWeakPtr(), barrier)); - - // Determine host_quota differently depending on type. - if (is_unlimited_) { - SetDesiredHostQuota(barrier, blink::mojom::QuotaStatusCode::kOk, - kNoLimit); - } else if (type_ == StorageType::kSyncable) { - SetDesiredHostQuota(barrier, blink::mojom::QuotaStatusCode::kOk, - kSyncableStorageDefaultHostQuota); - } else if (type_ == StorageType::kPersistent) { - manager()->GetPersistentHostQuota( - host, base::BindOnce(&UsageAndQuotaInfoGatherer::SetDesiredHostQuota, - weak_factory_.GetWeakPtr(), barrier)); - } else { - DCHECK_EQ(StorageType::kTemporary, type_); - // For temporary storage, OnGotSettings will set the host quota. - } - } - - void Aborted() override { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - weak_factory_.InvalidateWeakPtrs(); - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort, - /*usage=*/0, - /*quota=*/0, - /*is_override_enabled=*/false, - /*usage_breakdown=*/nullptr); - DeleteSoon(); - } - - void Completed() override { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - weak_factory_.InvalidateWeakPtrs(); - - int64_t host_quota = quota_override_size_.has_value() - ? quota_override_size_.value() - : desired_host_quota_; - int64_t temp_pool_free_space = - std::max(static_cast<int64_t>(0), - available_space_ - settings_.must_remain_available); - - // Constrain the desired |host_quota| to something that fits. - if (host_quota > temp_pool_free_space) { - if (is_unlimited_) { - host_quota = available_space_ + host_usage_; - } - } - - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk, host_usage_, - host_quota, is_override_enabled_, - std::move(host_usage_breakdown_)); - if (type_ == StorageType::kTemporary && !is_incognito_ && - !is_unlimited_) { - UMA_HISTOGRAM_MBYTES("Quota.QuotaForOrigin", host_quota); - UMA_HISTOGRAM_MBYTES("Quota.UsageByOrigin", host_usage_); - if (host_quota > 0) { - UMA_HISTOGRAM_PERCENTAGE("Quota.PercentUsedByOrigin", - std::min(100, static_cast<int>((host_usage_ * 100) / host_quota))); - } - } - DeleteSoon(); - } - - private: - QuotaManager* manager() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return static_cast<QuotaManager*>(observer()); - } - - void OnGotSettings(base::RepeatingClosure barrier_closure, - const QuotaSettings& settings) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - settings_ = settings; - barrier_closure.Run(); - if (type_ == StorageType::kTemporary && !is_unlimited_) { - int64_t host_quota = is_session_only_ - ? settings.session_only_per_host_quota - : settings.per_host_quota; - SetDesiredHostQuota(barrier_closure, blink::mojom::QuotaStatusCode::kOk, - host_quota); - } - } - - void OnGotCapacity(base::OnceClosure barrier_closure, - int64_t total_space, - int64_t available_space) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - total_space_ = total_space; - available_space_ = available_space; - std::move(barrier_closure).Run(); - } - - void OnGotHostUsage(base::OnceClosure barrier_closure, - int64_t usage, - blink::mojom::UsageBreakdownPtr usage_breakdown) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - host_usage_ = usage; - host_usage_breakdown_ = std::move(usage_breakdown); - std::move(barrier_closure).Run(); - } - - void SetDesiredHostQuota(base::OnceClosure barrier_closure, - blink::mojom::QuotaStatusCode status, - int64_t quota) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - desired_host_quota_ = quota; - std::move(barrier_closure).Run(); - } - - void OnBarrierComplete() { CallCompleted(); } - - const url::Origin origin_; - QuotaManager::UsageAndQuotaForDevtoolsCallback callback_; - const StorageType type_; - const bool is_unlimited_; - const bool is_session_only_; - const bool is_incognito_; - int64_t available_space_ = 0; - int64_t total_space_ = 0; - int64_t desired_host_quota_ = 0; - int64_t host_usage_ = 0; - const bool is_override_enabled_; - base::Optional<int64_t> quota_override_size_; - blink::mojom::UsageBreakdownPtr host_usage_breakdown_; - QuotaSettings settings_; - SEQUENCE_CHECKER(sequence_checker_); - - // Weak pointers are used to support cancelling work. - base::WeakPtrFactory<UsageAndQuotaInfoGatherer> weak_factory_{this}; - DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaInfoGatherer); -}; - -class QuotaManager::EvictionRoundInfoHelper : public QuotaTask { - public: - EvictionRoundInfoHelper(QuotaManager* manager, - EvictionRoundInfoCallback callback) - : QuotaTask(manager), callback_(std::move(callback)) {} - - protected: - void Run() override { - // Gather 2 pieces of info before deciding if we need to get GlobalUsage: - // settings and device_storage_capacity. - base::RepeatingClosure barrier = base::BarrierClosure( - 2, base::BindOnce(&EvictionRoundInfoHelper::OnBarrierComplete, - weak_factory_.GetWeakPtr())); - - manager()->GetQuotaSettings( - base::BindOnce(&EvictionRoundInfoHelper::OnGotSettings, - weak_factory_.GetWeakPtr(), barrier)); - manager()->GetStorageCapacity( - base::BindOnce(&EvictionRoundInfoHelper::OnGotCapacity, - weak_factory_.GetWeakPtr(), barrier)); - } - - void Aborted() override { - weak_factory_.InvalidateWeakPtrs(); - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort, - QuotaSettings(), 0, 0, 0, false); - DeleteSoon(); - } - - void Completed() override { - weak_factory_.InvalidateWeakPtrs(); - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk, settings_, - available_space_, total_space_, global_usage_, - global_usage_is_complete_); - DeleteSoon(); - } - - private: - QuotaManager* manager() const { - return static_cast<QuotaManager*>(observer()); - } - - void OnGotSettings(base::OnceClosure barrier_closure, - const QuotaSettings& settings) { - settings_ = settings; - std::move(barrier_closure).Run(); - } - - void OnGotCapacity(base::OnceClosure barrier_closure, - int64_t total_space, - int64_t available_space) { - total_space_ = total_space; - available_space_ = available_space; - std::move(barrier_closure).Run(); - } - - void OnBarrierComplete() { - // Avoid computing the full current_usage when there's no pressure. - int64_t consumed_space = total_space_ - available_space_; - if (consumed_space < settings_.pool_size && - available_space_ > settings_.should_remain_available) { - DCHECK(!global_usage_is_complete_); - global_usage_ = - manager()->GetUsageTracker(StorageType::kTemporary)->GetCachedUsage(); - CallCompleted(); - return; - } - manager()->GetGlobalUsage( - StorageType::kTemporary, - base::BindOnce(&EvictionRoundInfoHelper::OnGotGlobalUsage, - weak_factory_.GetWeakPtr())); - } - - void OnGotGlobalUsage(int64_t usage, int64_t unlimited_usage) { - global_usage_ = std::max(INT64_C(0), usage - unlimited_usage); - global_usage_is_complete_ = true; - CallCompleted(); - } - - EvictionRoundInfoCallback callback_; - QuotaSettings settings_; - int64_t available_space_ = 0; - int64_t total_space_ = 0; - int64_t global_usage_ = 0; - bool global_usage_is_complete_ = false; - base::WeakPtrFactory<EvictionRoundInfoHelper> weak_factory_{this}; - DISALLOW_COPY_AND_ASSIGN(EvictionRoundInfoHelper); -}; - -class QuotaManager::GetUsageInfoTask : public QuotaTask { - public: - GetUsageInfoTask(QuotaManager* manager, GetUsageInfoCallback callback) - : QuotaTask(manager), callback_(std::move(callback)) {} - - protected: - void Run() override { - remaining_trackers_ = 3; - // This will populate cached hosts and usage info. - manager() - ->GetUsageTracker(StorageType::kTemporary) - ->GetGlobalUsage(base::BindOnce(&GetUsageInfoTask::DidGetGlobalUsage, - weak_factory_.GetWeakPtr(), - StorageType::kTemporary)); - manager() - ->GetUsageTracker(StorageType::kPersistent) - ->GetGlobalUsage(base::BindOnce(&GetUsageInfoTask::DidGetGlobalUsage, - weak_factory_.GetWeakPtr(), - StorageType::kPersistent)); - manager() - ->GetUsageTracker(StorageType::kSyncable) - ->GetGlobalUsage(base::BindOnce(&GetUsageInfoTask::DidGetGlobalUsage, - weak_factory_.GetWeakPtr(), - StorageType::kSyncable)); - } - - void Completed() override { - std::move(callback_).Run(std::move(entries_)); - DeleteSoon(); - } - - void Aborted() override { - std::move(callback_).Run(UsageInfoEntries()); - DeleteSoon(); - } - - private: - void AddEntries(StorageType type, UsageTracker* tracker) { - std::map<std::string, int64_t> host_usage = tracker->GetCachedHostsUsage(); - for (const auto& host_usage_pair : host_usage) { - entries_.emplace_back(host_usage_pair.first, type, - host_usage_pair.second); - } - if (--remaining_trackers_ == 0) - CallCompleted(); - } - - void DidGetGlobalUsage(StorageType type, int64_t, int64_t) { - DCHECK(manager()->GetUsageTracker(type)); - AddEntries(type, manager()->GetUsageTracker(type)); - } - - QuotaManager* manager() const { - return static_cast<QuotaManager*>(observer()); - } - - GetUsageInfoCallback callback_; - UsageInfoEntries entries_; - int remaining_trackers_; - base::WeakPtrFactory<GetUsageInfoTask> weak_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(GetUsageInfoTask); -}; - -class QuotaManager::OriginDataDeleter : public QuotaTask { - public: - OriginDataDeleter(QuotaManager* manager, - const url::Origin& origin, - StorageType type, - QuotaClientTypes quota_client_types, - bool is_eviction, - StatusCallback callback) - : QuotaTask(manager), - origin_(origin), - type_(type), - quota_client_types_(std::move(quota_client_types)), - error_count_(0), - remaining_clients_(0), - skipped_clients_(0), - is_eviction_(is_eviction), - callback_(std::move(callback)) {} - - protected: - void Run() override { - DCHECK(manager()->client_types_.contains(type_)); - remaining_clients_ = manager()->client_types_[type_].size(); - - for (const auto& client_and_type : manager()->client_types_[type_]) { - QuotaClient* client = client_and_type.first; - QuotaClientType client_type = client_and_type.second; - if (quota_client_types_.contains(client_type)) { - static int tracing_id = 0; - TRACE_EVENT_ASYNC_BEGIN2( - "browsing_data", "QuotaManager::OriginDataDeleter", ++tracing_id, - "client_type", client_type, "origin", origin_.Serialize()); - client->DeleteOriginData( - origin_, type_, - base::BindOnce(&OriginDataDeleter::DidDeleteOriginData, - weak_factory_.GetWeakPtr(), tracing_id)); - } else { - ++skipped_clients_; - --remaining_clients_; - } - } - - if (remaining_clients_ == 0) - CallCompleted(); - } - - void Completed() override { - if (error_count_ == 0) { - // Only remove the entire origin if we didn't skip any client types. - if (skipped_clients_ == 0) - manager()->DeleteOriginFromDatabase(origin_, type_, is_eviction_); - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk); - } else { - std::move(callback_).Run( - blink::mojom::QuotaStatusCode::kErrorInvalidModification); - } - DeleteSoon(); - } - - void Aborted() override { - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort); - DeleteSoon(); - } - - private: - void DidDeleteOriginData(int tracing_id, - blink::mojom::QuotaStatusCode status) { - DCHECK_GT(remaining_clients_, 0U); - TRACE_EVENT_ASYNC_END0("browsing_data", "QuotaManager::OriginDataDeleter", - tracing_id); - - if (status != blink::mojom::QuotaStatusCode::kOk) - ++error_count_; - - if (--remaining_clients_ == 0) - CallCompleted(); - } - - QuotaManager* manager() const { - return static_cast<QuotaManager*>(observer()); - } - - const url::Origin origin_; - const StorageType type_; - const QuotaClientTypes quota_client_types_; - int error_count_; - size_t remaining_clients_; - int skipped_clients_; - const bool is_eviction_; - StatusCallback callback_; - - base::WeakPtrFactory<OriginDataDeleter> weak_factory_{this}; - DISALLOW_COPY_AND_ASSIGN(OriginDataDeleter); -}; - -class QuotaManager::HostDataDeleter : public QuotaTask { - public: - HostDataDeleter(QuotaManager* manager, - const std::string& host, - StorageType type, - QuotaClientTypes quota_client_types, - StatusCallback callback) - : QuotaTask(manager), - host_(host), - type_(type), - quota_client_types_(std::move(quota_client_types)), - error_count_(0), - remaining_clients_(0), - remaining_deleters_(0), - callback_(std::move(callback)) {} - - protected: - void Run() override { - DCHECK(manager()->client_types_.contains(type_)); - remaining_clients_ = manager()->client_types_[type_].size(); - - for (const auto& client_and_type : manager()->client_types_[type_]) { - client_and_type.first->GetOriginsForHost( - type_, host_, - base::BindOnce(&HostDataDeleter::DidGetOriginsForHost, - weak_factory_.GetWeakPtr())); - } - } - - void Completed() override { - if (error_count_ == 0) { - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk); - } else { - std::move(callback_).Run( - blink::mojom::QuotaStatusCode::kErrorInvalidModification); - } - DeleteSoon(); - } - - void Aborted() override { - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort); - DeleteSoon(); - } - - private: - void DidGetOriginsForHost(const std::vector<url::Origin>& origins) { - DCHECK_GT(remaining_clients_, 0U); - - for (const auto& origin : origins) - origins_.insert(origin); - - if (--remaining_clients_ == 0) { - if (!origins_.empty()) - ScheduleOriginsDeletion(); - else - CallCompleted(); - } - } - - void ScheduleOriginsDeletion() { - remaining_deleters_ = origins_.size(); - for (const auto& origin : origins_) { - OriginDataDeleter* deleter = new OriginDataDeleter( - manager(), origin, type_, std::move(quota_client_types_), false, - base::BindOnce(&HostDataDeleter::DidDeleteOriginData, - weak_factory_.GetWeakPtr())); - deleter->Start(); - } - } - - void DidDeleteOriginData(blink::mojom::QuotaStatusCode status) { - DCHECK_GT(remaining_deleters_, 0U); - - if (status != blink::mojom::QuotaStatusCode::kOk) - ++error_count_; - - if (--remaining_deleters_ == 0) - CallCompleted(); - } - - QuotaManager* manager() const { - return static_cast<QuotaManager*>(observer()); - } - - const std::string host_; - const StorageType type_; - const QuotaClientTypes quota_client_types_; - std::set<url::Origin> origins_; - int error_count_; - size_t remaining_clients_; - size_t remaining_deleters_; - StatusCallback callback_; - - base::WeakPtrFactory<HostDataDeleter> weak_factory_{this}; - DISALLOW_COPY_AND_ASSIGN(HostDataDeleter); -}; - -class QuotaManager::StorageCleanupHelper : public QuotaTask { - public: - StorageCleanupHelper(QuotaManager* manager, - StorageType type, - QuotaClientTypes quota_client_types, - base::OnceClosure callback) - : QuotaTask(manager), - type_(type), - quota_client_types_(std::move(quota_client_types)), - callback_(std::move(callback)) { - DCHECK(manager->client_types_.contains(type_)); - } - - protected: - void Run() override { - DCHECK(manager()->client_types_.contains(type_)); - base::RepeatingClosure barrier = base::BarrierClosure( - manager()->client_types_[type_].size(), - base::BindOnce(&StorageCleanupHelper::CallCompleted, - weak_factory_.GetWeakPtr())); - - // This may synchronously trigger |callback_| at the end of the for loop, - // make sure we do nothing after this block. - for (const auto& client_and_type : manager()->client_types_[type_]) { - QuotaClient* client = client_and_type.first; - QuotaClientType client_type = client_and_type.second; - if (quota_client_types_.contains(client_type)) { - client->PerformStorageCleanup(type_, barrier); - } else { - barrier.Run(); - } - } - } - - void Aborted() override { - weak_factory_.InvalidateWeakPtrs(); - std::move(callback_).Run(); - DeleteSoon(); - } - - void Completed() override { - weak_factory_.InvalidateWeakPtrs(); - std::move(callback_).Run(); - DeleteSoon(); - } - - private: - QuotaManager* manager() const { - return static_cast<QuotaManager*>(observer()); - } - - const StorageType type_; - const QuotaClientTypes quota_client_types_; - base::OnceClosure callback_; - base::WeakPtrFactory<StorageCleanupHelper> weak_factory_{this}; - DISALLOW_COPY_AND_ASSIGN(StorageCleanupHelper); -}; - -// Fetch origins that have been modified since the specified time. This is used -// to clear data for origins that have been modified within the user specified -// time frame. -// -// This class is granted ownership of itself when it is passed to -// DidGetModifiedBetween() via base::Owned(). When the closure for said -// function goes out of scope, the object is deleted. This is a thread-safe -// class. -class QuotaManager::GetModifiedSinceHelper { - public: - bool GetModifiedBetweenOnDBThread(StorageType type, - base::Time begin, - base::Time end, - QuotaDatabase* database) { - DCHECK(database); - return database->GetOriginsModifiedBetween(type, &origins_, begin, end); - } - - void DidGetModifiedBetween(const base::WeakPtr<QuotaManager>& manager, - GetOriginsCallback callback, - StorageType type, - bool success) { - if (!manager) { - // The operation was aborted. - std::move(callback).Run(std::set<url::Origin>(), type); - return; - } - manager->DidDatabaseWork(success); - std::move(callback).Run(origins_, type); - } - - private: - std::set<url::Origin> origins_; -}; - -// Gather origin info table for quota-internals page. -// -// This class is granted ownership of itself when it is passed to -// DidDumpQuotaTable() via base::Owned(). When the closure for said function -// goes out of scope, the object is deleted. -// This class is not thread-safe because there can be a race when entries_ is -// modified. -class QuotaManager::DumpQuotaTableHelper { - public: - bool DumpQuotaTableOnDBThread(QuotaDatabase* database) { - DCHECK(database); - return database->DumpQuotaTable(base::BindRepeating( - &DumpQuotaTableHelper::AppendEntry, base::Unretained(this))); - } - - void DidDumpQuotaTable(const base::WeakPtr<QuotaManager>& manager, - DumpQuotaTableCallback callback, - bool success) { - if (!manager) { - // The operation was aborted. - std::move(callback).Run(QuotaTableEntries()); - return; - } - manager->DidDatabaseWork(success); - std::move(callback).Run(entries_); - } - - private: - bool AppendEntry(const QuotaTableEntry& entry) { - entries_.push_back(entry); - return true; - } - - QuotaTableEntries entries_; -}; - -// Gather origin info table for quota-internals page. -// -// This class is granted ownership of itself when it is passed to -// DidDumpQuotaTable() via base::Owned(). When the closure for said function -// goes out of scope, the object is deleted. -// This class is not thread-safe because there can be races when entries_ is -// modified. -class QuotaManager::DumpOriginInfoTableHelper { - public: - bool DumpOriginInfoTableOnDBThread(QuotaDatabase* database) { - DCHECK(database); - return database->DumpOriginInfoTable(base::BindRepeating( - &DumpOriginInfoTableHelper::AppendEntry, base::Unretained(this))); - } - - void DidDumpOriginInfoTable(const base::WeakPtr<QuotaManager>& manager, - DumpOriginInfoTableCallback callback, - bool success) { - if (!manager) { - // The operation was aborted. - std::move(callback).Run(OriginInfoTableEntries()); - return; - } - manager->DidDatabaseWork(success); - std::move(callback).Run(entries_); - } - - private: - bool AppendEntry(const OriginInfoTableEntry& entry) { - entries_.push_back(entry); - return true; - } - - OriginInfoTableEntries entries_; -}; - -// QuotaManager --------------------------------------------------------------- - QuotaManager::QuotaManager( bool is_incognito, const base::FilePath& profile_path, scoped_refptr<base::SingleThreadTaskRunner> io_thread, + base::RepeatingClosure quota_change_callback, scoped_refptr<SpecialStoragePolicy> special_storage_policy, const GetQuotaSettingsFunc& get_settings_function) - : RefCountedDeleteOnSequence<QuotaManager>(io_thread), - is_incognito_(is_incognito), - profile_path_(profile_path), - proxy_(new QuotaManagerProxy(this, io_thread)), - db_disabled_(false), - eviction_disabled_(false), - io_thread_(io_thread), - db_runner_(base::ThreadPool::CreateSequencedTaskRunner( - {base::MayBlock(), base::TaskPriority::USER_VISIBLE, - base::TaskShutdownBehavior::BLOCK_SHUTDOWN})), - get_settings_function_(get_settings_function), - is_getting_eviction_origin_(false), - special_storage_policy_(std::move(special_storage_policy)), - get_volume_info_fn_(&QuotaManager::GetVolumeInfo) { - DCHECK_EQ(settings_.refresh_interval, base::TimeDelta::Max()); - if (!get_settings_function.is_null()) { - // Reset the interval to ensure we use the get_settings_function - // the first times settings_ is needed. - settings_.refresh_interval = base::TimeDelta(); - get_settings_task_runner_ = base::ThreadTaskRunnerHandle::Get(); - } - DETACH_FROM_SEQUENCE(sequence_checker_); -} - -void QuotaManager::SetQuotaSettings(const QuotaSettings& settings) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - settings_ = settings; - settings_timestamp_ = base::TimeTicks::Now(); -} - -void QuotaManager::GetUsageInfo(GetUsageInfoCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - GetUsageInfoTask* get_usage_info = - new GetUsageInfoTask(this, std::move(callback)); - get_usage_info->Start(); -} - -void QuotaManager::GetUsageAndQuotaForWebApps(const url::Origin& origin, - StorageType type, - UsageAndQuotaCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - GetUsageAndQuotaWithBreakdown( - origin, type, - base::BindOnce(&DidGetUsageAndQuotaStripBreakdown, std::move(callback))); -} - -void QuotaManager::GetUsageAndQuotaWithBreakdown( - const url::Origin& origin, - StorageType type, - UsageAndQuotaWithBreakdownCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - GetUsageAndQuotaForDevtools( - origin, type, - base::BindOnce(&DidGetUsageAndQuotaStripOverride, std::move(callback))); -} - -void QuotaManager::GetUsageAndQuotaForDevtools( - const url::Origin& origin, - StorageType type, - UsageAndQuotaForDevtoolsCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!IsSupportedType(type) || - (is_incognito_ && !IsSupportedIncognitoType(type))) { - std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorNotSupported, - /*usage=*/0, - /*quota=*/0, - /*is_override_enabled=*/false, - /*usage_breakdown=*/nullptr); - return; - } - LazyInitialize(); - - bool is_session_only = - type == StorageType::kTemporary && special_storage_policy_ && - special_storage_policy_->IsStorageSessionOnly(origin.GetURL()); - - base::Optional<int64_t> quota_override = GetQuotaOverrideForOrigin(origin); - - UsageAndQuotaInfoGatherer* helper = new UsageAndQuotaInfoGatherer( - this, origin, type, IsStorageUnlimited(origin, type), is_session_only, - is_incognito_, quota_override, std::move(callback)); - helper->Start(); -} - -void QuotaManager::GetUsageAndQuota(const url::Origin& origin, - StorageType type, - UsageAndQuotaCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (IsStorageUnlimited(origin, type)) { - // TODO(michaeln): This seems like a non-obvious odd behavior, probably for - // apps/extensions, but it would be good to eliminate this special case. - std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk, 0, kNoLimit); - return; - } - - if (!IsSupportedType(type) || - (is_incognito_ && !IsSupportedIncognitoType(type))) { - std::move(callback).Run( - /*status*/ blink::mojom::QuotaStatusCode::kErrorNotSupported, - /*usage*/ 0, - /*quota*/ 0); - return; - } - LazyInitialize(); - - bool is_session_only = - type == StorageType::kTemporary && special_storage_policy_ && - special_storage_policy_->IsStorageSessionOnly(origin.GetURL()); - - base::Optional<int64_t> quota_override = GetQuotaOverrideForOrigin(origin); - - UsageAndQuotaInfoGatherer* helper = new UsageAndQuotaInfoGatherer( - this, origin, type, IsStorageUnlimited(origin, type), is_session_only, - is_incognito_, quota_override, - base::BindOnce(&DidGetUsageAndQuotaStripOverride, - base::BindOnce(&DidGetUsageAndQuotaStripBreakdown, - std::move(callback)))); - helper->Start(); -} - -void QuotaManager::NotifyStorageAccessed(const url::Origin& origin, - StorageType type) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - NotifyStorageAccessedInternal(origin, type, base::Time::Now()); -} - -void QuotaManager::NotifyStorageModified(QuotaClientType client_id, - const url::Origin& origin, - StorageType type, - int64_t delta) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - NotifyStorageModifiedInternal(client_id, origin, type, delta, - base::Time::Now()); -} - -void QuotaManager::NotifyWriteFailed(const url::Origin& origin) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - auto age_of_disk_stats = base::TimeTicks::Now() - - std::get<0>(cached_disk_stats_for_storage_pressure_); - - // Avoid polling for free disk space if disk stats have been recently - // queried. - if (age_of_disk_stats < kStoragePressureCheckDiskStatsInterval) { - int64_t total_space = std::get<1>(cached_disk_stats_for_storage_pressure_); - int64_t available_space = - std::get<2>(cached_disk_stats_for_storage_pressure_); - MaybeRunStoragePressureCallback(origin, total_space, available_space); - } - - GetStorageCapacity( - base::BindOnce(&QuotaManager::MaybeRunStoragePressureCallback, - weak_factory_.GetWeakPtr(), origin)); -} - -void QuotaManager::NotifyOriginInUse(const url::Origin& origin) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(io_thread_->BelongsToCurrentThread()); - origins_in_use_[origin]++; -} - -void QuotaManager::NotifyOriginNoLongerInUse(const url::Origin& origin) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(io_thread_->BelongsToCurrentThread()); - DCHECK(IsOriginInUse(origin)); - int& count = origins_in_use_[origin]; - if (--count == 0) - origins_in_use_.erase(origin); -} - -void QuotaManager::SetUsageCacheEnabled(QuotaClientType client_id, - const url::Origin& origin, - StorageType type, - bool enabled) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - DCHECK(GetUsageTracker(type)); - GetUsageTracker(type)->SetUsageCacheEnabled(client_id, origin, enabled); -} - -void QuotaManager::DeleteOriginData(const url::Origin& origin, - StorageType type, - QuotaClientTypes quota_client_types, - StatusCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DeleteOriginDataInternal(origin, type, std::move(quota_client_types), false, - std::move(callback)); -} - -void QuotaManager::PerformStorageCleanup(StorageType type, - QuotaClientTypes quota_client_types, - base::OnceClosure callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - StorageCleanupHelper* deleter = new StorageCleanupHelper( - this, type, std::move(quota_client_types), std::move(callback)); - deleter->Start(); -} - -void QuotaManager::DeleteHostData(const std::string& host, - StorageType type, - QuotaClientTypes quota_client_types, - StatusCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - - DCHECK(client_types_.contains(type)); - if (host.empty() || client_types_[type].empty()) { - std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk); - return; - } - - HostDataDeleter* deleter = new HostDataDeleter( - this, host, type, std::move(quota_client_types), std::move(callback)); - deleter->Start(); -} - -void QuotaManager::GetPersistentHostQuota(const std::string& host, - QuotaCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - if (host.empty()) { - // This could happen if we are called on file:///. - // TODO(kinuko) We may want to respect --allow-file-access-from-files - // command line switch. - std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk, 0); - return; - } - - if (!persistent_host_quota_callbacks_.Add(host, std::move(callback))) - return; - - int64_t* quota_ptr = new int64_t(0); - PostTaskAndReplyWithResultForDBThread( - FROM_HERE, - base::BindOnce(&GetPersistentHostQuotaOnDBThread, host, - base::Unretained(quota_ptr)), - base::BindOnce(&QuotaManager::DidGetPersistentHostQuota, - weak_factory_.GetWeakPtr(), host, base::Owned(quota_ptr))); -} - -void QuotaManager::SetPersistentHostQuota(const std::string& host, - int64_t new_quota, - QuotaCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - if (host.empty()) { - // This could happen if we are called on file:///. - std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorNotSupported, - 0); - return; - } - - if (new_quota < 0) { - std::move(callback).Run( - blink::mojom::QuotaStatusCode::kErrorInvalidModification, -1); - return; - } - - // Cap the requested size at the per-host quota limit. - new_quota = std::min(new_quota, kPerHostPersistentQuotaLimit); - - if (db_disabled_) { - std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorInvalidAccess, - -1); - return; - } - - int64_t* new_quota_ptr = new int64_t(new_quota); - PostTaskAndReplyWithResultForDBThread( - FROM_HERE, - base::BindOnce(&SetPersistentHostQuotaOnDBThread, host, - base::Unretained(new_quota_ptr)), - base::BindOnce(&QuotaManager::DidSetPersistentHostQuota, - weak_factory_.GetWeakPtr(), host, std::move(callback), - base::Owned(new_quota_ptr))); -} - -void QuotaManager::GetGlobalUsage(StorageType type, - GlobalUsageCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - DCHECK(GetUsageTracker(type)); - GetUsageTracker(type)->GetGlobalUsage(std::move(callback)); -} - -void QuotaManager::GetHostUsageWithBreakdown( - const std::string& host, - StorageType type, - UsageWithBreakdownCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - DCHECK(GetUsageTracker(type)); - GetUsageTracker(type)->GetHostUsageWithBreakdown(host, std::move(callback)); -} - -std::map<std::string, std::string> QuotaManager::GetStatistics() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::map<std::string, std::string> statistics; - if (temporary_storage_evictor_) { - std::map<std::string, int64_t> stats; - temporary_storage_evictor_->GetStatistics(&stats); - for (const auto& origin_usage_pair : stats) { - statistics[origin_usage_pair.first] = - base::NumberToString(origin_usage_pair.second); - } - } - return statistics; -} - -bool QuotaManager::IsStorageUnlimited(const url::Origin& origin, - StorageType type) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // For syncable storage we should always enforce quota (since the - // quota must be capped by the server limit). - if (type == StorageType::kSyncable) - return false; - if (type == StorageType::kQuotaNotManaged) - return true; - return special_storage_policy_.get() && - special_storage_policy_->IsStorageUnlimited(origin.GetURL()); -} - -void QuotaManager::GetOriginsModifiedBetween(StorageType type, - base::Time begin, - base::Time end, - GetOriginsCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - GetModifiedSinceHelper* helper = new GetModifiedSinceHelper; - PostTaskAndReplyWithResultForDBThread( - FROM_HERE, - base::BindOnce(&GetModifiedSinceHelper::GetModifiedBetweenOnDBThread, - base::Unretained(helper), type, begin, end), - base::BindOnce(&GetModifiedSinceHelper::DidGetModifiedBetween, - base::Owned(helper), weak_factory_.GetWeakPtr(), - std::move(callback), type)); -} - -bool QuotaManager::ResetUsageTracker(StorageType type) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(GetUsageTracker(type)); - if (GetUsageTracker(type)->IsWorking()) - return false; - - auto usage_tracker = std::make_unique<UsageTracker>( - client_types_[type], type, special_storage_policy_.get()); - switch (type) { - case StorageType::kTemporary: - temporary_usage_tracker_ = std::move(usage_tracker); - return true; - case StorageType::kPersistent: - persistent_usage_tracker_ = std::move(usage_tracker); - return true; - case StorageType::kSyncable: - syncable_usage_tracker_ = std::move(usage_tracker); - return true; - default: - NOTREACHED(); - } - return true; -} - -QuotaManager::~QuotaManager() { - proxy_->manager_ = nullptr; - - // Iterating over |clients_for_ownership_| is correct here because we want to - // call OnQuotaManagerDestroyed() once per QuotaClient. - for (const auto& client : clients_for_ownership_) - client->OnQuotaManagerDestroyed(); - - if (database_) - db_runner_->DeleteSoon(FROM_HERE, database_.release()); -} - -QuotaManager::EvictionContext::EvictionContext() - : evicted_type(StorageType::kUnknown) {} - -QuotaManager::EvictionContext::~EvictionContext() = default; - -void QuotaManager::LazyInitialize() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(io_thread_->BelongsToCurrentThread()); - if (database_) { - // Already initialized. - return; - } - - // Use an empty path to open an in-memory only database for incognito. - database_ = std::make_unique<QuotaDatabase>( - is_incognito_ ? base::FilePath() - : profile_path_.AppendASCII(kDatabaseName)); - - temporary_usage_tracker_ = std::make_unique<UsageTracker>( - client_types_[StorageType::kTemporary], StorageType::kTemporary, - special_storage_policy_.get()); - persistent_usage_tracker_ = std::make_unique<UsageTracker>( - client_types_[StorageType::kPersistent], StorageType::kPersistent, - special_storage_policy_.get()); - syncable_usage_tracker_ = std::make_unique<UsageTracker>( - client_types_[StorageType::kSyncable], StorageType::kSyncable, - special_storage_policy_.get()); - - if (!is_incognito_) { - histogram_timer_.Start( - FROM_HERE, base::TimeDelta::FromMilliseconds(kReportHistogramInterval), - this, &QuotaManager::ReportHistogram); - } - - base::PostTaskAndReplyWithResult( - db_runner_.get(), FROM_HERE, - base::BindOnce(&QuotaDatabase::IsOriginDatabaseBootstrapped, - base::Unretained(database_.get())), - base::BindOnce(&QuotaManager::FinishLazyInitialize, - weak_factory_.GetWeakPtr())); -} - -void QuotaManager::FinishLazyInitialize(bool is_database_bootstrapped) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - is_database_bootstrapped_ = is_database_bootstrapped; - StartEviction(); -} - -void QuotaManager::BootstrapDatabaseForEviction( - GetOriginCallback did_get_origin_callback, - int64_t usage, - int64_t unlimited_usage) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // The usage cache should be fully populated now so we can - // seed the database with origins we know about. - std::set<url::Origin> origins = temporary_usage_tracker_->GetCachedOrigins(); - PostTaskAndReplyWithResultForDBThread( - FROM_HERE, - base::BindOnce(&BootstrapDatabaseOnDBThread, std::move(origins)), - base::BindOnce(&QuotaManager::DidBootstrapDatabase, - weak_factory_.GetWeakPtr(), - std::move(did_get_origin_callback))); -} - -void QuotaManager::DidBootstrapDatabase( - GetOriginCallback did_get_origin_callback, - bool success) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - is_database_bootstrapped_ = success; - DidDatabaseWork(success); - GetLRUOrigin(StorageType::kTemporary, std::move(did_get_origin_callback)); -} - -void QuotaManager::RegisterClient( - scoped_refptr<QuotaClient> client, - QuotaClientType client_type, - const std::vector<blink::mojom::StorageType>& storage_types) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!database_.get()) - << "All clients must be registered before the database is initialized"; - DCHECK(client.get()); - - for (blink::mojom::StorageType storage_type : storage_types) - client_types_[storage_type].insert({client.get(), client_type}); - clients_for_ownership_.push_back(std::move(client)); -} - -UsageTracker* QuotaManager::GetUsageTracker(StorageType type) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - switch (type) { - case StorageType::kTemporary: - return temporary_usage_tracker_.get(); - case StorageType::kPersistent: - return persistent_usage_tracker_.get(); - case StorageType::kSyncable: - return syncable_usage_tracker_.get(); - case StorageType::kQuotaNotManaged: - return nullptr; - case StorageType::kUnknown: - NOTREACHED(); - } - return nullptr; -} - -std::set<url::Origin> QuotaManager::GetCachedOrigins(StorageType type) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - DCHECK(GetUsageTracker(type)); - return GetUsageTracker(type)->GetCachedOrigins(); -} - -void QuotaManager::NotifyStorageAccessedInternal(const url::Origin& origin, - StorageType type, - base::Time accessed_time) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - if (type == StorageType::kTemporary && is_getting_eviction_origin_) { - // Record the accessed origins while GetLRUOrigin task is runing - // to filter out them from eviction. - access_notified_origins_.insert(origin); - } - - if (db_disabled_) - return; - PostTaskAndReplyWithResultForDBThread( - FROM_HERE, - base::BindOnce(&UpdateAccessTimeOnDBThread, origin, type, accessed_time), - base::BindOnce(&QuotaManager::DidDatabaseWork, - weak_factory_.GetWeakPtr())); -} - -void QuotaManager::NotifyStorageModifiedInternal(QuotaClientType client_id, - const url::Origin& origin, - StorageType type, - int64_t delta, - base::Time modified_time) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - DCHECK(GetUsageTracker(type)); - GetUsageTracker(type)->UpdateUsageCache(client_id, origin, delta); - - if (db_disabled_) - return; - - PostTaskAndReplyWithResultForDBThread( - FROM_HERE, - base::BindOnce(&UpdateModifiedTimeOnDBThread, origin, type, - modified_time), - base::BindOnce(&QuotaManager::DidDatabaseWork, - weak_factory_.GetWeakPtr())); -} - -void QuotaManager::DumpQuotaTable(DumpQuotaTableCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DumpQuotaTableHelper* helper = new DumpQuotaTableHelper; - PostTaskAndReplyWithResultForDBThread( - FROM_HERE, - base::BindOnce(&DumpQuotaTableHelper::DumpQuotaTableOnDBThread, - base::Unretained(helper)), - base::BindOnce(&DumpQuotaTableHelper::DidDumpQuotaTable, - base::Owned(helper), weak_factory_.GetWeakPtr(), - std::move(callback))); -} - -void QuotaManager::DumpOriginInfoTable(DumpOriginInfoTableCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DumpOriginInfoTableHelper* helper = new DumpOriginInfoTableHelper; - PostTaskAndReplyWithResultForDBThread( - FROM_HERE, - base::BindOnce(&DumpOriginInfoTableHelper::DumpOriginInfoTableOnDBThread, - base::Unretained(helper)), - base::BindOnce(&DumpOriginInfoTableHelper::DidDumpOriginInfoTable, - base::Owned(helper), weak_factory_.GetWeakPtr(), - std::move(callback))); -} - -void QuotaManager::StartEviction() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!temporary_storage_evictor_.get()); - if (eviction_disabled_) - return; - temporary_storage_evictor_ = std::make_unique<QuotaTemporaryStorageEvictor>( - this, kEvictionIntervalInMilliSeconds); - temporary_storage_evictor_->Start(); -} - -void QuotaManager::DeleteOriginFromDatabase(const url::Origin& origin, - StorageType type, - bool is_eviction) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - if (db_disabled_) - return; - - PostTaskAndReplyWithResultForDBThread( - FROM_HERE, - base::BindOnce(&DeleteOriginInfoOnDBThread, origin, type, is_eviction), - base::BindOnce(&QuotaManager::DidDatabaseWork, - weak_factory_.GetWeakPtr())); -} - -void QuotaManager::DidOriginDataEvicted(blink::mojom::QuotaStatusCode status) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(io_thread_->BelongsToCurrentThread()); - - // We only try evict origins that are not in use, so basically - // deletion attempt for eviction should not fail. Let's record - // the origin if we get error and exclude it from future eviction - // if the error happens consistently (> kThresholdOfErrorsToBeDenylisted). - if (status != blink::mojom::QuotaStatusCode::kOk) - origins_in_error_[eviction_context_.evicted_origin]++; - - std::move(eviction_context_.evict_origin_data_callback).Run(status); -} - -void QuotaManager::DeleteOriginDataInternal(const url::Origin& origin, - StorageType type, - QuotaClientTypes quota_client_types, - bool is_eviction, - StatusCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - - OriginDataDeleter* deleter = - new OriginDataDeleter(this, origin, type, std::move(quota_client_types), - is_eviction, std::move(callback)); - deleter->Start(); -} - -void QuotaManager::MaybeRunStoragePressureCallback(const url::Origin& origin, - int64_t total_space, - int64_t available_space) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // TODO(https://crbug.com/1059560): Figure out what 0 total_space means - // and how to handle the storage pressure callback in these cases. - if (total_space == 0) - return; - - if (!storage_pressure_callback_) { - // Quota will hold onto a storage pressure notification if no storage - // pressure callback is set. - origin_for_pending_storage_pressure_callback_ = std::move(origin); - return; - } - - if (available_space < kStoragePressureThresholdRatio * total_space) { - storage_pressure_callback_.Run(std::move(origin)); - } -} - -void QuotaManager::SimulateStoragePressure(const url::Origin origin) { - storage_pressure_callback_.Run(origin); -} - -void QuotaManager::DetermineStoragePressure(int64_t free_space, - int64_t total_space) { - if (!base::FeatureList::IsEnabled(features::kStoragePressureEvent)) { - return; - } - int64_t threshold_bytes = - RandomizeByPercent(kGBytes, kThresholdRandomizationPercent); - int64_t threshold = RandomizeByPercent( - static_cast<int64_t>(total_space * - (kThresholdRandomizationPercent / 100.0)), - kThresholdRandomizationPercent); - threshold = std::min(threshold_bytes, threshold); - - if (free_space < threshold) { - // TODO(https://crbug.com/1096549): Implement StoragePressureEvent - // dispatching. - } -} - -void QuotaManager::SetStoragePressureCallback( - base::RepeatingCallback<void(url::Origin)> storage_pressure_callback) { - storage_pressure_callback_ = storage_pressure_callback; - if (origin_for_pending_storage_pressure_callback_.has_value()) { - storage_pressure_callback_.Run( - std::move(origin_for_pending_storage_pressure_callback_.value())); - origin_for_pending_storage_pressure_callback_ = base::nullopt; - } -} - -int QuotaManager::GetOverrideHandleId() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return ++next_override_handle_id_; -} - -void QuotaManager::OverrideQuotaForOrigin(int handle_id, - const url::Origin& origin, - base::Optional<int64_t> quota_size) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (quota_size.has_value()) { - DCHECK_GE(next_override_handle_id_, handle_id); - // Bracket notation is safe here because we want to construct a new - // QuotaOverride in the case that one does not exist for origin. - devtools_overrides_[origin].active_override_session_ids.insert(handle_id); - devtools_overrides_[origin].quota_size = quota_size.value(); - } else { - devtools_overrides_.erase(origin); - } -} - -void QuotaManager::WithdrawOverridesForHandle(int handle_id) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::vector<url::Origin> origins_to_clear; - for (auto& devtools_override : devtools_overrides_) { - auto& quota_override = devtools_override.second; - auto& origin = devtools_override.first; - - quota_override.active_override_session_ids.erase(handle_id); - - if (!quota_override.active_override_session_ids.size()) { - origins_to_clear.push_back(origin); - } - } - - for (auto& origin : origins_to_clear) { - devtools_overrides_.erase(origin); - } -} - -base::Optional<int64_t> QuotaManager::GetQuotaOverrideForOrigin( - const url::Origin& origin) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!base::Contains(devtools_overrides_, origin)) { - return base::nullopt; - } - return devtools_overrides_[origin].quota_size; -} - -void QuotaManager::ReportHistogram() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!is_incognito_); - GetGlobalUsage( - StorageType::kTemporary, - base::BindOnce(&QuotaManager::DidGetTemporaryGlobalUsageForHistogram, - weak_factory_.GetWeakPtr())); -} - -void QuotaManager::DidGetTemporaryGlobalUsageForHistogram( - int64_t usage, - int64_t unlimited_usage) { - GetStorageCapacity( - base::BindOnce(&QuotaManager::DidGetStorageCapacityForHistogram, - weak_factory_.GetWeakPtr(), usage)); -} - -void QuotaManager::DidGetStorageCapacityForHistogram(int64_t usage, - int64_t total_space, - int64_t available_space) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfTemporaryStorage", usage); - if (total_space > 0) { - UMA_HISTOGRAM_PERCENTAGE("Quota.PercentUsedForTemporaryStorage2", - static_cast<int>((usage * 100) / total_space)); - UMA_HISTOGRAM_MBYTES("Quota.AvailableDiskSpace2", available_space); - UMA_HISTOGRAM_PERCENTAGE( - "Quota.PercentDiskAvailable2", - std::min(100, static_cast<int>((available_space * 100 / total_space)))); - } - - GetGlobalUsage( - StorageType::kPersistent, - base::BindOnce(&QuotaManager::DidGetPersistentGlobalUsageForHistogram, - weak_factory_.GetWeakPtr())); -} - -void QuotaManager::DidGetPersistentGlobalUsageForHistogram( - int64_t usage, - int64_t unlimited_usage) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfPersistentStorage", usage); - - // We DumpOriginInfoTable last to ensure the trackers caches are loaded. - DumpOriginInfoTable( - base::BindOnce(&QuotaManager::DidDumpOriginInfoTableForHistogram, - weak_factory_.GetWeakPtr())); -} - -void QuotaManager::DidDumpOriginInfoTableForHistogram( - const OriginInfoTableEntries& entries) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::map<url::Origin, int64_t> usage_map = - GetUsageTracker(StorageType::kTemporary)->GetCachedOriginsUsage(); - base::Time now = base::Time::Now(); - for (const auto& info : entries) { - if (info.type != StorageType::kTemporary) - continue; - - // Ignore stale database entries. If there is no map entry, the origin's - // data has been deleted. - auto it = usage_map.find(info.origin); - if (it == usage_map.end() || it->second == 0) - continue; - - base::TimeDelta age = now - std::max(info.last_access_time, - info.last_modified_time); - UMA_HISTOGRAM_COUNTS_1000("Quota.AgeOfOriginInDays", age.InDays()); - - int64_t kilobytes = std::max(it->second / INT64_C(1024), INT64_C(1)); - base::Histogram::FactoryGet( - "Quota.AgeOfDataInDays", 1, 1000, 50, - base::HistogramBase::kUmaTargetedHistogramFlag)-> - AddCount(age.InDays(), - base::saturated_cast<int>(kilobytes)); - } -} - -std::set<url::Origin> QuotaManager::GetEvictionOriginExceptions() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::set<url::Origin> exceptions; - for (const auto& p : origins_in_use_) { - if (p.second > 0) - exceptions.insert(p.first); - } - - for (const auto& p : origins_in_error_) { - if (p.second > QuotaManager::kThresholdOfErrorsToBeDenylisted) - exceptions.insert(p.first); - } - - return exceptions; -} - -void QuotaManager::DidGetEvictionOrigin( - GetOriginCallback callback, - const base::Optional<url::Origin>& origin) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // Make sure the returned origin is (still) not in the origin_in_use_ set - // and has not been accessed since we posted the task. - DCHECK(!origin.has_value() || !origin->GetURL().is_empty()); - if (origin.has_value() && - (base::Contains(origins_in_use_, *origin) || - base::Contains(access_notified_origins_, *origin))) { - std::move(callback).Run(base::nullopt); - } else { - std::move(callback).Run(origin); - } - access_notified_origins_.clear(); - - is_getting_eviction_origin_ = false; -} - -void QuotaManager::GetEvictionOrigin( - StorageType type, - int64_t global_quota, - GetOriginCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - // This must not be called while there's an in-flight task. - DCHECK(!is_getting_eviction_origin_); - is_getting_eviction_origin_ = true; - - auto did_get_origin_callback = - base::BindOnce(&QuotaManager::DidGetEvictionOrigin, - weak_factory_.GetWeakPtr(), std::move(callback)); - - if (!is_database_bootstrapped_ && !eviction_disabled_) { - // Once bootstrapped, GetLRUOrigin will be called. - GetGlobalUsage(StorageType::kTemporary, - base::BindOnce(&QuotaManager::BootstrapDatabaseForEviction, - weak_factory_.GetWeakPtr(), - std::move(did_get_origin_callback))); - return; - } - - GetLRUOrigin(type, std::move(did_get_origin_callback)); -} - -void QuotaManager::EvictOriginData(const url::Origin& origin, - StorageType type, - StatusCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(io_thread_->BelongsToCurrentThread()); - DCHECK_EQ(type, StorageType::kTemporary); - - eviction_context_.evicted_origin = origin; - eviction_context_.evicted_type = type; - eviction_context_.evict_origin_data_callback = std::move(callback); - - DeleteOriginDataInternal(origin, type, AllQuotaClientTypes(), true, - base::BindOnce(&QuotaManager::DidOriginDataEvicted, - weak_factory_.GetWeakPtr())); -} - -void QuotaManager::GetEvictionRoundInfo(EvictionRoundInfoCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(io_thread_->BelongsToCurrentThread()); - LazyInitialize(); - EvictionRoundInfoHelper* helper = - new EvictionRoundInfoHelper(this, std::move(callback)); - helper->Start(); -} - -void QuotaManager::GetLRUOrigin(StorageType type, GetOriginCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - LazyInitialize(); - // This must not be called while there's an in-flight task. - DCHECK(lru_origin_callback_.is_null()); - lru_origin_callback_ = std::move(callback); - if (db_disabled_) { - std::move(lru_origin_callback_).Run(base::nullopt); - return; - } - - auto origin = std::make_unique<base::Optional<url::Origin>>(); - auto* origin_ptr = origin.get(); - PostTaskAndReplyWithResultForDBThread( - FROM_HERE, - base::BindOnce(&GetLRUOriginOnDBThread, type, - GetEvictionOriginExceptions(), - base::RetainedRef(special_storage_policy_), - base::Unretained(origin_ptr)), - base::BindOnce(&QuotaManager::DidGetLRUOrigin, weak_factory_.GetWeakPtr(), - std::move(origin))); -} - -void QuotaManager::DidGetPersistentHostQuota(const std::string& host, - const int64_t* quota, - bool success) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DidDatabaseWork(success); - persistent_host_quota_callbacks_.Run( - host, blink::mojom::QuotaStatusCode::kOk, - std::min(*quota, kPerHostPersistentQuotaLimit)); -} - -void QuotaManager::DidSetPersistentHostQuota(const std::string& host, - QuotaCallback callback, - const int64_t* new_quota, - bool success) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DidDatabaseWork(success); - std::move(callback).Run( - success ? blink::mojom::QuotaStatusCode::kOk - : blink::mojom::QuotaStatusCode::kErrorInvalidAccess, - *new_quota); -} - -void QuotaManager::DidGetLRUOrigin( - std::unique_ptr<base::Optional<url::Origin>> origin, - bool success) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DidDatabaseWork(success); - - std::move(lru_origin_callback_).Run(*origin); -} - -namespace { -void DidGetSettingsThreadAdapter(base::TaskRunner* task_runner, - OptionalQuotaSettingsCallback callback, - base::Optional<QuotaSettings> settings) { - task_runner->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), std::move(settings))); -} -} // namespace - -void QuotaManager::GetQuotaSettings(QuotaSettingsCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (base::TimeTicks::Now() - settings_timestamp_ < - settings_.refresh_interval) { - std::move(callback).Run(settings_); - return; - } - - if (!settings_callbacks_.Add(std::move(callback))) - return; - - // We invoke our clients GetQuotaSettingsFunc on the - // UI thread and plumb the resulting value back to this thread. - get_settings_task_runner_->PostTask( - FROM_HERE, - base::BindOnce( - get_settings_function_, - base::BindOnce(&DidGetSettingsThreadAdapter, - base::RetainedRef(base::ThreadTaskRunnerHandle::Get()), - base::BindOnce(&QuotaManager::DidGetSettings, - weak_factory_.GetWeakPtr())))); -} - -void QuotaManager::DidGetSettings(base::Optional<QuotaSettings> settings) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!settings) { - settings = settings_; - settings->refresh_interval = base::TimeDelta::FromMinutes(1); - } - SetQuotaSettings(*settings); - settings_callbacks_.Run(*settings); - UMA_HISTOGRAM_MBYTES("Quota.GlobalTemporaryPoolSize", settings->pool_size); - LOG_IF(WARNING, settings->pool_size == 0) - << "No storage quota provided in QuotaSettings."; -} - -void QuotaManager::GetStorageCapacity(StorageCapacityCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!storage_capacity_callbacks_.Add(std::move(callback))) - return; - if (is_incognito_) { - GetQuotaSettings( - base::BindOnce(&QuotaManager::ContinueIncognitoGetStorageCapacity, - weak_factory_.GetWeakPtr())); - return; - } - base::PostTaskAndReplyWithResult( - db_runner_.get(), FROM_HERE, - base::BindOnce(&QuotaManager::CallGetVolumeInfo, get_volume_info_fn_, - profile_path_), - base::BindOnce(&QuotaManager::DidGetStorageCapacity, - weak_factory_.GetWeakPtr())); -} - -void QuotaManager::ContinueIncognitoGetStorageCapacity( - const QuotaSettings& settings) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - int64_t current_usage = - GetUsageTracker(StorageType::kTemporary)->GetCachedUsage(); - current_usage += GetUsageTracker(StorageType::kPersistent)->GetCachedUsage(); - int64_t available_space = - std::max(INT64_C(0), settings.pool_size - current_usage); - DidGetStorageCapacity(std::make_tuple(settings.pool_size, available_space)); -} - -void QuotaManager::DidGetStorageCapacity( - const std::tuple<int64_t, int64_t>& total_and_available) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - cached_disk_stats_for_storage_pressure_ = - std::make_tuple(base::TimeTicks::Now(), std::get<0>(total_and_available), - std::get<1>(total_and_available)); - storage_capacity_callbacks_.Run(std::get<0>(total_and_available), - std::get<1>(total_and_available)); -} - -void QuotaManager::DidDatabaseWork(bool success) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - db_disabled_ = !success; -} - -void QuotaManager::PostTaskAndReplyWithResultForDBThread( - const base::Location& from_here, - base::OnceCallback<bool(QuotaDatabase*)> task, - base::OnceCallback<void(bool)> reply) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // Deleting manager will post another task to DB sequence to delete - // |database_|, therefore we can be sure that database_ is alive when this - // task runs. - base::PostTaskAndReplyWithResult( - db_runner_.get(), from_here, - base::BindOnce(std::move(task), base::Unretained(database_.get())), - std::move(reply)); -} - -// static -std::tuple<int64_t, int64_t> QuotaManager::CallGetVolumeInfo( - GetVolumeInfoFn get_volume_info_fn, - const base::FilePath& path) { - if (!base::CreateDirectory(path)) { - LOG(WARNING) << "Create directory failed for path" << path.value(); - return std::make_tuple<int64_t, int64_t>(0, 0); - } - int64_t total; - int64_t available; - std::tie(total, available) = get_volume_info_fn(path); - if (total < 0 || available < 0) { - LOG(WARNING) << "Unable to get volume info: " << path.value(); - return std::make_tuple<int64_t, int64_t>(0, 0); - } - UMA_HISTOGRAM_MBYTES("Quota.TotalDiskSpace", total); - UMA_HISTOGRAM_MBYTES("Quota.AvailableDiskSpace", available); - if (total > 0) { - UMA_HISTOGRAM_PERCENTAGE("Quota.PercentDiskAvailable", - std::min(100, static_cast<int>((available * 100) / total))); - } - return std::make_tuple(total, available); -} - -// static -std::tuple<int64_t, int64_t> QuotaManager::GetVolumeInfo( - const base::FilePath& path) { - return std::make_tuple(base::SysInfo::AmountOfTotalDiskSpace(path), - base::SysInfo::AmountOfFreeDiskSpace(path)); -} + : QuotaManagerImpl(is_incognito, + profile_path, + std::move(io_thread), + std::move(quota_change_callback), + std::move(special_storage_policy), + get_settings_function) {} + +QuotaManager::~QuotaManager() = default; } // namespace storage diff --git a/chromium/storage/browser/quota/quota_manager.h b/chromium/storage/browser/quota/quota_manager.h index 75b74e97f7a..309fd4a34a1 100644 --- a/chromium/storage/browser/quota/quota_manager.h +++ b/chromium/storage/browser/quota/quota_manager.h @@ -1,579 +1,31 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2021 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 STORAGE_BROWSER_QUOTA_QUOTA_MANAGER_H_ #define STORAGE_BROWSER_QUOTA_QUOTA_MANAGER_H_ -#include <stdint.h> - -#include <list> -#include <map> -#include <memory> -#include <set> -#include <string> -#include <tuple> -#include <utility> -#include <vector> - -#include "base/callback.h" -#include "base/component_export.h" -#include "base/containers/flat_set.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/ref_counted_delete_on_sequence.h" -#include "base/memory/weak_ptr.h" -#include "base/optional.h" -#include "base/sequence_checker.h" -#include "base/stl_util.h" -#include "base/timer/timer.h" -#include "storage/browser/quota/quota_callbacks.h" -#include "storage/browser/quota/quota_client.h" -#include "storage/browser/quota/quota_client_type.h" -#include "storage/browser/quota/quota_database.h" -#include "storage/browser/quota/quota_settings.h" -#include "storage/browser/quota/quota_task.h" -#include "storage/browser/quota/special_storage_policy.h" -#include "third_party/blink/public/mojom/quota/quota_types.mojom-forward.h" -#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h" -#include "url/origin.h" - -namespace base { -class SequencedTaskRunner; -class SingleThreadTaskRunner; -class TaskRunner; -} // namespace base - -namespace quota_internals { -class QuotaInternalsProxy; -} // namespace quota_internals +#include "storage/browser/quota/quota_manager_impl.h" namespace storage { -class QuotaManagerProxy; -class QuotaOverrideHandle; -class QuotaTemporaryStorageEvictor; -class UsageTracker; - -// An interface called by QuotaTemporaryStorageEvictor. This is a grab bag of -// methods called by QuotaTemporaryStorageEvictor that need to be stubbed for -// testing. -class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaEvictionHandler { - public: - using EvictionRoundInfoCallback = - base::OnceCallback<void(blink::mojom::QuotaStatusCode status, - const QuotaSettings& settings, - int64_t available_space, - int64_t total_space, - int64_t global_usage, - bool global_usage_is_complete)>; - - // Called at the beginning of an eviction round to gather the info about - // the current settings, capacity, and usage. - virtual void GetEvictionRoundInfo(EvictionRoundInfoCallback callback) = 0; - - // Returns next origin to evict, or nullopt if there are no evictable - // origins. - virtual void GetEvictionOrigin(blink::mojom::StorageType type, - int64_t global_quota, - GetOriginCallback callback) = 0; - - // Called to evict an origin. - virtual void EvictOriginData(const url::Origin& origin, - blink::mojom::StorageType type, - StatusCallback callback) = 0; - - protected: - virtual ~QuotaEvictionHandler() = default; -}; - -struct UsageInfo { - UsageInfo(std::string host, blink::mojom::StorageType type, int64_t usage) - : host(std::move(host)), type(type), usage(usage) {} - const std::string host; - const blink::mojom::StorageType type; - const int64_t usage; -}; - -// Entry point into the Quota System +// QuotaManager will eventually become a mojo interface facilitating +// inter-process access to QuotaManagerImpl. As an intermediary step, +// QuotaManager will become an abstract base class, which will be implemented by +// QuotaManagerProxy. // -// Each StoragePartition has exactly one QuotaManager instance, which -// coordinates quota across the Web platform features subject to quota. -// Each storage system interacts with quota via their own implementations of -// the QuotaClient interface. -// -// The class sets limits and defines the parameters of the systems heuristics. -// QuotaManager coordinates clients to orchestrate the collection of usage -// information, enforce quota limits, and evict stale data. -// -// The constructor and proxy() methods can be called on any thread. All other -// methods must be called on the IO thread. -class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManager - : public QuotaTaskObserver, - public QuotaEvictionHandler, - public base::RefCountedDeleteOnSequence<QuotaManager> { +// As a first step, QuotaManager is an alias for QuotaManagerImpl. +class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManager : public QuotaManagerImpl { public: - using UsageAndQuotaCallback = base::OnceCallback< - void(blink::mojom::QuotaStatusCode, int64_t usage, int64_t quota)>; - - using UsageAndQuotaWithBreakdownCallback = - base::OnceCallback<void(blink::mojom::QuotaStatusCode, - int64_t usage, - int64_t quota, - blink::mojom::UsageBreakdownPtr usage_breakdown)>; - - using UsageAndQuotaForDevtoolsCallback = - base::OnceCallback<void(blink::mojom::QuotaStatusCode, - int64_t usage, - int64_t quota, - bool is_override_enabled, - blink::mojom::UsageBreakdownPtr usage_breakdown)>; - - static constexpr int64_t kGBytes = 1024 * 1024 * 1024; - static constexpr int64_t kNoLimit = INT64_MAX; - static constexpr int64_t kMBytes = 1024 * 1024; - static constexpr int kMinutesInMilliSeconds = 60 * 1000; - QuotaManager(bool is_incognito, const base::FilePath& profile_path, scoped_refptr<base::SingleThreadTaskRunner> io_thread, + base::RepeatingClosure quota_change_callback, scoped_refptr<SpecialStoragePolicy> special_storage_policy, const GetQuotaSettingsFunc& get_settings_function); - const QuotaSettings& settings() const { return settings_; } - void SetQuotaSettings(const QuotaSettings& settings); - - // Returns a proxy object that can be used on any thread. - QuotaManagerProxy* proxy() { return proxy_.get(); } - - // Called by clients or webapps. Returns usage per host. - void GetUsageInfo(GetUsageInfoCallback callback); - - // Called by Web Apps (deprecated quota API). - // This method is declared as virtual to allow test code to override it. - virtual void GetUsageAndQuotaForWebApps(const url::Origin& origin, - blink::mojom::StorageType type, - UsageAndQuotaCallback callback); - - // Called by Web Apps (navigator.storage.estimate()) - // This method is declared as virtual to allow test code to override it. - virtual void GetUsageAndQuotaWithBreakdown( - const url::Origin& origin, - blink::mojom::StorageType type, - UsageAndQuotaWithBreakdownCallback callback); - - // Called by DevTools. - virtual void GetUsageAndQuotaForDevtools( - const url::Origin& origin, - blink::mojom::StorageType type, - UsageAndQuotaForDevtoolsCallback callback); - - // Called by storage backends. - // - // For UnlimitedStorage origins, this version skips usage and quota handling - // to avoid extra query cost. Do not call this method for apps/user-facing - // code. - // - // This method is declared as virtual to allow test code to override it. - virtual void GetUsageAndQuota(const url::Origin& origin, - blink::mojom::StorageType type, - UsageAndQuotaCallback callback); - - // Called by storage backends via proxy. - // - // Quota-managed storage backends should call this method when storage is - // accessed. Used to maintain LRU ordering. - void NotifyStorageAccessed(const url::Origin& origin, - blink::mojom::StorageType type); - - // Called by storage backends via proxy. - // - // Quota-managed storage backends must call this method when they have made - // any modifications that change the amount of data stored in their storage. - void NotifyStorageModified(QuotaClientType client_id, - const url::Origin& origin, - blink::mojom::StorageType type, - int64_t delta); - - // Called by storage backends via proxy. - // - // Client storage must call this method whenever they run into disk - // write errors. Used as a hint to determine if the storage partition is out - // of space, and trigger actions if deemed appropriate. - // - // This method is declared as virtual to allow test code to override it. - virtual void NotifyWriteFailed(const url::Origin& origin); - - // Used to avoid evicting origins with open pages. - // A call to NotifyOriginInUse must be balanced by a later call - // to NotifyOriginNoLongerInUse. - void NotifyOriginInUse(const url::Origin& origin); - void NotifyOriginNoLongerInUse(const url::Origin& origin); - bool IsOriginInUse(const url::Origin& origin) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return base::Contains(origins_in_use_, origin); - } - - void SetUsageCacheEnabled(QuotaClientType client_id, - const url::Origin& origin, - blink::mojom::StorageType type, - bool enabled); - - // DeleteOriginData and DeleteHostData (surprisingly enough) delete data of a - // particular blink::mojom::StorageType associated with either a specific - // origin or set of origins. Each method additionally requires a - // |quota_client_types| which specifies the types of QuotaClients to delete - // from the origin. Pass in QuotaClientType::AllClients() to remove all - // clients from the origin, regardless of type. - virtual void DeleteOriginData(const url::Origin& origin, - blink::mojom::StorageType type, - QuotaClientTypes quota_client_types, - StatusCallback callback); - void DeleteHostData(const std::string& host, - blink::mojom::StorageType type, - QuotaClientTypes quota_client_types, - StatusCallback callback); - - // Instructs each QuotaClient to remove possible traces of deleted - // data on the disk. - void PerformStorageCleanup(blink::mojom::StorageType type, - QuotaClientTypes quota_client_types, - base::OnceClosure callback); - - // Called by UI and internal modules. - void GetPersistentHostQuota(const std::string& host, QuotaCallback callback); - void SetPersistentHostQuota(const std::string& host, - int64_t new_quota, - QuotaCallback callback); - void GetGlobalUsage(blink::mojom::StorageType type, - GlobalUsageCallback callback); - void GetHostUsageWithBreakdown(const std::string& host, - blink::mojom::StorageType type, - UsageWithBreakdownCallback callback); - - std::map<std::string, std::string> GetStatistics(); - - bool IsStorageUnlimited(const url::Origin& origin, - blink::mojom::StorageType type) const; - - virtual void GetOriginsModifiedBetween(blink::mojom::StorageType type, - base::Time begin, - base::Time end, - GetOriginsCallback callback); - - bool ResetUsageTracker(blink::mojom::StorageType type); - - // Called when StoragePartition is initialized if embedder has an - // implementation of StorageNotificationService. - void SetStoragePressureCallback( - base::RepeatingCallback<void(url::Origin)> storage_pressure_callback); - - // DevTools Quota Override methods: - int GetOverrideHandleId(); - void OverrideQuotaForOrigin(int handle_id, - const url::Origin& origin, - base::Optional<int64_t> quota_size); - // Called when a DevTools client releases all overrides, however, overrides - // will not be disabled for any origins for which there are other DevTools - // clients/QuotaOverrideHandle with an active override. - void WithdrawOverridesForHandle(int handle_id); - - // Cap size for per-host persistent quota determined by the histogram. - // This is a bit lax value because the histogram says nothing about per-host - // persistent storage usage and we determined by global persistent storage - // usage that is less than 10GB for almost all users. - static constexpr int64_t kPerHostPersistentQuotaLimit = 10 * 1024 * kMBytes; - - static constexpr int kEvictionIntervalInMilliSeconds = - 30 * kMinutesInMilliSeconds; - static constexpr int kThresholdOfErrorsToBeDenylisted = 3; - static constexpr int kThresholdRandomizationPercent = 5; - - static constexpr char kDatabaseName[] = "QuotaManager"; - static constexpr char kDaysBetweenRepeatedOriginEvictionsHistogram[] = - "Quota.DaysBetweenRepeatedOriginEvictions"; - static constexpr char kEvictedOriginAccessedCountHistogram[] = - "Quota.EvictedOriginAccessCount"; - static constexpr char kEvictedOriginDaysSinceAccessHistogram[] = - "Quota.EvictedOriginDaysSinceAccess"; - - // Kept non-const so that test code can change the value. - // TODO(kinuko): Make this a real const value and add a proper way to set - // the quota for syncable storage. (http://crbug.com/155488) - static int64_t kSyncableStorageDefaultHostQuota; - - void DisableDatabaseForTesting() { db_disabled_ = true; } - protected: ~QuotaManager() override; - - private: - friend class base::DeleteHelper<QuotaManager>; - friend class base::RefCountedDeleteOnSequence<QuotaManager>; - friend class quota_internals::QuotaInternalsProxy; - friend class MockQuotaManager; - friend class MockQuotaClient; - friend class QuotaManagerProxy; - friend class QuotaManagerTest; - friend class QuotaTemporaryStorageEvictor; - - class EvictionRoundInfoHelper; - class UsageAndQuotaInfoGatherer; - class GetUsageInfoTask; - class OriginDataDeleter; - class HostDataDeleter; - class GetModifiedSinceHelper; - class DumpQuotaTableHelper; - class DumpOriginInfoTableHelper; - class StorageCleanupHelper; - - struct QuotaOverride { - QuotaOverride(); - ~QuotaOverride(); - - QuotaOverride(const QuotaOverride& quota_override) = delete; - QuotaOverride& operator=(const QuotaOverride&) = delete; - - int64_t quota_size; - - // Keeps track of the DevTools clients that have an active override. - std::set<int> active_override_session_ids; - }; - - using QuotaTableEntry = QuotaDatabase::QuotaTableEntry; - using OriginInfoTableEntry = QuotaDatabase::OriginInfoTableEntry; - using QuotaTableEntries = std::vector<QuotaTableEntry>; - using OriginInfoTableEntries = std::vector<OriginInfoTableEntry>; - - using QuotaSettingsCallback = base::OnceCallback<void(const QuotaSettings&)>; - - // Function pointer type used to store the function which returns - // information about the volume containing the given FilePath. - // The value returned is std::tuple<total_space, available_space>. - using GetVolumeInfoFn = - std::tuple<int64_t, int64_t> (*)(const base::FilePath&); - - using DumpQuotaTableCallback = - base::OnceCallback<void(const QuotaTableEntries&)>; - using DumpOriginInfoTableCallback = - base::OnceCallback<void(const OriginInfoTableEntries&)>; - - // The values returned total_space, available_space. - using StorageCapacityCallback = base::OnceCallback<void(int64_t, int64_t)>; - - struct EvictionContext { - EvictionContext(); - ~EvictionContext(); - url::Origin evicted_origin; - blink::mojom::StorageType evicted_type; - StatusCallback evict_origin_data_callback; - }; - - // Lazily called on the IO thread when the first quota manager API is called. - // - // Initialize() must be called after all quota clients are added to the - // manager by RegisterClient. - void LazyInitialize(); - void FinishLazyInitialize(bool is_database_bootstraped); - void BootstrapDatabaseForEviction(GetOriginCallback did_get_origin_callback, - int64_t unused_usage, - int64_t unused_unlimited_usage); - void DidBootstrapDatabase(GetOriginCallback did_get_origin_callback, - bool success); - - // Called by clients via proxy. - // Registers a quota client to the manager. - void RegisterClient( - scoped_refptr<QuotaClient> client, - QuotaClientType client_type, - const std::vector<blink::mojom::StorageType>& storage_types); - - UsageTracker* GetUsageTracker(blink::mojom::StorageType type) const; - - // Extract cached origins list from the usage tracker. - // (Might return empty list if no origin is tracked by the tracker.) - std::set<url::Origin> GetCachedOrigins(blink::mojom::StorageType type); - - // These internal methods are separately defined mainly for testing. - void NotifyStorageAccessedInternal(const url::Origin& origin, - blink::mojom::StorageType type, - base::Time accessed_time); - void NotifyStorageModifiedInternal(QuotaClientType client_id, - const url::Origin& origin, - blink::mojom::StorageType type, - int64_t delta, - base::Time modified_time); - - void DumpQuotaTable(DumpQuotaTableCallback callback); - void DumpOriginInfoTable(DumpOriginInfoTableCallback callback); - - void DeleteOriginDataInternal(const url::Origin& origin, - blink::mojom::StorageType type, - QuotaClientTypes quota_client_types, - bool is_eviction, - StatusCallback callback); - - // Methods for eviction logic. - void StartEviction(); - void DeleteOriginFromDatabase(const url::Origin& origin, - blink::mojom::StorageType type, - bool is_eviction); - - void DidOriginDataEvicted(blink::mojom::QuotaStatusCode status); - - void ReportHistogram(); - void DidGetTemporaryGlobalUsageForHistogram(int64_t usage, - int64_t unlimited_usage); - void DidGetStorageCapacityForHistogram(int64_t usage, - int64_t total_space, - int64_t available_space); - void DidGetPersistentGlobalUsageForHistogram(int64_t usage, - int64_t unlimited_usage); - void DidDumpOriginInfoTableForHistogram( - const OriginInfoTableEntries& entries); - - std::set<url::Origin> GetEvictionOriginExceptions(); - void DidGetEvictionOrigin(GetOriginCallback callback, - const base::Optional<url::Origin>& origin); - - // QuotaEvictionHandler. - void GetEvictionOrigin(blink::mojom::StorageType type, - int64_t global_quota, - GetOriginCallback callback) override; - void EvictOriginData(const url::Origin& origin, - blink::mojom::StorageType type, - StatusCallback callback) override; - void GetEvictionRoundInfo(EvictionRoundInfoCallback callback) override; - - void GetLRUOrigin(blink::mojom::StorageType type, GetOriginCallback callback); - - void DidGetPersistentHostQuota(const std::string& host, - const int64_t* quota, - bool success); - void DidSetPersistentHostQuota(const std::string& host, - QuotaCallback callback, - const int64_t* new_quota, - bool success); - void DidGetLRUOrigin(std::unique_ptr<base::Optional<url::Origin>> origin, - bool success); - void GetQuotaSettings(QuotaSettingsCallback callback); - void DidGetSettings(base::Optional<QuotaSettings> settings); - void GetStorageCapacity(StorageCapacityCallback callback); - void ContinueIncognitoGetStorageCapacity(const QuotaSettings& settings); - void DidGetStorageCapacity( - const std::tuple<int64_t, int64_t>& total_and_available); - - void DidDatabaseWork(bool success); - - void DeleteOnCorrectThread() const; - - void MaybeRunStoragePressureCallback(const url::Origin& origin, - int64_t total_space, - int64_t available_space); - // Used from quota-internals page to test behavior of the storage pressure - // callback. - void SimulateStoragePressure(const url::Origin origin); - - // Evaluates disk statistics to identify storage pressure - // (low disk space availability) and starts the storage - // pressure event dispatch if appropriate. - // TODO(crbug.com/1088004): Implement UsageAndQuotaInfoGatherer::Completed() - // to use DetermineStoragePressure(). - // TODO(crbug.com/1102433): Define and explain StoragePressure in the README. - void DetermineStoragePressure(int64_t free_space, int64_t total_space); - - base::Optional<int64_t> GetQuotaOverrideForOrigin(const url::Origin&); - - void PostTaskAndReplyWithResultForDBThread( - const base::Location& from_here, - base::OnceCallback<bool(QuotaDatabase*)> task, - base::OnceCallback<void(bool)> reply); - - static std::tuple<int64_t, int64_t> CallGetVolumeInfo( - GetVolumeInfoFn get_volume_info_fn, - const base::FilePath& path); - static std::tuple<int64_t, int64_t> GetVolumeInfo(const base::FilePath& path); - - const bool is_incognito_; - const base::FilePath profile_path_; - - // proxy_ can be accessed by any thread so it must be thread-safe - const scoped_refptr<QuotaManagerProxy> proxy_; - bool db_disabled_; - bool eviction_disabled_; - base::Optional<url::Origin> origin_for_pending_storage_pressure_callback_; - scoped_refptr<base::SingleThreadTaskRunner> io_thread_; - scoped_refptr<base::SequencedTaskRunner> db_runner_; - mutable std::unique_ptr<QuotaDatabase> database_; - bool is_database_bootstrapped_ = false; - - GetQuotaSettingsFunc get_settings_function_; - scoped_refptr<base::TaskRunner> get_settings_task_runner_; - base::RepeatingCallback<void(url::Origin)> storage_pressure_callback_; - QuotaSettings settings_; - base::TimeTicks settings_timestamp_; - std::tuple<base::TimeTicks, int64_t, int64_t> - cached_disk_stats_for_storage_pressure_; - CallbackQueue<QuotaSettingsCallback, const QuotaSettings&> - settings_callbacks_; - CallbackQueue<StorageCapacityCallback, int64_t, int64_t> - storage_capacity_callbacks_; - - GetOriginCallback lru_origin_callback_; - std::set<url::Origin> access_notified_origins_; - - std::map<url::Origin, QuotaOverride> devtools_overrides_; - int next_override_handle_id_ = 0; - - // Owns the QuotaClient instances registered via RegisterClient(). - // - // Iterating over this list is almost always incorrect. Most algorithms should - // iterate over an entry in |client_types_|. - std::vector<scoped_refptr<QuotaClient>> clients_for_ownership_; - // Maps QuotaClient instances to client types. - // - // The QuotaClient instances pointed to by the map keys are guaranteed to be - // alive, because they are owned by |clients_for_ownership_|. - base::flat_map<blink::mojom::StorageType, - base::flat_map<QuotaClient*, QuotaClientType>> - client_types_; - - std::unique_ptr<UsageTracker> temporary_usage_tracker_; - std::unique_ptr<UsageTracker> persistent_usage_tracker_; - std::unique_ptr<UsageTracker> syncable_usage_tracker_; - // TODO(michaeln): Need a way to clear the cache, drop and - // reinstantiate the trackers when they're not handling requests. - - std::unique_ptr<QuotaTemporaryStorageEvictor> temporary_storage_evictor_; - EvictionContext eviction_context_; - bool is_getting_eviction_origin_; - - CallbackQueueMap<QuotaCallback, - std::string, - blink::mojom::QuotaStatusCode, - int64_t> - persistent_host_quota_callbacks_; - - // Map from origin to count. - std::map<url::Origin, int> origins_in_use_; - // Map from origin to error count. - std::map<url::Origin, int> origins_in_error_; - - scoped_refptr<SpecialStoragePolicy> special_storage_policy_; - - base::RepeatingTimer histogram_timer_; - - // Pointer to the function used to get volume information. This is - // overwritten by QuotaManagerTest in order to attain deterministic reported - // values. The default value points to QuotaManager::GetVolumeInfo. - GetVolumeInfoFn get_volume_info_fn_; - - SEQUENCE_CHECKER(sequence_checker_); - - base::WeakPtrFactory<QuotaManager> weak_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(QuotaManager); }; } // namespace storage diff --git a/chromium/storage/browser/quota/quota_manager_impl.cc b/chromium/storage/browser/quota/quota_manager_impl.cc new file mode 100644 index 00000000000..727512649d8 --- /dev/null +++ b/chromium/storage/browser/quota/quota_manager_impl.cc @@ -0,0 +1,1999 @@ +// Copyright 2013 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 "storage/browser/quota/quota_manager_impl.h" + +#include <stddef.h> +#include <stdint.h> + +#include <algorithm> +#include <functional> +#include <limits> +#include <memory> +#include <utility> + +#include "base/barrier_closure.h" +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/macros.h" +#include "base/memory/scoped_refptr.h" +#include "base/metrics/histogram_macros.h" +#include "base/numerics/safe_conversions.h" +#include "base/rand_util.h" +#include "base/sequence_checker.h" +#include "base/sequenced_task_runner.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/system/sys_info.h" +#include "base/task/post_task.h" +#include "base/task/thread_pool.h" +#include "base/task_runner_util.h" +#include "base/threading/thread_restrictions.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "base/types/pass_key.h" +#include "components/services/storage/public/mojom/quota_client.mojom.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "storage/browser/quota/client_usage_tracker.h" +#include "storage/browser/quota/mojo_quota_client_wrapper.h" +#include "storage/browser/quota/quota_client_type.h" +#include "storage/browser/quota/quota_features.h" +#include "storage/browser/quota/quota_macros.h" +#include "storage/browser/quota/quota_manager_proxy.h" +#include "storage/browser/quota/quota_override_handle.h" +#include "storage/browser/quota/quota_temporary_storage_evictor.h" +#include "storage/browser/quota/usage_tracker.h" +#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h" + +using blink::mojom::StorageType; + +namespace storage { + +namespace { + +constexpr int64_t kReportHistogramInterval = 60 * 60 * 1000; // 1 hour + +// Take action on write errors if there is <= 2% disk space +// available. +constexpr double kStoragePressureThresholdRatio = 0.02; + +// Limit how frequently QuotaManagerImpl polls for free disk space when +// only using that information to identify storage pressure. +constexpr base::TimeDelta kStoragePressureCheckDiskStatsInterval = + base::TimeDelta::FromMinutes(5); + +// Modifies a given value by a uniformly random amount from +// -percent to +percent. +int64_t RandomizeByPercent(int64_t value, int percent) { + double random_percent = (base::RandDouble() - 0.5) * percent * 2; + return value * (1 + (random_percent / 100.0)); +} +} // namespace + +// Heuristics: assuming average cloud server allows a few Gigs storage +// on the server side and the storage needs to be shared for user data +// and by multiple apps. +int64_t QuotaManagerImpl::kSyncableStorageDefaultHostQuota = 500 * kMBytes; + +namespace { + +bool IsSupportedType(StorageType type) { + return type == StorageType::kTemporary || type == StorageType::kPersistent || + type == StorageType::kSyncable; +} + +bool IsSupportedIncognitoType(StorageType type) { + return type == StorageType::kTemporary || type == StorageType::kPersistent; +} + +bool GetPersistentHostQuotaOnDBThread(const std::string& host, + int64_t* quota, + QuotaDatabase* database) { + DCHECK(database); + database->GetHostQuota(host, StorageType::kPersistent, quota); + return true; +} + +bool SetPersistentHostQuotaOnDBThread(const std::string& host, + int64_t* new_quota, + QuotaDatabase* database) { + DCHECK(database); + if (database->SetHostQuota(host, StorageType::kPersistent, *new_quota)) + return true; + *new_quota = 0; + return false; +} + +bool GetLRUOriginOnDBThread(StorageType type, + const std::set<url::Origin>& exceptions, + SpecialStoragePolicy* policy, + base::Optional<url::Origin>* origin, + QuotaDatabase* database) { + DCHECK(database); + database->GetLRUOrigin(type, exceptions, policy, origin); + return true; +} + +bool DeleteOriginInfoOnDBThread(const url::Origin& origin, + StorageType type, + bool is_eviction, + QuotaDatabase* database) { + DCHECK(database); + + base::Time now = base::Time::Now(); + + if (is_eviction) { + QuotaDatabase::OriginInfoTableEntry entry; + database->GetOriginInfo(origin, type, &entry); + UMA_HISTOGRAM_COUNTS_1M( + QuotaManagerImpl::kEvictedOriginAccessedCountHistogram, + entry.used_count); + UMA_HISTOGRAM_COUNTS_1000( + QuotaManagerImpl::kEvictedOriginDaysSinceAccessHistogram, + (now - entry.last_access_time).InDays()); + } + + if (!database->DeleteOriginInfo(origin, type)) + return false; + + // If the deletion is not due to an eviction, delete the entry in the eviction + // table as well due to privacy concerns. + if (!is_eviction) + return database->DeleteOriginLastEvictionTime(origin, type); + + base::Time last_eviction_time; + database->GetOriginLastEvictionTime(origin, type, &last_eviction_time); + + if (last_eviction_time != base::Time()) { + UMA_HISTOGRAM_COUNTS_1000( + QuotaManagerImpl::kDaysBetweenRepeatedOriginEvictionsHistogram, + (now - last_eviction_time).InDays()); + } + + return database->SetOriginLastEvictionTime(origin, type, now); +} + +bool BootstrapDatabaseOnDBThread(std::set<url::Origin> origins, + QuotaDatabase* database) { + DCHECK(database); + if (database->IsOriginDatabaseBootstrapped()) + return true; + + // Register existing origins with 0 last time access. + if (database->RegisterInitialOriginInfo(origins, StorageType::kTemporary)) { + database->SetOriginDatabaseBootstrapped(true); + return true; + } + return false; +} + +bool UpdateAccessTimeOnDBThread(const url::Origin& origin, + StorageType type, + base::Time accessed_time, + QuotaDatabase* database) { + DCHECK(database); + return database->SetOriginLastAccessTime(origin, type, accessed_time); +} + +bool UpdateModifiedTimeOnDBThread(const url::Origin& origin, + StorageType type, + base::Time modified_time, + QuotaDatabase* database) { + DCHECK(database); + return database->SetOriginLastModifiedTime(origin, type, modified_time); +} + +void DidGetUsageAndQuotaStripBreakdown( + QuotaManagerImpl::UsageAndQuotaCallback callback, + blink::mojom::QuotaStatusCode status, + int64_t usage, + int64_t quota, + blink::mojom::UsageBreakdownPtr usage_breakdown) { + std::move(callback).Run(status, usage, quota); +} + +void DidGetUsageAndQuotaStripOverride( + QuotaManagerImpl::UsageAndQuotaWithBreakdownCallback callback, + blink::mojom::QuotaStatusCode status, + int64_t usage, + int64_t quota, + bool is_override_enabled, + blink::mojom::UsageBreakdownPtr usage_breakdown) { + std::move(callback).Run(status, usage, quota, std::move(usage_breakdown)); +} + +} // namespace + +constexpr int64_t QuotaManagerImpl::kGBytes; +constexpr int64_t QuotaManagerImpl::kNoLimit; +constexpr int64_t QuotaManagerImpl::kPerHostPersistentQuotaLimit; +constexpr int QuotaManagerImpl::kEvictionIntervalInMilliSeconds; +constexpr int QuotaManagerImpl::kThresholdOfErrorsToBeDenylisted; +constexpr int QuotaManagerImpl::kThresholdRandomizationPercent; +constexpr char QuotaManagerImpl::kDatabaseName[]; +constexpr char QuotaManagerImpl::kDaysBetweenRepeatedOriginEvictionsHistogram[]; +constexpr char QuotaManagerImpl::kEvictedOriginAccessedCountHistogram[]; +constexpr char QuotaManagerImpl::kEvictedOriginDaysSinceAccessHistogram[]; + +QuotaManagerImpl::QuotaOverride::QuotaOverride() = default; +QuotaManagerImpl::QuotaOverride::~QuotaOverride() = default; + +class QuotaManagerImpl::UsageAndQuotaInfoGatherer : public QuotaTask { + public: + UsageAndQuotaInfoGatherer(QuotaManagerImpl* manager, + const url::Origin& origin, + StorageType type, + bool is_unlimited, + bool is_session_only, + bool is_incognito, + base::Optional<int64_t> quota_override_size, + UsageAndQuotaForDevtoolsCallback callback) + : QuotaTask(manager), + origin_(origin), + callback_(std::move(callback)), + type_(type), + is_unlimited_(is_unlimited), + is_session_only_(is_session_only), + is_incognito_(is_incognito), + is_override_enabled_(quota_override_size.has_value()), + quota_override_size_(quota_override_size) {} + + protected: + void Run() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Start the async process of gathering the info we need. + // Gather 4 pieces of info before computing an answer: + // settings, device_storage_capacity, host_usage, and host_quota. + base::RepeatingClosure barrier = base::BarrierClosure( + 4, base::BindOnce(&UsageAndQuotaInfoGatherer::OnBarrierComplete, + weak_factory_.GetWeakPtr())); + + const std::string& host = origin_.host(); + + manager()->GetQuotaSettings( + base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotSettings, + weak_factory_.GetWeakPtr(), barrier)); + manager()->GetStorageCapacity( + base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotCapacity, + weak_factory_.GetWeakPtr(), barrier)); + manager()->GetHostUsageWithBreakdown( + host, type_, + base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotHostUsage, + weak_factory_.GetWeakPtr(), barrier)); + + // Determine host_quota differently depending on type. + if (is_unlimited_) { + SetDesiredHostQuota(barrier, blink::mojom::QuotaStatusCode::kOk, + kNoLimit); + } else if (type_ == StorageType::kSyncable) { + SetDesiredHostQuota(barrier, blink::mojom::QuotaStatusCode::kOk, + kSyncableStorageDefaultHostQuota); + } else if (type_ == StorageType::kPersistent) { + manager()->GetPersistentHostQuota( + host, base::BindOnce(&UsageAndQuotaInfoGatherer::SetDesiredHostQuota, + weak_factory_.GetWeakPtr(), barrier)); + } else { + DCHECK_EQ(StorageType::kTemporary, type_); + // For temporary storage, OnGotSettings will set the host quota. + } + } + + void Aborted() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + weak_factory_.InvalidateWeakPtrs(); + std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort, + /*usage=*/0, + /*quota=*/0, + /*is_override_enabled=*/false, + /*usage_breakdown=*/nullptr); + DeleteSoon(); + } + + void Completed() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + weak_factory_.InvalidateWeakPtrs(); + + int64_t host_quota = quota_override_size_.has_value() + ? quota_override_size_.value() + : desired_host_quota_; + int64_t temp_pool_free_space = + std::max(static_cast<int64_t>(0), + available_space_ - settings_.must_remain_available); + + // Constrain the desired |host_quota| to something that fits. + if (host_quota > temp_pool_free_space) { + if (is_unlimited_) { + host_quota = available_space_ + host_usage_; + } + } + + std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk, host_usage_, + host_quota, is_override_enabled_, + std::move(host_usage_breakdown_)); + if (type_ == StorageType::kTemporary && !is_incognito_ && + !is_unlimited_) { + UMA_HISTOGRAM_MBYTES("Quota.QuotaForOrigin", host_quota); + UMA_HISTOGRAM_MBYTES("Quota.UsageByOrigin", host_usage_); + if (host_quota > 0) { + UMA_HISTOGRAM_PERCENTAGE("Quota.PercentUsedByOrigin", + std::min(100, static_cast<int>((host_usage_ * 100) / host_quota))); + } + } + DeleteSoon(); + } + + private: + QuotaManagerImpl* manager() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return static_cast<QuotaManagerImpl*>(observer()); + } + + void OnGotSettings(base::RepeatingClosure barrier_closure, + const QuotaSettings& settings) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + settings_ = settings; + barrier_closure.Run(); + if (type_ == StorageType::kTemporary && !is_unlimited_) { + int64_t host_quota = is_session_only_ + ? settings.session_only_per_host_quota + : settings.per_host_quota; + SetDesiredHostQuota(barrier_closure, blink::mojom::QuotaStatusCode::kOk, + host_quota); + } + } + + void OnGotCapacity(base::OnceClosure barrier_closure, + int64_t total_space, + int64_t available_space) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + total_space_ = total_space; + available_space_ = available_space; + std::move(barrier_closure).Run(); + } + + void OnGotHostUsage(base::OnceClosure barrier_closure, + int64_t usage, + blink::mojom::UsageBreakdownPtr usage_breakdown) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + host_usage_ = usage; + host_usage_breakdown_ = std::move(usage_breakdown); + std::move(barrier_closure).Run(); + } + + void SetDesiredHostQuota(base::OnceClosure barrier_closure, + blink::mojom::QuotaStatusCode status, + int64_t quota) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + desired_host_quota_ = quota; + std::move(barrier_closure).Run(); + } + + void OnBarrierComplete() { CallCompleted(); } + + const url::Origin origin_; + QuotaManagerImpl::UsageAndQuotaForDevtoolsCallback callback_; + const StorageType type_; + const bool is_unlimited_; + const bool is_session_only_; + const bool is_incognito_; + int64_t available_space_ = 0; + int64_t total_space_ = 0; + int64_t desired_host_quota_ = 0; + int64_t host_usage_ = 0; + const bool is_override_enabled_; + base::Optional<int64_t> quota_override_size_; + blink::mojom::UsageBreakdownPtr host_usage_breakdown_; + QuotaSettings settings_; + SEQUENCE_CHECKER(sequence_checker_); + + // Weak pointers are used to support cancelling work. + base::WeakPtrFactory<UsageAndQuotaInfoGatherer> weak_factory_{this}; +}; + +class QuotaManagerImpl::EvictionRoundInfoHelper : public QuotaTask { + public: + EvictionRoundInfoHelper(QuotaManagerImpl* manager, + EvictionRoundInfoCallback callback) + : QuotaTask(manager), callback_(std::move(callback)) {} + + protected: + void Run() override { + // Gather 2 pieces of info before deciding if we need to get GlobalUsage: + // settings and device_storage_capacity. + base::RepeatingClosure barrier = base::BarrierClosure( + 2, base::BindOnce(&EvictionRoundInfoHelper::OnBarrierComplete, + weak_factory_.GetWeakPtr())); + + manager()->GetQuotaSettings( + base::BindOnce(&EvictionRoundInfoHelper::OnGotSettings, + weak_factory_.GetWeakPtr(), barrier)); + manager()->GetStorageCapacity( + base::BindOnce(&EvictionRoundInfoHelper::OnGotCapacity, + weak_factory_.GetWeakPtr(), barrier)); + } + + void Aborted() override { + weak_factory_.InvalidateWeakPtrs(); + std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort, + QuotaSettings(), 0, 0, 0, false); + DeleteSoon(); + } + + void Completed() override { + weak_factory_.InvalidateWeakPtrs(); + std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk, settings_, + available_space_, total_space_, global_usage_, + global_usage_is_complete_); + DeleteSoon(); + } + + private: + QuotaManagerImpl* manager() const { + return static_cast<QuotaManagerImpl*>(observer()); + } + + void OnGotSettings(base::OnceClosure barrier_closure, + const QuotaSettings& settings) { + settings_ = settings; + std::move(barrier_closure).Run(); + } + + void OnGotCapacity(base::OnceClosure barrier_closure, + int64_t total_space, + int64_t available_space) { + total_space_ = total_space; + available_space_ = available_space; + std::move(barrier_closure).Run(); + } + + void OnBarrierComplete() { + // Avoid computing the full current_usage when there's no pressure. + int64_t consumed_space = total_space_ - available_space_; + if (consumed_space < settings_.pool_size && + available_space_ > settings_.should_remain_available) { + DCHECK(!global_usage_is_complete_); + global_usage_ = + manager()->GetUsageTracker(StorageType::kTemporary)->GetCachedUsage(); + CallCompleted(); + return; + } + manager()->GetGlobalUsage( + StorageType::kTemporary, + base::BindOnce(&EvictionRoundInfoHelper::OnGotGlobalUsage, + weak_factory_.GetWeakPtr())); + } + + void OnGotGlobalUsage(int64_t usage, int64_t unlimited_usage) { + global_usage_ = std::max(INT64_C(0), usage - unlimited_usage); + global_usage_is_complete_ = true; + CallCompleted(); + } + + EvictionRoundInfoCallback callback_; + QuotaSettings settings_; + int64_t available_space_ = 0; + int64_t total_space_ = 0; + int64_t global_usage_ = 0; + bool global_usage_is_complete_ = false; + base::WeakPtrFactory<EvictionRoundInfoHelper> weak_factory_{this}; +}; + +class QuotaManagerImpl::GetUsageInfoTask : public QuotaTask { + public: + GetUsageInfoTask(QuotaManagerImpl* manager, GetUsageInfoCallback callback) + : QuotaTask(manager), callback_(std::move(callback)) {} + + protected: + void Run() override { + remaining_trackers_ = 3; + // This will populate cached hosts and usage info. + manager() + ->GetUsageTracker(StorageType::kTemporary) + ->GetGlobalUsage(base::BindOnce(&GetUsageInfoTask::DidGetGlobalUsage, + weak_factory_.GetWeakPtr(), + StorageType::kTemporary)); + manager() + ->GetUsageTracker(StorageType::kPersistent) + ->GetGlobalUsage(base::BindOnce(&GetUsageInfoTask::DidGetGlobalUsage, + weak_factory_.GetWeakPtr(), + StorageType::kPersistent)); + manager() + ->GetUsageTracker(StorageType::kSyncable) + ->GetGlobalUsage(base::BindOnce(&GetUsageInfoTask::DidGetGlobalUsage, + weak_factory_.GetWeakPtr(), + StorageType::kSyncable)); + } + + void Completed() override { + std::move(callback_).Run(std::move(entries_)); + DeleteSoon(); + } + + void Aborted() override { + std::move(callback_).Run(UsageInfoEntries()); + DeleteSoon(); + } + + private: + void AddEntries(StorageType type, UsageTracker* tracker) { + std::map<std::string, int64_t> host_usage = tracker->GetCachedHostsUsage(); + for (const auto& host_usage_pair : host_usage) { + entries_.emplace_back(host_usage_pair.first, type, + host_usage_pair.second); + } + if (--remaining_trackers_ == 0) + CallCompleted(); + } + + void DidGetGlobalUsage(StorageType type, int64_t, int64_t) { + DCHECK(manager()->GetUsageTracker(type)); + AddEntries(type, manager()->GetUsageTracker(type)); + } + + QuotaManagerImpl* manager() const { + return static_cast<QuotaManagerImpl*>(observer()); + } + + GetUsageInfoCallback callback_; + UsageInfoEntries entries_; + int remaining_trackers_; + base::WeakPtrFactory<GetUsageInfoTask> weak_factory_{this}; +}; + +class QuotaManagerImpl::OriginDataDeleter : public QuotaTask { + public: + OriginDataDeleter(QuotaManagerImpl* manager, + const url::Origin& origin, + StorageType type, + QuotaClientTypes quota_client_types, + bool is_eviction, + StatusCallback callback) + : QuotaTask(manager), + origin_(origin), + type_(type), + quota_client_types_(std::move(quota_client_types)), + error_count_(0), + remaining_clients_(0), + skipped_clients_(0), + is_eviction_(is_eviction), + callback_(std::move(callback)) {} + + protected: + void Run() override { + DCHECK(manager()->client_types_.contains(type_)); + remaining_clients_ = manager()->client_types_[type_].size(); + + for (const auto& client_and_type : manager()->client_types_[type_]) { + QuotaClient* client = client_and_type.first; + QuotaClientType client_type = client_and_type.second; + if (quota_client_types_.contains(client_type)) { + static int tracing_id = 0; + TRACE_EVENT_ASYNC_BEGIN2("browsing_data", + "QuotaManagerImpl::OriginDataDeleter", + ++tracing_id, "client_type", client_type, + "origin", origin_.Serialize()); + client->DeleteOriginData( + origin_, type_, + base::BindOnce(&OriginDataDeleter::DidDeleteOriginData, + weak_factory_.GetWeakPtr(), tracing_id)); + } else { + ++skipped_clients_; + --remaining_clients_; + } + } + + if (remaining_clients_ == 0) + CallCompleted(); + } + + void Completed() override { + if (error_count_ == 0) { + // Only remove the entire origin if we didn't skip any client types. + if (skipped_clients_ == 0) + manager()->DeleteOriginFromDatabase(origin_, type_, is_eviction_); + std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk); + } else { + std::move(callback_).Run( + blink::mojom::QuotaStatusCode::kErrorInvalidModification); + } + DeleteSoon(); + } + + void Aborted() override { + std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort); + DeleteSoon(); + } + + private: + void DidDeleteOriginData(int tracing_id, + blink::mojom::QuotaStatusCode status) { + DCHECK_GT(remaining_clients_, 0U); + TRACE_EVENT_ASYNC_END0("browsing_data", + "QuotaManagerImpl::OriginDataDeleter", tracing_id); + + if (status != blink::mojom::QuotaStatusCode::kOk) + ++error_count_; + + if (--remaining_clients_ == 0) + CallCompleted(); + } + + QuotaManagerImpl* manager() const { + return static_cast<QuotaManagerImpl*>(observer()); + } + + const url::Origin origin_; + const StorageType type_; + const QuotaClientTypes quota_client_types_; + int error_count_; + size_t remaining_clients_; + int skipped_clients_; + const bool is_eviction_; + StatusCallback callback_; + + base::WeakPtrFactory<OriginDataDeleter> weak_factory_{this}; +}; + +class QuotaManagerImpl::HostDataDeleter : public QuotaTask { + public: + HostDataDeleter(QuotaManagerImpl* manager, + const std::string& host, + StorageType type, + QuotaClientTypes quota_client_types, + StatusCallback callback) + : QuotaTask(manager), + host_(host), + type_(type), + quota_client_types_(std::move(quota_client_types)), + error_count_(0), + remaining_clients_(0), + remaining_deleters_(0), + callback_(std::move(callback)) {} + + protected: + void Run() override { + DCHECK(manager()->client_types_.contains(type_)); + remaining_clients_ = manager()->client_types_[type_].size(); + + for (const auto& client_and_type : manager()->client_types_[type_]) { + client_and_type.first->GetOriginsForHost( + type_, host_, + base::BindOnce(&HostDataDeleter::DidGetOriginsForHost, + weak_factory_.GetWeakPtr())); + } + } + + void Completed() override { + if (error_count_ == 0) { + std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk); + } else { + std::move(callback_).Run( + blink::mojom::QuotaStatusCode::kErrorInvalidModification); + } + DeleteSoon(); + } + + void Aborted() override { + std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort); + DeleteSoon(); + } + + private: + void DidGetOriginsForHost(const std::vector<url::Origin>& origins) { + DCHECK_GT(remaining_clients_, 0U); + + for (const auto& origin : origins) + origins_.insert(origin); + + if (--remaining_clients_ == 0) { + if (!origins_.empty()) + ScheduleOriginsDeletion(); + else + CallCompleted(); + } + } + + void ScheduleOriginsDeletion() { + remaining_deleters_ = origins_.size(); + for (const auto& origin : origins_) { + OriginDataDeleter* deleter = new OriginDataDeleter( + manager(), origin, type_, std::move(quota_client_types_), false, + base::BindOnce(&HostDataDeleter::DidDeleteOriginData, + weak_factory_.GetWeakPtr())); + deleter->Start(); + } + } + + void DidDeleteOriginData(blink::mojom::QuotaStatusCode status) { + DCHECK_GT(remaining_deleters_, 0U); + + if (status != blink::mojom::QuotaStatusCode::kOk) + ++error_count_; + + if (--remaining_deleters_ == 0) + CallCompleted(); + } + + QuotaManagerImpl* manager() const { + return static_cast<QuotaManagerImpl*>(observer()); + } + + const std::string host_; + const StorageType type_; + const QuotaClientTypes quota_client_types_; + std::set<url::Origin> origins_; + int error_count_; + size_t remaining_clients_; + size_t remaining_deleters_; + StatusCallback callback_; + + base::WeakPtrFactory<HostDataDeleter> weak_factory_{this}; +}; + +class QuotaManagerImpl::StorageCleanupHelper : public QuotaTask { + public: + StorageCleanupHelper(QuotaManagerImpl* manager, + StorageType type, + QuotaClientTypes quota_client_types, + base::OnceClosure callback) + : QuotaTask(manager), + type_(type), + quota_client_types_(std::move(quota_client_types)), + callback_(std::move(callback)) { + DCHECK(manager->client_types_.contains(type_)); + } + + protected: + void Run() override { + DCHECK(manager()->client_types_.contains(type_)); + base::RepeatingClosure barrier = base::BarrierClosure( + manager()->client_types_[type_].size(), + base::BindOnce(&StorageCleanupHelper::CallCompleted, + weak_factory_.GetWeakPtr())); + + // This may synchronously trigger |callback_| at the end of the for loop, + // make sure we do nothing after this block. + for (const auto& client_and_type : manager()->client_types_[type_]) { + QuotaClient* client = client_and_type.first; + QuotaClientType client_type = client_and_type.second; + if (quota_client_types_.contains(client_type)) { + client->PerformStorageCleanup(type_, barrier); + } else { + barrier.Run(); + } + } + } + + void Aborted() override { + weak_factory_.InvalidateWeakPtrs(); + std::move(callback_).Run(); + DeleteSoon(); + } + + void Completed() override { + weak_factory_.InvalidateWeakPtrs(); + std::move(callback_).Run(); + DeleteSoon(); + } + + private: + QuotaManagerImpl* manager() const { + return static_cast<QuotaManagerImpl*>(observer()); + } + + const StorageType type_; + const QuotaClientTypes quota_client_types_; + base::OnceClosure callback_; + base::WeakPtrFactory<StorageCleanupHelper> weak_factory_{this}; +}; + +// Fetch origins that have been modified since the specified time. This is used +// to clear data for origins that have been modified within the user specified +// time frame. +// +// This class is granted ownership of itself when it is passed to +// DidGetModifiedBetween() via base::Owned(). When the closure for said +// function goes out of scope, the object is deleted. This is a thread-safe +// class. +class QuotaManagerImpl::GetModifiedSinceHelper { + public: + bool GetModifiedBetweenOnDBThread(StorageType type, + base::Time begin, + base::Time end, + QuotaDatabase* database) { + DCHECK(database); + return database->GetOriginsModifiedBetween(type, &origins_, begin, end); + } + + void DidGetModifiedBetween(const base::WeakPtr<QuotaManagerImpl>& manager, + GetOriginsCallback callback, + StorageType type, + bool success) { + if (!manager) { + // The operation was aborted. + std::move(callback).Run(std::set<url::Origin>(), type); + return; + } + manager->DidDatabaseWork(success); + std::move(callback).Run(origins_, type); + } + + private: + std::set<url::Origin> origins_; +}; + +// Gather origin info table for quota-internals page. +// +// This class is granted ownership of itself when it is passed to +// DidDumpQuotaTable() via base::Owned(). When the closure for said function +// goes out of scope, the object is deleted. +// This class is not thread-safe because there can be a race when entries_ is +// modified. +class QuotaManagerImpl::DumpQuotaTableHelper { + public: + bool DumpQuotaTableOnDBThread(QuotaDatabase* database) { + DCHECK(database); + return database->DumpQuotaTable(base::BindRepeating( + &DumpQuotaTableHelper::AppendEntry, base::Unretained(this))); + } + + void DidDumpQuotaTable(const base::WeakPtr<QuotaManagerImpl>& manager, + DumpQuotaTableCallback callback, + bool success) { + if (!manager) { + // The operation was aborted. + std::move(callback).Run(QuotaTableEntries()); + return; + } + manager->DidDatabaseWork(success); + std::move(callback).Run(entries_); + } + + private: + bool AppendEntry(const QuotaTableEntry& entry) { + entries_.push_back(entry); + return true; + } + + QuotaTableEntries entries_; +}; + +// Gather origin info table for quota-internals page. +// +// This class is granted ownership of itself when it is passed to +// DidDumpQuotaTable() via base::Owned(). When the closure for said function +// goes out of scope, the object is deleted. +// This class is not thread-safe because there can be races when entries_ is +// modified. +class QuotaManagerImpl::DumpOriginInfoTableHelper { + public: + bool DumpOriginInfoTableOnDBThread(QuotaDatabase* database) { + DCHECK(database); + return database->DumpOriginInfoTable(base::BindRepeating( + &DumpOriginInfoTableHelper::AppendEntry, base::Unretained(this))); + } + + void DidDumpOriginInfoTable(const base::WeakPtr<QuotaManagerImpl>& manager, + DumpOriginInfoTableCallback callback, + bool success) { + if (!manager) { + // The operation was aborted. + std::move(callback).Run(OriginInfoTableEntries()); + return; + } + manager->DidDatabaseWork(success); + std::move(callback).Run(entries_); + } + + private: + bool AppendEntry(const OriginInfoTableEntry& entry) { + entries_.push_back(entry); + return true; + } + + OriginInfoTableEntries entries_; +}; + +// QuotaManagerImpl ----------------------------------------------------------- + +QuotaManagerImpl::QuotaManagerImpl( + bool is_incognito, + const base::FilePath& profile_path, + scoped_refptr<base::SingleThreadTaskRunner> io_thread, + base::RepeatingClosure quota_change_callback, + scoped_refptr<SpecialStoragePolicy> special_storage_policy, + const GetQuotaSettingsFunc& get_settings_function) + : RefCountedDeleteOnSequence<QuotaManagerImpl>(io_thread), + is_incognito_(is_incognito), + profile_path_(profile_path), + proxy_(base::MakeRefCounted<QuotaManagerProxy>(this, io_thread)), + db_disabled_(false), + eviction_disabled_(false), + io_thread_(std::move(io_thread)), + db_runner_(base::ThreadPool::CreateSequencedTaskRunner( + {base::MayBlock(), base::TaskPriority::USER_VISIBLE, + base::TaskShutdownBehavior::BLOCK_SHUTDOWN})), + get_settings_function_(get_settings_function), + quota_change_callback_(std::move(quota_change_callback)), + is_getting_eviction_origin_(false), + special_storage_policy_(std::move(special_storage_policy)), + get_volume_info_fn_(&QuotaManagerImpl::GetVolumeInfo) { + DCHECK_EQ(settings_.refresh_interval, base::TimeDelta::Max()); + if (!get_settings_function.is_null()) { + // Reset the interval to ensure we use the get_settings_function + // the first times settings_ is needed. + settings_.refresh_interval = base::TimeDelta(); + get_settings_task_runner_ = base::ThreadTaskRunnerHandle::Get(); + } + DETACH_FROM_SEQUENCE(sequence_checker_); +} + +void QuotaManagerImpl::SetQuotaSettings(const QuotaSettings& settings) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + settings_ = settings; + settings_timestamp_ = base::TimeTicks::Now(); +} + +void QuotaManagerImpl::GetUsageInfo(GetUsageInfoCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + GetUsageInfoTask* get_usage_info = + new GetUsageInfoTask(this, std::move(callback)); + get_usage_info->Start(); +} + +void QuotaManagerImpl::GetUsageAndQuotaForWebApps( + const url::Origin& origin, + StorageType type, + UsageAndQuotaCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + GetUsageAndQuotaWithBreakdown( + origin, type, + base::BindOnce(&DidGetUsageAndQuotaStripBreakdown, std::move(callback))); +} + +void QuotaManagerImpl::GetUsageAndQuotaWithBreakdown( + const url::Origin& origin, + StorageType type, + UsageAndQuotaWithBreakdownCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + GetUsageAndQuotaForDevtools( + origin, type, + base::BindOnce(&DidGetUsageAndQuotaStripOverride, std::move(callback))); +} + +void QuotaManagerImpl::GetUsageAndQuotaForDevtools( + const url::Origin& origin, + StorageType type, + UsageAndQuotaForDevtoolsCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!IsSupportedType(type) || + (is_incognito_ && !IsSupportedIncognitoType(type))) { + std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorNotSupported, + /*usage=*/0, + /*quota=*/0, + /*is_override_enabled=*/false, + /*usage_breakdown=*/nullptr); + return; + } + LazyInitialize(); + + bool is_session_only = + type == StorageType::kTemporary && special_storage_policy_ && + special_storage_policy_->IsStorageSessionOnly(origin.GetURL()); + + base::Optional<int64_t> quota_override = GetQuotaOverrideForOrigin(origin); + + UsageAndQuotaInfoGatherer* helper = new UsageAndQuotaInfoGatherer( + this, origin, type, IsStorageUnlimited(origin, type), is_session_only, + is_incognito_, quota_override, std::move(callback)); + helper->Start(); +} + +void QuotaManagerImpl::GetUsageAndQuota(const url::Origin& origin, + StorageType type, + UsageAndQuotaCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (IsStorageUnlimited(origin, type)) { + // TODO(michaeln): This seems like a non-obvious odd behavior, probably for + // apps/extensions, but it would be good to eliminate this special case. + std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk, 0, kNoLimit); + return; + } + + if (!IsSupportedType(type) || + (is_incognito_ && !IsSupportedIncognitoType(type))) { + std::move(callback).Run( + /*status*/ blink::mojom::QuotaStatusCode::kErrorNotSupported, + /*usage*/ 0, + /*quota*/ 0); + return; + } + LazyInitialize(); + + bool is_session_only = + type == StorageType::kTemporary && special_storage_policy_ && + special_storage_policy_->IsStorageSessionOnly(origin.GetURL()); + + base::Optional<int64_t> quota_override = GetQuotaOverrideForOrigin(origin); + + UsageAndQuotaInfoGatherer* helper = new UsageAndQuotaInfoGatherer( + this, origin, type, IsStorageUnlimited(origin, type), is_session_only, + is_incognito_, quota_override, + base::BindOnce(&DidGetUsageAndQuotaStripOverride, + base::BindOnce(&DidGetUsageAndQuotaStripBreakdown, + std::move(callback)))); + helper->Start(); +} + +void QuotaManagerImpl::NotifyWriteFailed(const url::Origin& origin) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + auto age_of_disk_stats = base::TimeTicks::Now() - + std::get<0>(cached_disk_stats_for_storage_pressure_); + + // Avoid polling for free disk space if disk stats have been recently + // queried. + if (age_of_disk_stats < kStoragePressureCheckDiskStatsInterval) { + int64_t total_space = std::get<1>(cached_disk_stats_for_storage_pressure_); + int64_t available_space = + std::get<2>(cached_disk_stats_for_storage_pressure_); + MaybeRunStoragePressureCallback(origin, total_space, available_space); + } + + GetStorageCapacity( + base::BindOnce(&QuotaManagerImpl::MaybeRunStoragePressureCallback, + weak_factory_.GetWeakPtr(), origin)); +} + +void QuotaManagerImpl::NotifyOriginInUse(const url::Origin& origin) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(io_thread_->BelongsToCurrentThread()); + origins_in_use_[origin]++; +} + +void QuotaManagerImpl::NotifyOriginNoLongerInUse(const url::Origin& origin) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(io_thread_->BelongsToCurrentThread()); + DCHECK(IsOriginInUse(origin)); + int& count = origins_in_use_[origin]; + if (--count == 0) + origins_in_use_.erase(origin); +} + +void QuotaManagerImpl::SetUsageCacheEnabled(QuotaClientType client_id, + const url::Origin& origin, + StorageType type, + bool enabled) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + DCHECK(GetUsageTracker(type)); + GetUsageTracker(type)->SetUsageCacheEnabled(client_id, origin, enabled); +} + +void QuotaManagerImpl::DeleteOriginData(const url::Origin& origin, + StorageType type, + QuotaClientTypes quota_client_types, + StatusCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DeleteOriginDataInternal(origin, type, std::move(quota_client_types), false, + std::move(callback)); +} + +void QuotaManagerImpl::PerformStorageCleanup( + StorageType type, + QuotaClientTypes quota_client_types, + base::OnceClosure callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + StorageCleanupHelper* deleter = new StorageCleanupHelper( + this, type, std::move(quota_client_types), std::move(callback)); + deleter->Start(); +} + +void QuotaManagerImpl::DeleteHostData(const std::string& host, + StorageType type, + QuotaClientTypes quota_client_types, + StatusCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + + DCHECK(client_types_.contains(type)); + if (host.empty() || client_types_[type].empty()) { + std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk); + return; + } + + HostDataDeleter* deleter = new HostDataDeleter( + this, host, type, std::move(quota_client_types), std::move(callback)); + deleter->Start(); +} + +void QuotaManagerImpl::GetPersistentHostQuota(const std::string& host, + QuotaCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + if (host.empty()) { + // This could happen if we are called on file:///. + // TODO(kinuko) We may want to respect --allow-file-access-from-files + // command line switch. + std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk, 0); + return; + } + + if (!persistent_host_quota_callbacks_.Add(host, std::move(callback))) + return; + + int64_t* quota_ptr = new int64_t(0); + PostTaskAndReplyWithResultForDBThread( + FROM_HERE, + base::BindOnce(&GetPersistentHostQuotaOnDBThread, host, + base::Unretained(quota_ptr)), + base::BindOnce(&QuotaManagerImpl::DidGetPersistentHostQuota, + weak_factory_.GetWeakPtr(), host, base::Owned(quota_ptr))); +} + +void QuotaManagerImpl::SetPersistentHostQuota(const std::string& host, + int64_t new_quota, + QuotaCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + if (host.empty()) { + // This could happen if we are called on file:///. + std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorNotSupported, + 0); + return; + } + + if (new_quota < 0) { + std::move(callback).Run( + blink::mojom::QuotaStatusCode::kErrorInvalidModification, -1); + return; + } + + // Cap the requested size at the per-host quota limit. + new_quota = std::min(new_quota, kPerHostPersistentQuotaLimit); + + if (db_disabled_) { + std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorInvalidAccess, + -1); + return; + } + + int64_t* new_quota_ptr = new int64_t(new_quota); + PostTaskAndReplyWithResultForDBThread( + FROM_HERE, + base::BindOnce(&SetPersistentHostQuotaOnDBThread, host, + base::Unretained(new_quota_ptr)), + base::BindOnce(&QuotaManagerImpl::DidSetPersistentHostQuota, + weak_factory_.GetWeakPtr(), host, std::move(callback), + base::Owned(new_quota_ptr))); +} + +void QuotaManagerImpl::GetGlobalUsage(StorageType type, + GlobalUsageCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + DCHECK(GetUsageTracker(type)); + GetUsageTracker(type)->GetGlobalUsage(std::move(callback)); +} + +void QuotaManagerImpl::GetHostUsageWithBreakdown( + const std::string& host, + StorageType type, + UsageWithBreakdownCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + DCHECK(GetUsageTracker(type)); + GetUsageTracker(type)->GetHostUsageWithBreakdown(host, std::move(callback)); +} + +std::map<std::string, std::string> QuotaManagerImpl::GetStatistics() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + std::map<std::string, std::string> statistics; + if (temporary_storage_evictor_) { + std::map<std::string, int64_t> stats; + temporary_storage_evictor_->GetStatistics(&stats); + for (const auto& origin_usage_pair : stats) { + statistics[origin_usage_pair.first] = + base::NumberToString(origin_usage_pair.second); + } + } + return statistics; +} + +bool QuotaManagerImpl::IsStorageUnlimited(const url::Origin& origin, + StorageType type) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // For syncable storage we should always enforce quota (since the + // quota must be capped by the server limit). + if (type == StorageType::kSyncable) + return false; + if (type == StorageType::kQuotaNotManaged) + return true; + return special_storage_policy_.get() && + special_storage_policy_->IsStorageUnlimited(origin.GetURL()); +} + +void QuotaManagerImpl::GetOriginsModifiedBetween(StorageType type, + base::Time begin, + base::Time end, + GetOriginsCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + GetModifiedSinceHelper* helper = new GetModifiedSinceHelper; + PostTaskAndReplyWithResultForDBThread( + FROM_HERE, + base::BindOnce(&GetModifiedSinceHelper::GetModifiedBetweenOnDBThread, + base::Unretained(helper), type, begin, end), + base::BindOnce(&GetModifiedSinceHelper::DidGetModifiedBetween, + base::Owned(helper), weak_factory_.GetWeakPtr(), + std::move(callback), type)); +} + +bool QuotaManagerImpl::ResetUsageTracker(StorageType type) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(GetUsageTracker(type)); + if (GetUsageTracker(type)->IsWorking()) + return false; + + auto usage_tracker = std::make_unique<UsageTracker>( + client_types_[type], type, special_storage_policy_.get()); + switch (type) { + case StorageType::kTemporary: + temporary_usage_tracker_ = std::move(usage_tracker); + return true; + case StorageType::kPersistent: + persistent_usage_tracker_ = std::move(usage_tracker); + return true; + case StorageType::kSyncable: + syncable_usage_tracker_ = std::move(usage_tracker); + return true; + default: + NOTREACHED(); + } + return true; +} + +QuotaManagerImpl::~QuotaManagerImpl() { + proxy_->InvalidateQuotaManagerImpl(base::PassKey<QuotaManagerImpl>()); + + // Iterating over `legacy_clients_for_ownership_` is correct here because we + // want to call OnQuotaManagerDestroyed() once per QuotaClient. + for (const auto& client : legacy_clients_for_ownership_) + client->OnQuotaManagerDestroyed(); + + if (database_) + db_runner_->DeleteSoon(FROM_HERE, database_.release()); +} + +QuotaManagerImpl::EvictionContext::EvictionContext() + : evicted_type(StorageType::kUnknown) {} + +QuotaManagerImpl::EvictionContext::~EvictionContext() = default; + +void QuotaManagerImpl::LazyInitialize() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(io_thread_->BelongsToCurrentThread()); + if (database_) { + // Already initialized. + return; + } + + // Use an empty path to open an in-memory only database for incognito. + database_ = std::make_unique<QuotaDatabase>( + is_incognito_ ? base::FilePath() + : profile_path_.AppendASCII(kDatabaseName)); + + temporary_usage_tracker_ = std::make_unique<UsageTracker>( + client_types_[StorageType::kTemporary], StorageType::kTemporary, + special_storage_policy_.get()); + persistent_usage_tracker_ = std::make_unique<UsageTracker>( + client_types_[StorageType::kPersistent], StorageType::kPersistent, + special_storage_policy_.get()); + syncable_usage_tracker_ = std::make_unique<UsageTracker>( + client_types_[StorageType::kSyncable], StorageType::kSyncable, + special_storage_policy_.get()); + + if (!is_incognito_) { + histogram_timer_.Start( + FROM_HERE, base::TimeDelta::FromMilliseconds(kReportHistogramInterval), + this, &QuotaManagerImpl::ReportHistogram); + } + + base::PostTaskAndReplyWithResult( + db_runner_.get(), FROM_HERE, + base::BindOnce(&QuotaDatabase::IsOriginDatabaseBootstrapped, + base::Unretained(database_.get())), + base::BindOnce(&QuotaManagerImpl::FinishLazyInitialize, + weak_factory_.GetWeakPtr())); +} + +void QuotaManagerImpl::FinishLazyInitialize(bool is_database_bootstrapped) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + is_database_bootstrapped_ = is_database_bootstrapped; + StartEviction(); +} + +void QuotaManagerImpl::BootstrapDatabaseForEviction( + GetOriginCallback did_get_origin_callback, + int64_t usage, + int64_t unlimited_usage) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // The usage cache should be fully populated now so we can + // seed the database with origins we know about. + std::set<url::Origin> origins = temporary_usage_tracker_->GetCachedOrigins(); + PostTaskAndReplyWithResultForDBThread( + FROM_HERE, + base::BindOnce(&BootstrapDatabaseOnDBThread, std::move(origins)), + base::BindOnce(&QuotaManagerImpl::DidBootstrapDatabase, + weak_factory_.GetWeakPtr(), + std::move(did_get_origin_callback))); +} + +void QuotaManagerImpl::DidBootstrapDatabase( + GetOriginCallback did_get_origin_callback, + bool success) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + is_database_bootstrapped_ = success; + DidDatabaseWork(success); + GetLRUOrigin(StorageType::kTemporary, std::move(did_get_origin_callback)); +} + +void QuotaManagerImpl::RegisterClient( + mojo::PendingRemote<mojom::QuotaClient> client, + QuotaClientType client_type, + const std::vector<blink::mojom::StorageType>& storage_types) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!database_.get()) + << "All clients must be registered before the database is initialized"; + + clients_for_ownership_.emplace_back(std::move(client)); + mojom::QuotaClient* client_ptr = clients_for_ownership_.back().get(); + + // TODO(crbug.com/1163009): Remove this block after all QuotaClients have been + // mojofied. + legacy_clients_for_ownership_.push_back( + base::MakeRefCounted<MojoQuotaClientWrapper>(client_ptr)); + QuotaClient* legacy_client_ptr = legacy_clients_for_ownership_.back().get(); + + // TODO(crbug.com/1163009): Use client_ptr instead of legacy_client_ptr after + // all QuotaClients have been mojofied. + for (blink::mojom::StorageType storage_type : storage_types) + client_types_[storage_type].insert({legacy_client_ptr, client_type}); +} + +void QuotaManagerImpl::RegisterLegacyClient( + scoped_refptr<QuotaClient> client, + QuotaClientType client_type, + const std::vector<blink::mojom::StorageType>& storage_types) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!database_.get()) + << "All clients must be registered before the database is initialized"; + DCHECK(client.get()); + + for (blink::mojom::StorageType storage_type : storage_types) + client_types_[storage_type].insert({client.get(), client_type}); + legacy_clients_for_ownership_.push_back(std::move(client)); +} + +UsageTracker* QuotaManagerImpl::GetUsageTracker(StorageType type) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + switch (type) { + case StorageType::kTemporary: + return temporary_usage_tracker_.get(); + case StorageType::kPersistent: + return persistent_usage_tracker_.get(); + case StorageType::kSyncable: + return syncable_usage_tracker_.get(); + case StorageType::kQuotaNotManaged: + return nullptr; + case StorageType::kUnknown: + NOTREACHED(); + } + return nullptr; +} + +std::set<url::Origin> QuotaManagerImpl::GetCachedOrigins(StorageType type) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + DCHECK(GetUsageTracker(type)); + return GetUsageTracker(type)->GetCachedOrigins(); +} + +void QuotaManagerImpl::NotifyStorageAccessed(const url::Origin& origin, + StorageType type, + base::Time access_time) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + if (type == StorageType::kTemporary && is_getting_eviction_origin_) { + // Record the accessed origins while GetLRUOrigin task is runing + // to filter out them from eviction. + access_notified_origins_.insert(origin); + } + + if (db_disabled_) + return; + PostTaskAndReplyWithResultForDBThread( + FROM_HERE, + base::BindOnce(&UpdateAccessTimeOnDBThread, origin, type, access_time), + base::BindOnce(&QuotaManagerImpl::DidDatabaseWork, + weak_factory_.GetWeakPtr())); +} + +void QuotaManagerImpl::NotifyStorageModified(QuotaClientType client_id, + const url::Origin& origin, + StorageType type, + int64_t delta, + base::Time modification_time, + base::OnceClosure callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + DCHECK(GetUsageTracker(type)); + GetUsageTracker(type)->UpdateUsageCache(client_id, origin, delta); + + if (callback) + std::move(callback).Run(); + + if (db_disabled_) + return; + + PostTaskAndReplyWithResultForDBThread( + FROM_HERE, + base::BindOnce(&UpdateModifiedTimeOnDBThread, origin, type, + modification_time), + base::BindOnce(&QuotaManagerImpl::DidDatabaseWork, + weak_factory_.GetWeakPtr())); +} + +void QuotaManagerImpl::DumpQuotaTable(DumpQuotaTableCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DumpQuotaTableHelper* helper = new DumpQuotaTableHelper; + PostTaskAndReplyWithResultForDBThread( + FROM_HERE, + base::BindOnce(&DumpQuotaTableHelper::DumpQuotaTableOnDBThread, + base::Unretained(helper)), + base::BindOnce(&DumpQuotaTableHelper::DidDumpQuotaTable, + base::Owned(helper), weak_factory_.GetWeakPtr(), + std::move(callback))); +} + +void QuotaManagerImpl::DumpOriginInfoTable( + DumpOriginInfoTableCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DumpOriginInfoTableHelper* helper = new DumpOriginInfoTableHelper; + PostTaskAndReplyWithResultForDBThread( + FROM_HERE, + base::BindOnce(&DumpOriginInfoTableHelper::DumpOriginInfoTableOnDBThread, + base::Unretained(helper)), + base::BindOnce(&DumpOriginInfoTableHelper::DidDumpOriginInfoTable, + base::Owned(helper), weak_factory_.GetWeakPtr(), + std::move(callback))); +} + +void QuotaManagerImpl::StartEviction() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!temporary_storage_evictor_.get()); + if (eviction_disabled_) + return; + temporary_storage_evictor_ = std::make_unique<QuotaTemporaryStorageEvictor>( + this, kEvictionIntervalInMilliSeconds); + temporary_storage_evictor_->Start(); +} + +void QuotaManagerImpl::DeleteOriginFromDatabase(const url::Origin& origin, + StorageType type, + bool is_eviction) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + if (db_disabled_) + return; + + PostTaskAndReplyWithResultForDBThread( + FROM_HERE, + base::BindOnce(&DeleteOriginInfoOnDBThread, origin, type, is_eviction), + base::BindOnce(&QuotaManagerImpl::DidDatabaseWork, + weak_factory_.GetWeakPtr())); +} + +void QuotaManagerImpl::DidOriginDataEvicted( + blink::mojom::QuotaStatusCode status) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(io_thread_->BelongsToCurrentThread()); + + // We only try evict origins that are not in use, so basically + // deletion attempt for eviction should not fail. Let's record + // the origin if we get error and exclude it from future eviction + // if the error happens consistently (> kThresholdOfErrorsToBeDenylisted). + if (status != blink::mojom::QuotaStatusCode::kOk) + origins_in_error_[eviction_context_.evicted_origin]++; + + std::move(eviction_context_.evict_origin_data_callback).Run(status); +} + +void QuotaManagerImpl::DeleteOriginDataInternal( + const url::Origin& origin, + StorageType type, + QuotaClientTypes quota_client_types, + bool is_eviction, + StatusCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + + OriginDataDeleter* deleter = + new OriginDataDeleter(this, origin, type, std::move(quota_client_types), + is_eviction, std::move(callback)); + deleter->Start(); +} + +void QuotaManagerImpl::MaybeRunStoragePressureCallback( + const url::Origin& origin, + int64_t total_space, + int64_t available_space) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(https://crbug.com/1059560): Figure out what 0 total_space means + // and how to handle the storage pressure callback in these cases. + if (total_space == 0) + return; + + if (!storage_pressure_callback_) { + // Quota will hold onto a storage pressure notification if no storage + // pressure callback is set. + origin_for_pending_storage_pressure_callback_ = std::move(origin); + return; + } + + if (available_space < kStoragePressureThresholdRatio * total_space) { + storage_pressure_callback_.Run(std::move(origin)); + } +} + +void QuotaManagerImpl::SimulateStoragePressure(const url::Origin origin) { + storage_pressure_callback_.Run(origin); +} + +void QuotaManagerImpl::DetermineStoragePressure(int64_t total_space, + int64_t free_space) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!base::FeatureList::IsEnabled(features::kStoragePressureEvent)) { + return; + } + int64_t threshold_bytes = + RandomizeByPercent(kGBytes, kThresholdRandomizationPercent); + int64_t threshold = RandomizeByPercent( + static_cast<int64_t>(total_space * + (kThresholdRandomizationPercent / 100.0)), + kThresholdRandomizationPercent); + threshold = std::min(threshold_bytes, threshold); + if (free_space < threshold && !quota_change_callback_.is_null()) { + quota_change_callback_.Run(); + } +} + +void QuotaManagerImpl::SetStoragePressureCallback( + base::RepeatingCallback<void(url::Origin)> storage_pressure_callback) { + storage_pressure_callback_ = storage_pressure_callback; + if (origin_for_pending_storage_pressure_callback_.has_value()) { + storage_pressure_callback_.Run( + std::move(origin_for_pending_storage_pressure_callback_.value())); + origin_for_pending_storage_pressure_callback_ = base::nullopt; + } +} + +int QuotaManagerImpl::GetOverrideHandleId() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return ++next_override_handle_id_; +} + +void QuotaManagerImpl::OverrideQuotaForOrigin( + int handle_id, + const url::Origin& origin, + base::Optional<int64_t> quota_size) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (quota_size.has_value()) { + DCHECK_GE(next_override_handle_id_, handle_id); + // Bracket notation is safe here because we want to construct a new + // QuotaOverride in the case that one does not exist for origin. + devtools_overrides_[origin].active_override_session_ids.insert(handle_id); + devtools_overrides_[origin].quota_size = quota_size.value(); + } else { + devtools_overrides_.erase(origin); + } +} + +void QuotaManagerImpl::WithdrawOverridesForHandle(int handle_id) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + std::vector<url::Origin> origins_to_clear; + for (auto& devtools_override : devtools_overrides_) { + auto& quota_override = devtools_override.second; + auto& origin = devtools_override.first; + + quota_override.active_override_session_ids.erase(handle_id); + + if (!quota_override.active_override_session_ids.size()) { + origins_to_clear.push_back(origin); + } + } + + for (auto& origin : origins_to_clear) { + devtools_overrides_.erase(origin); + } +} + +base::Optional<int64_t> QuotaManagerImpl::GetQuotaOverrideForOrigin( + const url::Origin& origin) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!base::Contains(devtools_overrides_, origin)) { + return base::nullopt; + } + return devtools_overrides_[origin].quota_size; +} + +void QuotaManagerImpl::SetQuotaChangeCallbackForTesting( + base::RepeatingClosure storage_pressure_event_callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + quota_change_callback_ = std::move(storage_pressure_event_callback); +} + +void QuotaManagerImpl::ReportHistogram() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!is_incognito_); + GetGlobalUsage( + StorageType::kTemporary, + base::BindOnce(&QuotaManagerImpl::DidGetTemporaryGlobalUsageForHistogram, + weak_factory_.GetWeakPtr())); +} + +void QuotaManagerImpl::DidGetTemporaryGlobalUsageForHistogram( + int64_t usage, + int64_t unlimited_usage) { + GetStorageCapacity( + base::BindOnce(&QuotaManagerImpl::DidGetStorageCapacityForHistogram, + weak_factory_.GetWeakPtr(), usage)); +} + +void QuotaManagerImpl::DidGetStorageCapacityForHistogram( + int64_t usage, + int64_t total_space, + int64_t available_space) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfTemporaryStorage", usage); + if (total_space > 0) { + UMA_HISTOGRAM_PERCENTAGE("Quota.PercentUsedForTemporaryStorage2", + static_cast<int>((usage * 100) / total_space)); + UMA_HISTOGRAM_MBYTES("Quota.AvailableDiskSpace2", available_space); + UMA_HISTOGRAM_PERCENTAGE( + "Quota.PercentDiskAvailable2", + std::min(100, static_cast<int>((available_space * 100 / total_space)))); + } + + GetGlobalUsage( + StorageType::kPersistent, + base::BindOnce(&QuotaManagerImpl::DidGetPersistentGlobalUsageForHistogram, + weak_factory_.GetWeakPtr())); +} + +void QuotaManagerImpl::DidGetPersistentGlobalUsageForHistogram( + int64_t usage, + int64_t unlimited_usage) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfPersistentStorage", usage); + + // We DumpOriginInfoTable last to ensure the trackers caches are loaded. + DumpOriginInfoTable( + base::BindOnce(&QuotaManagerImpl::DidDumpOriginInfoTableForHistogram, + weak_factory_.GetWeakPtr())); +} + +void QuotaManagerImpl::DidDumpOriginInfoTableForHistogram( + const OriginInfoTableEntries& entries) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + std::map<url::Origin, int64_t> usage_map = + GetUsageTracker(StorageType::kTemporary)->GetCachedOriginsUsage(); + base::Time now = base::Time::Now(); + for (const auto& info : entries) { + if (info.type != StorageType::kTemporary) + continue; + + // Ignore stale database entries. If there is no map entry, the origin's + // data has been deleted. + auto it = usage_map.find(info.origin); + if (it == usage_map.end() || it->second == 0) + continue; + + base::TimeDelta age = now - std::max(info.last_access_time, + info.last_modified_time); + UMA_HISTOGRAM_COUNTS_1000("Quota.AgeOfOriginInDays", age.InDays()); + + int64_t kilobytes = std::max(it->second / INT64_C(1024), INT64_C(1)); + base::Histogram::FactoryGet( + "Quota.AgeOfDataInDays", 1, 1000, 50, + base::HistogramBase::kUmaTargetedHistogramFlag)-> + AddCount(age.InDays(), + base::saturated_cast<int>(kilobytes)); + } +} + +std::set<url::Origin> QuotaManagerImpl::GetEvictionOriginExceptions() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + std::set<url::Origin> exceptions; + for (const auto& p : origins_in_use_) { + if (p.second > 0) + exceptions.insert(p.first); + } + + for (const auto& p : origins_in_error_) { + if (p.second > QuotaManagerImpl::kThresholdOfErrorsToBeDenylisted) + exceptions.insert(p.first); + } + + return exceptions; +} + +void QuotaManagerImpl::DidGetEvictionOrigin( + GetOriginCallback callback, + const base::Optional<url::Origin>& origin) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Make sure the returned origin is (still) not in the origin_in_use_ set + // and has not been accessed since we posted the task. + DCHECK(!origin.has_value() || !origin->GetURL().is_empty()); + if (origin.has_value() && + (base::Contains(origins_in_use_, *origin) || + base::Contains(access_notified_origins_, *origin))) { + std::move(callback).Run(base::nullopt); + } else { + std::move(callback).Run(origin); + } + access_notified_origins_.clear(); + + is_getting_eviction_origin_ = false; +} + +void QuotaManagerImpl::GetEvictionOrigin(StorageType type, + int64_t global_quota, + GetOriginCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + // This must not be called while there's an in-flight task. + DCHECK(!is_getting_eviction_origin_); + is_getting_eviction_origin_ = true; + + auto did_get_origin_callback = + base::BindOnce(&QuotaManagerImpl::DidGetEvictionOrigin, + weak_factory_.GetWeakPtr(), std::move(callback)); + + if (!is_database_bootstrapped_ && !eviction_disabled_) { + // Once bootstrapped, GetLRUOrigin will be called. + GetGlobalUsage( + StorageType::kTemporary, + base::BindOnce(&QuotaManagerImpl::BootstrapDatabaseForEviction, + weak_factory_.GetWeakPtr(), + std::move(did_get_origin_callback))); + return; + } + + GetLRUOrigin(type, std::move(did_get_origin_callback)); +} + +void QuotaManagerImpl::EvictOriginData(const url::Origin& origin, + StorageType type, + StatusCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(io_thread_->BelongsToCurrentThread()); + DCHECK_EQ(type, StorageType::kTemporary); + + eviction_context_.evicted_origin = origin; + eviction_context_.evicted_type = type; + eviction_context_.evict_origin_data_callback = std::move(callback); + + DeleteOriginDataInternal( + origin, type, AllQuotaClientTypes(), true, + base::BindOnce(&QuotaManagerImpl::DidOriginDataEvicted, + weak_factory_.GetWeakPtr())); +} + +void QuotaManagerImpl::GetEvictionRoundInfo( + EvictionRoundInfoCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(io_thread_->BelongsToCurrentThread()); + LazyInitialize(); + EvictionRoundInfoHelper* helper = + new EvictionRoundInfoHelper(this, std::move(callback)); + helper->Start(); +} + +void QuotaManagerImpl::GetLRUOrigin(StorageType type, + GetOriginCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + LazyInitialize(); + // This must not be called while there's an in-flight task. + DCHECK(lru_origin_callback_.is_null()); + lru_origin_callback_ = std::move(callback); + if (db_disabled_) { + std::move(lru_origin_callback_).Run(base::nullopt); + return; + } + + auto origin = std::make_unique<base::Optional<url::Origin>>(); + auto* origin_ptr = origin.get(); + PostTaskAndReplyWithResultForDBThread( + FROM_HERE, + base::BindOnce(&GetLRUOriginOnDBThread, type, + GetEvictionOriginExceptions(), + base::RetainedRef(special_storage_policy_), + base::Unretained(origin_ptr)), + base::BindOnce(&QuotaManagerImpl::DidGetLRUOrigin, + weak_factory_.GetWeakPtr(), std::move(origin))); +} + +void QuotaManagerImpl::DidGetPersistentHostQuota(const std::string& host, + const int64_t* quota, + bool success) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DidDatabaseWork(success); + persistent_host_quota_callbacks_.Run( + host, blink::mojom::QuotaStatusCode::kOk, + std::min(*quota, kPerHostPersistentQuotaLimit)); +} + +void QuotaManagerImpl::DidSetPersistentHostQuota(const std::string& host, + QuotaCallback callback, + const int64_t* new_quota, + bool success) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DidDatabaseWork(success); + std::move(callback).Run( + success ? blink::mojom::QuotaStatusCode::kOk + : blink::mojom::QuotaStatusCode::kErrorInvalidAccess, + *new_quota); +} + +void QuotaManagerImpl::DidGetLRUOrigin( + std::unique_ptr<base::Optional<url::Origin>> origin, + bool success) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DidDatabaseWork(success); + + std::move(lru_origin_callback_).Run(*origin); +} + +namespace { +void DidGetSettingsThreadAdapter(base::TaskRunner* task_runner, + OptionalQuotaSettingsCallback callback, + base::Optional<QuotaSettings> settings) { + task_runner->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), std::move(settings))); +} +} // namespace + +void QuotaManagerImpl::GetQuotaSettings(QuotaSettingsCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (base::TimeTicks::Now() - settings_timestamp_ < + settings_.refresh_interval) { + std::move(callback).Run(settings_); + return; + } + + if (!settings_callbacks_.Add(std::move(callback))) + return; + + // We invoke our clients GetQuotaSettingsFunc on the + // UI thread and plumb the resulting value back to this thread. + get_settings_task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + get_settings_function_, + base::BindOnce(&DidGetSettingsThreadAdapter, + base::RetainedRef(base::ThreadTaskRunnerHandle::Get()), + base::BindOnce(&QuotaManagerImpl::DidGetSettings, + weak_factory_.GetWeakPtr())))); +} + +void QuotaManagerImpl::DidGetSettings(base::Optional<QuotaSettings> settings) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!settings) { + settings = settings_; + settings->refresh_interval = base::TimeDelta::FromMinutes(1); + } + SetQuotaSettings(*settings); + settings_callbacks_.Run(*settings); + UMA_HISTOGRAM_MBYTES("Quota.GlobalTemporaryPoolSize", settings->pool_size); + LOG_IF(WARNING, settings->pool_size == 0) + << "No storage quota provided in QuotaSettings."; +} + +void QuotaManagerImpl::GetStorageCapacity(StorageCapacityCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!storage_capacity_callbacks_.Add(std::move(callback))) + return; + if (is_incognito_) { + GetQuotaSettings( + base::BindOnce(&QuotaManagerImpl::ContinueIncognitoGetStorageCapacity, + weak_factory_.GetWeakPtr())); + return; + } + base::PostTaskAndReplyWithResult( + db_runner_.get(), FROM_HERE, + base::BindOnce(&QuotaManagerImpl::CallGetVolumeInfo, get_volume_info_fn_, + profile_path_), + base::BindOnce(&QuotaManagerImpl::DidGetStorageCapacity, + weak_factory_.GetWeakPtr())); +} + +void QuotaManagerImpl::ContinueIncognitoGetStorageCapacity( + const QuotaSettings& settings) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + int64_t current_usage = + GetUsageTracker(StorageType::kTemporary)->GetCachedUsage(); + current_usage += GetUsageTracker(StorageType::kPersistent)->GetCachedUsage(); + int64_t available_space = + std::max(INT64_C(0), settings.pool_size - current_usage); + DidGetStorageCapacity(std::make_tuple(settings.pool_size, available_space)); +} + +void QuotaManagerImpl::DidGetStorageCapacity( + const std::tuple<int64_t, int64_t>& total_and_available) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + int64_t total_space = std::get<0>(total_and_available); + int64_t available_space = std::get<1>(total_and_available); + cached_disk_stats_for_storage_pressure_ = + std::make_tuple(base::TimeTicks::Now(), total_space, available_space); + storage_capacity_callbacks_.Run(total_space, available_space); + DetermineStoragePressure(total_space, available_space); +} + +void QuotaManagerImpl::DidDatabaseWork(bool success) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + db_disabled_ = !success; +} + +void QuotaManagerImpl::PostTaskAndReplyWithResultForDBThread( + const base::Location& from_here, + base::OnceCallback<bool(QuotaDatabase*)> task, + base::OnceCallback<void(bool)> reply) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Deleting manager will post another task to DB sequence to delete + // |database_|, therefore we can be sure that database_ is alive when this + // task runs. + base::PostTaskAndReplyWithResult( + db_runner_.get(), from_here, + base::BindOnce(std::move(task), base::Unretained(database_.get())), + std::move(reply)); +} + +// static +std::tuple<int64_t, int64_t> QuotaManagerImpl::CallGetVolumeInfo( + GetVolumeInfoFn get_volume_info_fn, + const base::FilePath& path) { + if (!base::CreateDirectory(path)) { + LOG(WARNING) << "Create directory failed for path" << path.value(); + return std::make_tuple<int64_t, int64_t>(0, 0); + } + int64_t total; + int64_t available; + std::tie(total, available) = get_volume_info_fn(path); + if (total < 0 || available < 0) { + LOG(WARNING) << "Unable to get volume info: " << path.value(); + return std::make_tuple<int64_t, int64_t>(0, 0); + } + UMA_HISTOGRAM_MBYTES("Quota.TotalDiskSpace", total); + UMA_HISTOGRAM_MBYTES("Quota.AvailableDiskSpace", available); + if (total > 0) { + UMA_HISTOGRAM_PERCENTAGE("Quota.PercentDiskAvailable", + std::min(100, static_cast<int>((available * 100) / total))); + } + return std::make_tuple(total, available); +} + +// static +std::tuple<int64_t, int64_t> QuotaManagerImpl::GetVolumeInfo( + const base::FilePath& path) { + return std::make_tuple(base::SysInfo::AmountOfTotalDiskSpace(path), + base::SysInfo::AmountOfFreeDiskSpace(path)); +} + +} // namespace storage diff --git a/chromium/storage/browser/quota/quota_manager_impl.h b/chromium/storage/browser/quota/quota_manager_impl.h new file mode 100644 index 00000000000..817f16ecd9c --- /dev/null +++ b/chromium/storage/browser/quota/quota_manager_impl.h @@ -0,0 +1,613 @@ +// Copyright 2013 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 STORAGE_BROWSER_QUOTA_QUOTA_MANAGER_IMPL_H_ +#define STORAGE_BROWSER_QUOTA_QUOTA_MANAGER_IMPL_H_ + +#include <stdint.h> + +#include <list> +#include <map> +#include <memory> +#include <set> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +#include "base/callback.h" +#include "base/component_export.h" +#include "base/containers/contains.h" +#include "base/containers/flat_set.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_delete_on_sequence.h" +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "base/sequence_checker.h" +#include "base/time/time.h" +#include "base/timer/timer.h" +#include "components/services/storage/public/mojom/quota_client.mojom.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "storage/browser/quota/quota_callbacks.h" +#include "storage/browser/quota/quota_client.h" +#include "storage/browser/quota/quota_client_type.h" +#include "storage/browser/quota/quota_database.h" +#include "storage/browser/quota/quota_settings.h" +#include "storage/browser/quota/quota_task.h" +#include "storage/browser/quota/special_storage_policy.h" +#include "third_party/blink/public/mojom/quota/quota_types.mojom-forward.h" +#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h" +#include "url/origin.h" + +namespace base { +class SequencedTaskRunner; +class SingleThreadTaskRunner; +class TaskRunner; +} // namespace base + +namespace quota_internals { +class QuotaInternalsProxy; +} // namespace quota_internals + +namespace storage { + +class QuotaManagerProxy; +class QuotaOverrideHandle; +class QuotaTemporaryStorageEvictor; +class UsageTracker; + +// An interface called by QuotaTemporaryStorageEvictor. This is a grab bag of +// methods called by QuotaTemporaryStorageEvictor that need to be stubbed for +// testing. +class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaEvictionHandler { + public: + using EvictionRoundInfoCallback = + base::OnceCallback<void(blink::mojom::QuotaStatusCode status, + const QuotaSettings& settings, + int64_t available_space, + int64_t total_space, + int64_t global_usage, + bool global_usage_is_complete)>; + + // Called at the beginning of an eviction round to gather the info about + // the current settings, capacity, and usage. + virtual void GetEvictionRoundInfo(EvictionRoundInfoCallback callback) = 0; + + // Returns next origin to evict, or nullopt if there are no evictable + // origins. + virtual void GetEvictionOrigin(blink::mojom::StorageType type, + int64_t global_quota, + GetOriginCallback callback) = 0; + + // Called to evict an origin. + virtual void EvictOriginData(const url::Origin& origin, + blink::mojom::StorageType type, + StatusCallback callback) = 0; + + protected: + virtual ~QuotaEvictionHandler() = default; +}; + +struct UsageInfo { + UsageInfo(std::string host, blink::mojom::StorageType type, int64_t usage) + : host(std::move(host)), type(type), usage(usage) {} + const std::string host; + const blink::mojom::StorageType type; + const int64_t usage; +}; + +// Entry point into the Quota System +// +// Each StoragePartition has exactly one QuotaManagerImpl instance, which +// coordinates quota across the Web platform features subject to quota. +// Each storage system interacts with quota via their own implementations of +// the QuotaClient interface. +// +// The class sets limits and defines the parameters of the systems heuristics. +// QuotaManagerImpl coordinates clients to orchestrate the collection of usage +// information, enforce quota limits, and evict stale data. +// +// The constructor and proxy() methods can be called on any thread. All other +// methods must be called on the IO thread. +class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl + : public QuotaTaskObserver, + public QuotaEvictionHandler, + public base::RefCountedDeleteOnSequence<QuotaManagerImpl> { + public: + using UsageAndQuotaCallback = base::OnceCallback< + void(blink::mojom::QuotaStatusCode, int64_t usage, int64_t quota)>; + + using UsageAndQuotaWithBreakdownCallback = + base::OnceCallback<void(blink::mojom::QuotaStatusCode, + int64_t usage, + int64_t quota, + blink::mojom::UsageBreakdownPtr usage_breakdown)>; + + using UsageAndQuotaForDevtoolsCallback = + base::OnceCallback<void(blink::mojom::QuotaStatusCode, + int64_t usage, + int64_t quota, + bool is_override_enabled, + blink::mojom::UsageBreakdownPtr usage_breakdown)>; + + // Function pointer type used to store the function which returns + // information about the volume containing the given FilePath. + // The value returned is std::tuple<total_space, available_space>. + using GetVolumeInfoFn = + std::tuple<int64_t, int64_t> (*)(const base::FilePath&); + + static constexpr int64_t kGBytes = 1024 * 1024 * 1024; + static constexpr int64_t kNoLimit = INT64_MAX; + static constexpr int64_t kMBytes = 1024 * 1024; + static constexpr int kMinutesInMilliSeconds = 60 * 1000; + + QuotaManagerImpl(bool is_incognito, + const base::FilePath& profile_path, + scoped_refptr<base::SingleThreadTaskRunner> io_thread, + base::RepeatingClosure quota_change_callback, + scoped_refptr<SpecialStoragePolicy> special_storage_policy, + const GetQuotaSettingsFunc& get_settings_function); + QuotaManagerImpl(const QuotaManagerImpl&) = delete; + QuotaManagerImpl& operator=(const QuotaManagerImpl&) = delete; + + const QuotaSettings& settings() const { return settings_; } + void SetQuotaSettings(const QuotaSettings& settings); + + // Returns a proxy object that can be used on any thread. + QuotaManagerProxy* proxy() { return proxy_.get(); } + + // Called by clients or webapps. Returns usage per host. + void GetUsageInfo(GetUsageInfoCallback callback); + + // Called by Web Apps (deprecated quota API). + // This method is declared as virtual to allow test code to override it. + virtual void GetUsageAndQuotaForWebApps(const url::Origin& origin, + blink::mojom::StorageType type, + UsageAndQuotaCallback callback); + + // Called by Web Apps (navigator.storage.estimate()) + // This method is declared as virtual to allow test code to override it. + virtual void GetUsageAndQuotaWithBreakdown( + const url::Origin& origin, + blink::mojom::StorageType type, + UsageAndQuotaWithBreakdownCallback callback); + + // Called by DevTools. + virtual void GetUsageAndQuotaForDevtools( + const url::Origin& origin, + blink::mojom::StorageType type, + UsageAndQuotaForDevtoolsCallback callback); + + // Called by storage backends. + // + // For UnlimitedStorage origins, this version skips usage and quota handling + // to avoid extra query cost. Do not call this method for apps/user-facing + // code. + // + // This method is declared as virtual to allow test code to override it. + virtual void GetUsageAndQuota(const url::Origin& origin, + blink::mojom::StorageType type, + UsageAndQuotaCallback callback); + + // Called by storage backends via proxy. + // + // Quota-managed storage backends should call this method when storage is + // accessed. Used to maintain LRU ordering. + void NotifyStorageAccessed(const url::Origin& origin, + blink::mojom::StorageType type, + base::Time access_time); + + // Called by storage backends via proxy. + // + // Quota-managed storage backends must call this method when they have made + // any modifications that change the amount of data stored in their storage. + void NotifyStorageModified(QuotaClientType client_id, + const url::Origin& origin, + blink::mojom::StorageType type, + int64_t delta, + base::Time modification_time, + base::OnceClosure callback); + + // Called by storage backends via proxy. + // + // Client storage must call this method whenever they run into disk + // write errors. Used as a hint to determine if the storage partition is out + // of space, and trigger actions if deemed appropriate. + // + // This method is declared as virtual to allow test code to override it. + virtual void NotifyWriteFailed(const url::Origin& origin); + + // Used to avoid evicting origins with open pages. + // A call to NotifyOriginInUse must be balanced by a later call + // to NotifyOriginNoLongerInUse. + void NotifyOriginInUse(const url::Origin& origin); + void NotifyOriginNoLongerInUse(const url::Origin& origin); + bool IsOriginInUse(const url::Origin& origin) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return base::Contains(origins_in_use_, origin); + } + + void SetUsageCacheEnabled(QuotaClientType client_id, + const url::Origin& origin, + blink::mojom::StorageType type, + bool enabled); + + // DeleteOriginData and DeleteHostData (surprisingly enough) delete data of a + // particular blink::mojom::StorageType associated with either a specific + // origin or set of origins. Each method additionally requires a + // |quota_client_types| which specifies the types of QuotaClients to delete + // from the origin. Pass in QuotaClientType::AllClients() to remove all + // clients from the origin, regardless of type. + virtual void DeleteOriginData(const url::Origin& origin, + blink::mojom::StorageType type, + QuotaClientTypes quota_client_types, + StatusCallback callback); + void DeleteHostData(const std::string& host, + blink::mojom::StorageType type, + QuotaClientTypes quota_client_types, + StatusCallback callback); + + // Instructs each QuotaClient to remove possible traces of deleted + // data on the disk. + void PerformStorageCleanup(blink::mojom::StorageType type, + QuotaClientTypes quota_client_types, + base::OnceClosure callback); + + // Called by UI and internal modules. + void GetPersistentHostQuota(const std::string& host, QuotaCallback callback); + void SetPersistentHostQuota(const std::string& host, + int64_t new_quota, + QuotaCallback callback); + void GetGlobalUsage(blink::mojom::StorageType type, + GlobalUsageCallback callback); + void GetHostUsageWithBreakdown(const std::string& host, + blink::mojom::StorageType type, + UsageWithBreakdownCallback callback); + + std::map<std::string, std::string> GetStatistics(); + + bool IsStorageUnlimited(const url::Origin& origin, + blink::mojom::StorageType type) const; + + virtual void GetOriginsModifiedBetween(blink::mojom::StorageType type, + base::Time begin, + base::Time end, + GetOriginsCallback callback); + + bool ResetUsageTracker(blink::mojom::StorageType type); + + // Called when StoragePartition is initialized if embedder has an + // implementation of StorageNotificationService. + void SetStoragePressureCallback( + base::RepeatingCallback<void(url::Origin)> storage_pressure_callback); + + // DevTools Quota Override methods: + int GetOverrideHandleId(); + void OverrideQuotaForOrigin(int handle_id, + const url::Origin& origin, + base::Optional<int64_t> quota_size); + // Called when a DevTools client releases all overrides, however, overrides + // will not be disabled for any origins for which there are other DevTools + // clients/QuotaOverrideHandle with an active override. + void WithdrawOverridesForHandle(int handle_id); + + // Cap size for per-host persistent quota determined by the histogram. + // Cap size for per-host persistent quota determined by the histogram. + // This is a bit lax value because the histogram says nothing about per-host + // persistent storage usage and we determined by global persistent storage + // usage that is less than 10GB for almost all users. + static constexpr int64_t kPerHostPersistentQuotaLimit = 10 * 1024 * kMBytes; + + static constexpr int kEvictionIntervalInMilliSeconds = + 30 * kMinutesInMilliSeconds; + static constexpr int kThresholdOfErrorsToBeDenylisted = 3; + static constexpr int kThresholdRandomizationPercent = 5; + + static constexpr char kDatabaseName[] = "QuotaManager"; + static constexpr char kDaysBetweenRepeatedOriginEvictionsHistogram[] = + "Quota.DaysBetweenRepeatedOriginEvictions"; + static constexpr char kEvictedOriginAccessedCountHistogram[] = + "Quota.EvictedOriginAccessCount"; + static constexpr char kEvictedOriginDaysSinceAccessHistogram[] = + "Quota.EvictedOriginDaysSinceAccess"; + + // Kept non-const so that test code can change the value. + // TODO(kinuko): Make this a real const value and add a proper way to set + // the quota for syncable storage. (http://crbug.com/155488) + static int64_t kSyncableStorageDefaultHostQuota; + + void DisableDatabaseForTesting() { db_disabled_ = true; } + + void SetGetVolumeInfoFnForTesting(GetVolumeInfoFn fn) { + get_volume_info_fn_ = fn; + } + + protected: + ~QuotaManagerImpl() override; + void SetQuotaChangeCallbackForTesting( + base::RepeatingClosure storage_pressure_event_callback); + + private: + friend class base::DeleteHelper<QuotaManagerImpl>; + friend class base::RefCountedDeleteOnSequence<QuotaManagerImpl>; + friend class quota_internals::QuotaInternalsProxy; + friend class MockQuotaManager; + friend class MockQuotaClient; + friend class QuotaManagerProxy; + friend class QuotaManagerImplTest; + friend class QuotaTemporaryStorageEvictor; + + class EvictionRoundInfoHelper; + class UsageAndQuotaInfoGatherer; + class GetUsageInfoTask; + class OriginDataDeleter; + class HostDataDeleter; + class GetModifiedSinceHelper; + class DumpQuotaTableHelper; + class DumpOriginInfoTableHelper; + class StorageCleanupHelper; + + struct QuotaOverride { + QuotaOverride(); + ~QuotaOverride(); + + QuotaOverride(const QuotaOverride& quota_override) = delete; + QuotaOverride& operator=(const QuotaOverride&) = delete; + + int64_t quota_size; + + // Keeps track of the DevTools clients that have an active override. + std::set<int> active_override_session_ids; + }; + + using QuotaTableEntry = QuotaDatabase::QuotaTableEntry; + using OriginInfoTableEntry = QuotaDatabase::OriginInfoTableEntry; + using QuotaTableEntries = std::vector<QuotaTableEntry>; + using OriginInfoTableEntries = std::vector<OriginInfoTableEntry>; + + using QuotaSettingsCallback = base::OnceCallback<void(const QuotaSettings&)>; + + using DumpQuotaTableCallback = + base::OnceCallback<void(const QuotaTableEntries&)>; + using DumpOriginInfoTableCallback = + base::OnceCallback<void(const OriginInfoTableEntries&)>; + + // The values returned total_space, available_space. + using StorageCapacityCallback = base::OnceCallback<void(int64_t, int64_t)>; + + struct EvictionContext { + EvictionContext(); + ~EvictionContext(); + url::Origin evicted_origin; + blink::mojom::StorageType evicted_type; + StatusCallback evict_origin_data_callback; + }; + + // Lazily called on the IO thread when the first quota manager API is called. + // + // Initialize() must be called after all quota clients are added to the + // manager by RegisterClient(). + void LazyInitialize(); + void FinishLazyInitialize(bool is_database_bootstraped); + void BootstrapDatabaseForEviction(GetOriginCallback did_get_origin_callback, + int64_t unused_usage, + int64_t unused_unlimited_usage); + void DidBootstrapDatabase(GetOriginCallback did_get_origin_callback, + bool success); + + // Called by clients via proxy. + // Registers a quota client to the manager. + void RegisterClient( + mojo::PendingRemote<mojom::QuotaClient> client, + QuotaClientType client_type, + const std::vector<blink::mojom::StorageType>& storage_types); + + // Legacy overload for QuotaClients that have not been mojofied yet. + // + // TODO(crbug.com/1163009): Remove this overload after all QuotaClients have + // been mojofied. + void RegisterLegacyClient( + scoped_refptr<QuotaClient> client, + QuotaClientType client_type, + const std::vector<blink::mojom::StorageType>& storage_types); + + UsageTracker* GetUsageTracker(blink::mojom::StorageType type) const; + + // Extract cached origins list from the usage tracker. + // (Might return empty list if no origin is tracked by the tracker.) + std::set<url::Origin> GetCachedOrigins(blink::mojom::StorageType type); + + void DumpQuotaTable(DumpQuotaTableCallback callback); + void DumpOriginInfoTable(DumpOriginInfoTableCallback callback); + + void DeleteOriginDataInternal(const url::Origin& origin, + blink::mojom::StorageType type, + QuotaClientTypes quota_client_types, + bool is_eviction, + StatusCallback callback); + + // Methods for eviction logic. + void StartEviction(); + void DeleteOriginFromDatabase(const url::Origin& origin, + blink::mojom::StorageType type, + bool is_eviction); + + void DidOriginDataEvicted(blink::mojom::QuotaStatusCode status); + + void ReportHistogram(); + void DidGetTemporaryGlobalUsageForHistogram(int64_t usage, + int64_t unlimited_usage); + void DidGetStorageCapacityForHistogram(int64_t usage, + int64_t total_space, + int64_t available_space); + void DidGetPersistentGlobalUsageForHistogram(int64_t usage, + int64_t unlimited_usage); + void DidDumpOriginInfoTableForHistogram( + const OriginInfoTableEntries& entries); + + std::set<url::Origin> GetEvictionOriginExceptions(); + void DidGetEvictionOrigin(GetOriginCallback callback, + const base::Optional<url::Origin>& origin); + + // QuotaEvictionHandler. + void GetEvictionOrigin(blink::mojom::StorageType type, + int64_t global_quota, + GetOriginCallback callback) override; + void EvictOriginData(const url::Origin& origin, + blink::mojom::StorageType type, + StatusCallback callback) override; + void GetEvictionRoundInfo(EvictionRoundInfoCallback callback) override; + + void GetLRUOrigin(blink::mojom::StorageType type, GetOriginCallback callback); + + void DidGetPersistentHostQuota(const std::string& host, + const int64_t* quota, + bool success); + void DidSetPersistentHostQuota(const std::string& host, + QuotaCallback callback, + const int64_t* new_quota, + bool success); + void DidGetLRUOrigin(std::unique_ptr<base::Optional<url::Origin>> origin, + bool success); + void GetQuotaSettings(QuotaSettingsCallback callback); + void DidGetSettings(base::Optional<QuotaSettings> settings); + void GetStorageCapacity(StorageCapacityCallback callback); + void ContinueIncognitoGetStorageCapacity(const QuotaSettings& settings); + void DidGetStorageCapacity( + const std::tuple<int64_t, int64_t>& total_and_available); + + void DidDatabaseWork(bool success); + + void DeleteOnCorrectThread() const; + + void MaybeRunStoragePressureCallback(const url::Origin& origin, + int64_t total_space, + int64_t available_space); + // Used from quota-internals page to test behavior of the storage pressure + // callback. + void SimulateStoragePressure(const url::Origin origin); + + // Evaluates disk statistics to identify storage pressure + // (low disk space availability) and starts the storage + // pressure event dispatch if appropriate. + // TODO(crbug.com/1088004): Implement UsageAndQuotaInfoGatherer::Completed() + // to use DetermineStoragePressure(). + // TODO(crbug.com/1102433): Define and explain StoragePressure in the README. + void DetermineStoragePressure(int64_t free_space, int64_t total_space); + + base::Optional<int64_t> GetQuotaOverrideForOrigin(const url::Origin&); + + void PostTaskAndReplyWithResultForDBThread( + const base::Location& from_here, + base::OnceCallback<bool(QuotaDatabase*)> task, + base::OnceCallback<void(bool)> reply); + + static std::tuple<int64_t, int64_t> CallGetVolumeInfo( + GetVolumeInfoFn get_volume_info_fn, + const base::FilePath& path); + static std::tuple<int64_t, int64_t> GetVolumeInfo(const base::FilePath& path); + + const bool is_incognito_; + const base::FilePath profile_path_; + + // This member is thread-safe. The scoped_refptr is immutable (the object it + // points to never changes), and the underlying object is thread-safe. + const scoped_refptr<QuotaManagerProxy> proxy_; + + bool db_disabled_; + bool eviction_disabled_; + base::Optional<url::Origin> origin_for_pending_storage_pressure_callback_; + scoped_refptr<base::SingleThreadTaskRunner> io_thread_; + scoped_refptr<base::SequencedTaskRunner> db_runner_; + mutable std::unique_ptr<QuotaDatabase> database_; + bool is_database_bootstrapped_ = false; + + GetQuotaSettingsFunc get_settings_function_; + scoped_refptr<base::TaskRunner> get_settings_task_runner_; + base::RepeatingCallback<void(url::Origin)> storage_pressure_callback_; + base::RepeatingClosure quota_change_callback_; + QuotaSettings settings_; + base::TimeTicks settings_timestamp_; + std::tuple<base::TimeTicks, int64_t, int64_t> + cached_disk_stats_for_storage_pressure_; + CallbackQueue<QuotaSettingsCallback, const QuotaSettings&> + settings_callbacks_; + CallbackQueue<StorageCapacityCallback, int64_t, int64_t> + storage_capacity_callbacks_; + + GetOriginCallback lru_origin_callback_; + std::set<url::Origin> access_notified_origins_; + + std::map<url::Origin, QuotaOverride> devtools_overrides_; + int next_override_handle_id_ = 0; + + // Owns the QuotaClient remotes registered via RegisterClient(). + // + // Iterating over this list is almost always incorrect. Most algorithms should + // iterate over an entry in |client_types_|. + // + // TODO(crbug.com/1016065): Handle Storage Service crashes. Will likely entail + // using a mojo::RemoteSet here. + std::vector<mojo::Remote<mojom::QuotaClient>> clients_for_ownership_; + + // Owns the QuotaClient instances registered by RegisterLegacyClient() and + // their wrappers. + // + // TODO(crbug.com/1163009): Remove this member after all QuotaClients have + // been mojofied. + std::vector<scoped_refptr<QuotaClient>> legacy_clients_for_ownership_; + + // Maps QuotaClient instances to client types. + // + // The QuotaClient instances pointed to by the map keys are guaranteed to be + // alive, because they are owned by `legacy_clients_for_ownership_`. + // + // TODO(crbug.com/1163009): Replace the map key with mojom::QuotaClient* after + // all QuotaClients have been mojofied. + base::flat_map<blink::mojom::StorageType, + base::flat_map<QuotaClient*, QuotaClientType>> + client_types_; + + std::unique_ptr<UsageTracker> temporary_usage_tracker_; + std::unique_ptr<UsageTracker> persistent_usage_tracker_; + std::unique_ptr<UsageTracker> syncable_usage_tracker_; + // TODO(michaeln): Need a way to clear the cache, drop and + // reinstantiate the trackers when they're not handling requests. + + std::unique_ptr<QuotaTemporaryStorageEvictor> temporary_storage_evictor_; + EvictionContext eviction_context_; + bool is_getting_eviction_origin_; + + CallbackQueueMap<QuotaCallback, + std::string, + blink::mojom::QuotaStatusCode, + int64_t> + persistent_host_quota_callbacks_; + + // Map from origin to count. + std::map<url::Origin, int> origins_in_use_; + // Map from origin to error count. + std::map<url::Origin, int> origins_in_error_; + + scoped_refptr<SpecialStoragePolicy> special_storage_policy_; + + base::RepeatingTimer histogram_timer_; + + // Pointer to the function used to get volume information. This is + // overwritten by QuotaManagerImplTest in order to attain deterministic + // reported values. The default value points to + // QuotaManagerImpl::GetVolumeInfo. + GetVolumeInfoFn get_volume_info_fn_; + + SEQUENCE_CHECKER(sequence_checker_); + + base::WeakPtrFactory<QuotaManagerImpl> weak_factory_{this}; +}; + +} // namespace storage + +#endif // STORAGE_BROWSER_QUOTA_QUOTA_MANAGER_IMPL_H_ diff --git a/chromium/storage/browser/quota/quota_manager_proxy.cc b/chromium/storage/browser/quota/quota_manager_proxy.cc index df7ec05bc43..de6b66c63c8 100644 --- a/chromium/storage/browser/quota/quota_manager_proxy.cc +++ b/chromium/storage/browser/quota/quota_manager_proxy.cc @@ -5,161 +5,261 @@ #include "storage/browser/quota/quota_manager_proxy.h" #include <stdint.h> + +#include <memory> #include <utility> +#include <vector> #include "base/bind.h" -#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/memory/scoped_refptr.h" +#include "base/sequence_checker.h" #include "base/sequenced_task_runner.h" -#include "base/single_thread_task_runner.h" -#include "base/strings/string_number_conversions.h" -#include "base/task/post_task.h" -#include "base/task_runner_util.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "components/services/storage/public/mojom/quota_client.mojom.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "storage/browser/quota/quota_client.h" #include "storage/browser/quota/quota_client_type.h" -#include "storage/browser/quota/quota_manager.h" +#include "storage/browser/quota/quota_manager_impl.h" +#include "storage/browser/quota/quota_override_handle.h" #include "third_party/blink/public/mojom/quota/quota_types.mojom.h" +#include "url/origin.h" namespace storage { -namespace { +QuotaManagerProxy::QuotaManagerProxy( + QuotaManagerImpl* quota_manager_impl, + scoped_refptr<base::SequencedTaskRunner> quota_manager_impl_task_runner) + : quota_manager_impl_(quota_manager_impl), + quota_manager_impl_task_runner_( + std::move(quota_manager_impl_task_runner)) { + DCHECK(quota_manager_impl_task_runner_.get()); + + DETACH_FROM_SEQUENCE(quota_manager_impl_sequence_checker_); +} -void DidGetUsageAndQuota(base::SequencedTaskRunner* original_task_runner, - QuotaManagerProxy::UsageAndQuotaCallback callback, - blink::mojom::QuotaStatusCode status, - int64_t usage, - int64_t quota) { - if (!original_task_runner->RunsTasksInCurrentSequence()) { - original_task_runner->PostTask( - FROM_HERE, base::BindOnce(&DidGetUsageAndQuota, - base::RetainedRef(original_task_runner), - std::move(callback), status, usage, quota)); +void QuotaManagerProxy::RegisterLegacyClient( + scoped_refptr<QuotaClient> client, + QuotaClientType client_type, + const std::vector<blink::mojom::StorageType>& storage_types) { + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&QuotaManagerProxy::RegisterLegacyClient, this, + std::move(client), client_type, storage_types)); return; } - std::move(callback).Run(status, usage, quota); -} -} // namespace + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (!quota_manager_impl_) { + client->OnQuotaManagerDestroyed(); + return; + } + + quota_manager_impl_->RegisterLegacyClient(std::move(client), client_type, + storage_types); +} void QuotaManagerProxy::RegisterClient( - scoped_refptr<QuotaClient> client, + mojo::PendingRemote<mojom::QuotaClient> client, QuotaClientType client_type, const std::vector<blink::mojom::StorageType>& storage_types) { - if (!io_thread_->BelongsToCurrentThread()) { - io_thread_->PostTask( + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( FROM_HERE, base::BindOnce(&QuotaManagerProxy::RegisterClient, this, std::move(client), client_type, storage_types)); return; } - if (manager_) { - manager_->RegisterClient(std::move(client), client_type, storage_types); - } else { - client->OnQuotaManagerDestroyed(); + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (quota_manager_impl_) { + quota_manager_impl_->RegisterClient(std::move(client), client_type, + storage_types); } } void QuotaManagerProxy::NotifyStorageAccessed(const url::Origin& origin, - blink::mojom::StorageType type) { - if (!io_thread_->BelongsToCurrentThread()) { - io_thread_->PostTask( + blink::mojom::StorageType type, + base::Time access_time) { + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( FROM_HERE, base::BindOnce(&QuotaManagerProxy::NotifyStorageAccessed, - this, origin, type)); + this, origin, type, access_time)); return; } - if (manager_) - manager_->NotifyStorageAccessed(origin, type); + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (quota_manager_impl_) + quota_manager_impl_->NotifyStorageAccessed(origin, type, access_time); } -void QuotaManagerProxy::NotifyStorageModified(QuotaClientType client_id, - const url::Origin& origin, - blink::mojom::StorageType type, - int64_t delta) { - if (!io_thread_->BelongsToCurrentThread()) { - io_thread_->PostTask( - FROM_HERE, base::BindOnce(&QuotaManagerProxy::NotifyStorageModified, - this, client_id, origin, type, delta)); +void QuotaManagerProxy::NotifyStorageModified( + QuotaClientType client_id, + const url::Origin& origin, + blink::mojom::StorageType type, + int64_t delta, + base::Time modification_time, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceClosure callback) { + DCHECK(!callback || callback_task_runner); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&QuotaManagerProxy::NotifyStorageModified, this, + client_id, origin, type, delta, modification_time, + std::move(callback_task_runner), std::move(callback))); return; } - if (manager_) - manager_->NotifyStorageModified(client_id, origin, type, delta); + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (quota_manager_impl_) { + base::OnceClosure manager_callback; + if (callback) { + manager_callback = base::BindOnce( + [](scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceClosure callback) { + if (callback_task_runner->RunsTasksInCurrentSequence()) { + std::move(callback).Run(); + return; + } + callback_task_runner->PostTask(FROM_HERE, std::move(callback)); + }, + std::move(callback_task_runner), std::move(callback)); + } + quota_manager_impl_->NotifyStorageModified(client_id, origin, type, delta, + modification_time, + std::move(manager_callback)); + } } void QuotaManagerProxy::NotifyOriginInUse(const url::Origin& origin) { - if (!io_thread_->BelongsToCurrentThread()) { - io_thread_->PostTask( + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( FROM_HERE, base::BindOnce(&QuotaManagerProxy::NotifyOriginInUse, this, origin)); return; } - if (manager_) - manager_->NotifyOriginInUse(origin); + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (quota_manager_impl_) + quota_manager_impl_->NotifyOriginInUse(origin); } void QuotaManagerProxy::NotifyOriginNoLongerInUse(const url::Origin& origin) { - if (!io_thread_->BelongsToCurrentThread()) { - io_thread_->PostTask( + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( FROM_HERE, base::BindOnce(&QuotaManagerProxy::NotifyOriginNoLongerInUse, this, origin)); return; } - if (manager_) - manager_->NotifyOriginNoLongerInUse(origin); + + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (quota_manager_impl_) + quota_manager_impl_->NotifyOriginNoLongerInUse(origin); } void QuotaManagerProxy::NotifyWriteFailed(const url::Origin& origin) { - if (!io_thread_->BelongsToCurrentThread()) { - io_thread_->PostTask( + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( FROM_HERE, base::BindOnce(&QuotaManagerProxy::NotifyWriteFailed, this, origin)); return; } - if (manager_) - manager_->NotifyWriteFailed(origin); + + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (quota_manager_impl_) + quota_manager_impl_->NotifyWriteFailed(origin); } void QuotaManagerProxy::SetUsageCacheEnabled(QuotaClientType client_id, const url::Origin& origin, blink::mojom::StorageType type, bool enabled) { - if (!io_thread_->BelongsToCurrentThread()) { - io_thread_->PostTask( + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( FROM_HERE, base::BindOnce(&QuotaManagerProxy::SetUsageCacheEnabled, this, client_id, origin, type, enabled)); return; } - if (manager_) - manager_->SetUsageCacheEnabled(client_id, origin, type, enabled); + + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (quota_manager_impl_) + quota_manager_impl_->SetUsageCacheEnabled(client_id, origin, type, enabled); } +namespace { + +void DidGetUsageAndQuota( + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + QuotaManagerProxy::UsageAndQuotaCallback callback, + blink::mojom::QuotaStatusCode status, + int64_t usage, + int64_t quota) { + if (callback_task_runner->RunsTasksInCurrentSequence()) { + std::move(callback).Run(status, usage, quota); + return; + } + callback_task_runner->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), status, usage, quota)); +} + +} // namespace + void QuotaManagerProxy::GetUsageAndQuota( - base::SequencedTaskRunner* original_task_runner, const url::Origin& origin, blink::mojom::StorageType type, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, UsageAndQuotaCallback callback) { - if (!io_thread_->BelongsToCurrentThread()) { - io_thread_->PostTask( - FROM_HERE, base::BindOnce(&QuotaManagerProxy::GetUsageAndQuota, this, - base::RetainedRef(original_task_runner), - origin, type, std::move(callback))); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&QuotaManagerProxy::GetUsageAndQuota, this, origin, type, + std::move(callback_task_runner), std::move(callback))); return; } - if (!manager_) { - DidGetUsageAndQuota(original_task_runner, std::move(callback), + + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (!quota_manager_impl_) { + DidGetUsageAndQuota(std::move(callback_task_runner), std::move(callback), blink::mojom::QuotaStatusCode::kErrorAbort, 0, 0); return; } - manager_->GetUsageAndQuota( + quota_manager_impl_->GetUsageAndQuota( origin, type, - base::BindOnce(&DidGetUsageAndQuota, - base::RetainedRef(original_task_runner), + base::BindOnce(&DidGetUsageAndQuota, std::move(callback_task_runner), std::move(callback))); } +void QuotaManagerProxy::IsStorageUnlimited( + const url::Origin& origin, + blink::mojom::StorageType type, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceCallback<void(bool)> callback) { + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&QuotaManagerProxy::IsStorageUnlimited, this, + origin, type, std::move(callback_task_runner), + std::move(callback))); + return; + } + + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + bool is_storage_unlimited = + quota_manager_impl_ + ? quota_manager_impl_->IsStorageUnlimited(origin, type) + : false; + + if (callback_task_runner->RunsTasksInCurrentSequence()) { + std::move(callback).Run(is_storage_unlimited); + return; + } + callback_task_runner->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), is_storage_unlimited)); +} + std::unique_ptr<QuotaOverrideHandle> QuotaManagerProxy::GetQuotaOverrideHandle() { return std::make_unique<QuotaOverrideHandle>(this); @@ -169,40 +269,73 @@ void QuotaManagerProxy::OverrideQuotaForOrigin( int handle_id, url::Origin origin, base::Optional<int64_t> quota_size, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceClosure callback) { - io_thread_->PostTaskAndReply( - FROM_HERE, - base::BindOnce(&QuotaManager::OverrideQuotaForOrigin, - base::RetainedRef(manager_), handle_id, origin, - quota_size), - std::move(callback)); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&QuotaManagerProxy::OverrideQuotaForOrigin, this, + handle_id, origin, quota_size, + std::move(callback_task_runner), std::move(callback))); + return; + } + + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (quota_manager_impl_) + quota_manager_impl_->OverrideQuotaForOrigin(handle_id, origin, quota_size); + + if (callback_task_runner->RunsTasksInCurrentSequence()) { + std::move(callback).Run(); + return; + } + callback_task_runner->PostTask(FROM_HERE, std::move(callback)); } void QuotaManagerProxy::WithdrawOverridesForHandle(int handle_id) { - io_thread_->PostTask(FROM_HERE, - base::BindOnce(&QuotaManager::WithdrawOverridesForHandle, - base::RetainedRef(manager_), handle_id)); -} + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&QuotaManagerProxy::WithdrawOverridesForHandle, this, + handle_id)); + return; + } -QuotaManager* QuotaManagerProxy::quota_manager() const { - DCHECK(!io_thread_.get() || io_thread_->BelongsToCurrentThread()); - return manager_; + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (quota_manager_impl_) + quota_manager_impl_->WithdrawOverridesForHandle(handle_id); } -QuotaManagerProxy::QuotaManagerProxy( - QuotaManager* manager, - scoped_refptr<base::SingleThreadTaskRunner> io_thread) - : manager_(manager), io_thread_(std::move(io_thread)) {} +void QuotaManagerProxy::GetOverrideHandleId( + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceCallback<void(int)> callback) { + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&QuotaManagerProxy::GetOverrideHandleId, this, + std::move(callback_task_runner), std::move(callback))); + return; + } + + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + int handle_id = + quota_manager_impl_ ? quota_manager_impl_->GetOverrideHandleId() : 0; + + if (callback_task_runner->RunsTasksInCurrentSequence()) { + std::move(callback).Run(handle_id); + return; + } + callback_task_runner->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), handle_id)); +} QuotaManagerProxy::~QuotaManagerProxy() = default; -void QuotaManagerProxy::GetOverrideHandleId( - base::OnceCallback<void(int)> callback) { - io_thread_->PostTaskAndReplyWithResult( - FROM_HERE, - base::BindOnce(&QuotaManager::GetOverrideHandleId, - base::RetainedRef(manager_)), - std::move(callback)); +void QuotaManagerProxy::InvalidateQuotaManagerImpl( + base::PassKey<QuotaManagerImpl>) { + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + + DCHECK(quota_manager_impl_) << __func__ << " called multiple times"; + quota_manager_impl_ = nullptr; } } // namespace storage diff --git a/chromium/storage/browser/quota/quota_manager_proxy.h b/chromium/storage/browser/quota/quota_manager_proxy.h index 4655f7da5cb..ac64cf6265b 100644 --- a/chromium/storage/browser/quota/quota_manager_proxy.h +++ b/chromium/storage/browser/quota/quota_manager_proxy.h @@ -12,45 +12,88 @@ #include "base/callback.h" #include "base/component_export.h" -#include "base/files/file_path.h" -#include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" #include "base/sequenced_task_runner_helpers.h" +#include "base/thread_annotations.h" +#include "base/time/time.h" +#include "base/types/pass_key.h" +#include "components/services/storage/public/mojom/quota_client.mojom-forward.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "storage/browser/quota/quota_callbacks.h" -#include "storage/browser/quota/quota_client.h" #include "storage/browser/quota/quota_client_type.h" -#include "storage/browser/quota/quota_database.h" -#include "storage/browser/quota/quota_manager.h" -#include "storage/browser/quota/quota_override_handle.h" -#include "storage/browser/quota/quota_task.h" -#include "storage/browser/quota/special_storage_policy.h" +#include "storage/browser/quota/quota_manager_impl.h" #include "third_party/blink/public/mojom/quota/quota_types.mojom.h" -#include "url/origin.h" namespace base { + class SequencedTaskRunner; -class SingleThreadTaskRunner; -} + +} // namespace base + +namespace url { + +class Origin; + +} // namespace url namespace storage { -// The proxy may be called and finally released on any thread. +class QuotaClient; +class QuotaOverrideHandle; + +// Thread-safe proxy for QuotaManagerImpl. +// +// Most methods can be called from any thread. The few exceptions are marked +// accordingly in the associated comments. class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerProxy : public base::RefCountedThreadSafe<QuotaManagerProxy> { public: - using UsageAndQuotaCallback = QuotaManager::UsageAndQuotaCallback; + using UsageAndQuotaCallback = QuotaManagerImpl::UsageAndQuotaCallback; + + // The caller is responsible for calling InvalidateQuotaManagerImpl() before + // `quota_manager_impl` is destroyed. `quota_manager_impl_task_runner` must be + // associated with the sequence that `quota_manager_impl` may be used on. + // + // See the comment on `quota_manager_impl_` for an explanation why + // `quota_manager_impl` isn't a base::WeakPtr<QuotaManagerImpl>. + QuotaManagerProxy( + QuotaManagerImpl* quota_manager_impl, + scoped_refptr<base::SequencedTaskRunner> quota_manager_impl_task_runner); + + QuotaManagerProxy(const QuotaManagerProxy&) = delete; + QuotaManagerProxy& operator=(const QuotaManagerProxy&) = delete; + + // TODO(crbug.com/1163009): Remove this method after all QuotaClients have + // been mojofied. + virtual void RegisterLegacyClient( + scoped_refptr<QuotaClient> client, + QuotaClientType client_type, + const std::vector<blink::mojom::StorageType>& storage_types); virtual void RegisterClient( - scoped_refptr<QuotaClient> client, + mojo::PendingRemote<mojom::QuotaClient> client, QuotaClientType client_type, const std::vector<blink::mojom::StorageType>& storage_types); virtual void NotifyStorageAccessed(const url::Origin& origin, - blink::mojom::StorageType type); - virtual void NotifyStorageModified(QuotaClientType client_id, - const url::Origin& origin, blink::mojom::StorageType type, - int64_t delta); + base::Time access_time); + + // Notify the quota manager that storage has been modified for the given + // client. A |callback| may be optionally provided to be invoked on the + // given task runner when the quota system's state in memory has been + // updated. If a |callback| is provided then |callback_task_runner| must + // also be provided. If the quota manager runs on |callback_task_runner|, + // then the |callback| may be invoked synchronously. + virtual void NotifyStorageModified( + QuotaClientType client_id, + const url::Origin& origin, + blink::mojom::StorageType type, + int64_t delta, + base::Time modification_time, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner = nullptr, + base::OnceClosure callback = base::OnceClosure()); + virtual void NotifyOriginInUse(const url::Origin& origin); virtual void NotifyOriginNoLongerInUse(const url::Origin& origin); virtual void NotifyWriteFailed(const url::Origin& origin); @@ -59,39 +102,77 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerProxy const url::Origin& origin, blink::mojom::StorageType type, bool enabled); - virtual void GetUsageAndQuota(base::SequencedTaskRunner* original_task_runner, - const url::Origin& origin, - blink::mojom::StorageType type, - UsageAndQuotaCallback callback); + virtual void GetUsageAndQuota( + const url::Origin& origin, + blink::mojom::StorageType type, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + UsageAndQuotaCallback callback); + + virtual void IsStorageUnlimited( + const url::Origin& origin, + blink::mojom::StorageType type, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceCallback<void(bool)> callback); // DevTools Quota Override methods: std::unique_ptr<QuotaOverrideHandle> GetQuotaOverrideHandle(); // Called by QuotaOverrideHandle upon construction to asynchronously // fetch an id. - void GetOverrideHandleId(base::OnceCallback<void(int)>); - void OverrideQuotaForOrigin(int handle_id, - url::Origin origin, - base::Optional<int64_t> quota_size, - base::OnceClosure callback); + void GetOverrideHandleId( + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceCallback<void(int)> callback); + void OverrideQuotaForOrigin( + int handle_id, + url::Origin origin, + base::Optional<int64_t> quota_size, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceClosure callback); void WithdrawOverridesForHandle(int handle_id); - // This method may only be called on the IO thread. - // It may return nullptr if the manager has already been deleted. - QuotaManager* quota_manager() const; + // Called right before the QuotaManagerImpl is destroyed. + // This method may only be called on the QuotaManagerImpl sequence. + void InvalidateQuotaManagerImpl(base::PassKey<QuotaManagerImpl>); protected: - friend class QuotaManager; friend class base::RefCountedThreadSafe<QuotaManagerProxy>; + friend class base::DeleteHelper<QuotaManagerProxy>; - QuotaManagerProxy(QuotaManager* manager, - scoped_refptr<base::SingleThreadTaskRunner> io_thread); virtual ~QuotaManagerProxy(); private: - QuotaManager* manager_; // only accessed on the io thread - scoped_refptr<base::SingleThreadTaskRunner> io_thread_; - - DISALLOW_COPY_AND_ASSIGN(QuotaManagerProxy); + // Bound to QuotaManagerImpl's sequence. + // + // At the time this code is written, there is no way to explicitly bind a + // SequenceChecker to a sequence. The implementation ensures that the + // SequenceChecker binds to the right sequence by DCHECKing that + // `quota_manager_impl_task_runner_` runs on the current sequence before + // calling DCHECK_CALLED_ON_VALID_SEQUENCE(). New methods added to + // QuotaManagerProxy must follow the same pattern. + SEQUENCE_CHECKER(quota_manager_impl_sequence_checker_); + + // Conceptually, this member is a base::WeakPtr<QuotaManagerImpl>. Like a + // WeakPtr, it becomes null after the target QuotaManagerImpl is destroyed. + // + // base::WeakPtr cannot be used here for two reasons: + // 1) QuotaManagerProxy can be deleted on any thread, whereas WeakPtrs must be + // invalidated on the same sequence where they are accessed. In this case, + // the WeakPtr would need to be accessed on the QuotaManagerImpl sequence, + // and then invalidated wherever the destructor happens to run. + // 2) QuotaManagerProxy instances must be created during the QuotaManagerImpl + // constructor, before the QuotaManagerImpl's WeakPtrFactory is + // constructed. This is because the easiest way to ensure that + // QuotaManagerImpl exposes its QuotaManagerProxy in a thread-safe manner + // is to have the QuotaManagerImpl's QuotaManagerProxy reference be const. + QuotaManagerImpl* quota_manager_impl_ + GUARDED_BY_CONTEXT(quota_manager_impl_sequence_checker_); + + // TaskRunner that accesses QuotaManagerImpl's sequence. + // + // This member is not GUARDED_BY_CONTEXT() and may be accessed from any + // thread. This is safe because the scoped_refptr is immutable (always points + // to the same object), and the object it points to is thread-safe. + const scoped_refptr<base::SequencedTaskRunner> + quota_manager_impl_task_runner_; }; } // namespace storage diff --git a/chromium/storage/browser/quota/quota_manager_unittest.cc b/chromium/storage/browser/quota/quota_manager_unittest.cc index 434521fcd47..08458755356 100644 --- a/chromium/storage/browser/quota/quota_manager_unittest.cc +++ b/chromium/storage/browser/quota/quota_manager_unittest.cc @@ -13,6 +13,8 @@ #include <vector> #include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/containers/contains.h" #include "base/containers/span.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" @@ -21,7 +23,6 @@ #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/run_loop.h" -#include "base/stl_util.h" #include "base/system/sys_info.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" @@ -29,11 +30,16 @@ #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" +#include "components/services/storage/public/mojom/quota_client.mojom.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" +#include "storage/browser/quota/mojo_quota_client_wrapper.h" #include "storage/browser/quota/quota_client_type.h" #include "storage/browser/quota/quota_database.h" #include "storage/browser/quota/quota_features.h" -#include "storage/browser/quota/quota_manager.h" +#include "storage/browser/quota/quota_manager_impl.h" #include "storage/browser/quota/quota_manager_proxy.h" +#include "storage/browser/quota/quota_override_handle.h" #include "storage/browser/test/mock_quota_client.h" #include "storage/browser/test/mock_special_storage_policy.h" #include "testing/gtest/include/gtest/gtest.h" @@ -58,6 +64,7 @@ const int64_t kAvailableSpaceForApp = 13377331U; const int64_t kMustRemainAvailableForSystem = kAvailableSpaceForApp / 2; const int64_t kDefaultPoolSize = 1000; const int64_t kDefaultPerHostQuota = 200; +const int64_t kGigabytes = QuotaManagerImpl::kMBytes * 1024; // Returns a deterministic value for the amount of available disk space. int64_t GetAvailableDiskSpaceForTest() { @@ -78,68 +85,93 @@ url::Origin ToOrigin(const std::string& url) { } // namespace -class QuotaManagerTest : public testing::Test { +class QuotaManagerImplTest : public testing::Test { protected: - using QuotaTableEntry = QuotaManager::QuotaTableEntry; - using QuotaTableEntries = QuotaManager::QuotaTableEntries; - using OriginInfoTableEntries = QuotaManager::OriginInfoTableEntries; + using QuotaTableEntry = QuotaManagerImpl::QuotaTableEntry; + using QuotaTableEntries = QuotaManagerImpl::QuotaTableEntries; + using OriginInfoTableEntries = QuotaManagerImpl::OriginInfoTableEntries; public: - QuotaManagerTest() : mock_time_counter_(0) {} + QuotaManagerImplTest() : mock_time_counter_(0) {} void SetUp() override { ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); mock_special_storage_policy_ = base::MakeRefCounted<MockSpecialStoragePolicy>(); - ResetQuotaManager(false /* is_incognito */); + ResetQuotaManagerImpl(false /* is_incognito */); } void TearDown() override { // Make sure the quota manager cleans up correctly. - quota_manager_ = nullptr; + quota_manager_impl_ = nullptr; task_environment_.RunUntilIdle(); } protected: - void ResetQuotaManager(bool is_incognito) { - quota_manager_ = base::MakeRefCounted<QuotaManager>( + void ResetQuotaManagerImpl(bool is_incognito) { + quota_manager_impl_ = base::MakeRefCounted<QuotaManagerImpl>( is_incognito, data_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get(), + /*quota_change_callback=*/base::DoNothing(), mock_special_storage_policy_.get(), GetQuotaSettingsFunc()); SetQuotaSettings(kDefaultPoolSize, kDefaultPerHostQuota, is_incognito ? INT64_C(0) : kMustRemainAvailableForSystem); // Don't (automatically) start the eviction for testing. - quota_manager_->eviction_disabled_ = true; + quota_manager_impl_->eviction_disabled_ = true; // Don't query the hard disk for remaining capacity. - quota_manager_->get_volume_info_fn_ = &GetVolumeInfoForTests; + quota_manager_impl_->get_volume_info_fn_ = &GetVolumeInfoForTests; additional_callback_count_ = 0; } - scoped_refptr<MockQuotaClient> CreateAndRegisterClient( + MockQuotaClient* CreateAndRegisterClient( base::span<const MockOriginData> mock_data, QuotaClientType client_type, const std::vector<blink::mojom::StorageType> storage_types) { - scoped_refptr<MockQuotaClient> client = - base::MakeRefCounted<MockQuotaClient>(quota_manager_->proxy(), - mock_data, client_type); - quota_manager_->proxy()->RegisterClient(client, client_type, storage_types); - return client; + auto mock_quota_client = std::make_unique<storage::MockQuotaClient>( + quota_manager_impl_->proxy(), mock_data, client_type); + MockQuotaClient* mock_quota_client_ptr = mock_quota_client.get(); + + mojo::PendingRemote<storage::mojom::QuotaClient> quota_client; + mojo::MakeSelfOwnedReceiver(std::move(mock_quota_client), + quota_client.InitWithNewPipeAndPassReceiver()); + quota_manager_impl_->proxy()->RegisterClient(std::move(quota_client), + client_type, storage_types); + return mock_quota_client_ptr; + } + + // TODO(crbug.com/1163009): Remove this method and replace all calls with + // CreateAndRegisterClient() after all QuotaClients + // have been mojofied + MockQuotaClient* CreateAndRegisterLegacyClient( + base::span<const MockOriginData> mock_data, + QuotaClientType client_type, + const std::vector<blink::mojom::StorageType> storage_types) { + auto mock_quota_client = std::make_unique<storage::MockQuotaClient>( + quota_manager_impl_->proxy(), mock_data, client_type); + MockQuotaClient* mock_quota_client_ptr = mock_quota_client.get(); + legacy_clients_.push_back(std::move(mock_quota_client)); + + scoped_refptr<QuotaClient> legacy_client = + base::MakeRefCounted<MojoQuotaClientWrapper>(mock_quota_client_ptr); + quota_manager_impl_->proxy()->RegisterLegacyClient( + std::move(legacy_client), client_type, storage_types); + return mock_quota_client_ptr; } void GetUsageInfo() { usage_info_.clear(); - quota_manager_->GetUsageInfo(base::BindOnce( - &QuotaManagerTest::DidGetUsageInfo, weak_factory_.GetWeakPtr())); + quota_manager_impl_->GetUsageInfo(base::BindOnce( + &QuotaManagerImplTest::DidGetUsageInfo, weak_factory_.GetWeakPtr())); } void GetUsageAndQuotaForWebApps(const url::Origin& origin, StorageType type) { quota_status_ = QuotaStatusCode::kUnknown; usage_ = -1; quota_ = -1; - quota_manager_->GetUsageAndQuotaForWebApps( + quota_manager_impl_->GetUsageAndQuotaForWebApps( origin, type, - base::BindOnce(&QuotaManagerTest::DidGetUsageAndQuota, + base::BindOnce(&QuotaManagerImplTest::DidGetUsageAndQuota, weak_factory_.GetWeakPtr())); } @@ -149,9 +181,9 @@ class QuotaManagerTest : public testing::Test { usage_ = -1; quota_ = -1; usage_breakdown_ = nullptr; - quota_manager_->GetUsageAndQuotaWithBreakdown( + quota_manager_impl_->GetUsageAndQuotaWithBreakdown( origin, type, - base::BindOnce(&QuotaManagerTest::DidGetUsageAndQuotaWithBreakdown, + base::BindOnce(&QuotaManagerImplTest::DidGetUsageAndQuotaWithBreakdown, weak_factory_.GetWeakPtr())); } @@ -160,9 +192,9 @@ class QuotaManagerTest : public testing::Test { quota_status_ = QuotaStatusCode::kUnknown; usage_ = -1; quota_ = -1; - quota_manager_->GetUsageAndQuota( + quota_manager_impl_->GetUsageAndQuota( origin, type, - base::BindOnce(&QuotaManagerTest::DidGetUsageAndQuota, + base::BindOnce(&QuotaManagerImplTest::DidGetUsageAndQuota, weak_factory_.GetWeakPtr())); } @@ -176,65 +208,73 @@ class QuotaManagerTest : public testing::Test { (per_host_quota > 0) ? (per_host_quota - 1) : 0; settings.must_remain_available = must_remain_available; settings.refresh_interval = base::TimeDelta::Max(); - quota_manager_->SetQuotaSettings(settings); + quota_manager_impl_->SetQuotaSettings(settings); + } + + using GetVolumeInfoFn = + std::tuple<int64_t, int64_t> (*)(const base::FilePath&); + + void SetGetVolumeInfoFn(GetVolumeInfoFn fn) { + quota_manager_impl_->SetGetVolumeInfoFnForTesting(fn); } void GetPersistentHostQuota(const std::string& host) { quota_status_ = QuotaStatusCode::kUnknown; quota_ = -1; - quota_manager_->GetPersistentHostQuota( - host, base::BindOnce(&QuotaManagerTest::DidGetHostQuota, + quota_manager_impl_->GetPersistentHostQuota( + host, base::BindOnce(&QuotaManagerImplTest::DidGetHostQuota, weak_factory_.GetWeakPtr())); } void SetPersistentHostQuota(const std::string& host, int64_t new_quota) { quota_status_ = QuotaStatusCode::kUnknown; quota_ = -1; - quota_manager_->SetPersistentHostQuota( + quota_manager_impl_->SetPersistentHostQuota( host, new_quota, - base::BindOnce(&QuotaManagerTest::DidGetHostQuota, + base::BindOnce(&QuotaManagerImplTest::DidGetHostQuota, weak_factory_.GetWeakPtr())); } void GetGlobalUsage(StorageType type) { usage_ = -1; unlimited_usage_ = -1; - quota_manager_->GetGlobalUsage( - type, base::BindOnce(&QuotaManagerTest::DidGetGlobalUsage, + quota_manager_impl_->GetGlobalUsage( + type, base::BindOnce(&QuotaManagerImplTest::DidGetGlobalUsage, weak_factory_.GetWeakPtr())); } void GetHostUsageWithBreakdown(const std::string& host, StorageType type) { usage_ = -1; - quota_manager_->GetHostUsageWithBreakdown( + quota_manager_impl_->GetHostUsageWithBreakdown( host, type, - base::BindOnce(&QuotaManagerTest::DidGetHostUsageBreakdown, + base::BindOnce(&QuotaManagerImplTest::DidGetHostUsageBreakdown, weak_factory_.GetWeakPtr())); } void RunAdditionalUsageAndQuotaTask(const url::Origin& origin, StorageType type) { - quota_manager_->GetUsageAndQuota( + quota_manager_impl_->GetUsageAndQuota( origin, type, - base::BindOnce(&QuotaManagerTest::DidGetUsageAndQuotaAdditional, + base::BindOnce(&QuotaManagerImplTest::DidGetUsageAndQuotaAdditional, weak_factory_.GetWeakPtr())); } - void DeleteClientOriginData(QuotaClient* client, + void DeleteClientOriginData(mojom::QuotaClient* client, const url::Origin& origin, StorageType type) { DCHECK(client); quota_status_ = QuotaStatusCode::kUnknown; - client->DeleteOriginData(origin, type, - base::BindOnce(&QuotaManagerTest::StatusCallback, - weak_factory_.GetWeakPtr())); + client->DeleteOriginData( + origin, type, + base::BindOnce(&QuotaManagerImplTest::StatusCallback, + weak_factory_.GetWeakPtr())); } void EvictOriginData(const url::Origin& origin, StorageType type) { quota_status_ = QuotaStatusCode::kUnknown; - quota_manager_->EvictOriginData( + quota_manager_impl_->EvictOriginData( origin, type, - base::BindOnce(&QuotaManagerTest::StatusCallback, + base::BindOnce(&QuotaManagerImplTest::StatusCallback, weak_factory_.GetWeakPtr())); } @@ -242,9 +282,9 @@ class QuotaManagerTest : public testing::Test { StorageType type, QuotaClientTypes quota_client_types) { quota_status_ = QuotaStatusCode::kUnknown; - quota_manager_->DeleteOriginData( + quota_manager_impl_->DeleteOriginData( origin, type, std::move(quota_client_types), - base::BindOnce(&QuotaManagerTest::StatusCallback, + base::BindOnce(&QuotaManagerImplTest::StatusCallback, weak_factory_.GetWeakPtr())); } @@ -252,17 +292,18 @@ class QuotaManagerTest : public testing::Test { StorageType type, QuotaClientTypes quota_client_types) { quota_status_ = QuotaStatusCode::kUnknown; - quota_manager_->DeleteHostData( + quota_manager_impl_->DeleteHostData( host, type, std::move(quota_client_types), - base::BindOnce(&QuotaManagerTest::StatusCallback, + base::BindOnce(&QuotaManagerImplTest::StatusCallback, weak_factory_.GetWeakPtr())); } void GetStorageCapacity() { available_space_ = -1; total_space_ = -1; - quota_manager_->GetStorageCapacity(base::BindOnce( - &QuotaManagerTest::DidGetStorageCapacity, weak_factory_.GetWeakPtr())); + quota_manager_impl_->GetStorageCapacity( + base::BindOnce(&QuotaManagerImplTest::DidGetStorageCapacity, + weak_factory_.GetWeakPtr())); } void GetEvictionRoundInfo() { @@ -271,40 +312,40 @@ class QuotaManagerTest : public testing::Test { available_space_ = -1; total_space_ = -1; usage_ = -1; - quota_manager_->GetEvictionRoundInfo( - base::BindOnce(&QuotaManagerTest::DidGetEvictionRoundInfo, + quota_manager_impl_->GetEvictionRoundInfo( + base::BindOnce(&QuotaManagerImplTest::DidGetEvictionRoundInfo, weak_factory_.GetWeakPtr())); } std::set<url::Origin> GetCachedOrigins(StorageType type) { - return quota_manager_->GetCachedOrigins(type); + return quota_manager_impl_->GetCachedOrigins(type); } void NotifyStorageAccessed(const url::Origin& origin, StorageType type) { - quota_manager_->NotifyStorageAccessedInternal(origin, type, - IncrementMockTime()); + quota_manager_impl_->NotifyStorageAccessed(origin, type, + IncrementMockTime()); } void DeleteOriginFromDatabase(const url::Origin& origin, StorageType type) { - quota_manager_->DeleteOriginFromDatabase(origin, type, false); + quota_manager_impl_->DeleteOriginFromDatabase(origin, type, false); } void GetEvictionOrigin(StorageType type) { eviction_origin_.reset(); // The quota manager's default eviction policy is to use an LRU eviction // policy. - quota_manager_->GetEvictionOrigin( + quota_manager_impl_->GetEvictionOrigin( type, 0, - base::BindOnce(&QuotaManagerTest::DidGetEvictionOrigin, + base::BindOnce(&QuotaManagerImplTest::DidGetEvictionOrigin, weak_factory_.GetWeakPtr())); } void NotifyOriginInUse(const url::Origin& origin) { - quota_manager_->NotifyOriginInUse(origin); + quota_manager_impl_->NotifyOriginInUse(origin); } void NotifyOriginNoLongerInUse(const url::Origin& origin) { - quota_manager_->NotifyOriginNoLongerInUse(origin); + quota_manager_impl_->NotifyOriginNoLongerInUse(origin); } void GetOriginsModifiedBetween(StorageType type, @@ -312,22 +353,23 @@ class QuotaManagerTest : public testing::Test { base::Time end) { modified_origins_.clear(); modified_origins_type_ = StorageType::kUnknown; - quota_manager_->GetOriginsModifiedBetween( + quota_manager_impl_->GetOriginsModifiedBetween( type, begin, end, - base::BindOnce(&QuotaManagerTest::DidGetModifiedOrigins, + base::BindOnce(&QuotaManagerImplTest::DidGetModifiedOrigins, weak_factory_.GetWeakPtr())); } void DumpQuotaTable() { quota_entries_.clear(); - quota_manager_->DumpQuotaTable(base::BindOnce( - &QuotaManagerTest::DidDumpQuotaTable, weak_factory_.GetWeakPtr())); + quota_manager_impl_->DumpQuotaTable(base::BindOnce( + &QuotaManagerImplTest::DidDumpQuotaTable, weak_factory_.GetWeakPtr())); } void DumpOriginInfoTable() { origin_info_entries_.clear(); - quota_manager_->DumpOriginInfoTable(base::BindOnce( - &QuotaManagerTest::DidDumpOriginInfoTable, weak_factory_.GetWeakPtr())); + quota_manager_impl_->DumpOriginInfoTable( + base::BindOnce(&QuotaManagerImplTest::DidDumpOriginInfoTable, + weak_factory_.GetWeakPtr())); } void DidGetUsageInfo(UsageInfoEntries entries) { @@ -423,13 +465,14 @@ class QuotaManagerTest : public testing::Test { void SetStoragePressureCallback( base::RepeatingCallback<void(url::Origin)> callback) { - quota_manager_->SetStoragePressureCallback(std::move(callback)); + quota_manager_impl_->SetStoragePressureCallback(std::move(callback)); } void MaybeRunStoragePressureCallback(const url::Origin& origin, int64_t total, int64_t available) { - quota_manager_->MaybeRunStoragePressureCallback(origin, total, available); + quota_manager_impl_->MaybeRunStoragePressureCallback(origin, total, + available); } void set_additional_callback_count(int c) { additional_callback_count_ = c; } @@ -440,9 +483,11 @@ class QuotaManagerTest : public testing::Test { ++additional_callback_count_; } - QuotaManager* quota_manager() const { return quota_manager_.get(); } - void set_quota_manager(QuotaManager* quota_manager) { - quota_manager_ = quota_manager; + QuotaManagerImpl* quota_manager_impl() const { + return quota_manager_impl_.get(); + } + void set_quota_manager_impl(QuotaManagerImpl* quota_manager_impl) { + quota_manager_impl_ = quota_manager_impl; } MockSpecialStoragePolicy* mock_special_storage_policy() const { @@ -450,7 +495,11 @@ class QuotaManagerTest : public testing::Test { } std::unique_ptr<QuotaOverrideHandle> GetQuotaOverrideHandle() { - return quota_manager_->proxy()->GetQuotaOverrideHandle(); + return quota_manager_impl_->proxy()->GetQuotaOverrideHandle(); + } + + void SetQuotaChangeCallback(base::RepeatingClosure cb) { + quota_manager_impl_->SetQuotaChangeCallbackForTesting(std::move(cb)); } QuotaStatusCode status() const { return quota_status_; } @@ -496,7 +545,7 @@ class QuotaManagerTest : public testing::Test { base::ScopedTempDir data_dir_; - scoped_refptr<QuotaManager> quota_manager_; + scoped_refptr<QuotaManagerImpl> quota_manager_impl_; scoped_refptr<MockSpecialStoragePolicy> mock_special_storage_policy_; QuotaStatusCode quota_status_; @@ -519,12 +568,16 @@ class QuotaManagerTest : public testing::Test { int mock_time_counter_; - base::WeakPtrFactory<QuotaManagerTest> weak_factory_{this}; + // TODO(crbug.com/1163009): Remove this member after all QuotaClients have + // been mojofied. + std::vector<std::unique_ptr<MockQuotaClient>> legacy_clients_; + + base::WeakPtrFactory<QuotaManagerImplTest> weak_factory_{this}; - DISALLOW_COPY_AND_ASSIGN(QuotaManagerTest); + DISALLOW_COPY_AND_ASSIGN(QuotaManagerImplTest); }; -TEST_F(QuotaManagerTest, GetUsageInfo) { +TEST_F(QuotaManagerImplTest, GetUsageInfo) { static const MockOriginData kData1[] = { { "http://foo.com/", kTemp, 10 }, { "http://foo.com:8080/", kTemp, 15 }, @@ -537,12 +590,12 @@ TEST_F(QuotaManagerTest, GetUsageInfo) { { "http://bar.com/", kPerm, 40 }, { "http://example.com/", kPerm, 40 }, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); + CreateAndRegisterLegacyClient(kData2, QuotaClientType::kDatabase, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); GetUsageInfo(); task_environment_.RunUntilIdle(); @@ -564,7 +617,7 @@ TEST_F(QuotaManagerTest, GetUsageInfo) { } } -TEST_F(QuotaManagerTest, GetUsageAndQuota_Simple) { +TEST_F(QuotaManagerImplTest, GetUsageAndQuota_Simple) { static const MockOriginData kData[] = { { "http://foo.com/", kTemp, 10 }, { "http://foo.com/", kPerm, 80 }, @@ -593,7 +646,7 @@ TEST_F(QuotaManagerTest, GetUsageAndQuota_Simple) { EXPECT_EQ(quota_returned_for_foo, quota()); } -TEST_F(QuotaManagerTest, GetUsage_NoClient) { +TEST_F(QuotaManagerImplTest, GetUsage_NoClient) { GetUsageAndQuotaForWebApps(ToOrigin("http://foo.com/"), kTemp); task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kOk, status()); @@ -623,11 +676,11 @@ TEST_F(QuotaManagerTest, GetUsage_NoClient) { EXPECT_EQ(0, unlimited_usage()); } -TEST_F(QuotaManagerTest, GetUsage_EmptyClient) { - CreateAndRegisterClient(base::span<MockOriginData>(), - QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); +TEST_F(QuotaManagerImplTest, GetUsage_EmptyClient) { + CreateAndRegisterLegacyClient(base::span<MockOriginData>(), + QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); GetUsageAndQuotaForWebApps(ToOrigin("http://foo.com/"), kTemp); task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kOk, status()); @@ -657,7 +710,7 @@ TEST_F(QuotaManagerTest, GetUsage_EmptyClient) { EXPECT_EQ(0, unlimited_usage()); } -TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_MultiOrigins) { +TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_MultiOrigins) { static const MockOriginData kData[] = { { "http://foo.com/", kTemp, 10 }, { "http://foo.com:8080/", kTemp, 20 }, @@ -691,7 +744,7 @@ TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_MultiOrigins) { EXPECT_EQ(kPerHostQuota, quota()); } -TEST_F(QuotaManagerTest, GetUsage_MultipleClients) { +TEST_F(QuotaManagerImplTest, GetUsage_MultipleClients) { static const MockOriginData kData1[] = { { "http://foo.com/", kTemp, 1 }, { "http://bar.com/", kTemp, 2 }, @@ -708,9 +761,9 @@ TEST_F(QuotaManagerTest, GetUsage_MultipleClients) { CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, {blink::mojom::StorageType::kTemporary, blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterLegacyClient(kData2, QuotaClientType::kDatabase, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); const int64_t kPoolSize = GetAvailableDiskSpaceForTest(); const int64_t kPerHostQuota = kPoolSize / 5; @@ -753,7 +806,7 @@ TEST_F(QuotaManagerTest, GetUsage_MultipleClients) { EXPECT_EQ(8, unlimited_usage()); } -TEST_F(QuotaManagerTest, GetUsageWithBreakdown_Simple) { +TEST_F(QuotaManagerImplTest, GetUsageWithBreakdown_Simple) { blink::mojom::UsageBreakdown usage_breakdown_expected = blink::mojom::UsageBreakdown(); static const MockOriginData kData1[] = { @@ -765,9 +818,9 @@ TEST_F(QuotaManagerTest, GetUsageWithBreakdown_Simple) { static const MockOriginData kData3[] = { {"http://foo.com/", kTemp, 8}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, {blink::mojom::StorageType::kTemporary}); CreateAndRegisterClient(kData3, QuotaClientType::kAppcache, @@ -801,7 +854,7 @@ TEST_F(QuotaManagerTest, GetUsageWithBreakdown_Simple) { EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown())); } -TEST_F(QuotaManagerTest, GetUsageWithBreakdown_NoClient) { +TEST_F(QuotaManagerImplTest, GetUsageWithBreakdown_NoClient) { blink::mojom::UsageBreakdown usage_breakdown_expected = blink::mojom::UsageBreakdown(); @@ -828,7 +881,7 @@ TEST_F(QuotaManagerTest, GetUsageWithBreakdown_NoClient) { EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown())); } -TEST_F(QuotaManagerTest, GetUsageWithBreakdown_MultiOrigins) { +TEST_F(QuotaManagerImplTest, GetUsageWithBreakdown_MultiOrigins) { blink::mojom::UsageBreakdown usage_breakdown_expected = blink::mojom::UsageBreakdown(); static const MockOriginData kData[] = { @@ -855,7 +908,7 @@ TEST_F(QuotaManagerTest, GetUsageWithBreakdown_MultiOrigins) { EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown())); } -TEST_F(QuotaManagerTest, GetUsageWithBreakdown_MultipleClients) { +TEST_F(QuotaManagerImplTest, GetUsageWithBreakdown_MultipleClients) { blink::mojom::UsageBreakdown usage_breakdown_expected = blink::mojom::UsageBreakdown(); static const MockOriginData kData1[] = { @@ -870,9 +923,9 @@ TEST_F(QuotaManagerTest, GetUsageWithBreakdown_MultipleClients) { {"http://unlimited/", kTemp, 512}, }; mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/")); - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, {blink::mojom::StorageType::kTemporary, blink::mojom::StorageType::kPersistent}); @@ -910,12 +963,12 @@ TEST_F(QuotaManagerTest, GetUsageWithBreakdown_MultipleClients) { EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown())); } -void QuotaManagerTest::GetUsage_WithModifyTestBody(const StorageType type) { +void QuotaManagerImplTest::GetUsage_WithModifyTestBody(const StorageType type) { const MockOriginData data[] = { { "http://foo.com/", type, 10 }, { "http://foo.com:1/", type, 20 }, }; - scoped_refptr<MockQuotaClient> client = + MockQuotaClient* client = CreateAndRegisterClient(data, QuotaClientType::kFileSystem, {type}); GetUsageAndQuotaForWebApps(ToOrigin("http://foo.com/"), type); @@ -945,11 +998,11 @@ void QuotaManagerTest::GetUsage_WithModifyTestBody(const StorageType type) { EXPECT_EQ(0, unlimited_usage()); } -TEST_F(QuotaManagerTest, GetTemporaryUsage_WithModify) { +TEST_F(QuotaManagerImplTest, GetTemporaryUsage_WithModify) { GetUsage_WithModifyTestBody(kTemp); } -TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_WithAdditionalTasks) { +TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_WithAdditionalTasks) { static const MockOriginData kData[] = { { "http://foo.com/", kTemp, 10 }, { "http://foo.com:8080/", kTemp, 20 }, @@ -983,7 +1036,7 @@ TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_WithAdditionalTasks) { EXPECT_EQ(2, additional_callback_count()); } -TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_NukeManager) { +TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_NukeManager) { static const MockOriginData kData[] = { { "http://foo.com/", kTemp, 10 }, { "http://foo.com:8080/", kTemp, 20 }, @@ -1006,12 +1059,42 @@ TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_NukeManager) { DeleteOriginData(ToOrigin("http://bar.com/"), kTemp, AllQuotaClientTypes()); // Nuke before waiting for callbacks. - set_quota_manager(nullptr); + set_quota_manager_impl(nullptr); task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kErrorAbort, status()); } -TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Overbudget) { +// TODO(crbug.com/1163009): Remove this test after all QuotaClients have been +// mojofied +TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_NukeManager_Legacy) { + static const MockOriginData kData[] = { + {"http://foo.com/", kTemp, 10}, + {"http://foo.com:8080/", kTemp, 20}, + {"http://bar.com/", kTemp, 13}, + {"http://foo.com/", kPerm, 40}, + }; + CreateAndRegisterLegacyClient(kData, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); + const int kPoolSize = 100; + const int kPerHostQuota = 20; + SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem); + + set_additional_callback_count(0); + GetUsageAndQuotaForWebApps(ToOrigin("http://foo.com/"), kTemp); + RunAdditionalUsageAndQuotaTask(ToOrigin("http://foo.com/"), kTemp); + RunAdditionalUsageAndQuotaTask(ToOrigin("http://bar.com/"), kTemp); + + DeleteOriginData(ToOrigin("http://foo.com/"), kTemp, AllQuotaClientTypes()); + DeleteOriginData(ToOrigin("http://bar.com/"), kTemp, AllQuotaClientTypes()); + + // Nuke before waiting for callbacks. + set_quota_manager_impl(nullptr); + task_environment_.RunUntilIdle(); + EXPECT_EQ(QuotaStatusCode::kErrorAbort, status()); +} + +TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_Overbudget) { static const MockOriginData kData[] = { { "http://usage1/", kTemp, 1 }, { "http://usage10/", kTemp, 10 }, @@ -1049,7 +1132,7 @@ TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Overbudget) { EXPECT_EQ(kPerHostQuota, quota()); // should be clamped to the nominal quota } -TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Unlimited) { +TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_Unlimited) { static const MockOriginData kData[] = { { "http://usage10/", kTemp, 10 }, { "http://usage50/", kTemp, 50 }, @@ -1090,7 +1173,7 @@ TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Unlimited) { task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kOk, status()); EXPECT_EQ(0, usage()); - EXPECT_EQ(QuotaManager::kNoLimit, quota()); + EXPECT_EQ(QuotaManagerImpl::kNoLimit, quota()); // Test when overbugdet. const int kPerHostQuotaFor100 = 20; @@ -1118,7 +1201,7 @@ TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Unlimited) { task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kOk, status()); EXPECT_EQ(0, usage()); - EXPECT_EQ(QuotaManager::kNoLimit, quota()); + EXPECT_EQ(QuotaManagerImpl::kNoLimit, quota()); // Revoke the unlimited rights and make sure the change is noticed. mock_special_storage_policy()->Reset(); @@ -1154,29 +1237,29 @@ TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Unlimited) { EXPECT_EQ(kPerHostQuotaFor100, quota()); } -TEST_F(QuotaManagerTest, OriginInUse) { +TEST_F(QuotaManagerImplTest, OriginInUse) { const url::Origin kFooOrigin = ToOrigin("http://foo.com/"); const url::Origin kBarOrigin = ToOrigin("http://bar.com/"); - EXPECT_FALSE(quota_manager()->IsOriginInUse(kFooOrigin)); - quota_manager()->NotifyOriginInUse(kFooOrigin); // count of 1 - EXPECT_TRUE(quota_manager()->IsOriginInUse(kFooOrigin)); - quota_manager()->NotifyOriginInUse(kFooOrigin); // count of 2 - EXPECT_TRUE(quota_manager()->IsOriginInUse(kFooOrigin)); - quota_manager()->NotifyOriginNoLongerInUse(kFooOrigin); // count of 1 - EXPECT_TRUE(quota_manager()->IsOriginInUse(kFooOrigin)); - - EXPECT_FALSE(quota_manager()->IsOriginInUse(kBarOrigin)); - quota_manager()->NotifyOriginInUse(kBarOrigin); - EXPECT_TRUE(quota_manager()->IsOriginInUse(kBarOrigin)); - quota_manager()->NotifyOriginNoLongerInUse(kBarOrigin); - EXPECT_FALSE(quota_manager()->IsOriginInUse(kBarOrigin)); - - quota_manager()->NotifyOriginNoLongerInUse(kFooOrigin); - EXPECT_FALSE(quota_manager()->IsOriginInUse(kFooOrigin)); + EXPECT_FALSE(quota_manager_impl()->IsOriginInUse(kFooOrigin)); + quota_manager_impl()->NotifyOriginInUse(kFooOrigin); // count of 1 + EXPECT_TRUE(quota_manager_impl()->IsOriginInUse(kFooOrigin)); + quota_manager_impl()->NotifyOriginInUse(kFooOrigin); // count of 2 + EXPECT_TRUE(quota_manager_impl()->IsOriginInUse(kFooOrigin)); + quota_manager_impl()->NotifyOriginNoLongerInUse(kFooOrigin); // count of 1 + EXPECT_TRUE(quota_manager_impl()->IsOriginInUse(kFooOrigin)); + + EXPECT_FALSE(quota_manager_impl()->IsOriginInUse(kBarOrigin)); + quota_manager_impl()->NotifyOriginInUse(kBarOrigin); + EXPECT_TRUE(quota_manager_impl()->IsOriginInUse(kBarOrigin)); + quota_manager_impl()->NotifyOriginNoLongerInUse(kBarOrigin); + EXPECT_FALSE(quota_manager_impl()->IsOriginInUse(kBarOrigin)); + + quota_manager_impl()->NotifyOriginNoLongerInUse(kFooOrigin); + EXPECT_FALSE(quota_manager_impl()->IsOriginInUse(kFooOrigin)); } -TEST_F(QuotaManagerTest, GetAndSetPerststentHostQuota) { +TEST_F(QuotaManagerImplTest, GetAndSetPerststentHostQuota) { CreateAndRegisterClient(base::span<MockOriginData>(), QuotaClientType::kFileSystem, {blink::mojom::StorageType::kTemporary, @@ -1193,20 +1276,21 @@ TEST_F(QuotaManagerTest, GetAndSetPerststentHostQuota) { GetPersistentHostQuota("foo.com"); SetPersistentHostQuota("foo.com", 200); GetPersistentHostQuota("foo.com"); - SetPersistentHostQuota("foo.com", QuotaManager::kPerHostPersistentQuotaLimit); + SetPersistentHostQuota("foo.com", + QuotaManagerImpl::kPerHostPersistentQuotaLimit); GetPersistentHostQuota("foo.com"); task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaManager::kPerHostPersistentQuotaLimit, quota()); + EXPECT_EQ(QuotaManagerImpl::kPerHostPersistentQuotaLimit, quota()); // Persistent quota should be capped at the per-host quota limit. SetPersistentHostQuota("foo.com", - QuotaManager::kPerHostPersistentQuotaLimit + 100); + QuotaManagerImpl::kPerHostPersistentQuotaLimit + 100); GetPersistentHostQuota("foo.com"); task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaManager::kPerHostPersistentQuotaLimit, quota()); + EXPECT_EQ(QuotaManagerImpl::kPerHostPersistentQuotaLimit, quota()); } -TEST_F(QuotaManagerTest, GetAndSetPersistentUsageAndQuota) { +TEST_F(QuotaManagerImplTest, GetAndSetPersistentUsageAndQuota) { GetStorageCapacity(); CreateAndRegisterClient(base::span<MockOriginData>(), QuotaClientType::kFileSystem, @@ -1237,10 +1321,10 @@ TEST_F(QuotaManagerTest, GetAndSetPersistentUsageAndQuota) { GetUsageAndQuotaForStorageClient(ToOrigin("http://unlimited/"), kPerm); task_environment_.RunUntilIdle(); EXPECT_EQ(0, usage()); - EXPECT_EQ(QuotaManager::kNoLimit, quota()); + EXPECT_EQ(QuotaManagerImpl::kNoLimit, quota()); } -TEST_F(QuotaManagerTest, GetQuotaLowAvailableDiskSpace) { +TEST_F(QuotaManagerImplTest, GetQuotaLowAvailableDiskSpace) { static const MockOriginData kData[] = { {"http://foo.com/", kTemp, 100000}, {"http://unlimited/", kTemp, 4000000}, @@ -1253,9 +1337,9 @@ TEST_F(QuotaManagerTest, GetQuotaLowAvailableDiskSpace) { const int kPerHostQuota = kPoolSize / 5; // In here, we expect the low available space logic branch - // to be ignored. Doing so should have QuotaManager return the same per-host - // quota as what is set in QuotaSettings, despite being in a state of low - // available space. + // to be ignored. Doing so should have QuotaManagerImpl return the same + // per-host quota as what is set in QuotaSettings, despite being in a state of + // low available space. const int kMustRemainAvailable = static_cast<int>(GetAvailableDiskSpaceForTest() - 65536); SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailable); @@ -1267,7 +1351,7 @@ TEST_F(QuotaManagerTest, GetQuotaLowAvailableDiskSpace) { EXPECT_EQ(kPerHostQuota, quota()); } -TEST_F(QuotaManagerTest, GetSyncableQuota) { +TEST_F(QuotaManagerImplTest, GetSyncableQuota) { CreateAndRegisterClient(base::span<MockOriginData>(), QuotaClientType::kFileSystem, {blink::mojom::StorageType::kTemporary, @@ -1276,10 +1360,10 @@ TEST_F(QuotaManagerTest, GetSyncableQuota) { // Pre-condition check: available disk space (for testing) is less than // the default quota for syncable storage. EXPECT_LE(kAvailableSpaceForApp, - QuotaManager::kSyncableStorageDefaultHostQuota); + QuotaManagerImpl::kSyncableStorageDefaultHostQuota); // The quota manager should return - // QuotaManager::kSyncableStorageDefaultHostQuota as syncable quota, + // QuotaManagerImpl::kSyncableStorageDefaultHostQuota as syncable quota, // despite available space being less than the desired quota. Only // origins with unlimited storage, which is never the case for syncable // storage, shall have their quota calculation take into account the amount of @@ -1289,10 +1373,10 @@ TEST_F(QuotaManagerTest, GetSyncableQuota) { task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kOk, status()); EXPECT_EQ(0, usage()); - EXPECT_EQ(QuotaManager::kSyncableStorageDefaultHostQuota, quota()); + EXPECT_EQ(QuotaManagerImpl::kSyncableStorageDefaultHostQuota, quota()); } -TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_MultiOrigins) { +TEST_F(QuotaManagerImplTest, GetPersistentUsageAndQuota_MultiOrigins) { static const MockOriginData kData[] = { { "http://foo.com/", kPerm, 10 }, { "http://foo.com:8080/", kPerm, 20 }, @@ -1315,11 +1399,11 @@ TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_MultiOrigins) { EXPECT_EQ(100, quota()); } -TEST_F(QuotaManagerTest, GetPersistentUsage_WithModify) { +TEST_F(QuotaManagerImplTest, GetPersistentUsage_WithModify) { GetUsage_WithModifyTestBody(kPerm); } -TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_WithAdditionalTasks) { +TEST_F(QuotaManagerImplTest, GetPersistentUsageAndQuota_WithAdditionalTasks) { static const MockOriginData kData[] = { { "http://foo.com/", kPerm, 10 }, { "http://foo.com:8080/", kPerm, 20 }, @@ -1349,7 +1433,40 @@ TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_WithAdditionalTasks) { EXPECT_EQ(2, additional_callback_count()); } -TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_NukeManager) { +// TODO(crbug.com/1163009): Remove this test after all QuotaClients have been +// mojofied +TEST_F(QuotaManagerImplTest, + GetPersistentUsageAndQuota_WithAdditionalTasks_Legacy) { + static const MockOriginData kData[] = { + {"http://foo.com/", kPerm, 10}, + {"http://foo.com:8080/", kPerm, 20}, + {"http://bar.com/", kPerm, 13}, + {"http://foo.com/", kTemp, 40}, + }; + CreateAndRegisterLegacyClient(kData, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); + SetPersistentHostQuota("foo.com", 100); + + GetUsageAndQuotaForWebApps(ToOrigin("http://foo.com/"), kPerm); + GetUsageAndQuotaForWebApps(ToOrigin("http://foo.com/"), kPerm); + GetUsageAndQuotaForWebApps(ToOrigin("http://foo.com/"), kPerm); + task_environment_.RunUntilIdle(); + EXPECT_EQ(QuotaStatusCode::kOk, status()); + EXPECT_EQ(10 + 20, usage()); + EXPECT_EQ(100, quota()); + + set_additional_callback_count(0); + RunAdditionalUsageAndQuotaTask(ToOrigin("http://foo.com/"), kPerm); + GetUsageAndQuotaForWebApps(ToOrigin("http://foo.com/"), kPerm); + RunAdditionalUsageAndQuotaTask(ToOrigin("http://bar.com/"), kPerm); + task_environment_.RunUntilIdle(); + EXPECT_EQ(QuotaStatusCode::kOk, status()); + EXPECT_EQ(10 + 20, usage()); + EXPECT_EQ(2, additional_callback_count()); +} + +TEST_F(QuotaManagerImplTest, GetPersistentUsageAndQuota_NukeManager) { static const MockOriginData kData[] = { { "http://foo.com/", kPerm, 10 }, { "http://foo.com:8080/", kPerm, 20 }, @@ -1367,12 +1484,37 @@ TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_NukeManager) { RunAdditionalUsageAndQuotaTask(ToOrigin("http://bar.com/"), kPerm); // Nuke before waiting for callbacks. - set_quota_manager(nullptr); + set_quota_manager_impl(nullptr); task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kErrorAbort, status()); } -TEST_F(QuotaManagerTest, GetUsage_Simple) { +// TODO(crbug.com/1163009): Remove this test after all QuotaClients have been +// mojofied +TEST_F(QuotaManagerImplTest, GetPersistentUsageAndQuota_NukeManager_Legacy) { + static const MockOriginData kData[] = { + {"http://foo.com/", kPerm, 10}, + {"http://foo.com:8080/", kPerm, 20}, + {"http://bar.com/", kPerm, 13}, + {"http://foo.com/", kTemp, 40}, + }; + CreateAndRegisterLegacyClient(kData, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); + SetPersistentHostQuota("foo.com", 100); + + set_additional_callback_count(0); + GetUsageAndQuotaForWebApps(ToOrigin("http://foo.com/"), kPerm); + RunAdditionalUsageAndQuotaTask(ToOrigin("http://foo.com/"), kPerm); + RunAdditionalUsageAndQuotaTask(ToOrigin("http://bar.com/"), kPerm); + + // Nuke before waiting for callbacks. + set_quota_manager_impl(nullptr); + task_environment_.RunUntilIdle(); + EXPECT_EQ(QuotaStatusCode::kErrorAbort, status()); +} + +TEST_F(QuotaManagerImplTest, GetUsage_Simple) { static const MockOriginData kData[] = { { "http://foo.com/", kPerm, 1 }, { "http://foo.com:1/", kPerm, 20 }, @@ -1405,7 +1547,7 @@ TEST_F(QuotaManagerTest, GetUsage_Simple) { EXPECT_EQ(usage(), 4000 + 50000); } -TEST_F(QuotaManagerTest, GetUsage_WithModification) { +TEST_F(QuotaManagerImplTest, GetUsage_WithModification) { static const MockOriginData kData[] = { { "http://foo.com/", kPerm, 1 }, { "http://foo.com:1/", kPerm, 20 }, @@ -1416,7 +1558,7 @@ TEST_F(QuotaManagerTest, GetUsage_WithModification) { { "http://foo.com/", kTemp, 7000000 }, }; - scoped_refptr<MockQuotaClient> client = + MockQuotaClient* client = CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, {blink::mojom::StorageType::kTemporary, blink::mojom::StorageType::kPersistent}); @@ -1456,14 +1598,14 @@ TEST_F(QuotaManagerTest, GetUsage_WithModification) { EXPECT_EQ(usage(), 4000 + 50000 + 900000000); } -TEST_F(QuotaManagerTest, GetUsage_WithDeleteOrigin) { +TEST_F(QuotaManagerImplTest, GetUsage_WithDeleteOrigin) { static const MockOriginData kData[] = { { "http://foo.com/", kTemp, 1 }, { "http://foo.com:1/", kTemp, 20 }, { "http://foo.com/", kPerm, 300 }, { "http://bar.com/", kTemp, 4000 }, }; - scoped_refptr<MockQuotaClient> client = + MockQuotaClient* client = CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, {blink::mojom::StorageType::kTemporary, blink::mojom::StorageType::kPersistent}); @@ -1480,7 +1622,7 @@ TEST_F(QuotaManagerTest, GetUsage_WithDeleteOrigin) { task_environment_.RunUntilIdle(); int64_t predelete_host_pers = usage(); - DeleteClientOriginData(client.get(), ToOrigin("http://foo.com/"), kTemp); + DeleteClientOriginData(client, ToOrigin("http://foo.com/"), kTemp); task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kOk, status()); @@ -1497,14 +1639,14 @@ TEST_F(QuotaManagerTest, GetUsage_WithDeleteOrigin) { EXPECT_EQ(predelete_host_pers, usage()); } -TEST_F(QuotaManagerTest, GetStorageCapacity) { +TEST_F(QuotaManagerImplTest, GetStorageCapacity) { GetStorageCapacity(); task_environment_.RunUntilIdle(); EXPECT_LE(0, total_space()); EXPECT_LE(0, available_space()); } -TEST_F(QuotaManagerTest, EvictOriginData) { +TEST_F(QuotaManagerImplTest, EvictOriginData) { static const MockOriginData kData1[] = { { "http://foo.com/", kTemp, 1 }, { "http://foo.com:1/", kTemp, 20 }, @@ -1518,9 +1660,9 @@ TEST_F(QuotaManagerTest, EvictOriginData) { { "https://foo.com/", kTemp, 80 }, { "http://bar.com/", kTemp, 9 }, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, {blink::mojom::StorageType::kTemporary, blink::mojom::StorageType::kPersistent}); @@ -1538,12 +1680,12 @@ TEST_F(QuotaManagerTest, EvictOriginData) { int64_t predelete_host_pers = usage(); for (const MockOriginData& data : kData1) { - quota_manager()->NotifyStorageAccessed( - url::Origin::Create(GURL(data.origin)), data.type); + quota_manager_impl()->NotifyStorageAccessed( + url::Origin::Create(GURL(data.origin)), data.type, base::Time::Now()); } for (const MockOriginData& data : kData2) { - quota_manager()->NotifyStorageAccessed( - url::Origin::Create(GURL(data.origin)), data.type); + quota_manager_impl()->NotifyStorageAccessed( + url::Origin::Create(GURL(data.origin)), data.type, base::Time::Now()); } task_environment_.RunUntilIdle(); @@ -1571,14 +1713,14 @@ TEST_F(QuotaManagerTest, EvictOriginData) { EXPECT_EQ(predelete_host_pers, usage()); } -TEST_F(QuotaManagerTest, EvictOriginDataHistogram) { +TEST_F(QuotaManagerImplTest, EvictOriginDataHistogram) { const url::Origin kOrigin = ToOrigin("http://foo.com/"); static const MockOriginData kData[] = { {"http://foo.com/", kTemp, 1}, }; base::HistogramTester histograms; - scoped_refptr<MockQuotaClient> client = + MockQuotaClient* client = CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, {blink::mojom::StorageType::kTemporary}); @@ -1590,20 +1732,21 @@ TEST_F(QuotaManagerTest, EvictOriginDataHistogram) { // Ensure used count and time since access are recorded. histograms.ExpectTotalCount( - QuotaManager::kEvictedOriginAccessedCountHistogram, 1); + QuotaManagerImpl::kEvictedOriginAccessedCountHistogram, 1); histograms.ExpectBucketCount( - QuotaManager::kEvictedOriginAccessedCountHistogram, 0, 1); + QuotaManagerImpl::kEvictedOriginAccessedCountHistogram, 0, 1); histograms.ExpectTotalCount( - QuotaManager::kEvictedOriginDaysSinceAccessHistogram, 1); + QuotaManagerImpl::kEvictedOriginDaysSinceAccessHistogram, 1); // First eviction has no 'last' time to compare to. histograms.ExpectTotalCount( - QuotaManager::kDaysBetweenRepeatedOriginEvictionsHistogram, 0); + QuotaManagerImpl::kDaysBetweenRepeatedOriginEvictionsHistogram, 0); client->AddOriginAndNotify(kOrigin, kTemp, 100); // Change the used count of the origin. - quota_manager()->NotifyStorageAccessed(kOrigin, kTemp); + quota_manager_impl()->NotifyStorageAccessed(kOrigin, kTemp, + base::Time::Now()); task_environment_.RunUntilIdle(); GetGlobalUsage(kTemp); @@ -1614,15 +1757,15 @@ TEST_F(QuotaManagerTest, EvictOriginDataHistogram) { // The new used count should be logged. histograms.ExpectTotalCount( - QuotaManager::kEvictedOriginAccessedCountHistogram, 2); + QuotaManagerImpl::kEvictedOriginAccessedCountHistogram, 2); histograms.ExpectBucketCount( - QuotaManager::kEvictedOriginAccessedCountHistogram, 1, 1); + QuotaManagerImpl::kEvictedOriginAccessedCountHistogram, 1, 1); histograms.ExpectTotalCount( - QuotaManager::kEvictedOriginDaysSinceAccessHistogram, 2); + QuotaManagerImpl::kEvictedOriginDaysSinceAccessHistogram, 2); // Second eviction should log a 'time between repeated eviction' sample. histograms.ExpectTotalCount( - QuotaManager::kDaysBetweenRepeatedOriginEvictionsHistogram, 1); + QuotaManagerImpl::kDaysBetweenRepeatedOriginEvictionsHistogram, 1); client->AddOriginAndNotify(kOrigin, kTemp, 100); @@ -1633,10 +1776,10 @@ TEST_F(QuotaManagerTest, EvictOriginDataHistogram) { // Deletion from non-eviction source should not log a histogram sample. histograms.ExpectTotalCount( - QuotaManager::kDaysBetweenRepeatedOriginEvictionsHistogram, 1); + QuotaManagerImpl::kDaysBetweenRepeatedOriginEvictionsHistogram, 1); } -TEST_F(QuotaManagerTest, EvictOriginDataWithDeletionError) { +TEST_F(QuotaManagerImplTest, EvictOriginDataWithDeletionError) { static const MockOriginData kData[] = { { "http://foo.com/", kTemp, 1 }, { "http://foo.com:1/", kTemp, 20 }, @@ -1644,7 +1787,7 @@ TEST_F(QuotaManagerTest, EvictOriginDataWithDeletionError) { { "http://bar.com/", kTemp, 4000 }, }; static const int kNumberOfTemporaryOrigins = 3; - scoped_refptr<MockQuotaClient> client = + MockQuotaClient* client = CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, {blink::mojom::StorageType::kTemporary, blink::mojom::StorageType::kPersistent}); @@ -1667,7 +1810,8 @@ TEST_F(QuotaManagerTest, EvictOriginDataWithDeletionError) { client->AddOriginToErrorSet(ToOrigin("http://foo.com/"), kTemp); - for (int i = 0; i < QuotaManager::kThresholdOfErrorsToBeDenylisted + 1; ++i) { + for (int i = 0; i < QuotaManagerImpl::kThresholdOfErrorsToBeDenylisted + 1; + ++i) { EvictOriginData(ToOrigin("http://foo.com/"), kTemp); task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kErrorInvalidModification, status()); @@ -1717,7 +1861,7 @@ TEST_F(QuotaManagerTest, EvictOriginDataWithDeletionError) { EXPECT_EQ(predelete_host_pers, usage()); } -TEST_F(QuotaManagerTest, GetEvictionRoundInfo) { +TEST_F(QuotaManagerImplTest, GetEvictionRoundInfo) { static const MockOriginData kData[] = { { "http://foo.com/", kTemp, 1 }, { "http://foo.com:1/", kTemp, 20 }, @@ -1742,13 +1886,13 @@ TEST_F(QuotaManagerTest, GetEvictionRoundInfo) { EXPECT_LE(0, available_space()); } -TEST_F(QuotaManagerTest, DeleteHostDataNoClients) { +TEST_F(QuotaManagerImplTest, DeleteHostDataNoClients) { DeleteHostData(std::string(), kTemp, AllQuotaClientTypes()); task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kOk, status()); } -TEST_F(QuotaManagerTest, DeleteHostDataSimple) { +TEST_F(QuotaManagerImplTest, DeleteHostDataSimple) { static const MockOriginData kData[] = { { "http://foo.com/", kTemp, 1 }, }; @@ -1801,7 +1945,7 @@ TEST_F(QuotaManagerTest, DeleteHostDataSimple) { EXPECT_EQ(predelete_host_pers, usage()); } -TEST_F(QuotaManagerTest, DeleteHostDataMultiple) { +TEST_F(QuotaManagerImplTest, DeleteHostDataMultiple) { static const MockOriginData kData1[] = { { "http://foo.com/", kTemp, 1 }, { "http://foo.com:1/", kTemp, 20 }, @@ -1815,9 +1959,9 @@ TEST_F(QuotaManagerTest, DeleteHostDataMultiple) { { "https://foo.com/", kTemp, 80 }, { "http://bar.com/", kTemp, 9 }, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, {blink::mojom::StorageType::kTemporary, blink::mojom::StorageType::kPersistent}); @@ -1885,7 +2029,7 @@ TEST_F(QuotaManagerTest, DeleteHostDataMultiple) { EXPECT_EQ(predelete_bar_pers, usage()); } -TEST_F(QuotaManagerTest, DeleteHostDataMultipleClientsDifferentTypes) { +TEST_F(QuotaManagerImplTest, DeleteHostDataMultipleClientsDifferentTypes) { static const MockOriginData kData1[] = { {"http://foo.com/", kPerm, 1}, {"http://foo.com:1/", kPerm, 10}, @@ -1898,9 +2042,9 @@ TEST_F(QuotaManagerTest, DeleteHostDataMultipleClientsDifferentTypes) { {"https://foo.com/", kTemp, 1000000}, {"http://bar.com/", kTemp, 10000000}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, {blink::mojom::StorageType::kTemporary}); @@ -1973,7 +2117,7 @@ TEST_F(QuotaManagerTest, DeleteHostDataMultipleClientsDifferentTypes) { EXPECT_EQ(predelete_bar_pers - 1000, usage()); } -TEST_F(QuotaManagerTest, DeleteOriginDataNoClients) { +TEST_F(QuotaManagerImplTest, DeleteOriginDataNoClients) { DeleteOriginData(url::Origin::Create(GURL("http://foo.com/")), kTemp, AllQuotaClientTypes()); task_environment_.RunUntilIdle(); @@ -1982,7 +2126,7 @@ TEST_F(QuotaManagerTest, DeleteOriginDataNoClients) { // Single-run DeleteOriginData cases must be well covered by // EvictOriginData tests. -TEST_F(QuotaManagerTest, DeleteOriginDataMultiple) { +TEST_F(QuotaManagerImplTest, DeleteOriginDataMultiple) { static const MockOriginData kData1[] = { { "http://foo.com/", kTemp, 1 }, { "http://foo.com:1/", kTemp, 20 }, @@ -1996,9 +2140,9 @@ TEST_F(QuotaManagerTest, DeleteOriginDataMultiple) { { "https://foo.com/", kTemp, 80 }, { "http://bar.com/", kTemp, 9 }, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, {blink::mojom::StorageType::kTemporary, blink::mojom::StorageType::kPersistent}); @@ -2024,12 +2168,12 @@ TEST_F(QuotaManagerTest, DeleteOriginDataMultiple) { const int64_t predelete_bar_pers = usage(); for (const MockOriginData& data : kData1) { - quota_manager()->NotifyStorageAccessed( - url::Origin::Create(GURL(data.origin)), data.type); + quota_manager_impl()->NotifyStorageAccessed( + url::Origin::Create(GURL(data.origin)), data.type, base::Time::Now()); } for (const MockOriginData& data : kData2) { - quota_manager()->NotifyStorageAccessed( - url::Origin::Create(GURL(data.origin)), data.type); + quota_manager_impl()->NotifyStorageAccessed( + url::Origin::Create(GURL(data.origin)), data.type, base::Time::Now()); } task_environment_.RunUntilIdle(); @@ -2073,7 +2217,7 @@ TEST_F(QuotaManagerTest, DeleteOriginDataMultiple) { EXPECT_EQ(predelete_bar_pers, usage()); } -TEST_F(QuotaManagerTest, DeleteOriginDataMultipleClientsDifferentTypes) { +TEST_F(QuotaManagerImplTest, DeleteOriginDataMultipleClientsDifferentTypes) { static const MockOriginData kData1[] = { {"http://foo.com/", kPerm, 1}, {"http://foo.com:1/", kPerm, 10}, @@ -2086,9 +2230,9 @@ TEST_F(QuotaManagerTest, DeleteOriginDataMultipleClientsDifferentTypes) { {"https://foo.com/", kTemp, 1000000}, {"http://bar.com/", kTemp, 10000000}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary, + blink::mojom::StorageType::kPersistent}); CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, {blink::mojom::StorageType::kTemporary}); @@ -2117,12 +2261,12 @@ TEST_F(QuotaManagerTest, DeleteOriginDataMultipleClientsDifferentTypes) { const int64_t predelete_bar_pers = usage(); for (const MockOriginData& data : kData1) { - quota_manager()->NotifyStorageAccessed( - url::Origin::Create(GURL(data.origin)), data.type); + quota_manager_impl()->NotifyStorageAccessed( + url::Origin::Create(GURL(data.origin)), data.type, base::Time::Now()); } for (const MockOriginData& data : kData2) { - quota_manager()->NotifyStorageAccessed( - url::Origin::Create(GURL(data.origin)), data.type); + quota_manager_impl()->NotifyStorageAccessed( + url::Origin::Create(GURL(data.origin)), data.type, base::Time::Now()); } task_environment_.RunUntilIdle(); @@ -2169,7 +2313,7 @@ TEST_F(QuotaManagerTest, DeleteOriginDataMultipleClientsDifferentTypes) { EXPECT_EQ(predelete_bar_pers - 1000, usage()); } -TEST_F(QuotaManagerTest, GetCachedOrigins) { +TEST_F(QuotaManagerImplTest, GetCachedOrigins) { static const MockOriginData kData[] = { { "http://a.com/", kTemp, 1 }, { "http://a.com:1/", kTemp, 20 }, @@ -2214,7 +2358,7 @@ TEST_F(QuotaManagerTest, GetCachedOrigins) { } } -TEST_F(QuotaManagerTest, NotifyAndLRUOrigin) { +TEST_F(QuotaManagerImplTest, NotifyAndLRUOrigin) { static const MockOriginData kData[] = { { "http://a.com/", kTemp, 0 }, { "http://a.com:1/", kTemp, 0 }, @@ -2254,7 +2398,7 @@ TEST_F(QuotaManagerTest, NotifyAndLRUOrigin) { EXPECT_EQ("http://c.com/", eviction_origin()->GetURL().spec()); } -TEST_F(QuotaManagerTest, GetLRUOriginWithOriginInUse) { +TEST_F(QuotaManagerImplTest, GetLRUOriginWithOriginInUse) { static const MockOriginData kData[] = { { "http://a.com/", kTemp, 0 }, { "http://a.com:1/", kTemp, 0 }, @@ -2309,7 +2453,7 @@ TEST_F(QuotaManagerTest, GetLRUOriginWithOriginInUse) { EXPECT_EQ(ToOrigin("http://a.com/"), *eviction_origin()); } -TEST_F(QuotaManagerTest, GetOriginsModifiedBetween) { +TEST_F(QuotaManagerImplTest, GetOriginsModifiedBetween) { static const MockOriginData kData[] = { { "http://a.com/", kTemp, 0 }, { "http://a.com:1/", kTemp, 0 }, @@ -2317,7 +2461,7 @@ TEST_F(QuotaManagerTest, GetOriginsModifiedBetween) { { "http://b.com/", kPerm, 0 }, // persistent { "http://c.com/", kTemp, 0 }, }; - scoped_refptr<MockQuotaClient> client = + MockQuotaClient* client = CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, {blink::mojom::StorageType::kTemporary, blink::mojom::StorageType::kPersistent}); @@ -2363,7 +2507,7 @@ TEST_F(QuotaManagerTest, GetOriginsModifiedBetween) { EXPECT_EQ(modified_origins_type(), kTemp); } -TEST_F(QuotaManagerTest, DumpQuotaTable) { +TEST_F(QuotaManagerImplTest, DumpQuotaTable) { SetPersistentHostQuota("example1.com", 1); SetPersistentHostQuota("example2.com", 20); SetPersistentHostQuota("example3.com", 300); @@ -2387,15 +2531,15 @@ TEST_F(QuotaManagerTest, DumpQuotaTable) { EXPECT_TRUE(entries.empty()); } -TEST_F(QuotaManagerTest, DumpOriginInfoTable) { +TEST_F(QuotaManagerImplTest, DumpOriginInfoTable) { using std::make_pair; - quota_manager()->NotifyStorageAccessed(ToOrigin("http://example.com/"), - kTemp); - quota_manager()->NotifyStorageAccessed(ToOrigin("http://example.com/"), - kPerm); - quota_manager()->NotifyStorageAccessed(ToOrigin("http://example.com/"), - kPerm); + quota_manager_impl()->NotifyStorageAccessed(ToOrigin("http://example.com/"), + kTemp, base::Time::Now()); + quota_manager_impl()->NotifyStorageAccessed(ToOrigin("http://example.com/"), + kPerm, base::Time::Now()); + quota_manager_impl()->NotifyStorageAccessed(ToOrigin("http://example.com/"), + kPerm, base::Time::Now()); task_environment_.RunUntilIdle(); DumpOriginInfoTable(); @@ -2421,7 +2565,7 @@ TEST_F(QuotaManagerTest, DumpOriginInfoTable) { EXPECT_TRUE(entries.empty()); } -TEST_F(QuotaManagerTest, QuotaForEmptyHost) { +TEST_F(QuotaManagerImplTest, QuotaForEmptyHost) { GetPersistentHostQuota(std::string()); task_environment_.RunUntilIdle(); EXPECT_EQ(QuotaStatusCode::kOk, status()); @@ -2432,7 +2576,7 @@ TEST_F(QuotaManagerTest, QuotaForEmptyHost) { EXPECT_EQ(QuotaStatusCode::kErrorNotSupported, status()); } -TEST_F(QuotaManagerTest, DeleteSpecificClientTypeSingleOrigin) { +TEST_F(QuotaManagerImplTest, DeleteSpecificClientTypeSingleOrigin) { static const MockOriginData kData1[] = { { "http://foo.com/", kTemp, 1 }, }; @@ -2445,12 +2589,12 @@ TEST_F(QuotaManagerTest, DeleteSpecificClientTypeSingleOrigin) { static const MockOriginData kData4[] = { { "http://foo.com/", kTemp, 8 }, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary}); CreateAndRegisterClient(kData2, QuotaClientType::kAppcache, {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData3, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); + CreateAndRegisterLegacyClient(kData3, QuotaClientType::kDatabase, + {blink::mojom::StorageType::kTemporary}); CreateAndRegisterClient(kData4, QuotaClientType::kIndexedDatabase, {blink::mojom::StorageType::kTemporary}); @@ -2487,7 +2631,7 @@ TEST_F(QuotaManagerTest, DeleteSpecificClientTypeSingleOrigin) { EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage()); } -TEST_F(QuotaManagerTest, DeleteSpecificClientTypeSingleHost) { +TEST_F(QuotaManagerImplTest, DeleteSpecificClientTypeSingleHost) { static const MockOriginData kData1[] = { { "http://foo.com:1111/", kTemp, 1 }, }; @@ -2500,12 +2644,12 @@ TEST_F(QuotaManagerTest, DeleteSpecificClientTypeSingleHost) { static const MockOriginData kData4[] = { { "http://foo.com:4444/", kTemp, 8 }, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary}); CreateAndRegisterClient(kData2, QuotaClientType::kAppcache, {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData3, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); + CreateAndRegisterLegacyClient(kData3, QuotaClientType::kDatabase, + {blink::mojom::StorageType::kTemporary}); CreateAndRegisterClient(kData4, QuotaClientType::kIndexedDatabase, {blink::mojom::StorageType::kTemporary}); @@ -2538,7 +2682,7 @@ TEST_F(QuotaManagerTest, DeleteSpecificClientTypeSingleHost) { EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage()); } -TEST_F(QuotaManagerTest, DeleteMultipleClientTypesSingleOrigin) { +TEST_F(QuotaManagerImplTest, DeleteMultipleClientTypesSingleOrigin) { static const MockOriginData kData1[] = { { "http://foo.com/", kTemp, 1 }, }; @@ -2551,12 +2695,12 @@ TEST_F(QuotaManagerTest, DeleteMultipleClientTypesSingleOrigin) { static const MockOriginData kData4[] = { { "http://foo.com/", kTemp, 8 }, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary}); CreateAndRegisterClient(kData2, QuotaClientType::kAppcache, {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData3, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); + CreateAndRegisterLegacyClient(kData3, QuotaClientType::kDatabase, + {blink::mojom::StorageType::kTemporary}); CreateAndRegisterClient(kData4, QuotaClientType::kIndexedDatabase, {blink::mojom::StorageType::kTemporary}); @@ -2580,7 +2724,7 @@ TEST_F(QuotaManagerTest, DeleteMultipleClientTypesSingleOrigin) { EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage()); } -TEST_F(QuotaManagerTest, DeleteMultipleClientTypesSingleHost) { +TEST_F(QuotaManagerImplTest, DeleteMultipleClientTypesSingleHost) { static const MockOriginData kData1[] = { { "http://foo.com:1111/", kTemp, 1 }, }; @@ -2593,12 +2737,12 @@ TEST_F(QuotaManagerTest, DeleteMultipleClientTypesSingleHost) { static const MockOriginData kData4[] = { { "http://foo.com:4444/", kTemp, 8 }, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); + CreateAndRegisterLegacyClient(kData1, QuotaClientType::kFileSystem, + {blink::mojom::StorageType::kTemporary}); CreateAndRegisterClient(kData2, QuotaClientType::kAppcache, {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData3, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); + CreateAndRegisterLegacyClient(kData3, QuotaClientType::kDatabase, + {blink::mojom::StorageType::kTemporary}); CreateAndRegisterClient(kData4, QuotaClientType::kIndexedDatabase, {blink::mojom::StorageType::kTemporary}); @@ -2622,8 +2766,8 @@ TEST_F(QuotaManagerTest, DeleteMultipleClientTypesSingleHost) { EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage()); } -TEST_F(QuotaManagerTest, GetUsageAndQuota_Incognito) { - ResetQuotaManager(true); +TEST_F(QuotaManagerImplTest, GetUsageAndQuota_Incognito) { + ResetQuotaManagerImpl(true); static const MockOriginData kData[] = { { "http://foo.com/", kTemp, 10 }, @@ -2673,20 +2817,21 @@ TEST_F(QuotaManagerTest, GetUsageAndQuota_Incognito) { EXPECT_EQ(available_space() + usage(), quota()); } -TEST_F(QuotaManagerTest, GetUsageAndQuota_SessionOnly) { +TEST_F(QuotaManagerImplTest, GetUsageAndQuota_SessionOnly) { const url::Origin kEpheremalOrigin = ToOrigin("http://ephemeral/"); mock_special_storage_policy()->AddSessionOnly(kEpheremalOrigin.GetURL()); GetUsageAndQuotaForWebApps(kEpheremalOrigin, kTemp); task_environment_.RunUntilIdle(); - EXPECT_EQ(quota_manager()->settings().session_only_per_host_quota, quota()); + EXPECT_EQ(quota_manager_impl()->settings().session_only_per_host_quota, + quota()); GetUsageAndQuotaForWebApps(kEpheremalOrigin, kPerm); task_environment_.RunUntilIdle(); EXPECT_EQ(0, quota()); } -TEST_F(QuotaManagerTest, MaybeRunStoragePressureCallback) { +TEST_F(QuotaManagerImplTest, MaybeRunStoragePressureCallback) { bool callback_ran = false; auto cb = base::BindRepeating( [](bool* callback_ran, url::Origin origin) { *callback_ran = true; }, @@ -2694,7 +2839,7 @@ TEST_F(QuotaManagerTest, MaybeRunStoragePressureCallback) { SetStoragePressureCallback(std::move(cb)); - int64_t kGBytes = QuotaManager::kMBytes * 1024; + int64_t kGBytes = QuotaManagerImpl::kMBytes * 1024; MaybeRunStoragePressureCallback(url::Origin(), 100 * kGBytes, 2 * kGBytes); task_environment_.RunUntilIdle(); EXPECT_FALSE(callback_ran); @@ -2704,7 +2849,7 @@ TEST_F(QuotaManagerTest, MaybeRunStoragePressureCallback) { EXPECT_TRUE(callback_ran); } -TEST_F(QuotaManagerTest, OverrideQuotaForOrigin) { +TEST_F(QuotaManagerImplTest, OverrideQuotaForOrigin) { url::Origin origin = ToOrigin("https://foo.com"); std::unique_ptr<QuotaOverrideHandle> handle = GetQuotaOverrideHandle(); @@ -2720,7 +2865,7 @@ TEST_F(QuotaManagerTest, OverrideQuotaForOrigin) { EXPECT_EQ(5000, quota()); } -TEST_F(QuotaManagerTest, OverrideQuotaForOrigin_Disable) { +TEST_F(QuotaManagerImplTest, OverrideQuotaForOrigin_Disable) { url::Origin origin = ToOrigin("https://foo.com"); std::unique_ptr<QuotaOverrideHandle> handle1 = GetQuotaOverrideHandle(); std::unique_ptr<QuotaOverrideHandle> handle2 = GetQuotaOverrideHandle(); @@ -2757,7 +2902,7 @@ TEST_F(QuotaManagerTest, OverrideQuotaForOrigin_Disable) { EXPECT_EQ(kDefaultPerHostQuota, quota()); } -TEST_F(QuotaManagerTest, WithdrawQuotaOverride) { +TEST_F(QuotaManagerImplTest, WithdrawQuotaOverride) { url::Origin origin = ToOrigin("https://foo.com"); std::unique_ptr<QuotaOverrideHandle> handle1 = GetQuotaOverrideHandle(); std::unique_ptr<QuotaOverrideHandle> handle2 = GetQuotaOverrideHandle(); @@ -2798,4 +2943,57 @@ TEST_F(QuotaManagerTest, WithdrawQuotaOverride) { EXPECT_EQ(kDefaultPerHostQuota, quota()); } +TEST_F(QuotaManagerImplTest, QuotaChangeEvent_LargePartitionPressure) { + scoped_feature_list_.InitAndEnableFeature(features::kStoragePressureEvent); + bool quota_change_dispatched = false; + + SetQuotaChangeCallback( + base::BindLambdaForTesting([&] { quota_change_dispatched = true; })); + SetGetVolumeInfoFn([](const base::FilePath&) -> std::tuple<int64_t, int64_t> { + int64_t total = kGigabytes * 100; + int64_t available = kGigabytes * 2; + return std::make_tuple(total, available); + }); + GetStorageCapacity(); + task_environment_.RunUntilIdle(); + EXPECT_FALSE(quota_change_dispatched); + + SetGetVolumeInfoFn([](const base::FilePath&) -> std::tuple<int64_t, int64_t> { + int64_t total = kGigabytes * 100; + int64_t available = QuotaManagerImpl::kMBytes * 512; + return std::make_tuple(total, available); + }); + GetStorageCapacity(); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(quota_change_dispatched); +} + +TEST_F(QuotaManagerImplTest, QuotaChangeEvent_SmallPartitionPressure) { + scoped_feature_list_.InitAndEnableFeature(features::kStoragePressureEvent); + bool quota_change_dispatched = false; + + SetQuotaChangeCallback( + base::BindLambdaForTesting([&] { quota_change_dispatched = true; })); + SetGetVolumeInfoFn([](const base::FilePath&) -> std::tuple<int64_t, int64_t> { + int64_t total = kGigabytes * 10; + int64_t available = total * 2; + return std::make_tuple(total, available); + }); + GetStorageCapacity(); + task_environment_.RunUntilIdle(); + EXPECT_FALSE(quota_change_dispatched); + + SetGetVolumeInfoFn([](const base::FilePath&) -> std::tuple<int64_t, int64_t> { + // DetermineStoragePressure flow will trigger the storage pressure flow + // when available disk space is below 5% (+/- 0.25%) of total disk space. + // Available is 2% here to guarantee that it falls below the threshold. + int64_t total = kGigabytes * 10; + int64_t available = total * 0.02; + return std::make_tuple(total, available); + }); + GetStorageCapacity(); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(quota_change_dispatched); +} + } // namespace storage diff --git a/chromium/storage/browser/quota/quota_override_handle.cc b/chromium/storage/browser/quota/quota_override_handle.cc index 5c9ab714678..6ff4125c171 100644 --- a/chromium/storage/browser/quota/quota_override_handle.cc +++ b/chromium/storage/browser/quota/quota_override_handle.cc @@ -6,14 +6,17 @@ #include "base/memory/scoped_refptr.h" #include "base/sequence_checker.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "storage/browser/quota/quota_manager_proxy.h" namespace storage { QuotaOverrideHandle::QuotaOverrideHandle( - scoped_refptr<QuotaManagerProxy> quota_manager) - : quota_manager_(quota_manager) { - quota_manager_->GetOverrideHandleId( + scoped_refptr<QuotaManagerProxy> quota_manager_proxy) + : quota_manager_proxy_(std::move(quota_manager_proxy)) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + quota_manager_proxy_->GetOverrideHandleId( + base::SequencedTaskRunnerHandle::Get(), base::BindOnce(&QuotaOverrideHandle::DidGetOverrideHandleId, weak_ptr_factory_.GetWeakPtr())); } @@ -21,7 +24,7 @@ QuotaOverrideHandle::QuotaOverrideHandle( QuotaOverrideHandle::~QuotaOverrideHandle() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (id_.has_value()) { - quota_manager_->WithdrawOverridesForHandle(id_.value()); + quota_manager_proxy_->WithdrawOverridesForHandle(id_.value()); } } @@ -39,8 +42,9 @@ void QuotaOverrideHandle::OverrideQuotaForOrigin( origin, quota_size, std::move(callback))); return; } - quota_manager_->OverrideQuotaForOrigin(id_.value(), origin, quota_size, - std::move(callback)); + quota_manager_proxy_->OverrideQuotaForOrigin( + id_.value(), origin, quota_size, base::SequencedTaskRunnerHandle::Get(), + std::move(callback)); } void QuotaOverrideHandle::DidGetOverrideHandleId(int id) { diff --git a/chromium/storage/browser/quota/quota_override_handle.h b/chromium/storage/browser/quota/quota_override_handle.h index e7b6ec918f2..81f7ab9acbd 100644 --- a/chromium/storage/browser/quota/quota_override_handle.h +++ b/chromium/storage/browser/quota/quota_override_handle.h @@ -10,6 +10,7 @@ #include "base/memory/weak_ptr.h" #include "base/sequence_checker.h" #include "base/sequenced_task_runner.h" +#include "base/thread_annotations.h" #include "url/origin.h" namespace storage { @@ -38,12 +39,16 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaOverrideHandle { void DidGetUniqueId(); void DidGetOverrideHandleId(int id); - base::Optional<int> id_; - std::vector<base::OnceClosure> override_callback_queue_; - scoped_refptr<QuotaManagerProxy> quota_manager_; - SEQUENCE_CHECKER(sequence_checker_); - base::WeakPtrFactory<QuotaOverrideHandle> weak_ptr_factory_{this}; + + const scoped_refptr<QuotaManagerProxy> quota_manager_proxy_ + GUARDED_BY_CONTEXT(sequence_checker_); + base::Optional<int> id_ GUARDED_BY_CONTEXT(sequence_checker_); + std::vector<base::OnceClosure> override_callback_queue_ + GUARDED_BY_CONTEXT(sequence_checker_); + + base::WeakPtrFactory<QuotaOverrideHandle> weak_ptr_factory_ + GUARDED_BY_CONTEXT(sequence_checker_){this}; }; } // namespace storage diff --git a/chromium/storage/browser/quota/quota_settings.cc b/chromium/storage/browser/quota/quota_settings.cc index 4b8ac29b314..ebc116cd60b 100644 --- a/chromium/storage/browser/quota/quota_settings.cc +++ b/chromium/storage/browser/quota/quota_settings.cc @@ -27,7 +27,6 @@ namespace { const int64_t kMBytes = 1024 * 1024; const int kRandomizedPercentage = 10; const double kDefaultPerHostRatio = 0.75; -const double kDefaultPoolSizeRatio = 0.8; const double kIncognitoQuotaRatioLowerBound = 0.15; const double kIncognitoQuotaRatioUpperBound = 0.2; @@ -68,7 +67,13 @@ base::Optional<QuotaSettings> CalculateNominalDynamicSettings( // The fraction of the device's storage the browser is willing to use for // temporary storage. - const double kTemporaryPoolSizeRatio = kDefaultPoolSizeRatio; + const double kTemporaryPoolSizeRatio = features::kPoolSizeRatio.Get(); + + // The fixed size in bytes the browser is willing to use for temporary + // storage. If both the ratio and the absolute size are set, the lower value + // will be honored. + const int64_t kTemporaryPoolSizeFixed = + static_cast<int64_t>(features::kPoolSizeBytes.Get()); // The amount of the device's storage the browser attempts to // keep free. If there is less than this amount of storage free @@ -83,8 +88,10 @@ base::Optional<QuotaSettings> CalculateNominalDynamicSettings( // * 64GB storage -- min(6GB,2GB) = 2GB // * 16GB storage -- min(1.6GB,2GB) = 1.6GB // * 8GB storage -- min(800MB,2GB) = 800MB - const int64_t kShouldRemainAvailableFixed = 2048 * kMBytes; // 2GB - const double kShouldRemainAvailableRatio = 0.1; // 10% + const int64_t kShouldRemainAvailableFixed = + static_cast<int64_t>(features::kShouldRemainAvailableBytes.Get()); + const double kShouldRemainAvailableRatio = + features::kShouldRemainAvailableRatio.Get(); // The amount of the device's storage the browser attempts to // keep free at all costs. Data will be aggressively evicted. @@ -98,8 +105,10 @@ base::Optional<QuotaSettings> CalculateNominalDynamicSettings( // * 64GB storage -- min(640MB,1GB) = 640MB // * 16GB storage -- min(160MB,1GB) = 160MB // * 8GB storage -- min(80MB,1GB) = 80MB - const int64_t kMustRemainAvailableFixed = 1024 * kMBytes; // 1GB - const double kMustRemainAvailableRatio = 0.01; // 1% + const int64_t kMustRemainAvailableFixed = + static_cast<int64_t>(features::kMustRemainAvailableBytes.Get()); + const double kMustRemainAvailableRatio = + features::kMustRemainAvailableRatio.Get(); // The fraction of the temporary pool that can be utilized by a single host. const double kPerHostTemporaryRatio = kDefaultPerHostRatio; @@ -117,7 +126,13 @@ base::Optional<QuotaSettings> CalculateNominalDynamicSettings( return base::nullopt; } - int64_t pool_size = total * kTemporaryPoolSizeRatio; + // Pool size calculated by ratio. + int64_t pool_size_by_ratio = total * kTemporaryPoolSizeRatio; + + int64_t pool_size = + kTemporaryPoolSizeFixed > 0 + ? std::min(kTemporaryPoolSizeFixed, pool_size_by_ratio) + : pool_size_by_ratio; settings.pool_size = pool_size; settings.should_remain_available = diff --git a/chromium/storage/browser/quota/quota_settings_unittest.cc b/chromium/storage/browser/quota/quota_settings_unittest.cc index 2654212f214..a2b2a564f1c 100644 --- a/chromium/storage/browser/quota/quota_settings_unittest.cc +++ b/chromium/storage/browser/quota/quota_settings_unittest.cc @@ -8,6 +8,8 @@ #include "base/bind.h" #include "base/callback.h" #include "base/files/scoped_temp_dir.h" +#include "base/optional.h" +#include "base/run_loop.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" @@ -39,15 +41,28 @@ class QuotaSettingsTest : public testing::Test { QuotaSettingsTest() = default; void SetUp() override { ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); } - protected: - base::test::ScopedFeatureList scoped_feature_list_; - base::test::TaskEnvironment task_environment_; + // Synchronous proxy to GetNominalDynamicSettings(). + base::Optional<QuotaSettings> GetSettings( + bool is_incognito, + QuotaDeviceInfoHelper* device_info_helper) { + base::Optional<QuotaSettings> quota_settings; + base::RunLoop run_loop; + GetNominalDynamicSettings( + profile_path(), is_incognito, device_info_helper, + base::BindLambdaForTesting([&](base::Optional<QuotaSettings> settings) { + quota_settings = std::move(settings); + run_loop.Quit(); + })); + run_loop.Run(); + return quota_settings; + } + const base::FilePath& profile_path() const { return data_dir_.GetPath(); } - private: + protected: + base::test::ScopedFeatureList scoped_feature_list_; base::ScopedTempDir data_dir_; - QuotaSettings quota_settings_; - DISALLOW_COPY_AND_ASSIGN(QuotaSettingsTest); + base::test::TaskEnvironment task_environment_; }; class QuotaSettingsIncognitoTest : public QuotaSettingsTest { @@ -64,24 +79,15 @@ class QuotaSettingsIncognitoTest : public QuotaSettingsTest { } void GetAndTestSettings(const int64_t physical_memory_amount) { - bool callback_executed = false; - GetNominalDynamicSettings( - profile_path(), true, device_info_helper(), - base::BindLambdaForTesting([&](base::Optional<QuotaSettings> settings) { - callback_executed = true; - EXPECT_LE(physical_memory_amount * - GetIncognitoQuotaRatioLowerBound_ForTesting(), - settings->pool_size); - EXPECT_GE(physical_memory_amount * - GetIncognitoQuotaRatioUpperBound_ForTesting(), - settings->pool_size); - })); - task_environment_.RunUntilIdle(); - EXPECT_TRUE(callback_executed); - } - - MockQuotaDeviceInfoHelper* device_info_helper() { - return &device_info_helper_; + base::Optional<QuotaSettings> settings = + GetSettings(true, &device_info_helper_); + ASSERT_TRUE(settings.has_value()); + EXPECT_LE( + physical_memory_amount * GetIncognitoQuotaRatioLowerBound_ForTesting(), + settings->pool_size); + EXPECT_GE( + physical_memory_amount * GetIncognitoQuotaRatioUpperBound_ForTesting(), + settings->pool_size); } private: @@ -93,19 +99,76 @@ TEST_F(QuotaSettingsTest, Default) { ON_CALL(device_info_helper, AmountOfTotalDiskSpace(_)) .WillByDefault(::testing::Return(2000)); - bool callback_executed = false; - GetNominalDynamicSettings( - profile_path(), false, &device_info_helper, - base::BindLambdaForTesting([&](base::Optional<QuotaSettings> settings) { - callback_executed = true; - ASSERT_NE(settings, base::nullopt); - // 1600 = 2000 * default PoolSizeRatio (0.8) - EXPECT_EQ(settings->pool_size, 1600); - // 1200 = 1600 * default PerHostRatio (.75) - EXPECT_EQ(settings->per_host_quota, 1200); - })); - task_environment_.RunUntilIdle(); - EXPECT_TRUE(callback_executed); + base::Optional<QuotaSettings> settings = + GetSettings(false, &device_info_helper); + ASSERT_TRUE(settings.has_value()); + // 1600 = 2000 * default PoolSizeRatio (0.8) + EXPECT_EQ(settings->pool_size, 1600); + // 1200 = 1600 * default PerHostRatio (.75) + EXPECT_EQ(settings->per_host_quota, 1200); +} + +TEST_F(QuotaSettingsTest, FeatureParamsWithLargeFixedQuota) { + scoped_feature_list_.InitAndEnableFeatureWithParameters( + features::kStorageQuotaSettings, {{"MustRemainAvailableBytes", "500"}, + {"MustRemainAvailableRatio", "0.01"}, + {"PoolSizeBytes", "2000"}, + {"PoolSizeRatio", "0.8"}, + {"ShouldRemainAvailableBytes", "600"}, + {"ShouldRemainAvailableRatio", "0.1"}}); + + MockQuotaDeviceInfoHelper device_info_helper; + ON_CALL(device_info_helper, AmountOfTotalDiskSpace(_)) + .WillByDefault(::testing::Return(2000)); + + base::Optional<QuotaSettings> settings = + GetSettings(false, &device_info_helper); + ASSERT_TRUE(settings.has_value()); + + EXPECT_EQ(settings->pool_size, 1600); + EXPECT_EQ(settings->must_remain_available, 20); + EXPECT_EQ(settings->should_remain_available, 200); +} + +TEST_F(QuotaSettingsTest, FeatureParamsWithSmallFixedQuota) { + scoped_feature_list_.InitAndEnableFeatureWithParameters( + features::kStorageQuotaSettings, {{"MustRemainAvailableBytes", "5"}, + {"MustRemainAvailableRatio", "0.01"}, + {"PoolSizeBytes", "20"}, + {"PoolSizeRatio", "0.8"}, + {"ShouldRemainAvailableBytes", "60"}, + {"ShouldRemainAvailableRatio", "0.1"}}); + + MockQuotaDeviceInfoHelper device_info_helper; + ON_CALL(device_info_helper, AmountOfTotalDiskSpace(_)) + .WillByDefault(::testing::Return(2000)); + + base::Optional<QuotaSettings> settings = + GetSettings(false, &device_info_helper); + ASSERT_TRUE(settings.has_value()); + + EXPECT_EQ(settings->pool_size, 20); + EXPECT_EQ(settings->must_remain_available, 5); + EXPECT_EQ(settings->should_remain_available, 60); +} + +TEST_F(QuotaSettingsTest, FeatureParamsWithoutFixedQuota) { + scoped_feature_list_.InitAndEnableFeatureWithParameters( + features::kStorageQuotaSettings, {{"MustRemainAvailableRatio", "0.01"}, + {"PoolSizeRatio", "0.8"}, + {"ShouldRemainAvailableRatio", "0.1"}}); + + MockQuotaDeviceInfoHelper device_info_helper; + ON_CALL(device_info_helper, AmountOfTotalDiskSpace(_)) + .WillByDefault(::testing::Return(2000)); + + base::Optional<QuotaSettings> settings = + GetSettings(false, &device_info_helper); + ASSERT_TRUE(settings.has_value()); + + EXPECT_EQ(settings->pool_size, 1600); + EXPECT_EQ(settings->must_remain_available, 20); + EXPECT_EQ(settings->should_remain_available, 200); } TEST_F(QuotaSettingsIncognitoTest, IncognitoDynamicQuota_LowPhysicalMemory) { diff --git a/chromium/storage/browser/quota/quota_task.cc b/chromium/storage/browser/quota/quota_task.cc index ff0487573fe..24674ca68e6 100644 --- a/chromium/storage/browser/quota/quota_task.cc +++ b/chromium/storage/browser/quota/quota_task.cc @@ -8,9 +8,9 @@ #include <functional> #include "base/bind.h" +#include "base/containers/contains.h" #include "base/location.h" #include "base/single_thread_task_runner.h" -#include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" using base::TaskRunner; diff --git a/chromium/storage/browser/quota/quota_task.h b/chromium/storage/browser/quota/quota_task.h index 7ca4874bed8..49fe6cb75ed 100644 --- a/chromium/storage/browser/quota/quota_task.h +++ b/chromium/storage/browser/quota/quota_task.h @@ -30,6 +30,9 @@ class QuotaTaskObserver; // TODO(kinuko): Revise this using base::OnceCallback. class QuotaTask { public: + QuotaTask(const QuotaTask&) = delete; + QuotaTask& operator=(const QuotaTask&) = delete; + void Start(); protected: diff --git a/chromium/storage/browser/quota/quota_temporary_storage_evictor.cc b/chromium/storage/browser/quota/quota_temporary_storage_evictor.cc index ba99181034e..90034aadf3a 100644 --- a/chromium/storage/browser/quota/quota_temporary_storage_evictor.cc +++ b/chromium/storage/browser/quota/quota_temporary_storage_evictor.cc @@ -11,7 +11,7 @@ #include "base/auto_reset.h" #include "base/bind.h" #include "storage/browser/quota/quota_macros.h" -#include "storage/browser/quota/quota_manager.h" +#include "storage/browser/quota/quota_manager_impl.h" #include "third_party/blink/public/mojom/quota/quota_types.mojom.h" #include "url/gurl.h" diff --git a/chromium/storage/browser/quota/quota_temporary_storage_evictor_unittest.cc b/chromium/storage/browser/quota/quota_temporary_storage_evictor_unittest.cc index 2f1b729f32f..46154a0e245 100644 --- a/chromium/storage/browser/quota/quota_temporary_storage_evictor_unittest.cc +++ b/chromium/storage/browser/quota/quota_temporary_storage_evictor_unittest.cc @@ -11,12 +11,12 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/containers/contains.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/run_loop.h" -#include "base/stl_util.h" #include "base/test/task_environment.h" -#include "storage/browser/quota/quota_manager.h" +#include "storage/browser/quota/quota_manager_impl.h" #include "storage/browser/quota/quota_temporary_storage_evictor.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" diff --git a/chromium/storage/browser/quota/storage_policy_observer.cc b/chromium/storage/browser/quota/storage_policy_observer.cc new file mode 100644 index 00000000000..5fe45ed8ffe --- /dev/null +++ b/chromium/storage/browser/quota/storage_policy_observer.cc @@ -0,0 +1,136 @@ +// Copyright 2021 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 "storage/browser/quota/storage_policy_observer.h" + +#include <utility> + +#include "base/task/post_task.h" +#include "url/origin.h" + +namespace storage { + +// A helper class that lives on the IO thread and registers with +// SpecialStoragePolicy. It bounces back any OnPolicyChanged messages +// to the StoragePolicyObserver on the provided `reply_task_runner`. +class StoragePolicyObserverIOThread + : public storage::SpecialStoragePolicy::Observer { + public: + StoragePolicyObserverIOThread( + scoped_refptr<base::SequencedTaskRunner> reply_task_runner, + scoped_refptr<storage::SpecialStoragePolicy> storage_policy, + base::WeakPtr<StoragePolicyObserver> observer) + : reply_task_runner_(std::move(reply_task_runner)), + storage_policy_(std::move(storage_policy)), + observer_(std::move(observer)) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + storage_policy_->AddObserver(this); + } + + ~StoragePolicyObserverIOThread() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + storage_policy_->RemoveObserver(this); + } + + // storage::SpecialStoragePolicy::Observer implementation: + void OnPolicyChanged() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + reply_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&StoragePolicyObserver::OnPolicyChanged, observer_)); + } + + private: + scoped_refptr<base::SequencedTaskRunner> reply_task_runner_; + scoped_refptr<storage::SpecialStoragePolicy> storage_policy_; + + SEQUENCE_CHECKER(sequence_checker_); + base::WeakPtr<StoragePolicyObserver> observer_; +}; + +StoragePolicyObserver::StoragePolicyObserver( + ApplyPolicyUpdatesCallback callback, + scoped_refptr<base::SequencedTaskRunner> io_task_runner, + scoped_refptr<storage::SpecialStoragePolicy> storage_policy) + : callback_(callback), storage_policy_(std::move(storage_policy)) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!storage_policy_) + return; + + storage_policy_observer_ = base::SequenceBound<StoragePolicyObserverIOThread>( + std::move(io_task_runner), base::SequencedTaskRunnerHandle::Get(), + storage_policy_, weak_factory_.GetWeakPtr()); +} + +StoragePolicyObserver::~StoragePolicyObserver() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +void StoragePolicyObserver::StartTrackingOrigin(const url::Origin& origin) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // If the origin exists, emplace fails, and its state is unchanged. + const GURL origin_url = GURL(origin.Serialize()); + origin_state_.emplace(origin_url, OriginState()); + + OnPolicyChanged(); +} + +void StoragePolicyObserver::StartTrackingOrigins( + const std::vector<url::Origin>& origins) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + for (const auto& origin : origins) { + // If the origin exists, emplace fails, and its state is unchanged. + GURL origin_url = GURL(origin.Serialize()); + origin_state_.emplace(std::move(origin_url), OriginState()); + } + + OnPolicyChanged(); +} + +void StoragePolicyObserver::StopTrackingOrigin(const url::Origin& origin) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + const GURL origin_url = GURL(origin.Serialize()); + origin_state_.erase(origin_url); +} + +void StoragePolicyObserver::OnPolicyChanged() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + std::vector<storage::mojom::StoragePolicyUpdatePtr> policy_updates; + for (auto& entry : origin_state_) { + const GURL& origin = entry.first; + OriginState& state = entry.second; + state.should_purge_on_shutdown = ShouldPurgeOnShutdown(origin); + + if (state.should_purge_on_shutdown != state.will_purge_on_shutdown) { + state.will_purge_on_shutdown = state.should_purge_on_shutdown; + policy_updates.emplace_back(storage::mojom::StoragePolicyUpdate::New( + url::Origin::Create(origin), state.should_purge_on_shutdown)); + } + } + if (policy_updates.empty()) + return; + callback_.Run(std::move(policy_updates)); +} + +bool StoragePolicyObserver::ShouldPurgeOnShutdownForTesting( + const url::Origin& origin) { + const GURL origin_url = GURL(origin.Serialize()); + return ShouldPurgeOnShutdown(origin_url); +} + +bool StoragePolicyObserver::ShouldPurgeOnShutdown(const GURL& origin) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!storage_policy_) + return false; + if (!storage_policy_->IsStorageSessionOnly(origin)) + return false; + if (storage_policy_->IsStorageProtected(origin)) + return false; + return true; +} + +} // namespace storage diff --git a/chromium/storage/browser/quota/storage_policy_observer.h b/chromium/storage/browser/quota/storage_policy_observer.h new file mode 100644 index 00000000000..01990226bb6 --- /dev/null +++ b/chromium/storage/browser/quota/storage_policy_observer.h @@ -0,0 +1,85 @@ +// Copyright 2021 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 STORAGE_BROWSER_QUOTA_STORAGE_POLICY_OBSERVER_H_ +#define STORAGE_BROWSER_QUOTA_STORAGE_POLICY_OBSERVER_H_ + +#include <map> +#include <vector> + +#include "base/callback.h" +#include "base/component_export.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" +#include "base/threading/sequence_bound.h" +#include "components/services/storage/public/mojom/storage_policy_update.mojom.h" +#include "storage/browser/quota/special_storage_policy.h" +#include "url/gurl.h" + +namespace url { +class Origin; +} + +namespace storage { + +class StoragePolicyObserverIOThread; + +// A helper class that aggregates storage::mojom::StoragePolicyUpdate +// changes for tracked origins. `StartTrackingOrigin()` adds an origin +// to start tracking. When these origins have policy updates, +// the provided `callback` will be called with the policy update deltas. +class COMPONENT_EXPORT(STORAGE_BROWSER) StoragePolicyObserver { + public: + using ApplyPolicyUpdatesCallback = base::RepeatingCallback<void( + std::vector<storage::mojom::StoragePolicyUpdatePtr>)>; + + StoragePolicyObserver( + ApplyPolicyUpdatesCallback callback, + scoped_refptr<base::SequencedTaskRunner> io_task_runner, + scoped_refptr<storage::SpecialStoragePolicy> storage_policy); + ~StoragePolicyObserver(); + + StoragePolicyObserver(const StoragePolicyObserver&) = delete; + StoragePolicyObserver& operator=(const StoragePolicyObserver&) = delete; + + // These methods are idempotent. Tracking an origin that is already + // tracked and stopping tracking an origin that is not being tracked + // are noops. + void StartTrackingOrigin(const url::Origin& origin); + void StartTrackingOrigins(const std::vector<url::Origin>& origins); + void StopTrackingOrigin(const url::Origin& origin); + + // Called by StoragePolicyObserverIOThread. + void OnPolicyChanged(); + + bool ShouldPurgeOnShutdownForTesting(const url::Origin& origin); + + private: + bool ShouldPurgeOnShutdown(const GURL& origin); + + SEQUENCE_CHECKER(sequence_checker_); + + struct OriginState { + // Indicates that storage for this origin should be purged on shutdown. + bool should_purge_on_shutdown = false; + // Indicates the last value for `purge_on_shutdown` that was communicated. + bool will_purge_on_shutdown = false; + }; + // NOTE: The GURL key is specifically an origin GURL. + // Special storage policy uses GURLs and not Origins, so it's simpler + // to store everything in GURL form. + std::map<GURL, OriginState> origin_state_; + + const ApplyPolicyUpdatesCallback callback_; + const scoped_refptr<storage::SpecialStoragePolicy> storage_policy_; + + base::SequenceBound<StoragePolicyObserverIOThread> storage_policy_observer_; + + base::WeakPtrFactory<StoragePolicyObserver> weak_factory_{this}; +}; + +} // namespace storage + +#endif // STORAGE_BROWSER_QUOTA_STORAGE_POLICY_OBSERVER_H_ diff --git a/chromium/storage/browser/quota/usage_tracker.cc b/chromium/storage/browser/quota/usage_tracker.cc index 698f5e1abd1..af66268abeb 100644 --- a/chromium/storage/browser/quota/usage_tracker.cc +++ b/chromium/storage/browser/quota/usage_tracker.cc @@ -235,6 +235,9 @@ void UsageTracker::AccumulateClientHostUsage(base::OnceClosure callback, case QuotaClientType::kBackgroundFetch: info->usage_breakdown->backgroundFetch += usage; break; + case QuotaClientType::kNativeIO: + info->usage_breakdown->fileSystem += usage; + break; } std::move(callback).Run(); diff --git a/chromium/storage/browser/quota/usage_tracker.h b/chromium/storage/browser/quota/usage_tracker.h index 1c019126b5e..a2bdbf4c0b3 100644 --- a/chromium/storage/browser/quota/usage_tracker.h +++ b/chromium/storage/browser/quota/usage_tracker.h @@ -33,13 +33,15 @@ class ClientUsageTracker; // A helper class that gathers and tracks the amount of data stored in // all quota clients. // -// Ownership: Each QuotaManager instance owns 3 instances of this class (one per -// storage type: Persistent, Temporary, Syncable). -// Thread-safety: All methods except the constructor must be called on the same -// sequence. +// Ownership: Each QuotaManagerImpl instance owns 3 instances of this class (one +// per storage type: Persistent, Temporary, Syncable). Thread-safety: All +// methods except the constructor must be called on the same sequence. class COMPONENT_EXPORT(STORAGE_BROWSER) UsageTracker : public QuotaTaskObserver { public: + // TODO(crbug.com/1163009): Switch the map key type in `client_types` to + // mojom::QuotaClient* after all QuotaClients have + // been mojofied. UsageTracker( const base::flat_map<QuotaClient*, QuotaClientType>& client_types, blink::mojom::StorageType type, diff --git a/chromium/storage/common/BUILD.gn b/chromium/storage/common/BUILD.gn index 5e76c440e76..8fdf0dc0951 100644 --- a/chromium/storage/common/BUILD.gn +++ b/chromium/storage/common/BUILD.gn @@ -17,6 +17,8 @@ component("common") { "file_system/file_system_types.h", "file_system/file_system_util.cc", "file_system/file_system_util.h", + "quota/padding_key.cc", + "quota/padding_key.h", ] # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. diff --git a/chromium/storage/common/database/database_identifier.cc b/chromium/storage/common/database/database_identifier.cc index 26de8fe303b..0de222ada39 100644 --- a/chromium/storage/common/database/database_identifier.cc +++ b/chromium/storage/common/database/database_identifier.cc @@ -141,8 +141,8 @@ DatabaseIdentifier DatabaseIdentifier::Parse(const std::string& identifier) { if (SchemeIsUnique(scheme)) return DatabaseIdentifier(); - base::StringPiece port_str(identifier.begin() + last_underscore + 1, - identifier.end()); + auto port_str = base::MakeStringPiece( + identifier.begin() + last_underscore + 1, identifier.end()); int port = 0; if (!base::StringToInt(port_str, &port) || port < 0 || port >= 1 << 16) return DatabaseIdentifier(); diff --git a/chromium/storage/common/file_system/file_system_types.h b/chromium/storage/common/file_system/file_system_types.h index 26eac0f475a..c171e0f1ad5 100644 --- a/chromium/storage/common/file_system/file_system_types.h +++ b/chromium/storage/common/file_system/file_system_types.h @@ -54,14 +54,13 @@ enum FileSystemType { // Should be used only for testing. kFileSystemTypeTest, - // Indicates a local filesystem where we can access files using native - // local path. - kFileSystemTypeNativeLocal, + // Indicates a local filesystem where we can access files using local path. + kFileSystemTypeLocal, - // Indicates a local filesystem where we can access files using native - // local path, but with restricted access. - // Restricted native local file system is in read-only mode. - kFileSystemTypeRestrictedNativeLocal, + // Indicates a local filesystem where we can access files using local path, + // but with restricted access. + // Restricted local file system is in read-only mode. + kFileSystemTypeRestrictedLocal, // Indicates a transient, isolated file system for dragged files (which could // contain multiple dragged paths in the virtual root). @@ -69,7 +68,7 @@ enum FileSystemType { // Indicates media filesystem which we can access with same manner to // regular filesystem. - kFileSystemTypeNativeMedia, + kFileSystemTypeLocalMedia, // Indicates media filesystem to which we need special protocol to access, // such as MTP or PTP. @@ -88,9 +87,9 @@ enum FileSystemType { // Indicates an external filesystem accessible by file paths from platform // Apps. As of writing, on non Chrome OS platform, this is merely a - // kFileSystemTypeNativeLocal. On Chrome OS, the path is parsed by + // kFileSystemTypeLocal. On Chrome OS, the path is parsed by // the handlers of kFileSystemTypeExternal. - kFileSystemTypeNativeForPlatformApp, + kFileSystemTypeLocalForPlatformApp, // Indicates an isolated filesystem which is supposed to contain one // temporary which is supposed to go away when the last reference of diff --git a/chromium/storage/common/file_system/file_system_util.cc b/chromium/storage/common/file_system/file_system_util.cc index 3bead664f78..77ac0532ea4 100644 --- a/chromium/storage/common/file_system/file_system_util.cc +++ b/chromium/storage/common/file_system/file_system_util.cc @@ -282,21 +282,21 @@ std::string GetFileSystemTypeString(FileSystemType type) { return "External"; case kFileSystemTypeTest: return "Test"; - case kFileSystemTypeNativeLocal: - return "NativeLocal"; - case kFileSystemTypeRestrictedNativeLocal: - return "RestrictedNativeLocal"; + case kFileSystemTypeLocal: + return "Local"; + case kFileSystemTypeRestrictedLocal: + return "RestrictedLocal"; case kFileSystemTypeDragged: return "Dragged"; - case kFileSystemTypeNativeMedia: - return "NativeMedia"; + case kFileSystemTypeLocalMedia: + return "LocalMedia"; case kFileSystemTypeDeviceMedia: return "DeviceMedia"; case kFileSystemTypeSyncable: case kFileSystemTypeSyncableForInternalSync: return "Syncable"; - case kFileSystemTypeNativeForPlatformApp: - return "NativeForPlatformApp"; + case kFileSystemTypeLocalForPlatformApp: + return "LocalForPlatformApp"; case kFileSystemTypeForTransientFile: return "TransientFile"; case kFileSystemTypePluginPrivate: @@ -327,16 +327,18 @@ std::string GetFileSystemTypeString(FileSystemType type) { } std::string FilePathToString(const base::FilePath& file_path) { + // TODO(pkasting): Probably this should use AsUTF8Unsafe() across platforms. #if defined(OS_WIN) - return base::UTF16ToUTF8(file_path.value()); + return file_path.AsUTF8Unsafe(); #elif defined(OS_POSIX) || defined(OS_FUCHSIA) return file_path.value(); #endif } base::FilePath StringToFilePath(const std::string& file_path_string) { + // TODO(pkasting): Probably this should use FromUTF8Unsafe() across platforms. #if defined(OS_WIN) - return base::FilePath(base::UTF8ToUTF16(file_path_string)); + return base::FilePath::FromUTF8Unsafe(file_path_string); #elif defined(OS_POSIX) || defined(OS_FUCHSIA) return base::FilePath(file_path_string); #endif diff --git a/chromium/storage/browser/quota/padding_key.cc b/chromium/storage/common/quota/padding_key.cc index 788f6f4634c..14abee0526a 100644 --- a/chromium/storage/browser/quota/padding_key.cc +++ b/chromium/storage/common/quota/padding_key.cc @@ -2,14 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "storage/browser/quota/padding_key.h" +#include "storage/common/quota/padding_key.h" +#include <inttypes.h> #include <cstdint> #include <vector> - #include "base/no_destructor.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" #include "crypto/hmac.h" +#include "crypto/random.h" +#include "crypto/symmetric_key.h" +#include "net/base/schemeful_site.h" #include "net/http/http_request_headers.h" +#include "services/network/public/mojom/url_response_head.mojom-shared.h" using crypto::SymmetricKey; @@ -32,53 +38,44 @@ std::unique_ptr<SymmetricKey>* GetPaddingKeyInternal() { } // namespace -const SymmetricKey* GetDefaultPaddingKey() { - return GetPaddingKeyInternal()->get(); -} - -std::unique_ptr<SymmetricKey> CopyDefaultPaddingKey() { - return SymmetricKey::Import(kPaddingKeyAlgorithm, - (*GetPaddingKeyInternal())->key()); +bool ShouldPadResponseType(network::mojom::FetchResponseType type) { + return type == network::mojom::FetchResponseType::kOpaque || + type == network::mojom::FetchResponseType::kOpaqueRedirect; } -std::unique_ptr<SymmetricKey> DeserializePaddingKey( - const std::string& raw_key) { - return SymmetricKey::Import(kPaddingKeyAlgorithm, raw_key); +int64_t ComputeRandomResponsePadding() { + uint64_t raw_random = 0; + crypto::RandBytes(&raw_random, sizeof(uint64_t)); + return raw_random % kPaddingRange; } -std::string SerializeDefaultPaddingKey() { - return (*GetPaddingKeyInternal())->key(); -} - -void ResetPaddingKeyForTesting() { - *GetPaddingKeyInternal() = - SymmetricKey::GenerateRandomKey(kPaddingKeyAlgorithm, 128); -} - -int64_t ComputeResponsePadding(const std::string& response_url, - const crypto::SymmetricKey* padding_key, - bool has_metadata, - bool loaded_with_credentials, - const std::string& request_method) { +int64_t ComputeStableResponsePadding(const url::Origin& origin, + const std::string& response_url, + const base::Time& response_time, + const std::string& request_method, + int64_t side_data_size) { DCHECK(!response_url.empty()); - crypto::HMAC hmac(crypto::HMAC::SHA256); - CHECK(hmac.Init(padding_key)); + net::SchemefulSite site(origin); - std::string key = response_url; - if (has_metadata) - key += "METADATA"; - if (loaded_with_credentials) - key += "CREDENTIALED"; + DCHECK_GT(response_time, base::Time::UnixEpoch()); + int64_t microseconds = + (response_time - base::Time::UnixEpoch()).InMicroseconds(); // It should only be possible to have a CORS safelisted method here since // the spec does not permit other methods for no-cors requests. DCHECK(request_method == net::HttpRequestHeaders::kGetMethod || request_method == net::HttpRequestHeaders::kHeadMethod || request_method == net::HttpRequestHeaders::kPostMethod); - key += request_method; - uint64_t digest_start; + std::string key = base::StringPrintf( + "%s-%" PRId64 "-%s-%s-%" PRId64, response_url.c_str(), microseconds, + site.Serialize().c_str(), request_method.c_str(), side_data_size); + + crypto::HMAC hmac(crypto::HMAC::SHA256); + CHECK(hmac.Init(GetPaddingKeyInternal()->get())); + + uint64_t digest_start = 0; CHECK(hmac.Sign(key, reinterpret_cast<uint8_t*>(&digest_start), sizeof(digest_start))); return digest_start % kPaddingRange; diff --git a/chromium/storage/common/quota/padding_key.h b/chromium/storage/common/quota/padding_key.h new file mode 100644 index 00000000000..58e9f0301e3 --- /dev/null +++ b/chromium/storage/common/quota/padding_key.h @@ -0,0 +1,51 @@ +// 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 STORAGE_COMMON_QUOTA_PADDING_KEY_H_ +#define STORAGE_COMMON_QUOTA_PADDING_KEY_H_ + +#include <memory> +#include <string> + +#include "base/component_export.h" +#include "services/network/public/mojom/url_response_head.mojom-shared.h" +#include "url/gurl.h" + +namespace base { +class Time; +} // namespace base + +namespace url { +class Origin; +} // namespace url + +namespace storage { + +// Utility method to determine if a given type of response should be padded. +COMPONENT_EXPORT(STORAGE_COMMON) +bool ShouldPadResponseType(network::mojom::FetchResponseType type); + +// Compute a purely random padding size for a resource. A random padding is +// preferred except in cases where a site could rapidly trigger a large number +// of padded values for the same resource; e.g. from http cache. +COMPONENT_EXPORT(STORAGE_COMMON) +int64_t ComputeRandomResponsePadding(); + +// Compute a stable padding value for a resource. This should be used for +// cases where a site could trigger a large number of padding values to be +// generated for the same resource; e.g. http cache. The |origin| is the +// origin of the context that loaded the resource. Note, its important that the +// |response_time| be the time stored in the cache and not just the current +// time. The |side_data_size| should only be passed if padding is being +// computed for a side data blob. +COMPONENT_EXPORT(STORAGE_COMMON) +int64_t ComputeStableResponsePadding(const url::Origin& origin, + const std::string& response_url, + const base::Time& response_time, + const std::string& request_method, + int64_t side_data_size = 0); + +} // namespace storage + +#endif // STORAGE_COMMON_QUOTA_PADDING_KEY_H_ |