diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-05-12 15:59:20 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-05-25 06:57:22 +0000 |
commit | f7eaed5286974984ba5f9e3189d8f49d03e99f81 (patch) | |
tree | caed19b2af2024f35449fb0b781d0a25e09d4f8f /chromium/storage | |
parent | 9729c4479fe23554eae6e6dd1f30ff488f470c84 (diff) | |
download | qtwebengine-chromium-f7eaed5286974984ba5f9e3189d8f49d03e99f81.tar.gz |
BASELINE: Update Chromium to 100.0.4896.167
Change-Id: I98cbeb5d7543d966ffe04d8cefded0c493a11333
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/storage')
72 files changed, 4030 insertions, 3448 deletions
diff --git a/chromium/storage/BUILD.gn b/chromium/storage/BUILD.gn index 23789543889..052117693f0 100644 --- a/chromium/storage/BUILD.gn +++ b/chromium/storage/BUILD.gn @@ -10,4 +10,6 @@ test("storage_unittests") { ] data = [ "//storage/test/data/" ] + + data_deps = [ "//testing/buildbot/filters:storage_unittests_filters" ] } diff --git a/chromium/storage/DEPS b/chromium/storage/DEPS index 0bfd8069e88..fe6db678741 100644 --- a/chromium/storage/DEPS +++ b/chromium/storage/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/crash/core/common/crash_key.h", "+components/services/filesystem", "+components/services/storage", "+crypto", diff --git a/chromium/storage/browser/BUILD.gn b/chromium/storage/browser/BUILD.gn index bd02958142c..5e80213ac73 100644 --- a/chromium/storage/browser/BUILD.gn +++ b/chromium/storage/browser/BUILD.gn @@ -234,6 +234,7 @@ component("browser") { "//base:i18n", "//base/third_party/dynamic_annotations", "//build:chromeos_buildflags", + "//components/crash/core/common:crash_key", "//components/services/storage/public/cpp", "//components/services/storage/public/cpp/filesystem", "//mojo/public/cpp/bindings", @@ -242,6 +243,7 @@ component("browser") { "//services/network/public/cpp", "//services/network/public/mojom", "//sql", + "//storage/browser/quota:mojo_bindings", "//third_party/blink/public/common", "//third_party/leveldatabase", "//third_party/sqlite", @@ -328,6 +330,7 @@ source_set("unittests") { "quota/quota_manager_unittest.cc", "quota/quota_settings_unittest.cc", "quota/quota_temporary_storage_evictor_unittest.cc", + "quota/storage_policy_observer_unittest.cc", "quota/usage_tracker_unittest.cc", "test/mock_quota_manager_unittest.cc", ] @@ -379,6 +382,8 @@ static_library("test_support") { "test/mock_file_update_observer.h", "test/mock_quota_client.cc", "test/mock_quota_client.h", + "test/mock_quota_database.cc", + "test/mock_quota_database.h", "test/mock_quota_manager.cc", "test/mock_quota_manager.h", "test/mock_quota_manager_proxy.cc", diff --git a/chromium/storage/browser/blob/blob_builder_from_stream.cc b/chromium/storage/browser/blob/blob_builder_from_stream.cc index d688f78da36..a39af4e716c 100644 --- a/chromium/storage/browser/blob/blob_builder_from_stream.cc +++ b/chromium/storage/browser/blob/blob_builder_from_stream.cc @@ -92,7 +92,7 @@ class DataPipeConsumerHelper { if (result != MOJO_RESULT_OK) { // We requested a trap on a condition that can never occur. The state of // `pipe_` likely changed. - DCHECK(result == MOJO_RESULT_FAILED_PRECONDITION); + DCHECK_EQ(result, MOJO_RESULT_FAILED_PRECONDITION); InvokeDone(mojo::ScopedDataPipeConsumerHandle(), PassProgressClient(), /*success=*/true, current_offset_); delete this; diff --git a/chromium/storage/browser/blob/blob_memory_controller.cc b/chromium/storage/browser/blob/blob_memory_controller.cc index 40123a3c62f..9143dfcd229 100644 --- a/chromium/storage/browser/blob/blob_memory_controller.cc +++ b/chromium/storage/browser/blob/blob_memory_controller.cc @@ -96,11 +96,11 @@ BlobStorageLimits CalculateBlobStorageLimitsImpl( // Don't do specialty configuration for error size (-1). if (memory_size > 0) { -#if !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID) && \ +#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_ANDROID) && \ defined(ARCH_CPU_64_BITS) constexpr size_t kTwoGigabytes = 2ull * 1024 * 1024 * 1024; limits.max_blob_in_memory_space = kTwoGigabytes; -#elif defined(OS_ANDROID) +#elif BUILDFLAG(IS_ANDROID) limits.max_blob_in_memory_space = static_cast<size_t>(memory_size / 100ll); #else limits.max_blob_in_memory_space = static_cast<size_t>(memory_size / 5ll); @@ -116,7 +116,7 @@ BlobStorageLimits CalculateBlobStorageLimitsImpl( if (disk_size >= 0) { #if BUILDFLAG(IS_CHROMEOS_ASH) limits.desired_max_disk_space = static_cast<uint64_t>(disk_size / 2ll); -#elif defined(OS_ANDROID) +#elif BUILDFLAG(IS_ANDROID) limits.desired_max_disk_space = static_cast<uint64_t>(3ll * disk_size / 50); #else limits.desired_max_disk_space = static_cast<uint64_t>(disk_size / 10ll); diff --git a/chromium/storage/browser/blob/blob_registry_impl_unittest.cc b/chromium/storage/browser/blob/blob_registry_impl_unittest.cc index e7de3b85352..90a5b6b002d 100644 --- a/chromium/storage/browser/blob/blob_registry_impl_unittest.cc +++ b/chromium/storage/browser/blob/blob_registry_impl_unittest.cc @@ -7,13 +7,13 @@ #include <limits> #include <memory> #include <string> +#include <tuple> #include <utility> #include <vector> #include "base/bind.h" #include "base/callback_helpers.h" #include "base/files/scoped_temp_dir.h" -#include "base/ignore_result.h" #include "base/memory/raw_ptr.h" #include "base/rand_util.h" #include "base/run_loop.h" @@ -334,7 +334,7 @@ TEST_F(BlobRegistryImplTest, Register_ReferencedBlobClosedPipe) { std::vector<blink::mojom::DataElementPtr> elements; mojo::PendingRemote<blink::mojom::Blob> referenced_blob_remote; - ignore_result(referenced_blob_remote.InitWithNewPipeAndPassReceiver()); + std::ignore = referenced_blob_remote.InitWithNewPipeAndPassReceiver(); elements.push_back( blink::mojom::DataElement::NewBlob(blink::mojom::DataElementBlob::New( std::move(referenced_blob_remote), 0, 16))); @@ -975,7 +975,7 @@ TEST_F(BlobRegistryImplTest, Register_BytesProviderClosedPipe) { const std::string kId = "id"; mojo::PendingRemote<blink::mojom::BytesProvider> bytes_provider_remote; - ignore_result(bytes_provider_remote.InitWithNewPipeAndPassReceiver()); + std::ignore = bytes_provider_remote.InitWithNewPipeAndPassReceiver(); std::vector<blink::mojom::DataElementPtr> elements; elements.push_back( diff --git a/chromium/storage/browser/blob/blob_storage_constants.h b/chromium/storage/browser/blob/blob_storage_constants.h index 0803c4d7af9..19f6e2404b2 100644 --- a/chromium/storage/browser/blob/blob_storage_constants.h +++ b/chromium/storage/browser/blob/blob_storage_constants.h @@ -20,7 +20,7 @@ constexpr size_t kDefaultMaxBlobInMemorySpace = 500u * 1024 * 1024; constexpr uint64_t kDefaultMaxBlobDiskSpace = 0ull; constexpr uint64_t kDefaultMaxPageFileSize = 100ull * 1024 * 1024; -#if defined(OS_ANDROID) +#if BUILDFLAG(IS_ANDROID) // On minimal Android maximum in-memory space can be as low as 5MB. constexpr uint64_t kDefaultMinPageFileSize = 5ull * 1024 * 1024 / 2; const float kDefaultMaxBlobInMemorySpaceUnderPressureRatio = 0.02f; diff --git a/chromium/storage/browser/blob/blob_url_loader.cc b/chromium/storage/browser/blob/blob_url_loader.cc index b2e061c4e11..e3a0b7823c8 100644 --- a/chromium/storage/browser/blob/blob_url_loader.cc +++ b/chromium/storage/browser/blob/blob_url_loader.cc @@ -234,14 +234,21 @@ void BlobURLLoader::HeadersCompleted( // TODO(jam): some of this code can be shared with // services/network/url_loader.h - client_->OnReceiveResponse(std::move(response)); + + client_->OnReceiveResponse( + std::move(response), + base::FeatureList::IsEnabled(network::features::kCombineResponseBody) + ? std::move(response_body_consumer_handle_) + : mojo::ScopedDataPipeConsumerHandle()); sent_headers_ = true; if (metadata.has_value()) client_->OnReceiveCachedMetadata(std::move(metadata.value())); - client_->OnStartLoadingResponseBody( - std::move(response_body_consumer_handle_)); + if (!base::FeatureList::IsEnabled(network::features::kCombineResponseBody)) { + client_->OnStartLoadingResponseBody( + std::move(response_body_consumer_handle_)); + } } } // namespace storage diff --git a/chromium/storage/browser/blob/blob_url_registry.cc b/chromium/storage/browser/blob/blob_url_registry.cc index d658e7e901e..f79e7e0baab 100644 --- a/chromium/storage/browser/blob/blob_url_registry.cc +++ b/chromium/storage/browser/blob/blob_url_registry.cc @@ -20,13 +20,16 @@ BlobUrlRegistry::~BlobUrlRegistry() { bool BlobUrlRegistry::AddUrlMapping( const GURL& blob_url, mojo::PendingRemote<blink::mojom::Blob> blob, - // TODO(https://crbug.com/1224926): Remove this once experiment is over. - const base::UnguessableToken& unsafe_agent_cluster_id) { + // TODO(https://crbug.com/1224926): Remove these once experiment is over. + const base::UnguessableToken& unsafe_agent_cluster_id, + const absl::optional<net::SchemefulSite>& unsafe_top_level_site) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!BlobUrlUtils::UrlHasFragment(blob_url)); if (IsUrlMapped(blob_url)) return false; url_to_unsafe_agent_cluster_id_[blob_url] = unsafe_agent_cluster_id; + if (unsafe_top_level_site) + url_to_unsafe_top_level_site_[blob_url] = *unsafe_top_level_site; url_to_blob_[blob_url] = std::move(blob); return true; } @@ -35,13 +38,12 @@ bool BlobUrlRegistry::RemoveUrlMapping(const GURL& blob_url) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!BlobUrlUtils::UrlHasFragment(blob_url)); auto blob_it = url_to_blob_.find(blob_url); - auto agent_it = url_to_unsafe_agent_cluster_id_.find(blob_url); - if (blob_it == url_to_blob_.end() || - agent_it == url_to_unsafe_agent_cluster_id_.end()) { + if (blob_it == url_to_blob_.end()) { return false; } url_to_blob_.erase(blob_it); - url_to_unsafe_agent_cluster_id_.erase(agent_it); + url_to_unsafe_agent_cluster_id_.erase(blob_url); + url_to_unsafe_top_level_site_.erase(blob_url); return true; } @@ -66,6 +68,17 @@ absl::optional<base::UnguessableToken> BlobUrlRegistry::GetUnsafeAgentClusterID( return absl::nullopt; } +absl::optional<net::SchemefulSite> BlobUrlRegistry::GetUnsafeTopLevelSite( + const GURL& blob_url) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + auto it = url_to_unsafe_top_level_site_.find(blob_url); + if (it != url_to_unsafe_top_level_site_.end()) + return it->second; + if (fallback_) + return fallback_->GetUnsafeTopLevelSite(blob_url); + return absl::nullopt; +} + mojo::PendingRemote<blink::mojom::Blob> BlobUrlRegistry::GetBlobFromUrl( const GURL& url) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); diff --git a/chromium/storage/browser/blob/blob_url_registry.h b/chromium/storage/browser/blob/blob_url_registry.h index b2b7a3156d2..390a59b3588 100644 --- a/chromium/storage/browser/blob/blob_url_registry.h +++ b/chromium/storage/browser/blob/blob_url_registry.h @@ -12,6 +12,7 @@ #include "base/unguessable_token.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" +#include "net/base/schemeful_site.h" #include "storage/browser/blob/blob_storage_constants.h" #include "third_party/blink/public/mojom/blob/blob.mojom.h" @@ -34,8 +35,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobUrlRegistry { bool AddUrlMapping( const GURL& url, mojo::PendingRemote<blink::mojom::Blob> blob, - // TODO(https://crbug.com/1224926): Remove this once experiment is over. - const base::UnguessableToken& unsafe_agent_cluster_id); + // TODO(https://crbug.com/1224926): Remove these once experiment is over. + const base::UnguessableToken& unsafe_agent_cluster_id, + const absl::optional<net::SchemefulSite>& unsafe_top_level_site); // Removes the given URL mapping. Returns false if the url wasn't mapped. bool RemoveUrlMapping(const GURL& url); @@ -46,6 +48,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobUrlRegistry { // TODO(https://crbug.com/1224926): Remove this once experiment is over. absl::optional<base::UnguessableToken> GetUnsafeAgentClusterID( const GURL& blob_url) const; + absl::optional<net::SchemefulSite> GetUnsafeTopLevelSite( + const GURL& blob_url) const; // Returns the blob from the given url. Returns a null remote if the mapping // doesn't exist. @@ -77,6 +81,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobUrlRegistry { std::map<GURL, mojo::PendingRemote<blink::mojom::Blob>> url_to_blob_; // TODO(https://crbug.com/1224926): Remove this once experiment is over. std::map<GURL, base::UnguessableToken> url_to_unsafe_agent_cluster_id_; + std::map<GURL, net::SchemefulSite> url_to_unsafe_top_level_site_; std::map<base::UnguessableToken, std::pair<GURL, mojo::PendingRemote<blink::mojom::Blob>>> token_to_url_and_blob_; diff --git a/chromium/storage/browser/blob/blob_url_registry_unittest.cc b/chromium/storage/browser/blob/blob_url_registry_unittest.cc index 0795ecaf080..4a1cfeda0dd 100644 --- a/chromium/storage/browser/blob/blob_url_registry_unittest.cc +++ b/chromium/storage/browser/blob/blob_url_registry_unittest.cc @@ -40,6 +40,10 @@ TEST(BlobUrlRegistry, URLRegistration) { const GURL kURL2 = GURL("blob://Blob2"); base::UnguessableToken kTokenId1 = base::UnguessableToken::Create(); base::UnguessableToken kTokenId2 = base::UnguessableToken::Create(); + net::SchemefulSite kTopLevelSite1 = + net::SchemefulSite(GURL("https://example.com")); + net::SchemefulSite kTopLevelSite2 = + net::SchemefulSite(GURL("https://foobar.com")); base::test::SingleThreadTaskEnvironment task_environment_; @@ -52,24 +56,32 @@ TEST(BlobUrlRegistry, URLRegistration) { EXPECT_FALSE(registry.RemoveUrlMapping(kURL1)); EXPECT_EQ(0u, registry.url_count()); - EXPECT_TRUE(registry.AddUrlMapping(kURL1, blob1.Clone(), kTokenId1)); - EXPECT_FALSE(registry.AddUrlMapping(kURL1, blob2.Clone(), kTokenId1)); + EXPECT_TRUE( + registry.AddUrlMapping(kURL1, blob1.Clone(), kTokenId1, kTopLevelSite1)); + EXPECT_FALSE( + registry.AddUrlMapping(kURL1, blob2.Clone(), kTokenId1, kTopLevelSite1)); EXPECT_EQ(kTokenId1, registry.GetUnsafeAgentClusterID(kURL1)); + EXPECT_EQ(kTopLevelSite1, registry.GetUnsafeTopLevelSite(kURL1)); EXPECT_TRUE(registry.IsUrlMapped(kURL1)); EXPECT_EQ(kBlobId1, UuidFromBlob(registry.GetBlobFromUrl(kURL1))); EXPECT_EQ(1u, registry.url_count()); - EXPECT_TRUE(registry.AddUrlMapping(kURL2, blob2.Clone(), kTokenId2)); + EXPECT_TRUE( + registry.AddUrlMapping(kURL2, blob2.Clone(), kTokenId2, kTopLevelSite2)); EXPECT_EQ(kTokenId2, registry.GetUnsafeAgentClusterID(kURL2)); + EXPECT_EQ(kTopLevelSite2, registry.GetUnsafeTopLevelSite(kURL2)); EXPECT_EQ(2u, registry.url_count()); EXPECT_TRUE(registry.RemoveUrlMapping(kURL2)); EXPECT_FALSE(registry.IsUrlMapped(kURL2)); EXPECT_EQ(absl::nullopt, registry.GetUnsafeAgentClusterID(kURL2)); + EXPECT_EQ(absl::nullopt, registry.GetUnsafeTopLevelSite(kURL2)); // Both urls point to the same blob. - EXPECT_TRUE(registry.AddUrlMapping(kURL2, blob1.Clone(), kTokenId2)); + EXPECT_TRUE( + registry.AddUrlMapping(kURL2, blob1.Clone(), kTokenId2, kTopLevelSite2)); EXPECT_EQ(kTokenId2, registry.GetUnsafeAgentClusterID(kURL2)); + EXPECT_EQ(kTopLevelSite2, registry.GetUnsafeTopLevelSite(kURL2)); EXPECT_EQ(UuidFromBlob(registry.GetBlobFromUrl(kURL1)), UuidFromBlob(registry.GetBlobFromUrl(kURL2))); } diff --git a/chromium/storage/browser/blob/blob_url_store_impl.cc b/chromium/storage/browser/blob/blob_url_store_impl.cc index de637458878..a42d12fd035 100644 --- a/chromium/storage/browser/blob/blob_url_store_impl.cc +++ b/chromium/storage/browser/blob/blob_url_store_impl.cc @@ -6,11 +6,13 @@ #include "base/bind.h" #include "base/strings/strcat.h" +#include "components/crash/core/common/crash_key.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "storage/browser/blob/blob_impl.h" #include "storage/browser/blob/blob_url_loader_factory.h" #include "storage/browser/blob/blob_url_registry.h" #include "storage/browser/blob/blob_url_utils.h" +#include "url/url_util.h" namespace storage { @@ -73,8 +75,9 @@ BlobURLStoreImpl::~BlobURLStoreImpl() { void BlobURLStoreImpl::Register( mojo::PendingRemote<blink::mojom::Blob> blob, const GURL& url, - // TODO(https://crbug.com/1224926): Remove this once experiment is over. + // TODO(https://crbug.com/1224926): Remove these once experiment is over. const base::UnguessableToken& unsafe_agent_cluster_id, + const absl::optional<net::SchemefulSite>& unsafe_top_level_site, RegisterCallback callback) { // TODO(mek): Generate blob URLs here, rather than validating the URLs the // renderer process generated. @@ -84,7 +87,8 @@ void BlobURLStoreImpl::Register( } if (registry_) - registry_->AddUrlMapping(url, std::move(blob), unsafe_agent_cluster_id); + registry_->AddUrlMapping(url, std::move(blob), unsafe_agent_cluster_id, + unsafe_top_level_site); urls_.insert(url); std::move(callback).Run(); } @@ -112,10 +116,16 @@ void BlobURLStoreImpl::ResolveAsURLLoaderFactory( const GURL& url, mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, ResolveAsURLLoaderFactoryCallback callback) { - BlobURLLoaderFactory::Create( - registry_ ? registry_->GetBlobFromUrl(url) : mojo::NullRemote(), url, - std::move(receiver)); - std::move(callback).Run(registry_->GetUnsafeAgentClusterID(url)); + if (!registry_) { + BlobURLLoaderFactory::Create(mojo::NullRemote(), url, std::move(receiver)); + std::move(callback).Run(absl::nullopt, absl::nullopt); + return; + } + + BlobURLLoaderFactory::Create(registry_->GetBlobFromUrl(url), url, + std::move(receiver)); + std::move(callback).Run(registry_->GetUnsafeAgentClusterID(url), + registry_->GetUnsafeTopLevelSite(url)); } void BlobURLStoreImpl::ResolveForNavigation( @@ -137,6 +147,14 @@ void BlobURLStoreImpl::ResolveForNavigation( bool BlobURLStoreImpl::BlobUrlIsValid(const GURL& url, const char* method) const { + // TODO(crbug.com/1278268): Remove crash keys. + static crash_reporter::CrashKeyString<256> origin_key("origin"); + static crash_reporter::CrashKeyString<256> url_key("url"); + crash_reporter::ScopedCrashKeyString scoped_origin_key( + &origin_key, origin_.GetDebugString()); + crash_reporter::ScopedCrashKeyString scoped_url_key( + &url_key, url.possibly_invalid_spec()); + if (!url.SchemeIsBlob()) { mojo::ReportBadMessage( base::StrCat({"Invalid scheme passed to BlobURLStore::", method})); @@ -148,11 +166,15 @@ bool BlobURLStoreImpl::BlobUrlIsValid(const GURL& url, // `origin_` will always be a non-opaque file: origin for pages loaded from // file:// URLs. To deal with this, we treat file:// origins and // opaque origins separately from non-opaque origins. + // URLs created by blink::BlobURL::CreateBlobURL() will always get "blank" as + // origin if the scheme is local, which usually includes the file scheme and + // on Android also the content scheme. bool valid_origin = true; if (url_origin.scheme() == url::kFileScheme) { valid_origin = origin_.scheme() == url::kFileScheme; } else if (url_origin.opaque()) { - valid_origin = origin_.opaque() || origin_.scheme() == url::kFileScheme; + valid_origin = origin_.opaque() || + base::Contains(url::GetLocalSchemes(), origin_.scheme()); } else { valid_origin = origin_ == url_origin; } diff --git a/chromium/storage/browser/blob/blob_url_store_impl.h b/chromium/storage/browser/blob/blob_url_store_impl.h index 3db3a95d427..c46b7ffb18b 100644 --- a/chromium/storage/browser/blob/blob_url_store_impl.h +++ b/chromium/storage/browser/blob/blob_url_store_impl.h @@ -34,8 +34,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobURLStoreImpl void Register( mojo::PendingRemote<blink::mojom::Blob> blob, const GURL& url, - // TODO(https://crbug.com/1224926): Remove this once experiment is over. + // TODO(https://crbug.com/1224926): Remove these once experiment is over. const base::UnguessableToken& unsafe_agent_cluster_id, + const absl::optional<net::SchemefulSite>& unsafe_top_level_site, RegisterCallback callback) override; void Revoke(const GURL& url) override; void Resolve(const GURL& url, ResolveCallback callback) override; diff --git a/chromium/storage/browser/blob/blob_url_store_impl_unittest.cc b/chromium/storage/browser/blob/blob_url_store_impl_unittest.cc index 04c44794d55..0da6acf7a41 100644 --- a/chromium/storage/browser/blob/blob_url_store_impl_unittest.cc +++ b/chromium/storage/browser/blob/blob_url_store_impl_unittest.cc @@ -82,7 +82,7 @@ class BlobURLStoreImplTest : public testing::Test { const GURL& url) { base::RunLoop loop; store->Register(std::move(blob), url, agent_cluster_id_, - loop.QuitClosure()); + net::SchemefulSite(), loop.QuitClosure()); loop.Run(); } @@ -284,7 +284,8 @@ TEST_F(BlobURLStoreImplTest, ResolveAsURLLoaderFactory) { [](base::OnceClosure done, const base::UnguessableToken& agent_registered, const absl::optional<base::UnguessableToken>& - unsafe_agent_cluster_id) { + unsafe_agent_cluster_id, + const absl::optional<net::SchemefulSite>& unsafe_top_level_site) { EXPECT_EQ(agent_registered, unsafe_agent_cluster_id); std::move(done).Run(); }, diff --git a/chromium/storage/browser/blob/scoped_file.cc b/chromium/storage/browser/blob/scoped_file.cc index 9a9a2f9121c..98cac2782c8 100644 --- a/chromium/storage/browser/blob/scoped_file.cc +++ b/chromium/storage/browser/blob/scoped_file.cc @@ -27,7 +27,7 @@ ScopedFile::ScopedFile(const base::FilePath& path, DCHECK(path.empty() || policy != DELETE_ON_SCOPE_OUT || file_task_runner_.get()) << "path:" << path.value() << " policy:" << policy - << " runner:" << file_task_runner.get(); + << " runner:" << file_task_runner_.get(); } ScopedFile::ScopedFile(ScopedFile&& other) { diff --git a/chromium/storage/browser/database/database_quota_client.cc b/chromium/storage/browser/database/database_quota_client.cc index 8240da802da..eea28bf1fe1 100644 --- a/chromium/storage/browser/database/database_quota_client.cc +++ b/chromium/storage/browser/database/database_quota_client.cc @@ -18,6 +18,7 @@ #include "base/task/sequenced_task_runner.h" #include "base/task/task_runner_util.h" #include "base/threading/sequenced_task_runner_handle.h" +#include "components/services/storage/public/cpp/buckets/bucket_locator.h" #include "net/base/completion_once_callback.h" #include "net/base/net_errors.h" #include "storage/browser/database/database_tracker.h" @@ -42,17 +43,22 @@ DatabaseQuotaClient::~DatabaseQuotaClient() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -void DatabaseQuotaClient::GetStorageKeyUsage( - const StorageKey& storage_key, - StorageType type, - GetStorageKeyUsageCallback callback) { +void DatabaseQuotaClient::GetBucketUsage(const BucketLocator& bucket, + GetBucketUsageCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!callback.is_null()); - DCHECK_EQ(type, StorageType::kTemporary); + DCHECK_EQ(bucket.type, StorageType::kTemporary); + + // Skip non-default buckets because Storage Buckets are not planned to be + // supported by WebSQL. + if (!bucket.is_default) { + std::move(callback).Run(0); + return; + } OriginInfo info; - if (db_tracker_.GetOriginInfo(GetIdentifierFromOrigin(storage_key.origin()), - &info)) { + if (db_tracker_.GetOriginInfo( + GetIdentifierFromOrigin(bucket.storage_key.origin()), &info)) { std::move(callback).Run(info.TotalSize()); } else { std::move(callback).Run(0); @@ -77,42 +83,23 @@ void DatabaseQuotaClient::GetStorageKeysForType( std::move(callback).Run(all_storage_keys); } -void DatabaseQuotaClient::GetStorageKeysForHost( - StorageType type, - const std::string& host, - GetStorageKeysForHostCallback callback) { +void DatabaseQuotaClient::DeleteBucketData(const BucketLocator& bucket, + DeleteBucketDataCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!callback.is_null()); - DCHECK_EQ(type, StorageType::kTemporary); - - std::vector<StorageKey> host_storage_keys; - // In the vast majority of cases, this vector will end up with exactly one - // storage key. The storage key will be https://host or http://host. - host_storage_keys.reserve(1); + DCHECK_EQ(bucket.type, StorageType::kTemporary); - std::vector<std::string> origin_identifiers; - if (db_tracker_.GetAllOriginIdentifiers(&origin_identifiers)) { - for (const auto& identifier : origin_identifiers) { - StorageKey storage_key = StorageKey(GetOriginFromIdentifier(identifier)); - if (host == storage_key.origin().host()) - host_storage_keys.push_back(std::move(storage_key)); - } + // Skip non-default buckets because Storage Buckets are not planned to be + // supported by WebSQL. + if (!bucket.is_default) { + std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk); + return; } - std::move(callback).Run(host_storage_keys); -} - -void DatabaseQuotaClient::DeleteStorageKeyData( - const StorageKey& storage_key, - StorageType type, - DeleteStorageKeyDataCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!callback.is_null()); - DCHECK_EQ(type, StorageType::kTemporary); db_tracker_.DeleteDataForOrigin( - storage_key.origin(), + bucket.storage_key.origin(), base::BindOnce( - [](DeleteStorageKeyDataCallback callback, int result) { + [](DeleteBucketDataCallback callback, int result) { std::move(callback).Run( (result == net::OK) ? blink::mojom::QuotaStatusCode::kOk : blink::mojom::QuotaStatusCode::kUnknown); diff --git a/chromium/storage/browser/database/database_quota_client.h b/chromium/storage/browser/database/database_quota_client.h index d4c9b427314..96510e4213d 100644 --- a/chromium/storage/browser/database/database_quota_client.h +++ b/chromium/storage/browser/database/database_quota_client.h @@ -5,31 +5,25 @@ #ifndef STORAGE_BROWSER_DATABASE_DATABASE_QUOTA_CLIENT_H_ #define STORAGE_BROWSER_DATABASE_DATABASE_QUOTA_CLIENT_H_ -#include <set> -#include <string> - #include "base/component_export.h" #include "base/memory/ref_counted.h" #include "base/sequence_checker.h" #include "base/task/single_thread_task_runner.h" #include "base/thread_annotations.h" -#include "components/services/storage/public/cpp/storage_key_quota_client.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.h" -namespace blink { -class StorageKey; -} // namespace blink - namespace storage { class DatabaseTracker; +struct BucketLocator; // Integrates WebSQL databases with the quota management system. // // This interface is used on the IO thread by the quota manager. class COMPONENT_EXPORT(STORAGE_BROWSER) DatabaseQuotaClient - : public StorageKeyQuotaClient { + : public mojom::QuotaClient { public: explicit DatabaseQuotaClient(DatabaseTracker& tracker); @@ -38,18 +32,13 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) DatabaseQuotaClient ~DatabaseQuotaClient() override; - // StorageKeyQuotaClient method overrides. - void GetStorageKeyUsage(const blink::StorageKey& storage_key, - blink::mojom::StorageType type, - GetStorageKeyUsageCallback callback) override; + // mojom::QuotaClient method overrides. + void GetBucketUsage(const BucketLocator& bucket, + GetBucketUsageCallback callback) override; void GetStorageKeysForType(blink::mojom::StorageType type, GetStorageKeysForTypeCallback callback) override; - void GetStorageKeysForHost(blink::mojom::StorageType type, - const std::string& host, - GetStorageKeysForHostCallback callback) override; - void DeleteStorageKeyData(const blink::StorageKey& storage_key, - blink::mojom::StorageType type, - DeleteStorageKeyDataCallback callback) override; + void DeleteBucketData(const BucketLocator& bucket, + DeleteBucketDataCallback callback) override; void PerformStorageCleanup(blink::mojom::StorageType type, PerformStorageCleanupCallback callback) override; diff --git a/chromium/storage/browser/database/database_quota_client_unittest.cc b/chromium/storage/browser/database/database_quota_client_unittest.cc index 06f0483ca51..17fe31dbd1b 100644 --- a/chromium/storage/browser/database/database_quota_client_unittest.cc +++ b/chromium/storage/browser/database/database_quota_client_unittest.cc @@ -4,6 +4,7 @@ #include <stdint.h> +#include <cstdint> #include <map> #include <string> #include <utility> @@ -13,6 +14,7 @@ #include "base/callback_helpers.h" #include "base/containers/contains.h" #include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" #include "base/location.h" #include "base/memory/scoped_refptr.h" #include "base/run_loop.h" @@ -20,13 +22,17 @@ #include "base/task/single_thread_task_runner.h" #include "base/test/bind.h" #include "base/test/task_environment.h" +#include "base/test/test_future.h" #include "base/threading/sequenced_task_runner_handle.h" +#include "components/services/storage/public/cpp/buckets/bucket_locator.h" +#include "components/services/storage/public/cpp/quota_error_or.h" #include "components/services/storage/public/mojom/quota_client.mojom.h" #include "net/base/completion_once_callback.h" #include "net/base/net_errors.h" #include "storage/browser/database/database_quota_client.h" #include "storage/browser/database/database_tracker.h" #include "storage/browser/database/database_util.h" +#include "storage/browser/quota/quota_manager.h" #include "storage/browser/quota/quota_manager_proxy.h" #include "storage/browser/quota/special_storage_policy.h" #include "storage/common/database/database_identifier.h" @@ -46,11 +52,13 @@ static const blink::mojom::StorageType kTemp = // Mocks DatabaseTracker methods used by DatabaseQuotaClient. class MockDatabaseTracker : public DatabaseTracker { public: - MockDatabaseTracker() - : DatabaseTracker(base::FilePath(), - /*is_incognito=*/false, + MockDatabaseTracker(const base::FilePath& path, + bool is_incognito, + scoped_refptr<QuotaManagerProxy> quota_manager_proxy) + : DatabaseTracker(path, + is_incognito, /*special_storage_policy=*/nullptr, - /*quota_manager_proxy=*/nullptr, + quota_manager_proxy, DatabaseTracker::CreatePassKey()) {} bool GetOriginInfo(const std::string& origin_identifier, @@ -126,20 +134,28 @@ class MockDatabaseTracker : public DatabaseTracker { }; // Base class for our test fixtures. -class DatabaseQuotaClientTest : public testing::Test { +class DatabaseQuotaClientTest : public testing::TestWithParam<bool> { public: const blink::StorageKey kStorageKeyA; const blink::StorageKey kStorageKeyB; - const blink::StorageKey kStorageKeyOther; DatabaseQuotaClientTest() : kStorageKeyA( blink::StorageKey::CreateFromStringForTesting("http://host")), kStorageKeyB( - blink::StorageKey::CreateFromStringForTesting("http://host:8000")), - kStorageKeyOther( - blink::StorageKey::CreateFromStringForTesting("http://other")), - mock_tracker_(base::MakeRefCounted<MockDatabaseTracker>()) {} + blink::StorageKey::CreateFromStringForTesting("http://host:8000")) { + } + + void SetUp() override { + ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); + quota_manager_ = base::MakeRefCounted<QuotaManager>( + /*is_incognito_=*/false, data_dir_.GetPath(), + base::ThreadTaskRunnerHandle::Get(), + /*quota_change_callback=*/base::DoNothing(), + /*special_storage_policy=*/nullptr, GetQuotaSettingsFunc()); + mock_tracker_ = base::MakeRefCounted<MockDatabaseTracker>( + data_dir_.GetPath(), is_incognito(), quota_manager_->proxy()); + } void TearDown() override { base::RunLoop run_loop; @@ -151,19 +167,26 @@ class DatabaseQuotaClientTest : public testing::Test { run_loop.Run(); } - static int64_t GetStorageKeyUsage(mojom::QuotaClient& client, - const blink::StorageKey& storage_key, - blink::mojom::StorageType type) { - int result = -1; - base::RunLoop loop; - client.GetStorageKeyUsage(storage_key, type, - base::BindLambdaForTesting([&](int64_t usage) { - result = usage; - loop.Quit(); - })); - loop.Run(); - EXPECT_GT(result, -1); - return result; + bool is_incognito() const { return GetParam(); } + + BucketLocator CreateBucketForTesting(const blink::StorageKey& storage_key, + const std::string& name, + blink::mojom::StorageType type) { + base::test::TestFuture<storage::QuotaErrorOr<storage::BucketInfo>> + bucket_future; + quota_manager_->proxy()->CreateBucketForTesting( + storage_key, name, type, base::SequencedTaskRunnerHandle::Get(), + bucket_future.GetCallback()); + auto bucket = bucket_future.Take(); + EXPECT_TRUE(bucket.ok()); + return bucket->ToBucketLocator(); + } + + int64_t GetBucketUsage(mojom::QuotaClient& client, + const BucketLocator& bucket) { + base::test::TestFuture<int64_t> usage_future; + client.GetBucketUsage(bucket, usage_future.GetCallback()); + return usage_future.Get(); } static std::vector<blink::StorageKey> GetStorageKeysForType( @@ -181,85 +204,36 @@ class DatabaseQuotaClientTest : public testing::Test { return result; } - static std::vector<blink::StorageKey> GetStorageKeysForHost( - mojom::QuotaClient& client, - blink::mojom::StorageType type, - const std::string& host) { - std::vector<blink::StorageKey> result; - base::RunLoop loop; - client.GetStorageKeysForHost( - type, host, - base::BindLambdaForTesting( - [&](const std::vector<blink::StorageKey>& storage_keys) { - result = storage_keys; - loop.Quit(); - })); - loop.Run(); - return result; - } - - static blink::mojom::QuotaStatusCode DeleteStorageKeyData( - mojom::QuotaClient& client, - blink::mojom::StorageType type, - const blink::StorageKey& storage_key) { - blink::mojom::QuotaStatusCode result = - blink::mojom::QuotaStatusCode::kUnknown; - base::RunLoop loop; - client.DeleteStorageKeyData( - storage_key, type, - base::BindLambdaForTesting([&](blink::mojom::QuotaStatusCode code) { - result = code; - loop.Quit(); - })); - loop.Run(); - return result; + blink::mojom::QuotaStatusCode DeleteBucketData(mojom::QuotaClient& client, + const BucketLocator& bucket) { + base::test::TestFuture<blink::mojom::QuotaStatusCode> delete_future; + client.DeleteBucketData(bucket, delete_future.GetCallback()); + return delete_future.Get(); } + base::ScopedTempDir data_dir_; base::test::TaskEnvironment task_environment_; scoped_refptr<MockDatabaseTracker> mock_tracker_; + scoped_refptr<QuotaManager> quota_manager_; base::WeakPtrFactory<DatabaseQuotaClientTest> weak_factory_{this}; }; -TEST_F(DatabaseQuotaClientTest, GetStorageKeyUsage) { +TEST_P(DatabaseQuotaClientTest, GetBucketUsage) { DatabaseQuotaClient client(*mock_tracker_); + auto bucket_a = + CreateBucketForTesting(kStorageKeyA, kDefaultBucketName, kTemp); + auto bucket_b = + CreateBucketForTesting(kStorageKeyB, kDefaultBucketName, kTemp); - EXPECT_EQ(0, GetStorageKeyUsage(client, kStorageKeyA, kTemp)); + EXPECT_EQ(0, GetBucketUsage(client, bucket_a)); mock_tracker_->AddMockDatabase(kStorageKeyA.origin(), "fooDB", 1000); - EXPECT_EQ(1000, GetStorageKeyUsage(client, kStorageKeyA, kTemp)); + EXPECT_EQ(1000, GetBucketUsage(client, bucket_a)); - EXPECT_EQ(0, GetStorageKeyUsage(client, kStorageKeyB, kTemp)); + EXPECT_EQ(0, GetBucketUsage(client, bucket_b)); } -TEST_F(DatabaseQuotaClientTest, GetStorageKeysForHost) { - DatabaseQuotaClient client(*mock_tracker_); - - EXPECT_EQ(kStorageKeyA.origin().host(), kStorageKeyB.origin().host()); - EXPECT_NE(kStorageKeyA.origin().host(), kStorageKeyOther.origin().host()); - - std::vector<blink::StorageKey> storage_keys = - GetStorageKeysForHost(client, kTemp, kStorageKeyA.origin().host()); - EXPECT_TRUE(storage_keys.empty()); - - mock_tracker_->AddMockDatabase(kStorageKeyA.origin(), "fooDB", 1000); - storage_keys = - GetStorageKeysForHost(client, kTemp, kStorageKeyA.origin().host()); - EXPECT_EQ(storage_keys.size(), 1ul); - EXPECT_THAT(storage_keys, testing::Contains(kStorageKeyA)); - - mock_tracker_->AddMockDatabase(kStorageKeyB.origin(), "barDB", 1000); - storage_keys = - GetStorageKeysForHost(client, kTemp, kStorageKeyA.origin().host()); - EXPECT_EQ(storage_keys.size(), 2ul); - EXPECT_THAT(storage_keys, testing::Contains(kStorageKeyA)); - EXPECT_THAT(storage_keys, testing::Contains(kStorageKeyB)); - - EXPECT_TRUE( - GetStorageKeysForHost(client, kTemp, kStorageKeyOther.origin().host()) - .empty()); -} - -TEST_F(DatabaseQuotaClientTest, GetStorageKeysForType) { +TEST_P(DatabaseQuotaClientTest, GetStorageKeysForType) { DatabaseQuotaClient client(*mock_tracker_); EXPECT_TRUE(GetStorageKeysForType(client, kTemp).empty()); @@ -271,18 +245,34 @@ TEST_F(DatabaseQuotaClientTest, GetStorageKeysForType) { EXPECT_THAT(storage_keys, testing::Contains(kStorageKeyA)); } -TEST_F(DatabaseQuotaClientTest, DeleteStorageKeyData) { +TEST_P(DatabaseQuotaClientTest, DeleteBucketData) { DatabaseQuotaClient client(*mock_tracker_); + auto bucket_a = + CreateBucketForTesting(kStorageKeyA, kDefaultBucketName, kTemp); mock_tracker_->set_async_delete(false); EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, - DeleteStorageKeyData(client, kTemp, kStorageKeyA)); + DeleteBucketData(client, bucket_a)); EXPECT_EQ(1, mock_tracker_->delete_called_count()); mock_tracker_->set_async_delete(true); EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, - DeleteStorageKeyData(client, kTemp, kStorageKeyA)); + DeleteBucketData(client, bucket_a)); EXPECT_EQ(2, mock_tracker_->delete_called_count()); } +TEST_P(DatabaseQuotaClientTest, NonDefaultBucket) { + DatabaseQuotaClient client(*mock_tracker_); + auto bucket = CreateBucketForTesting(kStorageKeyA, "inbox_bucket", kTemp); + ASSERT_FALSE(bucket.is_default); + + EXPECT_EQ(0, GetBucketUsage(client, bucket)); + EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, + DeleteBucketData(client, bucket)); +} + +INSTANTIATE_TEST_SUITE_P(DatabaseQuotaClientTests, + DatabaseQuotaClientTest, + testing::Bool()); + } // namespace storage diff --git a/chromium/storage/browser/database/database_tracker.cc b/chromium/storage/browser/database/database_tracker.cc index 842886e2fce..74423c6f847 100644 --- a/chromium/storage/browser/database/database_tracker.cc +++ b/chromium/storage/browser/database/database_tracker.cc @@ -425,8 +425,8 @@ bool DatabaseTracker::DeleteClosedDatabase( quota_manager_proxy_->NotifyStorageModified( QuotaClientType::kDatabase, blink::StorageKey(GetOriginFromIdentifier(origin_identifier)), - blink::mojom::StorageType::kTemporary, -db_file_size, - base::Time::Now()); + blink::mojom::StorageType::kTemporary, -db_file_size, base::Time::Now(), + base::SequencedTaskRunnerHandle::Get(), base::DoNothing()); // Clean up the main database and invalidate the cached record. databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); @@ -504,8 +504,8 @@ bool DatabaseTracker::DeleteOrigin(const std::string& origin_identifier, quota_manager_proxy_->NotifyStorageModified( QuotaClientType::kDatabase, blink::StorageKey(GetOriginFromIdentifier(origin_identifier)), - blink::mojom::StorageType::kTemporary, -deleted_size, - base::Time::Now()); + blink::mojom::StorageType::kTemporary, -deleted_size, base::Time::Now(), + base::SequencedTaskRunnerHandle::Get(), base::DoNothing()); } return true; @@ -704,7 +704,8 @@ int64_t DatabaseTracker::UpdateOpenDatabaseInfoAndNotify( QuotaClientType::kDatabase, blink::StorageKey(GetOriginFromIdentifier(origin_id)), blink::mojom::StorageType::kTemporary, new_size - old_size, - base::Time::Now()); + base::Time::Now(), base::SequencedTaskRunnerHandle::Get(), + base::DoNothing()); for (auto& observer : observers_) observer.OnDatabaseSizeChanged(origin_id, name, new_size); } diff --git a/chromium/storage/browser/database/database_tracker_unittest.cc b/chromium/storage/browser/database/database_tracker_unittest.cc index 9a83aca64f4..32b315b90b9 100644 --- a/chromium/storage/browser/database/database_tracker_unittest.cc +++ b/chromium/storage/browser/database/database_tracker_unittest.cc @@ -270,7 +270,7 @@ class DatabaseTracker_TestHelper_Test { base::TouchFile(tracker->GetFullDBFilePath(kOrigin2, kDB3), three_days_ago, three_days_ago)); - // Delete databases modified since yesterday. db2 is whitelisted. + // Delete databases modified since yesterday. db2 is in the allowlist. base::Time yesterday = base::Time::Now(); yesterday -= base::Days(1); diff --git a/chromium/storage/browser/database/vfs_backend.cc b/chromium/storage/browser/database/vfs_backend.cc index beb7d59db2a..b9b8a8f363b 100644 --- a/chromium/storage/browser/database/vfs_backend.cc +++ b/chromium/storage/browser/database/vfs_backend.cc @@ -12,7 +12,7 @@ #include "build/build_config.h" #include "third_party/sqlite/sqlite3.h" -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) #include <windows.h> #endif @@ -129,7 +129,7 @@ int VfsBackend::DeleteFile(const base::FilePath& file_path, bool sync_dir) { return SQLITE_IOERR_DELETE; int error_code = SQLITE_OK; -#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) if (sync_dir) { base::File dir(file_path.DirName(), base::File::FLAG_READ); if (dir.IsValid()) { @@ -145,9 +145,9 @@ int VfsBackend::DeleteFile(const base::FilePath& file_path, bool sync_dir) { // static uint32_t VfsBackend::GetFileAttributes(const base::FilePath& file_path) { -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) uint32_t attributes = ::GetFileAttributes(file_path.value().c_str()); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) uint32_t attributes = 0; if (!access(file_path.value().c_str(), R_OK)) attributes |= static_cast<uint32_t>(R_OK); diff --git a/chromium/storage/browser/file_system/dragged_file_util_unittest.cc b/chromium/storage/browser/file_system/dragged_file_util_unittest.cc index 20c902e9612..22681a159b1 100644 --- a/chromium/storage/browser/file_system/dragged_file_util_unittest.cc +++ b/chromium/storage/browser/file_system/dragged_file_util_unittest.cc @@ -52,9 +52,7 @@ constexpr const base::FilePath::CharType* kRootPaths[] = { }; base::FilePath GetTopLevelPath(const base::FilePath& path) { - std::vector<base::FilePath::StringType> components; - path.GetComponents(&components); - return base::FilePath(components[0]); + return base::FilePath(path.GetComponents()[0]); } bool IsDirectoryEmpty(FileSystemContext* context, const FileSystemURL& url) { @@ -372,7 +370,7 @@ TEST_F(DraggedFileUtilTest, ReadDirectoryTest) { entry.name = current.BaseName(); expected_entry_map[entry.name.value()] = entry; -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) // Creates a symlink for each file/directory. // They should be ignored by ReadDirectory, so we don't add them // to expected_entry_map. diff --git a/chromium/storage/browser/file_system/external_mount_points.cc b/chromium/storage/browser/file_system/external_mount_points.cc index 6ee8f8aa5f5..7630b664fd4 100644 --- a/chromium/storage/browser/file_system/external_mount_points.cc +++ b/chromium/storage/browser/file_system/external_mount_points.cc @@ -160,8 +160,8 @@ bool ExternalMountPoints::CrackVirtualPath( return false; // The virtual_path should comprise of <mount_name> and <relative_path> parts. - std::vector<base::FilePath::StringType> components; - virtual_path.GetComponents(&components); + std::vector<base::FilePath::StringType> components = + virtual_path.GetComponents(); if (components.size() < 1) return false; diff --git a/chromium/storage/browser/file_system/file_system_context.cc b/chromium/storage/browser/file_system/file_system_context.cc index 0b2c0fa920b..808295dbe43 100644 --- a/chromium/storage/browser/file_system/file_system_context.cc +++ b/chromium/storage/browser/file_system/file_system_context.cc @@ -110,6 +110,7 @@ int FileSystemContext::GetPermissionPolicy(FileSystemType type) { case kFileSystemTypeArcContent: case kFileSystemTypeArcDocumentsProvider: case kFileSystemTypeSmbFs: + case kFileSystemTypeFuseBox: return FILE_PERMISSION_USE_FILE_PERMISSION; case kFileSystemTypeRestrictedLocal: @@ -427,10 +428,10 @@ void FileSystemContext::OpenFileSystem(const blink::StorageKey& storage_key, // Quota manager isn't provided by all tests. if (quota_manager_proxy()) { // Ensure default bucket for `storage_key` exists so that Quota API - // is aware of the usage. Bucket type 'temporary' is used even though the - // actual storage type of the file system being opened may be different. - quota_manager_proxy()->GetOrCreateBucket( - storage_key, kDefaultBucketName, io_task_runner_.get(), + // is aware of the usage. + quota_manager_proxy()->GetOrCreateBucketDeprecated( + storage_key, kDefaultBucketName, FileSystemTypeToQuotaStorageType(type), + io_task_runner_.get(), base::BindOnce(&FileSystemContext::OnGetOrCreateBucket, weak_factory_.GetWeakPtr(), storage_key, type, mode, std::move(callback))); @@ -445,11 +446,6 @@ void FileSystemContext::OnGetOrCreateBucket( OpenFileSystemMode mode, OpenFileSystemCallback callback, QuotaErrorOr<BucketInfo> result) { - if (!result.ok()) { - std::move(callback).Run(GURL(), std::string(), - base::File::FILE_ERROR_FAILED); - return; - } ResolveURLOnOpenFileSystem(storage_key, type, mode, std::move(callback)); } @@ -467,9 +463,22 @@ void FileSystemContext::ResolveURLOnOpenFileSystem( return; } + // Bind `this` to the callback to ensure this instance stays alive while the + // URL is resolving. backend->ResolveURL( CreateCrackedFileSystemURL(storage_key, type, base::FilePath()), mode, - std::move(callback)); + base::BindOnce(&FileSystemContext::DidResolveURLOnOpenFileSystem, this, + std::move(callback))); +} + +void FileSystemContext::DidResolveURLOnOpenFileSystem( + OpenFileSystemCallback callback, + const GURL& filesystem_root, + const std::string& filesystem_name, + base::File::Error error) { + DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); + + std::move(callback).Run(filesystem_root, filesystem_name, error); } void FileSystemContext::ResolveURL(const FileSystemURL& url, diff --git a/chromium/storage/browser/file_system/file_system_context.h b/chromium/storage/browser/file_system/file_system_context.h index d92761afb80..15b2f72ef95 100644 --- a/chromium/storage/browser/file_system/file_system_context.h +++ b/chromium/storage/browser/file_system/file_system_context.h @@ -414,6 +414,10 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) FileSystemContext FileSystemType type, OpenFileSystemMode mode, OpenFileSystemCallback callback); + void DidResolveURLOnOpenFileSystem(OpenFileSystemCallback callback, + const GURL& filesystem_root, + const std::string& filesystem_name, + base::File::Error error); // Returns a FileSystemBackend, used only by test code. SandboxFileSystemBackend* sandbox_backend() const { 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 abf6649cd9c..7268996ae35 100644 --- a/chromium/storage/browser/file_system/file_system_quota_client.cc +++ b/chromium/storage/browser/file_system/file_system_quota_client.cc @@ -19,6 +19,7 @@ #include "base/location.h" #include "base/sequence_checker.h" #include "base/task/sequenced_task_runner.h" +#include "components/services/storage/public/cpp/buckets/bucket_locator.h" #include "storage/browser/file_system/file_system_backend.h" #include "storage/browser/file_system/file_system_context.h" #include "storage/common/file_system/file_system_types.h" @@ -92,16 +93,6 @@ std::vector<blink::StorageKey> GetStorageKeysForTypeOnFileTaskRunner( return quota_util->GetStorageKeysForTypeOnFileTaskRunner(type); } -std::vector<blink::StorageKey> GetStorageKeysForHostOnFileTaskRunner( - FileSystemContext* context, - FileSystemType type, - const std::string& host) { - FileSystemQuotaUtil* quota_util = context->GetQuotaUtil(type); - if (!quota_util) - return {}; - return quota_util->GetStorageKeysForHostOnFileTaskRunner(type, host); -} - blink::mojom::QuotaStatusCode DeleteStorageKeyOnFileTaskRunner( FileSystemContext* context, const blink::StorageKey& storage_key, @@ -139,15 +130,21 @@ FileSystemQuotaClient::~FileSystemQuotaClient() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -void FileSystemQuotaClient::GetStorageKeyUsage( - const blink::StorageKey& storage_key, - blink::mojom::StorageType storage_type, - GetStorageKeyUsageCallback callback) { +void FileSystemQuotaClient::GetBucketUsage(const BucketLocator& bucket, + GetBucketUsageCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!callback.is_null()); + // Skip non-default buckets until Storage Buckets are supported for + // FileSystem. + // TODO(crbug.com/1218100): Integrate with StorageBuckets. + if (!bucket.is_default) { + std::move(callback).Run(0); + return; + } + base::span<const FileSystemType> types = - QuotaStorageTypeToFileSystemTypes(storage_type); + QuotaStorageTypeToFileSystemTypes(bucket.type); base::RepeatingCallback<void(int64_t)> barrier = base::BarrierCallback<int64_t>( @@ -165,7 +162,8 @@ void FileSystemQuotaClient::GetStorageKeyUsage( base::BindOnce( &FileSystemQuotaUtil::GetStorageKeyUsageOnFileTaskRunner, base::Unretained(quota_util), - base::RetainedRef(file_system_context_.get()), storage_key, type), + base::RetainedRef(file_system_context_.get()), bucket.storage_key, + type), barrier); } else { barrier.Run(0); @@ -197,41 +195,22 @@ void FileSystemQuotaClient::GetStorageKeysForType( } } -void FileSystemQuotaClient::GetStorageKeysForHost( - blink::mojom::StorageType storage_type, - const std::string& host, - GetStorageKeysForHostCallback callback) { +void FileSystemQuotaClient::DeleteBucketData( + const BucketLocator& bucket, + DeleteBucketDataCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!callback.is_null()); - base::span<const FileSystemType> types = - QuotaStorageTypeToFileSystemTypes(storage_type); - - base::RepeatingCallback<void(std::vector<blink::StorageKey>)> barrier = - base::BarrierCallback<std::vector<blink::StorageKey>>( - types.size(), - base::BindOnce(&MergeWithoutDuplicates<blink::StorageKey>) - .Then(std::move(callback))); - - for (auto type : types) { - file_task_runner()->PostTaskAndReplyWithResult( - FROM_HERE, - base::BindOnce(&GetStorageKeysForHostOnFileTaskRunner, - base::RetainedRef(file_system_context_.get()), type, - host), - barrier); + // Skip non-default buckets until Storage Buckets are supported for + // FileSystem. + // TODO(crbug.com/1218100): Integrate with StorageBuckets. + if (!bucket.is_default) { + std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk); + return; } -} - -void FileSystemQuotaClient::DeleteStorageKeyData( - const blink::StorageKey& storage_key, - blink::mojom::StorageType type, - DeleteStorageKeyDataCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!callback.is_null()); base::span<const FileSystemType> fs_types = - QuotaStorageTypeToFileSystemTypes(type); + QuotaStorageTypeToFileSystemTypes(bucket.type); base::RepeatingCallback<void(blink::mojom::QuotaStatusCode)> barrier = base::BarrierCallback<blink::mojom::QuotaStatusCode>( @@ -250,7 +229,7 @@ void FileSystemQuotaClient::DeleteStorageKeyData( FROM_HERE, base::BindOnce(&DeleteStorageKeyOnFileTaskRunner, base::RetainedRef(file_system_context_.get()), - storage_key, fs_type), + bucket.storage_key, fs_type), barrier); } } diff --git a/chromium/storage/browser/file_system/file_system_quota_client.h b/chromium/storage/browser/file_system/file_system_quota_client.h index 3fe518389f2..8c52337042a 100644 --- a/chromium/storage/browser/file_system/file_system_quota_client.h +++ b/chromium/storage/browser/file_system/file_system_quota_client.h @@ -5,14 +5,12 @@ #ifndef STORAGE_BROWSER_FILE_SYSTEM_FILE_SYSTEM_QUOTA_CLIENT_H_ #define STORAGE_BROWSER_FILE_SYSTEM_FILE_SYSTEM_QUOTA_CLIENT_H_ -#include <string> - #include "base/component_export.h" #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" #include "base/sequence_checker.h" #include "base/thread_annotations.h" -#include "components/services/storage/public/cpp/storage_key_quota_client.h" +#include "components/services/storage/public/mojom/quota_client.mojom.h" #include "storage/browser/file_system/file_system_quota_util.h" #include "storage/browser/quota/quota_client_type.h" #include "storage/common/file_system/file_system_types.h" @@ -22,18 +20,15 @@ namespace base { class SequencedTaskRunner; } -namespace blink { -class StorageKey; -} // namespace blink - namespace storage { class FileSystemContext; +struct BucketLocator; // All of the public methods of this class are called by the quota manager // (except for the constructor/destructor). class COMPONENT_EXPORT(STORAGE_BROWSER) FileSystemQuotaClient - : public StorageKeyQuotaClient { + : public mojom::QuotaClient { public: explicit FileSystemQuotaClient(FileSystemContext* file_system_context); ~FileSystemQuotaClient() override; @@ -41,18 +36,13 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) FileSystemQuotaClient FileSystemQuotaClient(const FileSystemQuotaClient&) = delete; FileSystemQuotaClient& operator=(const FileSystemQuotaClient&) = delete; - // QuotaClient methods. - void GetStorageKeyUsage(const blink::StorageKey& storage_key, - blink::mojom::StorageType type, - GetStorageKeyUsageCallback callback) override; + // mojom::QuotaClient methods. + void GetBucketUsage(const BucketLocator& bucket, + GetBucketUsageCallback callback) override; void GetStorageKeysForType(blink::mojom::StorageType type, GetStorageKeysForTypeCallback callback) override; - void GetStorageKeysForHost(blink::mojom::StorageType type, - const std::string& host, - GetStorageKeysForHostCallback callback) override; - void DeleteStorageKeyData(const blink::StorageKey& storage_key, - blink::mojom::StorageType type, - DeleteStorageKeyDataCallback callback) override; + void DeleteBucketData(const BucketLocator& bucket, + DeleteBucketDataCallback callback) override; void PerformStorageCleanup(blink::mojom::StorageType type, PerformStorageCleanupCallback callback) override; diff --git a/chromium/storage/browser/file_system/file_system_quota_client_unittest.cc b/chromium/storage/browser/file_system/file_system_quota_client_unittest.cc index f8c7fee5bb4..b7bb5a5caec 100644 --- a/chromium/storage/browser/file_system/file_system_quota_client_unittest.cc +++ b/chromium/storage/browser/file_system/file_system_quota_client_unittest.cc @@ -14,14 +14,20 @@ #include "base/run_loop.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "base/test/test_future.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "components/services/storage/public/cpp/buckets/bucket_locator.h" #include "components/services/storage/public/mojom/quota_client.mojom.h" #include "storage/browser/file_system/file_system_context.h" #include "storage/browser/file_system/file_system_quota_client.h" #include "storage/browser/file_system/file_system_usage_cache.h" #include "storage/browser/file_system/file_system_util.h" #include "storage/browser/file_system/obfuscated_file_util.h" +#include "storage/browser/quota/quota_manager.h" #include "storage/browser/quota/quota_manager_proxy.h" #include "storage/browser/test/async_file_test_helper.h" +#include "storage/browser/test/mock_quota_manager.h" +#include "storage/browser/test/mock_quota_manager_proxy.h" #include "storage/browser/test/test_file_system_context.h" #include "storage/common/file_system/file_system_types.h" #include "storage/common/file_system/file_system_util.h" @@ -63,8 +69,16 @@ class FileSystemQuotaClientTest : public testing::TestWithParam<bool> { blink::features::kPersistentQuotaIsTemporaryQuota); } ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); + + quota_manager_ = base::MakeRefCounted<MockQuotaManager>( + /*is_incognito_=*/false, data_dir_.GetPath(), + base::ThreadTaskRunnerHandle::Get(), + /*special_storage_policy=*/nullptr); + quota_manager_proxy_ = base::MakeRefCounted<storage::MockQuotaManagerProxy>( + quota_manager_.get(), base::ThreadTaskRunnerHandle::Get()); + file_system_context_ = CreateFileSystemContextForTesting( - /*quota_manager_proxy=*/nullptr, data_dir_.GetPath()); + quota_manager_proxy_, data_dir_.GetPath()); } bool persistent_quota_is_temporary_quota() const { return GetParam(); } @@ -82,19 +96,16 @@ class FileSystemQuotaClientTest : public testing::TestWithParam<bool> { return file_system_context_.get(); } - void GetStorageKeyUsageAsync(storage::mojom::QuotaClient& quota_client, - const std::string& origin_url, - StorageType type) { - quota_client.GetStorageKeyUsage( - StorageKey::CreateFromStringForTesting(origin_url), type, - base::BindOnce(&FileSystemQuotaClientTest::OnGetUsage, - weak_factory_.GetWeakPtr())); + void GetBucketUsageAsync(storage::mojom::QuotaClient& quota_client, + const BucketLocator& bucket) { + quota_client.GetBucketUsage( + bucket, base::BindOnce(&FileSystemQuotaClientTest::OnGetUsage, + weak_factory_.GetWeakPtr())); } - int64_t GetStorageKeyUsage(storage::mojom::QuotaClient& quota_client, - const std::string& origin_url, - StorageType type) { - GetStorageKeyUsageAsync(quota_client, origin_url, type); + int64_t GetBucketUsage(storage::mojom::QuotaClient& quota_client, + const BucketLocator& bucket) { + GetBucketUsageAsync(quota_client, bucket); base::RunLoop().RunUntilIdle(); return usage_; } @@ -110,27 +121,11 @@ class FileSystemQuotaClientTest : public testing::TestWithParam<bool> { return storage_keys_; } - const std::vector<StorageKey>& GetStorageKeysForHost( - storage::mojom::QuotaClient& quota_client, - StorageType type, - const std::string& host) { - storage_keys_.clear(); - quota_client.GetStorageKeysForHost( - type, host, - base::BindOnce(&FileSystemQuotaClientTest::OnGetStorageKeys, - weak_factory_.GetWeakPtr())); - base::RunLoop().RunUntilIdle(); - return storage_keys_; - } - - void RunAdditionalStorageKeyUsageTask( - storage::mojom::QuotaClient& quota_client, - const std::string& origin_url, - StorageType type) { - quota_client.GetStorageKeyUsage( - StorageKey::CreateFromStringForTesting(origin_url), type, - base::BindOnce(&FileSystemQuotaClientTest::OnGetAdditionalUsage, - weak_factory_.GetWeakPtr())); + void RunAdditionalBucketUsageTask(storage::mojom::QuotaClient& quota_client, + const BucketLocator& bucket) { + quota_client.GetBucketUsage( + bucket, base::BindOnce(&FileSystemQuotaClientTest::OnGetAdditionalUsage, + weak_factory_.GetWeakPtr())); } bool CreateFileSystemDirectory(const base::FilePath& file_path, @@ -171,6 +166,11 @@ class FileSystemQuotaClientTest : public testing::TestWithParam<bool> { for (const TestFile& file : files) { base::FilePath path = base::FilePath().AppendASCII(file.name); if (file.isDirectory) { + auto bucket = + GetOrCreateBucket(file.origin_url, kDefaultBucketName, + FileSystemTypeToQuotaStorageType(file.type)); + quota_manager_->SetQuota(bucket.storage_key, bucket.type, + 1024 * 1024 * 100); ASSERT_TRUE( CreateFileSystemDirectory(path, file.origin_url, file.type)); if (path.empty()) { @@ -179,8 +179,7 @@ class FileSystemQuotaClientTest : public testing::TestWithParam<bool> { // create it later, this will fail due to a quota mismatch. If we // call this before we create the root, it succeeds, but hasn't // actually created the cache. - GetStorageKeyUsage(quota_client, file.origin_url, - FileSystemTypeToQuotaStorageType(file.type)); + GetBucketUsage(quota_client, bucket); } } else { ASSERT_TRUE( @@ -209,18 +208,38 @@ class FileSystemQuotaClientTest : public testing::TestWithParam<bool> { return file_paths_cost; } - void DeleteStorageKeyData(FileSystemQuotaClient* quota_client, - const std::string& origin, - StorageType type) { - deletion_status_ = blink::mojom::QuotaStatusCode::kUnknown; - quota_client->DeleteStorageKeyData( - StorageKey::CreateFromStringForTesting(origin), type, - base::BindOnce(&FileSystemQuotaClientTest::OnDeleteStorageKey, - weak_factory_.GetWeakPtr())); + void DeleteBucketData(FileSystemQuotaClient* quota_client, + const BucketLocator& bucket) { + base::test::TestFuture<blink::mojom::QuotaStatusCode> future; + quota_client->DeleteBucketData(bucket, future.GetCallback()); + ASSERT_EQ(future.Get(), blink::mojom::QuotaStatusCode::kOk); + } + + BucketLocator GetOrCreateBucket(const std::string& origin, + const std::string& name, + StorageType type) { + base::test::TestFuture<storage::QuotaErrorOr<storage::BucketInfo>> future; + quota_manager_->GetOrCreateBucketDeprecated( + blink::StorageKey::CreateFromStringForTesting(origin), name, type, + future.GetCallback()); + auto bucket = future.Take(); + EXPECT_TRUE(bucket.ok()); + return bucket->ToBucketLocator(); + } + + BucketLocator GetBucket(const std::string& origin, + const std::string& name, + StorageType type) { + base::test::TestFuture<storage::QuotaErrorOr<storage::BucketInfo>> future; + quota_manager_->GetBucket( + blink::StorageKey::CreateFromStringForTesting(origin), name, type, + future.GetCallback()); + auto bucket = future.Take(); + EXPECT_TRUE(bucket.ok()); + return bucket->ToBucketLocator(); } int64_t usage() const { return usage_; } - blink::mojom::QuotaStatusCode status() { return deletion_status_; } int additional_callback_count() const { return additional_callback_count_; } void set_additional_callback_count(int count) { additional_callback_count_ = count; @@ -237,26 +256,24 @@ class FileSystemQuotaClientTest : public testing::TestWithParam<bool> { ++additional_callback_count_; } - void OnDeleteStorageKey(blink::mojom::QuotaStatusCode status) { - deletion_status_ = status; - } - base::test::ScopedFeatureList feature_list_; base::ScopedTempDir data_dir_; base::test::TaskEnvironment task_environment_; scoped_refptr<FileSystemContext> file_system_context_; + scoped_refptr<MockQuotaManager> quota_manager_; + scoped_refptr<storage::MockQuotaManagerProxy> quota_manager_proxy_; + int64_t usage_ = 0; int additional_callback_count_ = 0; std::vector<StorageKey> storage_keys_; - blink::mojom::QuotaStatusCode deletion_status_ = - blink::mojom::QuotaStatusCode::kUnknown; base::WeakPtrFactory<FileSystemQuotaClientTest> weak_factory_{this}; }; TEST_P(FileSystemQuotaClientTest, NoFileSystemTest) { FileSystemQuotaClient quota_client(GetFileSystemContext()); - EXPECT_EQ(0, GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary)); + auto bucket = GetOrCreateBucket(kDummyURL1, kDefaultBucketName, kTemporary); + EXPECT_EQ(0, GetBucketUsage(quota_client, bucket)); } TEST_P(FileSystemQuotaClientTest, NoFileTest) { @@ -265,13 +282,23 @@ TEST_P(FileSystemQuotaClientTest, NoFileTest) { InitializeOriginFiles(quota_client, {{true, "", 0, kDummyURL1, kFileSystemTypeTemporary}}); + auto bucket = GetOrCreateBucket(kDummyURL1, kDefaultBucketName, kTemporary); for (int i = 0; i < 2; i++) { - EXPECT_EQ(0, GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary)); + EXPECT_EQ(0, GetBucketUsage(quota_client, bucket)); } } +TEST_P(FileSystemQuotaClientTest, NonDefaultBucket) { + FileSystemQuotaClient quota_client(GetFileSystemContext()); + auto bucket = GetOrCreateBucket(kDummyURL1, "logs_bucket", kTemporary); + ASSERT_FALSE(bucket.is_default); + EXPECT_EQ(0, GetBucketUsage(quota_client, bucket)); + DeleteBucketData("a_client, bucket); +} + TEST_P(FileSystemQuotaClientTest, OneFileTest) { FileSystemQuotaClient quota_client(GetFileSystemContext()); + const std::vector<TestFile> kFiles = { {true, "", 0, kDummyURL1, kFileSystemTypeTemporary}, {false, "foo", 4921, kDummyURL1, kFileSystemTypeTemporary}, @@ -280,9 +307,9 @@ TEST_P(FileSystemQuotaClientTest, OneFileTest) { const int64_t file_paths_cost = ComputeFilePathsCostForOriginAndType( kFiles, kDummyURL1, kFileSystemTypeTemporary); + auto bucket = GetBucket(kDummyURL1, kDefaultBucketName, kTemporary); for (int i = 0; i < 2; i++) { - EXPECT_EQ(4921 + file_paths_cost, - GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary)); + EXPECT_EQ(4921 + file_paths_cost, GetBucketUsage(quota_client, bucket)); } } @@ -297,9 +324,10 @@ TEST_P(FileSystemQuotaClientTest, TwoFilesTest) { const int64_t file_paths_cost = ComputeFilePathsCostForOriginAndType( kFiles, kDummyURL1, kFileSystemTypeTemporary); + auto bucket = GetBucket(kDummyURL1, kDefaultBucketName, kTemporary); for (int i = 0; i < 2; i++) { EXPECT_EQ(10310 + 41 + file_paths_cost, - GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary)); + GetBucketUsage(quota_client, bucket)); } } @@ -315,9 +343,9 @@ TEST_P(FileSystemQuotaClientTest, EmptyFilesTest) { const int64_t file_paths_cost = ComputeFilePathsCostForOriginAndType( kFiles, kDummyURL1, kFileSystemTypeTemporary); + auto bucket = GetBucket(kDummyURL1, kDefaultBucketName, kTemporary); for (int i = 0; i < 2; i++) { - EXPECT_EQ(file_paths_cost, - GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary)); + EXPECT_EQ(file_paths_cost, GetBucketUsage(quota_client, bucket)); } } @@ -333,9 +361,10 @@ TEST_P(FileSystemQuotaClientTest, SubDirectoryTest) { const int64_t file_paths_cost = ComputeFilePathsCostForOriginAndType( kFiles, kDummyURL1, kFileSystemTypeTemporary); + auto bucket = GetBucket(kDummyURL1, kDefaultBucketName, kTemporary); for (int i = 0; i < 2; i++) { EXPECT_EQ(11921 + 4814 + file_paths_cost, - GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary)); + GetBucketUsage(quota_client, bucket)); } } @@ -361,14 +390,17 @@ TEST_P(FileSystemQuotaClientTest, MultiTypeTest) { for (int i = 0; i < 2; i++) { if (persistent_quota_is_temporary_quota()) { + auto bucket = GetBucket(kDummyURL1, kDefaultBucketName, kTemporary); EXPECT_EQ(133 + 14 + file_paths_cost_temporary + 193 + 9 + file_paths_cost_persistent, - GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary)); + GetBucketUsage(quota_client, bucket)); } else { + auto bucket_temp = GetBucket(kDummyURL1, kDefaultBucketName, kTemporary); + auto bucket_perm = GetBucket(kDummyURL1, kDefaultBucketName, kPersistent); EXPECT_EQ(133 + 14 + file_paths_cost_temporary, - GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary)); + GetBucketUsage(quota_client, bucket_temp)); EXPECT_EQ(193 + 9 + file_paths_cost_persistent, - GetStorageKeyUsage(quota_client, kDummyURL1, kPersistent)); + GetBucketUsage(quota_client, bucket_perm)); } } } @@ -409,21 +441,29 @@ TEST_P(FileSystemQuotaClientTest, MultiDomainTest) { for (int i = 0; i < 2; i++) { if (persistent_quota_is_temporary_quota()) { + auto bucket1 = GetBucket(kDummyURL1, kDefaultBucketName, kTemporary); + auto bucket2 = GetBucket(kDummyURL2, kDefaultBucketName, kTemporary); EXPECT_EQ(1331 + 134 + file_paths_cost_temporary1 + 1903 + 19 + file_paths_cost_persistent1, - GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary)); + GetBucketUsage(quota_client, bucket1)); EXPECT_EQ(1319 + 113 + file_paths_cost_temporary2 + 2013 + 18 + file_paths_cost_persistent2, - GetStorageKeyUsage(quota_client, kDummyURL2, kTemporary)); + GetBucketUsage(quota_client, bucket2)); } else { + auto bucket1_temp = GetBucket(kDummyURL1, kDefaultBucketName, kTemporary); + auto bucket1_perm = + GetBucket(kDummyURL1, kDefaultBucketName, kPersistent); + auto bucket2_temp = GetBucket(kDummyURL2, kDefaultBucketName, kTemporary); + auto bucket2_perm = + GetBucket(kDummyURL2, kDefaultBucketName, kPersistent); EXPECT_EQ(1331 + 134 + file_paths_cost_temporary1, - GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary)); + GetBucketUsage(quota_client, bucket1_temp)); EXPECT_EQ(1903 + 19 + file_paths_cost_persistent1, - GetStorageKeyUsage(quota_client, kDummyURL1, kPersistent)); + GetBucketUsage(quota_client, bucket1_perm)); EXPECT_EQ(1319 + 113 + file_paths_cost_temporary2, - GetStorageKeyUsage(quota_client, kDummyURL2, kTemporary)); + GetBucketUsage(quota_client, bucket2_temp)); EXPECT_EQ(2013 + 18 + file_paths_cost_persistent2, - GetStorageKeyUsage(quota_client, kDummyURL2, kPersistent)); + GetBucketUsage(quota_client, bucket2_perm)); } } } @@ -441,18 +481,19 @@ TEST_P(FileSystemQuotaClientTest, GetUsage_MultipleTasks) { // Dispatching three GetUsage tasks. set_additional_callback_count(0); - GetStorageKeyUsageAsync(quota_client, kDummyURL1, kTemporary); - RunAdditionalStorageKeyUsageTask(quota_client, kDummyURL1, kTemporary); - RunAdditionalStorageKeyUsageTask(quota_client, kDummyURL1, kTemporary); + auto bucket = GetBucket(kDummyURL1, kDefaultBucketName, kTemporary); + GetBucketUsageAsync(quota_client, bucket); + RunAdditionalBucketUsageTask(quota_client, bucket); + RunAdditionalBucketUsageTask(quota_client, bucket); base::RunLoop().RunUntilIdle(); EXPECT_EQ(11 + 22 + file_paths_cost, usage()); EXPECT_EQ(2, additional_callback_count()); // Once more, in a different order. set_additional_callback_count(0); - RunAdditionalStorageKeyUsageTask(quota_client, kDummyURL1, kTemporary); - GetStorageKeyUsageAsync(quota_client, kDummyURL1, kTemporary); - RunAdditionalStorageKeyUsageTask(quota_client, kDummyURL1, kTemporary); + RunAdditionalBucketUsageTask(quota_client, bucket); + GetBucketUsageAsync(quota_client, bucket); + RunAdditionalBucketUsageTask(quota_client, bucket); base::RunLoop().RunUntilIdle(); EXPECT_EQ(11 + 22 + file_paths_cost, usage()); EXPECT_EQ(2, additional_callback_count()); @@ -481,38 +522,6 @@ TEST_P(FileSystemQuotaClientTest, GetStorageKeysForType) { } } -TEST_P(FileSystemQuotaClientTest, GetStorageKeysForHost) { - FileSystemQuotaClient quota_client(GetFileSystemContext()); - const char* kURL1 = "http://foo.com/"; - const char* kURL2 = "https://foo.com/"; - const char* kURL3 = "http://foo.com:1/"; - const char* kURL4 = "http://foo2.com/"; - const char* kURL5 = "http://foo.com:2/"; - InitializeOriginFiles(quota_client, - { - {true, "", 0, kURL1, kFileSystemTypeTemporary}, - {true, "", 0, kURL2, kFileSystemTypeTemporary}, - {true, "", 0, kURL3, kFileSystemTypeTemporary}, - {true, "", 0, kURL4, kFileSystemTypeTemporary}, - {true, "", 0, kURL5, kFileSystemTypePersistent}, - }); - - if (persistent_quota_is_temporary_quota()) { - EXPECT_THAT(GetStorageKeysForHost(quota_client, kTemporary, "foo.com"), - testing::UnorderedElementsAre( - StorageKey::CreateFromStringForTesting(kURL1), - StorageKey::CreateFromStringForTesting(kURL2), - StorageKey::CreateFromStringForTesting(kURL3), - StorageKey::CreateFromStringForTesting(kURL5))); - } else { - EXPECT_THAT(GetStorageKeysForHost(quota_client, kTemporary, "foo.com"), - testing::UnorderedElementsAre( - StorageKey::CreateFromStringForTesting(kURL1), - StorageKey::CreateFromStringForTesting(kURL2), - StorageKey::CreateFromStringForTesting(kURL3))); - } -} - TEST_P(FileSystemQuotaClientTest, DeleteOriginTest) { FileSystemQuotaClient quota_client(GetFileSystemContext()); const std::vector<TestFile> kFiles = { @@ -551,43 +560,58 @@ TEST_P(FileSystemQuotaClientTest, DeleteOriginTest) { ComputeFilePathsCostForOriginAndType(kFiles, "https://bar.com/", kFileSystemTypePersistent); - DeleteStorageKeyData("a_client, "http://foo.com/", kTemporary); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, status()); + auto foo_temp_bucket = + GetBucket("http://foo.com/", kDefaultBucketName, kTemporary); + DeleteBucketData("a_client, foo_temp_bucket); if (!persistent_quota_is_temporary_quota()) { - DeleteStorageKeyData("a_client, "http://bar.com/", kPersistent); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, status()); + auto foo_perm_bucket = + GetBucket("http://bar.com/", kDefaultBucketName, kPersistent); + DeleteBucketData("a_client, foo_perm_bucket); } - DeleteStorageKeyData("a_client, "http://buz.com/", kTemporary); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, status()); + auto buz_temp_bucket = + GetOrCreateBucket("http://buz.com/", kDefaultBucketName, kTemporary); + DeleteBucketData("a_client, buz_temp_bucket); - EXPECT_EQ(0, GetStorageKeyUsage(quota_client, "http://foo.com/", kTemporary)); - EXPECT_EQ(0, GetStorageKeyUsage(quota_client, "http://buz.com/", kTemporary)); + EXPECT_EQ(0, GetBucketUsage(quota_client, foo_temp_bucket)); + EXPECT_EQ(0, GetBucketUsage(quota_client, buz_temp_bucket)); + + auto foo_https_temp_bucket = + GetBucket("https://foo.com/", kDefaultBucketName, kTemporary); EXPECT_EQ(2 + file_paths_cost_temporary_foo_https, - GetStorageKeyUsage(quota_client, "https://foo.com/", kTemporary)); + GetBucketUsage(quota_client, foo_https_temp_bucket)); + + auto bar_temp_bucket = + GetBucket("http://bar.com/", kDefaultBucketName, kTemporary); EXPECT_EQ(8 + file_paths_cost_temporary_bar + (persistent_quota_is_temporary_quota() ? 16 + file_paths_cost_persistent_bar : 0), - GetStorageKeyUsage(quota_client, "http://bar.com/", kTemporary)); + GetBucketUsage(quota_client, bar_temp_bucket)); + + auto bar_https_temp_bucket = + GetBucket("https://bar.com/", kDefaultBucketName, kTemporary); EXPECT_EQ(64 + file_paths_cost_temporary_bar_https + (persistent_quota_is_temporary_quota() ? 32 + file_paths_cost_persistent_bar_https : 0), - GetStorageKeyUsage(quota_client, "https://bar.com/", kTemporary)); + GetBucketUsage(quota_client, bar_https_temp_bucket)); if (!persistent_quota_is_temporary_quota()) { - EXPECT_EQ(0, - GetStorageKeyUsage(quota_client, "http://bar.com/", kPersistent)); + auto bar_perm_bucket = + GetBucket("http://bar.com/", kDefaultBucketName, kPersistent); + EXPECT_EQ(0, GetBucketUsage(quota_client, bar_perm_bucket)); + + auto foo_perm_bucket = + GetBucket("http://foo.com/", kDefaultBucketName, kPersistent); EXPECT_EQ(4 + file_paths_cost_persistent_foo, - GetStorageKeyUsage(quota_client, "http://foo.com/", kPersistent)); - EXPECT_EQ( - 32 + file_paths_cost_persistent_bar_https, - GetStorageKeyUsage(quota_client, "https://bar.com/", kPersistent)); + GetBucketUsage(quota_client, foo_perm_bucket)); + + auto bar_https_perm_bucket = + GetBucket("https://bar.com/", kDefaultBucketName, kPersistent); + EXPECT_EQ(32 + file_paths_cost_persistent_bar_https, + GetBucketUsage(quota_client, bar_https_perm_bucket)); } } diff --git a/chromium/storage/browser/file_system/file_system_quota_util.h b/chromium/storage/browser/file_system/file_system_quota_util.h index d329f63de74..dd34c1e1ee0 100644 --- a/chromium/storage/browser/file_system/file_system_quota_util.h +++ b/chromium/storage/browser/file_system/file_system_quota_util.h @@ -7,7 +7,6 @@ #include <stdint.h> -#include <string> #include <vector> #include "base/component_export.h" @@ -48,10 +47,6 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) FileSystemQuotaUtil { virtual std::vector<blink::StorageKey> GetStorageKeysForTypeOnFileTaskRunner( FileSystemType type) = 0; - virtual std::vector<blink::StorageKey> GetStorageKeysForHostOnFileTaskRunner( - FileSystemType type, - const std::string& host) = 0; - // Returns the amount of data used for the `storage_key` for usage tracking. virtual int64_t GetStorageKeyUsageOnFileTaskRunner( FileSystemContext* file_system_context, diff --git a/chromium/storage/browser/file_system/file_system_url.cc b/chromium/storage/browser/file_system/file_system_url.cc index 1c164adb270..ec5efe42a13 100644 --- a/chromium/storage/browser/file_system/file_system_url.cc +++ b/chromium/storage/browser/file_system/file_system_url.cc @@ -12,6 +12,7 @@ #include "storage/common/file_system/file_system_types.h" #include "storage/common/file_system/file_system_util.h" #include "third_party/blink/public/common/storage_key/storage_key.h" +#include "url/gurl.h" #include "url/origin.h" namespace storage { @@ -69,10 +70,9 @@ FileSystemURL::FileSystemURL(const GURL& url, GURL origin_url; // URL should be able to be parsed and the parsed origin should match the // StorageKey's origin member. - is_valid_ = - ParseFileSystemSchemeURL(url, &origin_url, &mount_type_, - &virtual_path_) && - storage_key.origin().IsSameOriginWith(url::Origin::Create(origin_url)); + is_valid_ = ParseFileSystemSchemeURL(url, &origin_url, &mount_type_, + &virtual_path_) && + storage_key.origin().IsSameOriginWith(origin_url); storage_key_ = storage_key; path_ = virtual_path_; type_ = mount_type_; @@ -113,11 +113,12 @@ GURL FileSystemURL::ToGURL() const { if (!is_valid_) return GURL(); - std::string url = - GetFileSystemRootURI(storage_key_.origin().GetURL(), mount_type_).spec(); - if (url.empty()) + GURL url = GetFileSystemRootURI(storage_key_.origin().GetURL(), mount_type_); + if (!url.is_valid()) return GURL(); + std::string url_string = url.spec(); + // Exactly match with DOMFileSystemBase::createFileSystemURL()'s encoding // behavior, where the path is escaped by KURL::encodeWithURLEscapeSequences // which is essentially encodeURIComponent except '/'. @@ -125,10 +126,10 @@ GURL FileSystemURL::ToGURL() const { virtual_path_.NormalizePathSeparatorsTo('/').AsUTF8Unsafe(), false /* use_plus */); base::ReplaceSubstringsAfterOffset(&escaped, 0, "%2F", "/"); - url.append(escaped); + url_string.append(escaped); // Build nested GURL. - return GURL(url); + return GURL(url_string); } std::string FileSystemURL::DebugString() const { diff --git a/chromium/storage/browser/file_system/file_system_url.h b/chromium/storage/browser/file_system/file_system_url.h index e29a3306cfc..65441a4977f 100644 --- a/chromium/storage/browser/file_system/file_system_url.h +++ b/chromium/storage/browser/file_system/file_system_url.h @@ -10,8 +10,10 @@ #include "base/component_export.h" #include "base/files/file_path.h" +#include "components/services/storage/public/cpp/buckets/bucket_locator.h" #include "storage/common/file_system/file_system_mount_option.h" #include "storage/common/file_system/file_system_types.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/common/storage_key/storage_key.h" #include "url/gurl.h" @@ -29,6 +31,7 @@ namespace storage { // virtual_path() returns 'foo/bar', // type() returns the same value as mount_type(), // path() returns the same value as virtual_path(), +// bucket() returns an empty string unless explicitly set with SetBucket(), // // All other accessors return empty or invalid value. // @@ -133,6 +136,12 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) FileSystemURL { const FileSystemMountOption& mount_option() const { return mount_option_; } + // Returns the BucketLocator for this URL's partitioned file location. In + // the majority of cases, this will not be populated and the default storage + // bucket will be used. + const absl::optional<BucketLocator>& bucket() const { return bucket_; } + void SetBucket(const BucketLocator& bucket) { bucket_ = bucket; } + // Returns the formatted URL of this instance. GURL ToGURL() const; @@ -191,6 +200,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) FileSystemURL { base::FilePath path_; std::string filesystem_id_; FileSystemMountOption mount_option_; + + // Values that must be explicitly set. + absl::optional<BucketLocator> bucket_; }; using FileSystemURLSet = std::set<FileSystemURL, FileSystemURL::Comparator>; diff --git a/chromium/storage/browser/file_system/isolated_context.cc b/chromium/storage/browser/file_system/isolated_context.cc index 2ab691523aa..f862b8eb708 100644 --- a/chromium/storage/browser/file_system/isolated_context.cc +++ b/chromium/storage/browser/file_system/isolated_context.cc @@ -351,8 +351,8 @@ bool IsolatedContext::CrackVirtualPath( *mount_option = FileSystemMountOption(); // The virtual_path should comprise <id_or_name> and <relative_path> parts. - std::vector<base::FilePath::StringType> components; - virtual_path.GetComponents(&components); + std::vector<base::FilePath::StringType> components = + virtual_path.GetComponents(); if (components.size() < 1) return false; auto component_iter = components.begin(); 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 20c6378fe3a..90f910646d7 100644 --- a/chromium/storage/browser/file_system/local_file_util_unittest.cc +++ b/chromium/storage/browser/file_system/local_file_util_unittest.cc @@ -136,7 +136,7 @@ TEST_F(LocalFileUtilTest, CreateAndClose) { } // base::CreateSymbolicLink is supported on most POSIX, but not on Fuchsia. -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) TEST_F(LocalFileUtilTest, CreateFailForSymlink) { // Create symlink target file. const char* target_name = "symlink_target"; diff --git a/chromium/storage/browser/file_system/native_file_util.cc b/chromium/storage/browser/file_system/native_file_util.cc index 106a742df16..4d0f7df58e5 100644 --- a/chromium/storage/browser/file_system/native_file_util.cc +++ b/chromium/storage/browser/file_system/native_file_util.cc @@ -16,9 +16,9 @@ #include "storage/browser/file_system/file_system_url.h" #include "storage/common/file_system/file_system_mount_option.h" -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) #include "windows.h" -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) namespace storage { @@ -270,7 +270,7 @@ base::File::Error NativeFileUtil::CopyOrMoveFile( if (error == base::File::FILE_OK) { if (info.is_directory != src_is_directory) return base::File::FILE_ERROR_INVALID_OPERATION; -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) // Overwriting an empty directory with another directory isn't supported // natively on Windows, so treat this an unsupported. A higher layer is // responsible for handling it. @@ -288,7 +288,7 @@ base::File::Error NativeFileUtil::CopyOrMoveFile( // Cache permissions of dest file before copy/move overwrites the file. bool should_retain_file_permissions = false; -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) int dest_mode; if (options.Has(FileSystemOperation::CopyOrMoveOption:: kPreserveDestinationPermissions)) { @@ -296,14 +296,14 @@ base::File::Error NativeFileUtil::CopyOrMoveFile( should_retain_file_permissions = base::GetPosixFilePermissions(dest_path, &dest_mode); } -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) DWORD dest_attributes; if (options.Has(FileSystemOperation::CopyOrMoveOption:: kPreserveDestinationPermissions)) { dest_attributes = ::GetFileAttributes(dest_path.value().c_str()); should_retain_file_permissions = dest_attributes != INVALID_FILE_ATTRIBUTES; } -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) switch (mode) { case COPY_NOSYNC: @@ -328,11 +328,11 @@ base::File::Error NativeFileUtil::CopyOrMoveFile( } if (should_retain_file_permissions) { -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) base::SetPosixFilePermissions(dest_path, dest_mode); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) ::SetFileAttributes(dest_path.value().c_str(), dest_attributes); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) } return base::File::FILE_OK; diff --git a/chromium/storage/browser/file_system/native_file_util_unittest.cc b/chromium/storage/browser/file_system/native_file_util_unittest.cc index 16a87636fa5..602751dcb45 100644 --- a/chromium/storage/browser/file_system/native_file_util_unittest.cc +++ b/chromium/storage/browser/file_system/native_file_util_unittest.cc @@ -15,9 +15,9 @@ #include "storage/browser/file_system/native_file_util.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) #include "windows.h" -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) namespace storage { namespace { @@ -53,7 +53,7 @@ class NativeFileUtilTest : public testing::Test { return info.size; } -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) void ExpectFileHasPermissionsPosix(base::FilePath file, int expected_mode) { base::File::Info file_info; int mode; @@ -62,9 +62,9 @@ class NativeFileUtilTest : public testing::Test { EXPECT_TRUE(base::GetPosixFilePermissions(file, &mode)); EXPECT_EQ(mode, expected_mode); } -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) void ExpectFileHasPermissionsWin(base::FilePath file, DWORD expected_attributes) { base::File::Info file_info; @@ -75,7 +75,7 @@ class NativeFileUtilTest : public testing::Test { EXPECT_NE(attributes, INVALID_FILE_ATTRIBUTES); EXPECT_EQ(attributes, expected_attributes); } -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) private: base::ScopedTempDir data_dir_; @@ -362,7 +362,7 @@ TEST_F(NativeFileUtilTest, MoveFile) { NativeFileUtil::CopyOrMoveFile( dir, to_file, FileSystemOperation::CopyOrMoveOptionSet(), move)); -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) // Source is a directory, destination is a directory. EXPECT_EQ(base::File::FILE_ERROR_NOT_A_FILE, NativeFileUtil::CopyOrMoveFile( @@ -422,7 +422,7 @@ TEST_F(NativeFileUtilTest, MoveFile_Directory) { EXPECT_EQ(1020, GetSize(to_file)); } -#if !defined(OS_WIN) +#if !BUILDFLAG(IS_WIN) TEST_F(NativeFileUtilTest, MoveFile_OverwriteEmptyDirectory) { base::FilePath from_directory = Path("fromdirectory"); base::FilePath to_directory = Path("todirectory"); @@ -507,7 +507,7 @@ TEST_F(NativeFileUtilTest, PreserveLastModified) { EXPECT_EQ(file_info1.last_modified, file_info2.last_modified); } -#if defined(OS_POSIX) || defined(OS_WIN) +#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_WIN) TEST_F(NativeFileUtilTest, PreserveDestinationPermissions) { // Ensure both the src and dest files exist. base::FilePath to_file = Path("to-file"); @@ -523,25 +523,25 @@ TEST_F(NativeFileUtilTest, PreserveDestinationPermissions) { ASSERT_TRUE(created); EXPECT_TRUE(FileExists(from_file)); -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) int dest_initial_mode; ASSERT_TRUE(base::GetPosixFilePermissions(to_file, &dest_initial_mode)); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) DWORD dest_initial_attributes = ::GetFileAttributes(to_file.value().c_str()); ASSERT_NE(dest_initial_attributes, INVALID_FILE_ATTRIBUTES); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) // Give dest file some distinct permissions it didn't have before. -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) int old_dest_mode = dest_initial_mode | S_IRGRP | S_IXOTH; EXPECT_NE(old_dest_mode, dest_initial_mode); EXPECT_TRUE(base::SetPosixFilePermissions(to_file, old_dest_mode)); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) DWORD old_dest_attributes = FILE_ATTRIBUTE_NORMAL; EXPECT_NE(old_dest_attributes, dest_initial_attributes); EXPECT_TRUE( ::SetFileAttributes(to_file.value().c_str(), old_dest_attributes)); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) // Test for copy (nosync). ASSERT_EQ(base::File::FILE_OK, @@ -550,11 +550,11 @@ TEST_F(NativeFileUtilTest, PreserveDestinationPermissions) { CopyOrMoveOptionSet( CopyOrMoveOption::kPreserveDestinationPermissions), NativeFileUtil::COPY_NOSYNC)); -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) ExpectFileHasPermissionsPosix(to_file, old_dest_mode); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) ExpectFileHasPermissionsWin(to_file, old_dest_attributes); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) // Test for copy (sync). ASSERT_EQ(base::File::FILE_OK, @@ -563,11 +563,11 @@ TEST_F(NativeFileUtilTest, PreserveDestinationPermissions) { CopyOrMoveOptionSet( CopyOrMoveOption::kPreserveDestinationPermissions), NativeFileUtil::COPY_SYNC)); -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) ExpectFileHasPermissionsPosix(to_file, old_dest_mode); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) ExpectFileHasPermissionsWin(to_file, old_dest_attributes); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) // Test for move. ASSERT_EQ(base::File::FILE_OK, @@ -576,15 +576,15 @@ TEST_F(NativeFileUtilTest, PreserveDestinationPermissions) { CopyOrMoveOptionSet( CopyOrMoveOption::kPreserveDestinationPermissions), NativeFileUtil::MOVE)); -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) ExpectFileHasPermissionsPosix(to_file, old_dest_mode); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) ExpectFileHasPermissionsWin(to_file, old_dest_attributes); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) } -#endif // defined(OS_POSIX) || defined(OS_WIN) +#endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_WIN) -#if defined(OS_POSIX) || defined(OS_WIN) +#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_WIN) TEST_F(NativeFileUtilTest, PreserveLastModifiedAndDestinationPermissions) { base::FilePath from_file = Path("fromfile"); base::FilePath to_file1 = Path("tofile1"); @@ -618,22 +618,22 @@ TEST_F(NativeFileUtilTest, PreserveLastModifiedAndDestinationPermissions) { // Get initial permissions of the dest files. We can assume that the 3 // destination files have the same permissions. -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) int dest_initial_mode; ASSERT_TRUE(base::GetPosixFilePermissions(to_file1, &dest_initial_mode)); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) DWORD dest_initial_attributes = ::GetFileAttributes(to_file1.value().c_str()); ASSERT_NE(dest_initial_attributes, INVALID_FILE_ATTRIBUTES); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) // Give dest files some distinct permissions they didn't have before. -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) int old_dest_mode = dest_initial_mode | S_IRGRP | S_IXOTH; EXPECT_NE(old_dest_mode, dest_initial_mode); EXPECT_TRUE(base::SetPosixFilePermissions(to_file1, old_dest_mode)); EXPECT_TRUE(base::SetPosixFilePermissions(to_file2, old_dest_mode)); EXPECT_TRUE(base::SetPosixFilePermissions(to_file3, old_dest_mode)); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) DWORD old_dest_attributes = FILE_ATTRIBUTE_NORMAL; EXPECT_NE(old_dest_attributes, dest_initial_attributes); EXPECT_TRUE( @@ -642,7 +642,7 @@ TEST_F(NativeFileUtilTest, PreserveLastModifiedAndDestinationPermissions) { ::SetFileAttributes(to_file2.value().c_str(), old_dest_attributes)); EXPECT_TRUE( ::SetFileAttributes(to_file3.value().c_str(), old_dest_attributes)); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) // Test for copy (nosync). ASSERT_EQ(base::File::FILE_OK, @@ -658,11 +658,11 @@ TEST_F(NativeFileUtilTest, PreserveLastModifiedAndDestinationPermissions) { NativeFileUtil::GetFileInfo(to_file1, &to_file_info)); EXPECT_EQ(from_file_info.last_modified, to_file_info.last_modified); -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) ExpectFileHasPermissionsPosix(to_file1, old_dest_mode); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) ExpectFileHasPermissionsWin(to_file1, old_dest_attributes); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) // Test for copy (sync). ASSERT_EQ(base::File::FILE_OK, @@ -676,11 +676,11 @@ TEST_F(NativeFileUtilTest, PreserveLastModifiedAndDestinationPermissions) { NativeFileUtil::GetFileInfo(to_file2, &to_file_info)); EXPECT_EQ(from_file_info.last_modified, to_file_info.last_modified); -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) ExpectFileHasPermissionsPosix(to_file2, old_dest_mode); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) ExpectFileHasPermissionsWin(to_file2, old_dest_attributes); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) // Test for move. ASSERT_EQ(base::File::FILE_OK, @@ -694,12 +694,12 @@ TEST_F(NativeFileUtilTest, PreserveLastModifiedAndDestinationPermissions) { NativeFileUtil::GetFileInfo(to_file3, &to_file_info)); EXPECT_EQ(from_file_info.last_modified, to_file_info.last_modified); -#if defined(OS_POSIX) +#if BUILDFLAG(IS_POSIX) ExpectFileHasPermissionsPosix(to_file3, old_dest_mode); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) ExpectFileHasPermissionsWin(to_file3, old_dest_attributes); -#endif // defined(OS_POSIX) +#endif // BUILDFLAG(IS_POSIX) } -#endif // defined(OS_POSIX) || defined(OS_WIN) +#endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_WIN) } // namespace storage 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 7074ca38fa7..a46e047e994 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 @@ -27,7 +27,7 @@ namespace { // 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) +#if BUILDFLAG(IS_FUCHSIA) // This function is not implemented on FUCHSIA, yet. (crbug.com/986608) return true; #else @@ -85,9 +85,9 @@ struct ObfuscatedFileUtilMemoryDelegate::DecomposedPath { ObfuscatedFileUtilMemoryDelegate::ObfuscatedFileUtilMemoryDelegate( const base::FilePath& file_system_directory) - : root_(std::make_unique<Entry>(Entry::kDirectory)) { + : root_(std::make_unique<Entry>(Entry::kDirectory)), + root_path_components_(file_system_directory.GetComponents()) { DETACH_FROM_SEQUENCE(sequence_checker_); - file_system_directory.GetComponents(&root_path_components_); } ObfuscatedFileUtilMemoryDelegate::~ObfuscatedFileUtilMemoryDelegate() { @@ -98,8 +98,7 @@ absl::optional<ObfuscatedFileUtilMemoryDelegate::DecomposedPath> ObfuscatedFileUtilMemoryDelegate::ParsePath(const base::FilePath& path) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DecomposedPath dp; - - path.GetComponents(&dp.components); + dp.components = path.GetComponents(); // Ensure |path| is under |root_|. if (dp.components.size() < root_path_components_.size()) @@ -378,7 +377,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFile( if (dest_dp->entry->type != src_dp->entry->type) return base::File::FILE_ERROR_INVALID_OPERATION; -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) // Overwriting an empty directory with another directory isn't // supported natively on Windows. // To keep the behavior indistinguishable from on-disk operation, @@ -549,7 +548,7 @@ int ObfuscatedFileUtilMemoryDelegate::WriteFile( // See crbug.com/1043914 for more context. // |MaxDirectMapped| function is not implemented on FUCHSIA, yet. // (crbug.com/986608) -#if !defined(OS_FUCHSIA) +#if !BUILDFLAG(IS_FUCHSIA) if (last_position >= base::MaxDirectMapped() / 2) { // TODO(https://crbug.com/1043914): Allocated memory is rounded up to // 100MB blocks to reduce memory allocation delays. Switch to a more diff --git a/chromium/storage/browser/file_system/obfuscated_file_util_memory_delegate_unittest.cc b/chromium/storage/browser/file_system/obfuscated_file_util_memory_delegate_unittest.cc index 2430d3136fb..76cf12f10ad 100644 --- a/chromium/storage/browser/file_system/obfuscated_file_util_memory_delegate_unittest.cc +++ b/chromium/storage/browser/file_system/obfuscated_file_util_memory_delegate_unittest.cc @@ -488,7 +488,7 @@ TEST_F(ObfuscatedFileUtilMemoryDelegateTest, MoveDirectoryOverDirectory) { base::File::Error result = file_util()->CopyOrMoveFile( dir, dir2, FileSystemOperation::CopyOrMoveOptionSet(), move); -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) EXPECT_EQ(base::File::FILE_ERROR_NOT_A_FILE, result); #else EXPECT_EQ(base::File::FILE_OK, result); @@ -578,7 +578,7 @@ TEST_F(ObfuscatedFileUtilMemoryDelegateTest, MoveFile_Directory) { EXPECT_EQ(1020, GetSize(to_file)); } -#if !defined(OS_WIN) +#if !BUILDFLAG(IS_WIN) TEST_F(ObfuscatedFileUtilMemoryDelegateTest, MoveFile_OverwriteEmptyDirectory) { base::FilePath from_directory = Path("fromdirectory"); base::FilePath to_directory = Path("todirectory"); 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 f8db2da7705..604674b3d16 100644 --- a/chromium/storage/browser/file_system/obfuscated_file_util_unittest.cc +++ b/chromium/storage/browser/file_system/obfuscated_file_util_unittest.cc @@ -1284,10 +1284,8 @@ TEST_P(ObfuscatedFileUtilTest, TestPathQuotas) { bool exclusive = true; bool recursive = true; url = CreateURLFromUTF8("directory/to/use"); - std::vector<base::FilePath::StringType> components; - url.path().GetComponents(&components); path_cost = 0; - for (const auto& component : components) { + for (const auto& component : url.path().GetComponents()) { path_cost += ObfuscatedFileUtil::ComputeFilePathCost(base::FilePath(component)); } @@ -2043,7 +2041,7 @@ TEST_P(ObfuscatedFileUtilTest, TestFileEnumeratorTimestamp) { } // crbug.com/176470 -#if defined(OS_WIN) || defined(OS_ANDROID) +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID) #define MAYBE_TestQuotaOnCopyFile DISABLED_TestQuotaOnCopyFile #else #define MAYBE_TestQuotaOnCopyFile TestQuotaOnCopyFile 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 c42bd25902c..c3f943b01a5 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 @@ -274,23 +274,6 @@ PluginPrivateFileSystemBackend::GetStorageKeysForTypeOnFileTaskRunner( return storage_keys; } -std::vector<blink::StorageKey> -PluginPrivateFileSystemBackend::GetStorageKeysForHostOnFileTaskRunner( - FileSystemType type, - const std::string& host) { - if (!CanHandleType(type)) - return std::vector<blink::StorageKey>(); - std::unique_ptr<ObfuscatedFileUtil::AbstractStorageKeyEnumerator> enumerator( - obfuscated_file_util()->CreateStorageKeyEnumerator()); - std::vector<blink::StorageKey> storage_keys; - absl::optional<blink::StorageKey> storage_key; - while ((storage_key = enumerator->Next()).has_value()) { - if (host == storage_key->origin().host()) - storage_keys.push_back(std::move(storage_key).value()); - } - return storage_keys; -} - int64_t PluginPrivateFileSystemBackend::GetStorageKeyUsageOnFileTaskRunner( FileSystemContext* context, const blink::StorageKey& storage_key, diff --git a/chromium/storage/browser/file_system/plugin_private_file_system_backend.h b/chromium/storage/browser/file_system/plugin_private_file_system_backend.h index 9174aa0c0cd..5b36b8826f4 100644 --- a/chromium/storage/browser/file_system/plugin_private_file_system_backend.h +++ b/chromium/storage/browser/file_system/plugin_private_file_system_backend.h @@ -127,9 +127,6 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) PluginPrivateFileSystemBackend FileSystemType type) override; std::vector<blink::StorageKey> GetStorageKeysForTypeOnFileTaskRunner( FileSystemType type) override; - std::vector<blink::StorageKey> GetStorageKeysForHostOnFileTaskRunner( - FileSystemType type, - const std::string& host) override; int64_t GetStorageKeyUsageOnFileTaskRunner( FileSystemContext* context, const blink::StorageKey& storage_key, 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 6827bd07cb7..618bc3a7978 100644 --- a/chromium/storage/browser/file_system/quota/quota_backend_impl.cc +++ b/chromium/storage/browser/file_system/quota/quota_backend_impl.cc @@ -145,7 +145,8 @@ void QuotaBackendImpl::ReserveQuotaInternal(const QuotaReservationInfo& info) { quota_manager_proxy_->NotifyStorageModified( QuotaClientType::kFileSystem, blink::StorageKey(info.origin), FileSystemTypeToQuotaStorageType(info.type), info.delta, - base::Time::Now()); + base::Time::Now(), base::SequencedTaskRunnerHandle::Get(), + base::DoNothing()); } base::File::Error QuotaBackendImpl::GetUsageCachePath( diff --git a/chromium/storage/browser/file_system/sandbox_directory_database.cc b/chromium/storage/browser/file_system/sandbox_directory_database.cc index e8eab17fd5e..9f6bb1bff35 100644 --- a/chromium/storage/browser/file_system/sandbox_directory_database.cc +++ b/chromium/storage/browser/file_system/sandbox_directory_database.cc @@ -746,7 +746,7 @@ bool SandboxDirectoryDatabase::Init(RecoveryOption recovery_option) { SandboxDirectoryRepairResult::DB_REPAIR_FAILED, SandboxDirectoryRepairResult::DB_REPAIR_MAX); LOG(WARNING) << "Failed to repair SandboxDirectoryDatabase."; - FALLTHROUGH; + [[fallthrough]]; case DELETE_ON_CORRUPTION: LOG(WARNING) << "Clearing SandboxDirectoryDatabase."; if (!leveldb_chrome::DeleteDB(filesystem_data_directory_, options).ok()) 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 7c4a09757aa..f409b5885d9 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 @@ -340,9 +340,10 @@ SandboxFileSystemBackendDelegate::DeleteStorageKeyDataOnFileTaskRunner( bool result = obfuscated_file_util()->DeleteDirectoryForStorageKeyAndType( storage_key, GetTypeString(type)); if (result && proxy && usage) { - proxy->NotifyStorageModified(QuotaClientType::kFileSystem, storage_key, - FileSystemTypeToQuotaStorageType(type), -usage, - base::Time::Now()); + proxy->NotifyStorageModified( + QuotaClientType::kFileSystem, storage_key, + FileSystemTypeToQuotaStorageType(type), -usage, base::Time::Now(), + base::SequencedTaskRunnerHandle::Get(), base::DoNothing()); } if (result) @@ -384,23 +385,6 @@ SandboxFileSystemBackendDelegate::GetStorageKeysForTypeOnFileTaskRunner( return storage_keys; } -std::vector<blink::StorageKey> -SandboxFileSystemBackendDelegate::GetStorageKeysForHostOnFileTaskRunner( - FileSystemType type, - const std::string& host) { - DCHECK(file_task_runner_->RunsTasksInCurrentSequence()); - std::vector<blink::StorageKey> storage_keys; - std::unique_ptr<StorageKeyEnumerator> enumerator( - CreateStorageKeyEnumerator()); - absl::optional<blink::StorageKey> storage_key; - while ((storage_key = enumerator->Next()).has_value()) { - if (host == storage_key->origin().host() && - enumerator->HasFileSystemType(type)) - storage_keys.push_back(std::move(storage_key).value()); - } - return storage_keys; -} - int64_t SandboxFileSystemBackendDelegate::GetStorageKeyUsageOnFileTaskRunner( FileSystemContext* file_system_context, const blink::StorageKey& storage_key, diff --git a/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate.h b/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate.h index cc1cc70dc73..7abbc1a959e 100644 --- a/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate.h +++ b/chromium/storage/browser/file_system/sandbox_file_system_backend_delegate.h @@ -156,9 +156,6 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) SandboxFileSystemBackendDelegate FileSystemType type) override; std::vector<blink::StorageKey> GetStorageKeysForTypeOnFileTaskRunner( FileSystemType type) override; - std::vector<blink::StorageKey> GetStorageKeysForHostOnFileTaskRunner( - FileSystemType type, - const std::string& host) override; int64_t GetStorageKeyUsageOnFileTaskRunner( FileSystemContext* context, const blink::StorageKey& storage_key, diff --git a/chromium/storage/browser/file_system/sandbox_origin_database.cc b/chromium/storage/browser/file_system/sandbox_origin_database.cc index 21c8ec86631..800c1248c07 100644 --- a/chromium/storage/browser/file_system/sandbox_origin_database.cc +++ b/chromium/storage/browser/file_system/sandbox_origin_database.cc @@ -116,7 +116,7 @@ bool SandboxOriginDatabase::Init(InitOption init_option, UMA_HISTOGRAM_ENUMERATION(kSandboxOriginDatabaseRepairHistogramLabel, SandboxOriginRepairResult::DB_REPAIR_FAILED, SandboxOriginRepairResult::DB_REPAIR_MAX); - FALLTHROUGH; + [[fallthrough]]; case DELETE_ON_CORRUPTION: if (!base::DeletePathRecursively(file_system_directory_)) return false; diff --git a/chromium/storage/browser/file_system/sandbox_quota_observer.cc b/chromium/storage/browser/file_system/sandbox_quota_observer.cc index d9855e49fd5..5428baf2f27 100644 --- a/chromium/storage/browser/file_system/sandbox_quota_observer.cc +++ b/chromium/storage/browser/file_system/sandbox_quota_observer.cc @@ -45,7 +45,8 @@ void SandboxQuotaObserver::OnUpdate(const FileSystemURL& url, int64_t delta) { if (quota_manager_proxy_.get()) { quota_manager_proxy_->NotifyStorageModified( QuotaClientType::kFileSystem, url.storage_key(), - FileSystemTypeToQuotaStorageType(url.type()), delta, base::Time::Now()); + FileSystemTypeToQuotaStorageType(url.type()), delta, base::Time::Now(), + base::SequencedTaskRunnerHandle::Get(), base::DoNothing()); } base::FilePath usage_file_path = GetUsageCachePath(url); diff --git a/chromium/storage/browser/quota/BUILD.gn b/chromium/storage/browser/quota/BUILD.gn new file mode 100644 index 00000000000..f930e025dab --- /dev/null +++ b/chromium/storage/browser/quota/BUILD.gn @@ -0,0 +1,17 @@ +# 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("mojo_bindings") { + sources = [ "quota_internals.mojom" ] + webui_module_path = "/" + + public_deps = [ "//url/mojom:url_mojom_origin" ] + + disable_variants = true + + component_output_prefix = "storage_browser_interfaces" + component_macro_prefix = "STORAGE_BROWSER_INTERFACES" +} diff --git a/chromium/storage/browser/quota/OWNERS b/chromium/storage/browser/quota/OWNERS index 8e88df27dc1..96896d848b8 100644 --- a/chromium/storage/browser/quota/OWNERS +++ b/chromium/storage/browser/quota/OWNERS @@ -7,3 +7,6 @@ jsbell@chromium.org kinuko@chromium.org mek@chromium.org pwnall@chromium.org + +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS
\ No newline at end of file diff --git a/chromium/storage/browser/quota/client_usage_tracker.cc b/chromium/storage/browser/quota/client_usage_tracker.cc index 87fd0d5690c..2e6a60daf09 100644 --- a/chromium/storage/browser/quota/client_usage_tracker.cc +++ b/chromium/storage/browser/quota/client_usage_tracker.cc @@ -5,63 +5,26 @@ #include "storage/browser/quota/client_usage_tracker.h" #include <stdint.h> +#include <iterator> +#include "base/barrier_closure.h" #include "base/bind.h" #include "base/callback_helpers.h" #include "base/containers/contains.h" -#include "base/metrics/histogram_macros.h" +#include "base/metrics/histogram_functions.h" #include "third_party/blink/public/common/storage_key/storage_key.h" namespace storage { namespace { -using StorageKeySetByHost = ClientUsageTracker::StorageKeySetByHost; - -void DidGetHostUsage(UsageCallback callback, - int64_t limited_usage, - int64_t unlimited_usage) { - DCHECK_GE(limited_usage, 0); - DCHECK_GE(unlimited_usage, 0); - std::move(callback).Run(limited_usage + unlimited_usage); -} - -bool EraseStorageKeyFromStorageKeySet(StorageKeySetByHost* storage_keys_by_host, - const std::string& host, - const blink::StorageKey& storage_key) { - auto it = storage_keys_by_host->find(host); - if (it == storage_keys_by_host->end()) - return false; - - if (!it->second.erase(storage_key)) - return false; - - if (it->second.empty()) - storage_keys_by_host->erase(host); - return true; -} - -bool StorageKeySetContainsStorageKey(const StorageKeySetByHost& storage_keys, - const std::string& host, - const blink::StorageKey& storage_key) { - auto itr = storage_keys.find(host); - return itr != storage_keys.end() && base::Contains(itr->second, storage_key); -} - void RecordSkippedOriginHistogram(const InvalidOriginReason reason) { - UMA_HISTOGRAM_ENUMERATION("Quota.SkippedInvalidOriginUsage", reason); + base::UmaHistogramEnumeration("Quota.SkippedInvalidOriginUsage", reason); } } // namespace struct ClientUsageTracker::AccumulateInfo { - AccumulateInfo() = default; - ~AccumulateInfo() = default; - - AccumulateInfo(const AccumulateInfo&) = delete; - AccumulateInfo& operator=(const AccumulateInfo&) = delete; - - size_t pending_jobs = 0; int64_t limited_usage = 0; int64_t unlimited_usage = 0; }; @@ -73,9 +36,6 @@ ClientUsageTracker::ClientUsageTracker( scoped_refptr<SpecialStoragePolicy> special_storage_policy) : client_(client), type_(type), - global_limited_usage_(0), - global_unlimited_usage_(0), - global_usage_retrieved_(false), special_storage_policy_(std::move(special_storage_policy)) { DCHECK(client_); if (special_storage_policy_.get()) @@ -88,83 +48,94 @@ ClientUsageTracker::~ClientUsageTracker() { special_storage_policy_->RemoveObserver(this); } -void ClientUsageTracker::GetGlobalUsage(GlobalUsageCallback callback) { +void ClientUsageTracker::GetBucketsUsage(const std::set<BucketLocator>& buckets, + UsageCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (global_usage_retrieved_ && - non_cached_limited_storage_keys_by_host_.empty() && - non_cached_unlimited_storage_keys_by_host_.empty()) { - std::move(callback).Run(global_limited_usage_ + global_unlimited_usage_, - global_unlimited_usage_); - return; - } + DCHECK_GT(buckets.size(), 0u); - client_->GetStorageKeysForType( - type_, - base::BindOnce(&ClientUsageTracker::DidGetStorageKeysForGlobalUsage, - weak_factory_.GetWeakPtr(), std::move(callback))); -} + auto info = std::make_unique<AccumulateInfo>(); + auto* info_ptr = info.get(); + base::RepeatingClosure barrier = base::BarrierClosure( + buckets.size(), + base::BindOnce(&ClientUsageTracker::FinallySendBucketsUsage, + weak_factory_.GetWeakPtr(), std::move(callback), + std::move(info))); -void ClientUsageTracker::GetHostUsage(const std::string& host, - UsageCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (base::Contains(cached_hosts_, host) && - !base::Contains(non_cached_limited_storage_keys_by_host_, host) && - !base::Contains(non_cached_unlimited_storage_keys_by_host_, host)) { - // TODO(kinuko): Drop host_usage_map_ cache periodically. - std::move(callback).Run(GetCachedHostUsage(host)); - return; - } + for (const auto& bucket : buckets) { + // TODO(https://crbug.com/941480): `storage_key` should not be opaque or + // have an empty url, but sometimes it is. + if (bucket.storage_key.origin().opaque()) { + DVLOG(1) << "GetBucketsUsage for opaque storage_key!"; + RecordSkippedOriginHistogram(InvalidOriginReason::kIsOpaque); + barrier.Run(); + continue; + } - if (!host_usage_accumulators_.Add( - host, base::BindOnce(&DidGetHostUsage, std::move(callback)))) - return; - client_->GetStorageKeysForHost( - type_, host, - base::BindOnce(&ClientUsageTracker::DidGetStorageKeysForHostUsage, - weak_factory_.GetWeakPtr(), host)); + if (bucket.storage_key.origin().GetURL().is_empty()) { + DVLOG(1) << "GetBucketsUsage for storage_key with empty url!"; + RecordSkippedOriginHistogram(InvalidOriginReason::kIsEmpty); + barrier.Run(); + continue; + } + + // Use a cached usage value, if we have one. + int64_t cached_usage = GetCachedBucketUsage(bucket); + if (cached_usage != -1) { + AccumulateBucketsUsage(barrier, bucket, info_ptr, cached_usage); + continue; + } + + client_->GetBucketUsage( + bucket, + // base::Unretained usage is safe here because barrier holds the + // std::unque_ptr that keeps AccumulateInfo alive, and the barrier + // will outlive all the AccumulateClientGlobalUsage closures. + base::BindOnce(&ClientUsageTracker::AccumulateBucketsUsage, + weak_factory_.GetWeakPtr(), barrier, bucket, + base::Unretained(info_ptr))); + } } -void ClientUsageTracker::UpdateUsageCache(const blink::StorageKey& storage_key, - int64_t delta) { +void ClientUsageTracker::UpdateBucketUsageCache(const BucketLocator& bucket, + int64_t delta) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - const std::string& host = storage_key.origin().host(); - if (base::Contains(cached_hosts_, host)) { - if (!IsUsageCacheEnabledForStorageKey(storage_key)) - return; - - // Constrain |delta| to avoid negative usage values. - // TODO(michaeln): crbug/463729 - delta = std::max(delta, -cached_usage_by_host_[host][storage_key]); - cached_usage_by_host_[host][storage_key] += delta; - UpdateGlobalUsageValue(IsStorageUnlimited(storage_key) - ? &global_unlimited_usage_ - : &global_limited_usage_, - delta); + if (!IsUsageCacheEnabledForStorageKey(bucket.storage_key)) + return; + auto bucket_it = cached_bucket_usage_.find(bucket); + if (bucket_it != cached_bucket_usage_.end()) { + // Constrain `delta` to avoid negative usage values. + // TODO(crbug.com/463729): At least one storage API sends deltas that + // result in negative total usage. The line below works around this bug. + // Fix the bug, and remove the workaround. + delta = std::max(delta, -bucket_it->second); + bucket_it->second += delta; return; } + // Retrieve bucket usage and update cache. + GetBucketUsage(bucket, base::DoNothing()); +} - // We call GetHostUsage() so that the cache still updates, but we don't need - // to do anything else with the usage so we do not pass a callback. - GetHostUsage(host, base::DoNothing()); +void ClientUsageTracker::DeleteBucketCache(const BucketLocator& bucket) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + cached_bucket_usage_.erase(bucket); } int64_t ClientUsageTracker::GetCachedUsage() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); int64_t usage = 0; - for (const auto& host_and_usage_map : cached_usage_by_host_) { - for (const auto& storage_key_and_usage : host_and_usage_map.second) - usage += storage_key_and_usage.second; - } + for (const auto& bucket_and_usage : cached_bucket_usage_) + usage += bucket_and_usage.second; return usage; } std::map<std::string, int64_t> ClientUsageTracker::GetCachedHostsUsage() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); std::map<std::string, int64_t> host_usage; - for (const auto& host_and_usage_map : cached_usage_by_host_) { - const std::string& host = host_and_usage_map.first; - host_usage[host] += GetCachedHostUsage(host); + for (const auto& bucket_and_usage : cached_bucket_usage_) { + const std::string& host = + bucket_and_usage.first.storage_key.origin().host(); + host_usage[host] += bucket_and_usage.second; } return host_usage; } @@ -173,255 +144,121 @@ std::map<blink::StorageKey, int64_t> ClientUsageTracker::GetCachedStorageKeysUsage() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); std::map<blink::StorageKey, int64_t> storage_key_usage; - for (const auto& host_and_usage_map : cached_usage_by_host_) { - for (const auto& storage_key_and_usage : host_and_usage_map.second) - storage_key_usage[storage_key_and_usage.first] += - storage_key_and_usage.second; + for (const auto& bucket_and_usage : cached_bucket_usage_) { + const blink::StorageKey& storage_key = bucket_and_usage.first.storage_key; + storage_key_usage[storage_key] += bucket_and_usage.second; } return storage_key_usage; } -std::set<blink::StorageKey> ClientUsageTracker::GetCachedStorageKeys() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::set<blink::StorageKey> storage_keys; - for (const auto& host_and_usage_map : cached_usage_by_host_) { - for (const auto& storage_key_and_usage : host_and_usage_map.second) - storage_keys.insert(storage_key_and_usage.first); - } - return storage_keys; -} - void ClientUsageTracker::SetUsageCacheEnabled( const blink::StorageKey& storage_key, bool enabled) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - const std::string& host = storage_key.origin().host(); - if (!enabled) { - // Erase `storage_key` from cache and subtract its usage. - auto host_it = cached_usage_by_host_.find(host); - if (host_it != cached_usage_by_host_.end()) { - UsageMap& cached_usage_for_host = host_it->second; - - auto storage_key_it = cached_usage_for_host.find(storage_key); - if (storage_key_it != cached_usage_for_host.end()) { - int64_t usage = storage_key_it->second; - UpdateUsageCache(storage_key, -usage); - cached_usage_for_host.erase(storage_key_it); - if (cached_usage_for_host.empty()) { - cached_usage_by_host_.erase(host_it); - cached_hosts_.erase(host); - } - } - } + if (enabled) { + non_cached_limited_storage_keys_.erase(storage_key); + non_cached_unlimited_storage_keys_.erase(storage_key); + return; + } - if (IsStorageUnlimited(storage_key)) - non_cached_unlimited_storage_keys_by_host_[host].insert(storage_key); - else - non_cached_limited_storage_keys_by_host_[host].insert(storage_key); - } else { - // Erase `storage_key` from `non_cached_storage_keys_` and invalidate the - // usage cache for the host. - if (EraseStorageKeyFromStorageKeySet( - &non_cached_limited_storage_keys_by_host_, host, storage_key) || - EraseStorageKeyFromStorageKeySet( - &non_cached_unlimited_storage_keys_by_host_, host, storage_key)) { - cached_hosts_.erase(host); - global_usage_retrieved_ = false; + // Find all buckets for `storage_key` in `cached_bucket_usage_` + // and remove them from the cache. Erases cached bucket usage while iterating. + for (auto it = cached_bucket_usage_.cbegin(); + it != cached_bucket_usage_.cend();) { + if (it->first.storage_key == storage_key) { + // Calling erase() while iterating is safe because (1) std::map::erase() + // only invalidates the iterator pointing to the erased element, and (2) + // `it` is advanced off of the erased element before erase() is called. + cached_bucket_usage_.erase(it++); + } else { + ++it; } } -} -void ClientUsageTracker::DidGetStorageKeysForGlobalUsage( - GlobalUsageCallback callback, - const std::vector<blink::StorageKey>& storage_keys) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::map<std::string, std::vector<blink::StorageKey>> storage_keys_by_host; - for (const auto& storage_key : storage_keys) - storage_keys_by_host[storage_key.origin().host()].push_back(storage_key); - - AccumulateInfo* info = new AccumulateInfo; - // Getting host usage may synchronously return the result if the usage is - // cached, which may in turn dispatch the completion callback before we finish - // looping over all hosts (because info->pending_jobs may reach 0 during the - // loop). To avoid this, we add one more pending host as a sentinel and - // fire the sentinel callback at the end. - info->pending_jobs = storage_keys_by_host.size() + 1; - auto accumulator = base::BindRepeating( - &ClientUsageTracker::AccumulateHostUsage, weak_factory_.GetWeakPtr(), - base::Owned(info), - // The `accumulator` is called multiple times, but the `callback` inside - // of it will only be called a single time, so we give ownership to the - // `accumulator` itself. - base::OwnedRef(std::move(callback))); - - for (const auto& host_and_storage_keys : storage_keys_by_host) { - const std::string& host = host_and_storage_keys.first; - const std::vector<blink::StorageKey>& storage_keys_for_host = - host_and_storage_keys.second; - if (host_usage_accumulators_.Add(host, accumulator)) - GetUsageForStorageKeys(host, storage_keys_for_host); + // Add to `non_cached_*_storage_keys_` to exclude `storage_key` from quota + // restrictions. + if (IsStorageUnlimited(storage_key)) { + non_cached_unlimited_storage_keys_.insert(storage_key); + } else { + non_cached_limited_storage_keys_.insert(storage_key); } - - // Fire the sentinel as we've now called GetUsageForStorageKeys for all - // clients. - std::move(accumulator).Run(0, 0); } -void ClientUsageTracker::AccumulateHostUsage(AccumulateInfo* info, - GlobalUsageCallback& callback, - int64_t limited_usage, - int64_t unlimited_usage) { - DCHECK_GT(info->pending_jobs, 0U); +bool ClientUsageTracker::IsUsageCacheEnabledForStorageKey( + const blink::StorageKey& storage_key) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - info->limited_usage += limited_usage; - info->unlimited_usage += unlimited_usage; - if (--info->pending_jobs) - return; - - DCHECK_GE(info->limited_usage, 0); - DCHECK_GE(info->unlimited_usage, 0); - - global_usage_retrieved_ = true; - std::move(callback).Run(info->limited_usage + info->unlimited_usage, - info->unlimited_usage); + return !base::Contains(non_cached_limited_storage_keys_, storage_key) && + !base::Contains(non_cached_unlimited_storage_keys_, storage_key); } -void ClientUsageTracker::DidGetStorageKeysForHostUsage( - const std::string& host, - const std::vector<blink::StorageKey>& storage_keys) { +void ClientUsageTracker::AccumulateBucketsUsage( + base::OnceClosure barrier_callback, + const BucketLocator& bucket, + AccumulateInfo* info, + int64_t usage) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - GetUsageForStorageKeys(host, storage_keys); -} + // Defend against confusing inputs from clients. + // TODO(crbug.com/1292210): Remove this check after fixing QuotaClients. + if (usage < 0) + usage = 0; -void ClientUsageTracker::GetUsageForStorageKeys( - const std::string& host, - const std::vector<blink::StorageKey>& storage_keys) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - AccumulateInfo* info = new AccumulateInfo; - // Getting storage_key usage may synchronously return the result if the usage - // is cached, which may in turn dispatch the completion callback before we - // finish looping over all storage_keys (because info->pending_jobs may reach - // 0 during the loop). To avoid this, we add one more pending storage_key as - // a sentinel and fire the sentinel callback at the end. - info->pending_jobs = storage_keys.size() + 1; - auto accumulator = - base::BindRepeating(&ClientUsageTracker::AccumulateStorageKeyUsage, - weak_factory_.GetWeakPtr(), base::Owned(info), host); - - for (const auto& storage_key : storage_keys) { - DCHECK_EQ(host, storage_key.origin().host()); - - int64_t storage_key_usage = 0; - if (GetCachedStorageKeyUsage(storage_key, &storage_key_usage)) { - accumulator.Run(storage_key, storage_key_usage); - } else { - client_->GetStorageKeyUsage(storage_key, type_, - base::BindOnce(accumulator, storage_key)); - } + if (IsStorageUnlimited(bucket.storage_key)) { + info->unlimited_usage += usage; + } else { + info->limited_usage += usage; } - // Fire the sentinel as we've now called GetStorageKeyUsage for all clients. - accumulator.Run(absl::nullopt, 0); + if (IsUsageCacheEnabledForStorageKey(bucket.storage_key)) + CacheBucketUsage(bucket, usage); + std::move(barrier_callback).Run(); } -void ClientUsageTracker::AccumulateStorageKeyUsage( - AccumulateInfo* info, - const std::string& host, - const absl::optional<blink::StorageKey>& storage_key, - int64_t usage) { - DCHECK_GT(info->pending_jobs, 0U); +void ClientUsageTracker::FinallySendBucketsUsage( + UsageCallback callback, + std::unique_ptr<AccumulateInfo> info) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (storage_key.has_value()) { - // TODO(https://crbug.com/941480): `storage_key` should not be opaque or - // have an empty url, but sometimes it is. - if (storage_key->origin().opaque()) { - DVLOG(1) << "AccumulateStorageKeyUsage for opaque storage_key!"; - RecordSkippedOriginHistogram(InvalidOriginReason::kIsOpaque); - } else if (storage_key->origin().GetURL().is_empty()) { - DVLOG(1) << "AccumulateStorageKeyUsage for storage_key with empty url!"; - RecordSkippedOriginHistogram(InvalidOriginReason::kIsEmpty); - } else { - if (usage < 0) - usage = 0; - - if (IsStorageUnlimited(*storage_key)) - info->unlimited_usage += usage; - else - info->limited_usage += usage; - if (IsUsageCacheEnabledForStorageKey(*storage_key)) - AddCachedStorageKey(*storage_key, usage); - } - } - if (--info->pending_jobs) - return; + DCHECK_GE(info->limited_usage, 0); + DCHECK_GE(info->unlimited_usage, 0); - AddCachedHost(host); - host_usage_accumulators_.Run( - host, info->limited_usage, info->unlimited_usage); + std::move(callback).Run(info->limited_usage + info->unlimited_usage, + info->unlimited_usage); } -void ClientUsageTracker::AddCachedStorageKey( - const blink::StorageKey& storage_key, - int64_t new_usage) { +void ClientUsageTracker::CacheBucketUsage(const BucketLocator& bucket, + int64_t new_usage) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(IsUsageCacheEnabledForStorageKey(storage_key)); - - const std::string& host = storage_key.origin().host(); - int64_t* usage = &cached_usage_by_host_[host][storage_key]; - int64_t delta = new_usage - *usage; - *usage = new_usage; - if (delta) { - UpdateGlobalUsageValue(IsStorageUnlimited(storage_key) - ? &global_unlimited_usage_ - : &global_limited_usage_, - delta); - } + DCHECK(IsUsageCacheEnabledForStorageKey(bucket.storage_key)); + cached_bucket_usage_[bucket] = new_usage; } -void ClientUsageTracker::AddCachedHost(const std::string& host) { +int64_t ClientUsageTracker::GetCachedBucketUsage( + const BucketLocator& bucket) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - cached_hosts_.insert(host); + auto bucket_it = cached_bucket_usage_.find(bucket); + if (bucket_it == cached_bucket_usage_.end()) + return -1; + return bucket_it->second; } -int64_t ClientUsageTracker::GetCachedHostUsage(const std::string& host) const { +void ClientUsageTracker::GetBucketUsage(const BucketLocator& bucket, + UsageCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - auto it = cached_usage_by_host_.find(host); - if (it == cached_usage_by_host_.end()) - return 0; - - int64_t usage = 0; - const UsageMap& usage_map = it->second; - for (const auto& storage_key_and_usage : usage_map) - usage += storage_key_and_usage.second; - return usage; + client_->GetBucketUsage( + bucket, + base::BindOnce(&ClientUsageTracker::DidGetBucketUsage, + weak_factory_.GetWeakPtr(), bucket, std::move(callback))); + return; } -bool ClientUsageTracker::GetCachedStorageKeyUsage( - const blink::StorageKey& storage_key, - int64_t* usage) const { +void ClientUsageTracker::DidGetBucketUsage(const BucketLocator& bucket, + UsageCallback callback, + int64_t usage) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - const std::string& host = storage_key.origin().host(); - auto host_it = cached_usage_by_host_.find(host); - if (host_it == cached_usage_by_host_.end()) - return false; + if (IsUsageCacheEnabledForStorageKey(bucket.storage_key)) + CacheBucketUsage(bucket, usage); - auto storage_key_it = host_it->second.find(storage_key); - if (storage_key_it == host_it->second.end()) - return false; - - DCHECK(IsUsageCacheEnabledForStorageKey(storage_key)); - *usage = storage_key_it->second; - return true; -} - -bool ClientUsageTracker::IsUsageCacheEnabledForStorageKey( - const blink::StorageKey& storage_key) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - const std::string& host = storage_key.origin().host(); - return !StorageKeySetContainsStorageKey( - non_cached_limited_storage_keys_by_host_, host, storage_key) && - !StorageKeySetContainsStorageKey( - non_cached_unlimited_storage_keys_by_host_, host, storage_key); + int64_t unlimited_usage = IsStorageUnlimited(bucket.storage_key) ? usage : 0; + std::move(callback).Run(usage, unlimited_usage); } void ClientUsageTracker::OnGranted(const url::Origin& origin_url, @@ -431,16 +268,8 @@ void ClientUsageTracker::OnGranted(const url::Origin& origin_url, // APIs are converted to use StorageKey instead of Origin. const blink::StorageKey storage_key(origin_url); if (change_flags & SpecialStoragePolicy::STORAGE_UNLIMITED) { - int64_t usage = 0; - if (GetCachedStorageKeyUsage(storage_key, &usage)) { - global_unlimited_usage_ += usage; - global_limited_usage_ -= usage; - } - - const std::string& host = storage_key.origin().host(); - if (EraseStorageKeyFromStorageKeySet( - &non_cached_limited_storage_keys_by_host_, host, storage_key)) - non_cached_unlimited_storage_keys_by_host_[host].insert(storage_key); + if (non_cached_limited_storage_keys_.erase(storage_key)) + non_cached_unlimited_storage_keys_.insert(storage_key); } } @@ -451,52 +280,17 @@ void ClientUsageTracker::OnRevoked(const url::Origin& origin_url, // APIs are converted to use StorageKey instead of Origin. const blink::StorageKey storage_key(origin_url); if (change_flags & SpecialStoragePolicy::STORAGE_UNLIMITED) { - int64_t usage = 0; - if (GetCachedStorageKeyUsage(storage_key, &usage)) { - global_unlimited_usage_ -= usage; - global_limited_usage_ += usage; - } - - const std::string& host = storage_key.origin().host(); - if (EraseStorageKeyFromStorageKeySet( - &non_cached_unlimited_storage_keys_by_host_, host, storage_key)) - non_cached_limited_storage_keys_by_host_[host].insert(storage_key); + if (non_cached_unlimited_storage_keys_.erase(storage_key)) + non_cached_limited_storage_keys_.insert(storage_key); } } void ClientUsageTracker::OnCleared() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - global_limited_usage_ += global_unlimited_usage_; - global_unlimited_usage_ = 0; - - for (const auto& host_and_storage_keys : - non_cached_unlimited_storage_keys_by_host_) { - const auto& host = host_and_storage_keys.first; - for (const auto& storage_key : host_and_storage_keys.second) - non_cached_limited_storage_keys_by_host_[host].insert(storage_key); - } - non_cached_unlimited_storage_keys_by_host_.clear(); -} - -void ClientUsageTracker::UpdateGlobalUsageValue(int64_t* usage_value, - int64_t delta) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - *usage_value += delta; - if (*usage_value >= 0) - return; - - // If we have a negative global usage value, recalculate them. - // TODO(michaeln): There are book keeping bugs, crbug/463729 - global_limited_usage_ = 0; - global_unlimited_usage_ = 0; - for (const auto& host_and_usage_map : cached_usage_by_host_) { - for (const auto& storage_key_and_usage : host_and_usage_map.second) { - if (IsStorageUnlimited(storage_key_and_usage.first)) - global_unlimited_usage_ += storage_key_and_usage.second; - else - global_limited_usage_ += storage_key_and_usage.second; - } - } + non_cached_limited_storage_keys_.insert( + std::make_move_iterator(non_cached_unlimited_storage_keys_.begin()), + std::make_move_iterator(non_cached_unlimited_storage_keys_.end())); + non_cached_unlimited_storage_keys_.clear(); } bool ClientUsageTracker::IsStorageUnlimited( diff --git a/chromium/storage/browser/quota/client_usage_tracker.h b/chromium/storage/browser/quota/client_usage_tracker.h index e22336bb40d..6fe44ebf6fa 100644 --- a/chromium/storage/browser/quota/client_usage_tracker.h +++ b/chromium/storage/browser/quota/client_usage_tracker.h @@ -16,6 +16,7 @@ #include "base/callback.h" #include "base/memory/raw_ptr.h" #include "base/sequence_checker.h" +#include "components/services/storage/public/cpp/buckets/bucket_locator.h" #include "components/services/storage/public/mojom/quota_client.mojom.h" #include "storage/browser/quota/quota_callbacks.h" #include "storage/browser/quota/quota_task.h" @@ -41,17 +42,13 @@ enum class InvalidOriginReason { kMaxValue = kIsEmpty }; -// Holds per-client usage tracking information and caches -// per-host usage data. +// Holds per-client usage tracking information and caches bucket usage data. // // A UsageTracker object will own one ClientUsageTracker instance per client. // This class is not thread-safe. All methods other than the constructor must be // called on the same sequence. class ClientUsageTracker : public SpecialStoragePolicy::Observer { public: - using StorageKeySetByHost = - std::map<std::string, std::set<blink::StorageKey>>; - // The caller must ensure that `client` outlives this instance. ClientUsageTracker( UsageTracker* tracker, @@ -64,81 +61,86 @@ class ClientUsageTracker : public SpecialStoragePolicy::Observer { ~ClientUsageTracker() override; - void GetGlobalUsage(GlobalUsageCallback callback); - void GetHostUsage(const std::string& host, UsageCallback callback); - void UpdateUsageCache(const blink::StorageKey& storage_key, int64_t delta); + // Computes total usage and unlimited usage for `buckets`. + void GetBucketsUsage(const std::set<BucketLocator>& buckets, + UsageCallback callback); + + // Reflects an increase by `delta` to `bucket`'s quota usage. + // + // This can be called with a `bucket` whose usage is not yet cached. + // A negative `delta` value reflects a reduction in quota usage. + // Negative `delta` values are clamped to ensure the total cached usage never + // goes below zero (crbug.com/463729). + void UpdateBucketUsageCache(const BucketLocator& bucket, int64_t delta); + + // Deletes `bucket` from the cache if it exists. Called either for bucket + // deletion or disabling cache for `bucket`'s Storage Key. + void DeleteBucketCache(const BucketLocator& bucket); + + // Accumulates all cached usage to determine storage pressure. int64_t GetCachedUsage() const; + + // Returns cached usage organized by host. Expected to be called after + // GetGlobalUsage which retrieves and caches host usage. std::map<std::string, int64_t> GetCachedHostsUsage() const; + + // Returns cached usage organized by StorageKey. Used for histogram recording. + // TODO(ayui): Update to return bucket usage map. std::map<blink::StorageKey, int64_t> GetCachedStorageKeysUsage() const; - std::set<blink::StorageKey> GetCachedStorageKeys() const; - bool IsUsageCacheEnabledForStorageKey( - const blink::StorageKey& storage_key) const; + + // Sets if a `storage_key` for `client_` should / should not be excluded from + // quota restrictions. void SetUsageCacheEnabled(const blink::StorageKey& storage_key, bool enabled); private: - using UsageMap = std::map<blink::StorageKey, int64_t>; - struct AccumulateInfo; - void DidGetStorageKeysForGlobalUsage( - GlobalUsageCallback callback, - const std::vector<blink::StorageKey>& storage_keys); - void AccumulateHostUsage(AccumulateInfo* info, - GlobalUsageCallback& callback, - int64_t limited_usage, - int64_t unlimited_usage); - - void DidGetStorageKeysForHostUsage( - const std::string& host, - const std::vector<blink::StorageKey>& storage_keys); - - void GetUsageForStorageKeys( - const std::string& host, - const std::vector<blink::StorageKey>& storage_keys); - void AccumulateStorageKeyUsage( - AccumulateInfo* info, - const std::string& host, - const absl::optional<blink::StorageKey>& storage_key, - int64_t usage); - - // Methods used by our GatherUsage tasks, as a task makes progress - // storage keys and hosts are added incrementally to the cache. - void AddCachedStorageKey(const blink::StorageKey& storage_key, int64_t usage); - void AddCachedHost(const std::string& host); - - int64_t GetCachedHostUsage(const std::string& host) const; - bool GetCachedStorageKeyUsage(const blink::StorageKey& storage_key, - int64_t* usage) const; - - // SpecialStoragePolicy::Observer overrides + bool IsUsageCacheEnabledForStorageKey( + const blink::StorageKey& storage_key) const; + + void AccumulateBucketsUsage(base::OnceClosure barrier_callback, + const BucketLocator& bucket, + AccumulateInfo* info, + int64_t usage); + + void FinallySendBucketsUsage(UsageCallback callback, + std::unique_ptr<AccumulateInfo> info); + + // Adds `bucket` and its `usage` to the cache. An existing cached value is + // replaced with the new value provided here. Used by tasks that gather + // global/host usage to incrementally cache as usage is retrieved. + void CacheBucketUsage(const BucketLocator& bucket, int64_t usage); + + // Gets cached `bucket` usage. Returns -1 if no usage is cached. + int64_t GetCachedBucketUsage(const BucketLocator& bucket) const; + + // Retrieves `bucket` usage from the tracked QuotaClient and adds to the + // cache. + void GetBucketUsage(const BucketLocator& bucket, UsageCallback callback); + void DidGetBucketUsage(const BucketLocator& bucket, + UsageCallback callback, + int64_t usage); + + // SpecialStoragePolicy::Observer overrides. // TODO(crbug.com/1215208): Migrate to use StorageKey when the StoragePolicy // is migrated to use StorageKey instead of Origin. void OnGranted(const url::Origin& origin_url, int change_flags) override; void OnRevoked(const url::Origin& origin_url, int change_flags) override; void OnCleared() override; - void UpdateGlobalUsageValue(int64_t* usage_value, int64_t delta); - bool IsStorageUnlimited(const blink::StorageKey& storage_key) const; raw_ptr<mojom::QuotaClient> client_; const blink::mojom::StorageType type_; - int64_t global_limited_usage_; - int64_t global_unlimited_usage_; - bool global_usage_retrieved_; - std::set<std::string> cached_hosts_; - std::map<std::string, UsageMap> cached_usage_by_host_; - - StorageKeySetByHost non_cached_limited_storage_keys_by_host_; - StorageKeySetByHost non_cached_unlimited_storage_keys_by_host_; - - CallbackQueueMap< - base::OnceCallback<void(int64_t limited_usage, int64_t unlimited_usage)>, - std::string, - int64_t, - int64_t> - host_usage_accumulators_; + // The implementation relies on a collection whose erase() only invalidates + // iterators that point to the erased element. This comment is intended to + // prevent accidental conversion to other containers, such as base::flat_map. + std::map<BucketLocator, int64_t> cached_bucket_usage_; + + // Storage Keys that are excluded from quota restrictions. + std::set<blink::StorageKey> non_cached_limited_storage_keys_; + std::set<blink::StorageKey> non_cached_unlimited_storage_keys_; const scoped_refptr<SpecialStoragePolicy> special_storage_policy_; diff --git a/chromium/storage/browser/quota/quota_callbacks.h b/chromium/storage/browser/quota/quota_callbacks.h index d64ab3dc188..5598c143d84 100644 --- a/chromium/storage/browser/quota/quota_callbacks.h +++ b/chromium/storage/browser/quota/quota_callbacks.h @@ -30,17 +30,14 @@ using UsageInfoEntries = std::vector<UsageInfo>; // Common callback types that are used throughout in the quota module. using AddChangeListenerCallback = base::OnceCallback<void()>; -using GlobalUsageCallback = +using UsageCallback = base::OnceCallback<void(int64_t usage, int64_t unlimited_usage)>; using QuotaCallback = base::OnceCallback<void(blink::mojom::QuotaStatusCode status, int64_t quota)>; -using UsageCallback = base::OnceCallback<void(int64_t usage)>; using UsageWithBreakdownCallback = base::OnceCallback<void(int64_t usage, blink::mojom::UsageBreakdownPtr usage_breakdown)>; -using AvailableSpaceCallback = - base::OnceCallback<void(blink::mojom::QuotaStatusCode, int64_t)>; using StatusCallback = base::OnceCallback<void(blink::mojom::QuotaStatusCode)>; using GetBucketsCallback = base::OnceCallback<void(const std::set<BucketLocator>& buckets, diff --git a/chromium/storage/browser/quota/quota_database.cc b/chromium/storage/browser/quota/quota_database.cc index a995b943c57..dc62026cc97 100644 --- a/chromium/storage/browser/quota/quota_database.cc +++ b/chromium/storage/browser/quota/quota_database.cc @@ -19,6 +19,7 @@ #include "base/metrics/histogram_functions.h" #include "components/services/storage/public/cpp/buckets/constants.h" #include "sql/database.h" +#include "sql/error_metrics.h" #include "sql/meta_table.h" #include "sql/statement.h" #include "sql/transaction.h" @@ -52,7 +53,15 @@ const int kQuotaDatabaseCompatibleVersion = 8; // Definitions for database schema. const char kHostQuotaTable[] = "quota"; const char kBucketTable[] = "buckets"; + +// Deprecated flag that ensured that the buckets table was bootstrapped +// with existing storage key data for eviction logic. +// TODO(crbug.com/1254535): Remove once enough time has passed to ensure that +// this flag is no longer stored and supported in the QuotaDatabase. const char kIsOriginTableBootstrapped[] = "IsOriginTableBootstrapped"; +// Flag to ensure that all existing data for storage keys have been +// registered into the buckets table. +const char kBucketsTableBootstrapped[] = "IsBucketsTableBootstrapped"; const int kCommitIntervalMs = 30000; @@ -193,6 +202,26 @@ QuotaErrorOr<BucketInfo> QuotaDatabase::GetOrCreateBucket( /*use_count=*/0, now, now); } +QuotaErrorOr<BucketInfo> QuotaDatabase::GetOrCreateBucketDeprecated( + const StorageKey& storage_key, + const std::string& bucket_name, + StorageType type) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + QuotaErrorOr<BucketInfo> bucket_result = + GetBucket(storage_key, bucket_name, type); + + if (bucket_result.ok()) + return bucket_result; + + if (bucket_result.error() != QuotaError::kNotFound) + return bucket_result.error(); + + base::Time now = base::Time::Now(); + return CreateBucketInternal(storage_key, type, bucket_name, /*use_count=*/0, + now, now); +} + QuotaErrorOr<BucketInfo> QuotaDatabase::CreateBucketForTesting( const StorageKey& storage_key, const std::string& bucket_name, @@ -326,7 +355,7 @@ QuotaError QuotaDatabase::SetStorageKeyLastAccessTime( StorageType type, base::Time last_accessed) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - QuotaError open_error = EnsureOpened(EnsureOpenedMode::kCreateIfNotFound); + QuotaError open_error = EnsureOpened(EnsureOpenedMode::kFailIfNotFound); if (open_error != QuotaError::kNone) return open_error; @@ -335,15 +364,8 @@ QuotaError QuotaDatabase::SetStorageKeyLastAccessTime( // TODO(crbug/1210252): Update to not execute 2 sql statements. QuotaErrorOr<BucketInfo> result = GetBucket(storage_key, kDefaultBucketName, type); - if (!result.ok()) { - if (result.error() != QuotaError::kNotFound) - return result.error(); - - QuotaErrorOr<BucketInfo> created_bucket = - CreateBucketInternal(storage_key, type, kDefaultBucketName, - /*use_count=*/1, last_accessed, last_accessed); - return created_bucket.ok() ? QuotaError::kNone : created_bucket.error(); - } + if (!result.ok()) + return result.error(); // clang-format off static constexpr char kSql[] = @@ -370,9 +392,12 @@ QuotaError QuotaDatabase::SetBucketLastAccessTime(BucketId bucket_id, if (open_error != QuotaError::kNone) return open_error; - BucketTableEntry entry; - if (!GetBucketInfo(bucket_id, &entry)) - return QuotaError::kNotFound; + // Check if bucket exists first. Running an update statement on a bucket that + // doesn't exist fails DCHECK and crashes. + // TODO(crbug/1210252): Update to not execute 2 sql statements. + QuotaErrorOr<BucketTableEntry> entry = GetBucketInfo(bucket_id); + if (!entry.ok()) + return entry.error(); // clang-format off static constexpr char kSql[] = @@ -391,43 +416,6 @@ QuotaError QuotaDatabase::SetBucketLastAccessTime(BucketId bucket_id, return QuotaError::kNone; } -QuotaError QuotaDatabase::SetStorageKeyLastModifiedTime( - const StorageKey& storage_key, - StorageType type, - base::Time last_modified) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - QuotaError open_error = EnsureOpened(EnsureOpenedMode::kCreateIfNotFound); - if (open_error != QuotaError::kNone) - return open_error; - - // Check if bucket exists first. Running an update statement on a bucket that - // doesn't exist fails DCHECK and crashes. - // TODO(crbug/1210252): Update to not execute 2 sql statements. - QuotaErrorOr<BucketInfo> result = - GetBucket(storage_key, kDefaultBucketName, type); - if (!result.ok()) { - if (result.error() != QuotaError::kNotFound) - return result.error(); - - QuotaErrorOr<BucketInfo> created_bucket = - CreateBucketInternal(storage_key, type, kDefaultBucketName, - /*use_count=*/0, last_modified, last_modified); - return created_bucket.ok() ? QuotaError::kNone : created_bucket.error(); - } - - static constexpr char kSql[] = - "UPDATE buckets SET last_modified = ? WHERE id = ?"; - sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); - statement.BindTime(0, last_modified); - statement.BindInt64(1, result->id.value()); - - if (!statement.Run()) - return QuotaError::kDatabaseError; - - ScheduleCommit(); - return QuotaError::kNone; -} - QuotaError QuotaDatabase::SetBucketLastModifiedTime(BucketId bucket_id, base::Time last_modified) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -436,9 +424,12 @@ QuotaError QuotaDatabase::SetBucketLastModifiedTime(BucketId bucket_id, if (open_error != QuotaError::kNone) return open_error; - BucketTableEntry entry; - if (!GetBucketInfo(bucket_id, &entry)) - return QuotaError::kNotFound; + // Check if bucket exists first. Running an update statement on a bucket that + // doesn't exist fails DCHECK and crashes. + // TODO(crbug/1210252): Update to not execute 2 sql statements. + QuotaErrorOr<BucketTableEntry> entry = GetBucketInfo(bucket_id); + if (!entry.ok()) + return entry.error(); static constexpr char kSql[] = "UPDATE buckets SET last_modified = ? WHERE id = ?"; @@ -453,49 +444,52 @@ QuotaError QuotaDatabase::SetBucketLastModifiedTime(BucketId bucket_id, return QuotaError::kNone; } -bool QuotaDatabase::RegisterInitialStorageKeyInfo( - const std::set<StorageKey>& storage_keys, - StorageType type) { +QuotaError QuotaDatabase::RegisterInitialStorageKeyInfo( + base::flat_map<StorageType, std::set<StorageKey>> storage_keys_by_type) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (EnsureOpened(EnsureOpenedMode::kCreateIfNotFound) != QuotaError::kNone) - return false; + QuotaError open_error = EnsureOpened(EnsureOpenedMode::kCreateIfNotFound); + if (open_error != QuotaError::kNone) + return open_error; - for (const auto& storage_key : storage_keys) { - static constexpr char kSql[] = - // clang-format off - "INSERT OR IGNORE INTO buckets(" - "storage_key," - "host," - "type," - "name," - "use_count," - "last_accessed," - "last_modified," - "expiration," - "quota) " - "VALUES (?, ?, ?, ?, 0, 0, 0, ?, 0)"; - // clang-format on - sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); - statement.BindString(0, storage_key.Serialize()); - statement.BindString(1, storage_key.origin().host()); - statement.BindInt(2, static_cast<int>(type)); - statement.BindString(3, kDefaultBucketName); - statement.BindTime(4, base::Time::Max()); - - if (!statement.Run()) - return false; + for (const auto& type_and_storage_keys : storage_keys_by_type) { + StorageType storage_type = type_and_storage_keys.first; + for (const auto& storage_key : type_and_storage_keys.second) { + static constexpr char kSql[] = + // clang-format off + "INSERT OR IGNORE INTO buckets(" + "storage_key," + "host," + "type," + "name," + "use_count," + "last_accessed," + "last_modified," + "expiration," + "quota) " + "VALUES (?, ?, ?, ?, 0, 0, 0, ?, 0)"; + // clang-format on + sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); + statement.BindString(0, storage_key.Serialize()); + statement.BindString(1, storage_key.origin().host()); + statement.BindInt(2, static_cast<int>(storage_type)); + statement.BindString(3, kDefaultBucketName); + statement.BindTime(4, base::Time::Max()); + + if (!statement.Run()) + return QuotaError::kDatabaseError; + } } - ScheduleCommit(); - return true; + return QuotaError::kNone; } -bool QuotaDatabase::GetBucketInfo(BucketId bucket_id, - QuotaDatabase::BucketTableEntry* entry) { +QuotaErrorOr<QuotaDatabase::BucketTableEntry> QuotaDatabase::GetBucketInfo( + BucketId bucket_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!bucket_id.is_null()); - if (EnsureOpened(EnsureOpenedMode::kFailIfNotFound) != QuotaError::kNone) - return false; + QuotaError open_error = EnsureOpened(EnsureOpenedMode::kFailIfNotFound); + if (open_error != QuotaError::kNone) + return open_error; static constexpr char kSql[] = // clang-format off @@ -512,19 +506,20 @@ bool QuotaDatabase::GetBucketInfo(BucketId bucket_id, sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); statement.BindInt64(0, bucket_id.value()); - if (!statement.Step()) - return false; + if (!statement.Step()) { + return statement.Succeeded() ? QuotaError::kNotFound + : QuotaError::kDatabaseError; + } absl::optional<StorageKey> storage_key = StorageKey::Deserialize(statement.ColumnString(0)); if (!storage_key.has_value()) - return false; + return QuotaError::kNotFound; - *entry = BucketTableEntry(bucket_id, std::move(storage_key).value(), - static_cast<StorageType>(statement.ColumnInt(1)), - statement.ColumnString(2), statement.ColumnInt(3), - statement.ColumnTime(4), statement.ColumnTime(5)); - return true; + return BucketTableEntry(bucket_id, std::move(storage_key).value(), + static_cast<StorageType>(statement.ColumnInt(1)), + statement.ColumnString(2), statement.ColumnInt(3), + statement.ColumnTime(4), statement.ColumnTime(5)); } QuotaError QuotaDatabase::DeleteHostQuota(const std::string& host, @@ -547,26 +542,6 @@ QuotaError QuotaDatabase::DeleteHostQuota(const std::string& host, return QuotaError::kNone; } -QuotaError QuotaDatabase::DeleteStorageKeyInfo(const StorageKey& storage_key, - StorageType type) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - QuotaError open_error = EnsureOpened(EnsureOpenedMode::kFailIfNotFound); - if (open_error != QuotaError::kNone) - return open_error; - - static constexpr char kSql[] = - "DELETE FROM buckets WHERE storage_key = ? AND type = ?"; - sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); - statement.BindString(0, storage_key.Serialize()); - statement.BindInt(1, static_cast<int>(type)); - - if (!statement.Run()) - return QuotaError::kDatabaseError; - - ScheduleCommit(); - return QuotaError::kNone; -} - QuotaError QuotaDatabase::DeleteBucketInfo(BucketId bucket_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!bucket_id.is_null()); @@ -687,21 +662,52 @@ QuotaErrorOr<std::set<BucketLocator>> QuotaDatabase::GetBucketsModifiedBetween( return buckets; } -bool QuotaDatabase::IsBootstrappedForEviction() { +bool QuotaDatabase::IsBootstrapped() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (EnsureOpened(EnsureOpenedMode::kCreateIfNotFound) != QuotaError::kNone) return false; int flag = 0; - return meta_table_->GetValue(kIsOriginTableBootstrapped, &flag) && flag; + return meta_table_->GetValue(kBucketsTableBootstrapped, &flag) && flag; } -bool QuotaDatabase::SetBootstrappedForEviction(bool bootstrap_flag) { +QuotaError QuotaDatabase::SetIsBootstrapped(bool bootstrap_flag) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (EnsureOpened(EnsureOpenedMode::kCreateIfNotFound) != QuotaError::kNone) - return false; + QuotaError open_error = EnsureOpened(EnsureOpenedMode::kCreateIfNotFound); + if (open_error != QuotaError::kNone) + return open_error; - return meta_table_->SetValue(kIsOriginTableBootstrapped, bootstrap_flag); + // Delete deprecated bootstrap flag if it still exists. + // TODO(crbug.com/1254535): Remove once enough time has passed to ensure that + // this flag is no longer stored and supported in the QuotaDatabase. + meta_table_->DeleteKey(kIsOriginTableBootstrapped); + + return meta_table_->SetValue(kBucketsTableBootstrapped, bootstrap_flag) + ? QuotaError::kNone + : QuotaError::kDatabaseError; +} + +QuotaError QuotaDatabase::CorruptForTesting( + base::OnceCallback<void(const base::FilePath&)> corrupter) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (db_) { + // Commit the long-running transaction. + db_->CommitTransaction(); + db_->Close(); + } + + std::move(corrupter).Run(db_file_path_); + + if (!db_) + return QuotaError::kDatabaseError; + if (!OpenDatabase()) + return QuotaError::kDatabaseError; + + // Begin a long-running transaction. This matches EnsureOpen(). + if (!db_->BeginTransaction()) + return QuotaError::kDatabaseError; + return QuotaError::kNone; } void QuotaDatabase::Commit() { @@ -752,6 +758,13 @@ QuotaError QuotaDatabase::EnsureOpened(EnsureOpenedMode mode) { db_->set_histogram_tag("Quota"); + // UMA logging and don't crash on database errors in DCHECK builds. + db_->set_error_callback( + base::BindRepeating([](int sqlite_error_code, sql::Statement* statement) { + sql::UmaHistogramSqliteResult("Quota.QuotaDatabaseError", + sqlite_error_code); + })); + if (!OpenDatabase() || !EnsureDatabaseVersion()) { LOG(ERROR) << "Could not open the quota database, resetting."; if (!ResetSchema()) { diff --git a/chromium/storage/browser/quota/quota_database.h b/chromium/storage/browser/quota/quota_database.h index 84b0f5f22be..75de1871c82 100644 --- a/chromium/storage/browser/quota/quota_database.h +++ b/chromium/storage/browser/quota/quota_database.h @@ -13,7 +13,6 @@ #include <string> #include "base/callback.h" -#include "base/compiler_specific.h" #include "base/component_export.h" #include "base/files/file_path.h" #include "base/sequence_checker.h" @@ -56,7 +55,8 @@ enum class DatabaseResetReason { // // 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. +// constructor, must called on the DB thread. QuotaDatabase should only be +// subclassed in tests. class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase { public: struct COMPONENT_EXPORT(STORAGE_BROWSER) BucketTableEntry { @@ -88,7 +88,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase { QuotaDatabase(const QuotaDatabase&) = delete; QuotaDatabase& operator=(const QuotaDatabase&) = delete; - ~QuotaDatabase(); + virtual ~QuotaDatabase(); // Returns quota if entry is found. Returns QuotaError::kNotFound no entry if // found. @@ -111,6 +111,15 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase { const blink::StorageKey& storage_key, const std::string& bucket_name); + // Same as GetOrCreateBucket but takes in StorageType. This should only be + // used by FileSystem, and is expected to be removed when + // StorageType::kSyncable and StorageType::kPersistent are deprecated. + // (crbug.com/1233525, crbug.com/1286964). + QuotaErrorOr<BucketInfo> GetOrCreateBucketDeprecated( + const blink::StorageKey& storage_key, + const std::string& bucket_name, + blink::mojom::StorageType type); + // TODO(crbug.com/1208141): Remove `storage_type` when the only supported // StorageType is kTemporary. QuotaErrorOr<BucketInfo> CreateBucketForTesting( @@ -144,49 +153,36 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase { // TODO(crbug.com/1202167): Remove once all usages have updated to use // SetBucketLastAccessTime. - QuotaError SetStorageKeyLastAccessTime(const blink::StorageKey& storage_key, - blink::mojom::StorageType type, - base::Time last_accessed) - WARN_UNUSED_RESULT; + [[nodiscard]] QuotaError SetStorageKeyLastAccessTime( + const blink::StorageKey& storage_key, + blink::mojom::StorageType type, + base::Time last_accessed); // Called by QuotaClient implementers to update when the bucket was last // accessed. If `bucket_id` refers to a bucket with an opaque StorageKey, the // bucket's last access time will not be updated and the function will return // QuotaError::kNotFound. Returns QuotaError::kNone on a successful update. - QuotaError SetBucketLastAccessTime(BucketId bucket_id, - base::Time last_accessed) - WARN_UNUSED_RESULT; - - // TODO(crbug.com/1202167): Remove once all usages have updated to use - // SetBucketLastModifiedTime. - QuotaError SetStorageKeyLastModifiedTime(const blink::StorageKey& storage_key, - blink::mojom::StorageType type, - base::Time last_modified) - WARN_UNUSED_RESULT; + [[nodiscard]] QuotaError SetBucketLastAccessTime(BucketId bucket_id, + base::Time last_accessed); // Called by QuotaClient implementers to update when the bucket was last // modified. Returns QuotaError::kNone on a successful update. - QuotaError SetBucketLastModifiedTime(BucketId bucket_id, - base::Time last_modified) - WARN_UNUSED_RESULT; + [[nodiscard]] QuotaError SetBucketLastModifiedTime(BucketId bucket_id, + base::Time last_modified); - // Register initial `storage_keys` info `type` to the database. + // Register initial `storage_keys_by_type` into the database. // This method is assumed to be called only after the installation or // the database schema reset. - bool RegisterInitialStorageKeyInfo( - const std::set<blink::StorageKey>& storage_keys, - blink::mojom::StorageType type); - - // Gets the table entry for `bucket`. Returns whether the record for an - // origin bucket can be found. - bool GetBucketInfo(BucketId bucket_id, BucketTableEntry* entry); + QuotaError RegisterInitialStorageKeyInfo( + base::flat_map<blink::mojom::StorageType, std::set<blink::StorageKey>> + storage_keys_by_type); - // Removes all buckets for `storage_key` with `type`. - QuotaError DeleteStorageKeyInfo(const blink::StorageKey& storage_key, - blink::mojom::StorageType type); + // Returns the BucketTableEntry for `bucket` if one exists. Returns a + // QuotaError if not found or the operation has failed. + QuotaErrorOr<BucketTableEntry> GetBucketInfo(BucketId bucket_id); - // Deletes the specified bucket. - QuotaError DeleteBucketInfo(BucketId bucket_id); + // Deletes the specified bucket. This method is virtual for testing. + virtual QuotaError DeleteBucketInfo(BucketId bucket_id); // Returns the BucketLocator for the least recently used bucket. Will exclude // buckets with ids in `bucket_exceptions` and origins that have the special @@ -207,11 +203,17 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase { base::Time begin, base::Time end); - // Returns false if SetBootstrappedForEviction() has never - // been called before, which means existing storage keys may not have been - // registered. - bool IsBootstrappedForEviction(); - bool SetBootstrappedForEviction(bool bootstrap_flag); + // Returns false if SetIsBootstrapped() has never been called before, which + // means existing storage keys may not have been registered. Bootstrapping + // ensures that there is a bucket entry in the buckets table for all storage + // keys that have stored data by quota managed Storage APIs. + bool IsBootstrapped(); + QuotaError SetIsBootstrapped(bool bootstrap_flag); + + // Returns QuotaError::kNone if the database was successfully reopened after + // `corrupter` was run, or QuotaError::kDatabaseError otherwise. + QuotaError CorruptForTesting( + base::OnceCallback<void(const base::FilePath&)> corrupter); // Manually disable database to test database error scenarios for testing. void SetDisabledForTesting(bool disable) { is_disabled_ = disable; } diff --git a/chromium/storage/browser/quota/quota_database_unittest.cc b/chromium/storage/browser/quota/quota_database_unittest.cc index 23c6820cae9..18f6f282e6d 100644 --- a/chromium/storage/browser/quota/quota_database_unittest.cc +++ b/chromium/storage/browser/quota/quota_database_unittest.cc @@ -12,6 +12,7 @@ #include "base/bind.h" #include "base/callback.h" #include "base/containers/contains.h" +#include "base/containers/flat_map.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/test/metrics/histogram_tester.h" @@ -20,6 +21,7 @@ #include "components/services/storage/public/cpp/buckets/bucket_locator.h" #include "components/services/storage/public/cpp/buckets/constants.h" #include "sql/database.h" +#include "sql/error_metrics.h" #include "sql/meta_table.h" #include "sql/statement.h" #include "sql/test/scoped_error_expecter.h" @@ -44,10 +46,7 @@ static const blink::mojom::StorageType kPerm = bool ContainsBucket(const std::set<BucketLocator>& buckets, const BucketInfo& target_bucket) { - BucketLocator target_bucket_locator( - target_bucket.id, target_bucket.storage_key, target_bucket.type, - target_bucket.name == kDefaultBucketName); - auto it = buckets.find(target_bucket_locator); + auto it = buckets.find(target_bucket.ToBucketLocator()); return it != buckets.end(); } @@ -250,6 +249,34 @@ TEST_P(QuotaDatabaseTest, GetOrCreateBucket) { ASSERT_EQ(retrieved_bucket.type, created_bucket.type); } +TEST_P(QuotaDatabaseTest, GetOrCreateBucketDeprecated) { + QuotaDatabase db(use_in_memory_db() ? base::FilePath() : DbPath()); + EXPECT_TRUE(EnsureOpened(&db, EnsureOpenedMode::kCreateIfNotFound)); + StorageKey storage_key = + StorageKey::CreateFromStringForTesting("http://google/"); + std::string bucket_name = "google_bucket"; + + QuotaErrorOr<BucketInfo> result = + db.GetOrCreateBucketDeprecated(storage_key, bucket_name, kPerm); + ASSERT_TRUE(result.ok()); + + BucketInfo created_bucket = result.value(); + ASSERT_GT(created_bucket.id.value(), 0); + ASSERT_EQ(created_bucket.name, bucket_name); + ASSERT_EQ(created_bucket.storage_key, storage_key); + ASSERT_EQ(created_bucket.type, kPerm); + + // Should return the same bucket when querying again. + result = db.GetOrCreateBucketDeprecated(storage_key, bucket_name, kPerm); + ASSERT_TRUE(result.ok()); + + BucketInfo retrieved_bucket = result.value(); + ASSERT_EQ(retrieved_bucket.id, created_bucket.id); + ASSERT_EQ(retrieved_bucket.name, created_bucket.name); + ASSERT_EQ(retrieved_bucket.storage_key, created_bucket.storage_key); + ASSERT_EQ(retrieved_bucket.type, created_bucket.type); +} + TEST_P(QuotaDatabaseTest, GetBucket) { QuotaDatabase db(use_in_memory_db() ? base::FilePath() : DbPath()); EXPECT_TRUE(EnsureOpened(&db, EnsureOpenedMode::kCreateIfNotFound)); @@ -426,7 +453,7 @@ TEST_P(QuotaDatabaseTest, GetBucketWithNoDb) { // TODO(crbug.com/1216094): Update test to have its behavior on Fuchsia/Win // match with other platforms, and enable test on all platforms. -#if !defined(OS_FUCHSIA) && !defined(OS_WIN) +#if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_WIN) TEST_F(QuotaDatabaseTest, GetBucketWithOpenDatabaseError) { base::HistogramTester histograms; sql::test::ScopedErrorExpecter expecter; @@ -449,66 +476,7 @@ TEST_F(QuotaDatabaseTest, GetBucketWithOpenDatabaseError) { histograms.ExpectBucketCount("Quota.QuotaDatabaseReset", DatabaseResetReason::kOpenDatabase, 1); } -#endif // !defined(OS_FUCHSIA) && !defined(OS_WIN) - -TEST_P(QuotaDatabaseTest, DeleteStorageKeyInfo) { - QuotaDatabase db(use_in_memory_db() ? base::FilePath() : DbPath()); - EXPECT_TRUE(EnsureOpened(&db, EnsureOpenedMode::kCreateIfNotFound)); - - const StorageKey storage_key = - StorageKey::CreateFromStringForTesting("http://example-a/"); - QuotaErrorOr<BucketInfo> temp_bucket1 = - db.CreateBucketForTesting(storage_key, "temp1", kTemp); - QuotaErrorOr<BucketInfo> temp_bucket2 = - db.CreateBucketForTesting(storage_key, "temp2", kTemp); - QuotaErrorOr<BucketInfo> perm_bucket = - db.CreateBucketForTesting(storage_key, "perm", kPerm); - - db.DeleteStorageKeyInfo(storage_key, kTemp); - - QuotaErrorOr<BucketInfo> result = - db.GetBucket(storage_key, temp_bucket1->name, kTemp); - ASSERT_FALSE(result.ok()); - ASSERT_EQ(result.error(), QuotaError::kNotFound); - - result = db.GetBucket(storage_key, temp_bucket2->name, kTemp); - ASSERT_FALSE(result.ok()); - ASSERT_EQ(result.error(), QuotaError::kNotFound); - - result = db.GetBucket(storage_key, perm_bucket->name, kPerm); - ASSERT_TRUE(result.ok()); - - db.DeleteStorageKeyInfo(storage_key, kPerm); - - result = db.GetBucket(storage_key, perm_bucket->name, kPerm); - ASSERT_FALSE(result.ok()); - ASSERT_EQ(result.error(), QuotaError::kNotFound); -} - -TEST_P(QuotaDatabaseTest, SetStorageKeyLastModifiedTime) { - QuotaDatabase db(use_in_memory_db() ? base::FilePath() : DbPath()); - EXPECT_TRUE(EnsureOpened(&db, EnsureOpenedMode::kCreateIfNotFound)); - - const StorageKey storage_key = - StorageKey::CreateFromStringForTesting("http://example/"); - base::Time now = base::Time::Now(); - - // Should create a bucket if one doesn't exist. - EXPECT_EQ(db.SetStorageKeyLastModifiedTime(storage_key, kTemp, now), - QuotaError::kNone); - - QuotaErrorOr<BucketInfo> bucket = - db.GetBucket(storage_key, kDefaultBucketName, kTemp); - EXPECT_TRUE(bucket.ok()); - - EXPECT_EQ(db.SetStorageKeyLastModifiedTime(storage_key, kTemp, now), - QuotaError::kNone); - - QuotaDatabase::BucketTableEntry info; - EXPECT_TRUE(db.GetBucketInfo(bucket->id, &info)); - EXPECT_EQ(now, info.last_modified); - EXPECT_EQ(0, info.use_count); -} +#endif // !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_WIN) TEST_P(QuotaDatabaseTest, BucketLastAccessTimeLRU) { QuotaDatabase db(use_in_memory_db() ? base::FilePath() : DbPath()); @@ -616,21 +584,21 @@ TEST_P(QuotaDatabaseTest, SetStorageKeyLastAccessTime) { StorageKey::CreateFromStringForTesting("http://example/"); base::Time now = base::Time::Now(); - // Should create a bucket if one doesn't exist. + // Should error if bucket doesn't exist. EXPECT_EQ(db.SetStorageKeyLastAccessTime(storage_key, kTemp, now), - QuotaError::kNone); + QuotaError::kNotFound); QuotaErrorOr<BucketInfo> bucket = - db.GetBucket(storage_key, kDefaultBucketName, kTemp); - EXPECT_TRUE(bucket.ok()); + db.CreateBucketForTesting(storage_key, kDefaultBucketName, kTemp); EXPECT_EQ(db.SetStorageKeyLastAccessTime(storage_key, kTemp, now), QuotaError::kNone); - QuotaDatabase::BucketTableEntry info; - EXPECT_TRUE(db.GetBucketInfo(bucket->id, &info)); - EXPECT_EQ(now, info.last_accessed); - EXPECT_EQ(2, info.use_count); + QuotaErrorOr<QuotaDatabase::BucketTableEntry> info = + db.GetBucketInfo(bucket->id); + EXPECT_TRUE(info.ok()); + EXPECT_EQ(now, info->last_accessed); + EXPECT_EQ(1, info->use_count); } TEST_P(QuotaDatabaseTest, GetStorageKeysForType) { @@ -776,37 +744,44 @@ TEST_P(QuotaDatabaseTest, BucketLastModifiedBetween) { TEST_P(QuotaDatabaseTest, RegisterInitialStorageKeyInfo) { QuotaDatabase db(use_in_memory_db() ? base::FilePath() : DbPath()); + base::flat_map<blink::mojom::StorageType, std::set<StorageKey>> + storage_keys_by_type; const StorageKey kStorageKeys[] = { StorageKey::CreateFromStringForTesting("http://a/"), StorageKey::CreateFromStringForTesting("http://b/"), StorageKey::CreateFromStringForTesting("http://c/")}; - std::set<StorageKey> storage_keys(kStorageKeys, std::end(kStorageKeys)); + storage_keys_by_type.emplace( + kTemp, std::set<StorageKey>(kStorageKeys, std::end(kStorageKeys))); + storage_keys_by_type.emplace( + kPerm, std::set<StorageKey>(kStorageKeys, std::end(kStorageKeys))); - EXPECT_TRUE(db.RegisterInitialStorageKeyInfo(storage_keys, kTemp)); + EXPECT_EQ(db.RegisterInitialStorageKeyInfo(storage_keys_by_type), + QuotaError::kNone); QuotaErrorOr<BucketInfo> bucket_result = db.GetBucket(StorageKey::CreateFromStringForTesting("http://a/"), kDefaultBucketName, kTemp); ASSERT_TRUE(bucket_result.ok()); - QuotaDatabase::BucketTableEntry info; - info.use_count = -1; - EXPECT_TRUE(db.GetBucketInfo(bucket_result->id, &info)); - EXPECT_EQ(0, info.use_count); + QuotaErrorOr<QuotaDatabase::BucketTableEntry> info = + db.GetBucketInfo(bucket_result->id); + EXPECT_TRUE(info.ok()); + EXPECT_EQ(0, info->use_count); EXPECT_EQ(db.SetStorageKeyLastAccessTime( StorageKey::CreateFromStringForTesting("http://a/"), kTemp, base::Time::FromDoubleT(1.0)), QuotaError::kNone); - info.use_count = -1; - EXPECT_TRUE(db.GetBucketInfo(bucket_result->id, &info)); - EXPECT_EQ(1, info.use_count); + info = db.GetBucketInfo(bucket_result->id); + EXPECT_TRUE(info.ok()); + EXPECT_EQ(1, info->use_count); - EXPECT_TRUE(db.RegisterInitialStorageKeyInfo(storage_keys, kTemp)); + EXPECT_EQ(db.RegisterInitialStorageKeyInfo(storage_keys_by_type), + QuotaError::kNone); - info.use_count = -1; - EXPECT_TRUE(db.GetBucketInfo(bucket_result->id, &info)); - EXPECT_EQ(1, info.use_count); + info = db.GetBucketInfo(bucket_result->id); + EXPECT_TRUE(info.ok()); + EXPECT_EQ(1, info->use_count); } TEST_P(QuotaDatabaseTest, DumpQuotaTable) { @@ -864,33 +839,35 @@ TEST_P(QuotaDatabaseTest, GetBucketInfo) { AssignBucketTable(&db, kTableEntries); { - Entry entry; - EXPECT_TRUE(db.GetBucketInfo(kTableEntries[0].bucket_id, &entry)); - EXPECT_EQ(kTableEntries[0].bucket_id, entry.bucket_id); - EXPECT_EQ(kTableEntries[0].type, entry.type); - EXPECT_EQ(kTableEntries[0].storage_key, entry.storage_key); - EXPECT_EQ(kTableEntries[0].name, entry.name); - EXPECT_EQ(kTableEntries[0].use_count, entry.use_count); - EXPECT_EQ(kTableEntries[0].last_accessed, entry.last_accessed); - EXPECT_EQ(kTableEntries[0].last_modified, entry.last_modified); + QuotaErrorOr<QuotaDatabase::BucketTableEntry> entry = + db.GetBucketInfo(kTableEntries[0].bucket_id); + EXPECT_TRUE(entry.ok()); + EXPECT_EQ(kTableEntries[0].bucket_id, entry->bucket_id); + EXPECT_EQ(kTableEntries[0].type, entry->type); + EXPECT_EQ(kTableEntries[0].storage_key, entry->storage_key); + EXPECT_EQ(kTableEntries[0].name, entry->name); + EXPECT_EQ(kTableEntries[0].use_count, entry->use_count); + EXPECT_EQ(kTableEntries[0].last_accessed, entry->last_accessed); + EXPECT_EQ(kTableEntries[0].last_modified, entry->last_modified); } { // BucketId 456 is not in the database. - Entry entry; - EXPECT_FALSE(db.GetBucketInfo(BucketId(456), &entry)); + QuotaErrorOr<QuotaDatabase::BucketTableEntry> entry = + db.GetBucketInfo(BucketId(456)); + EXPECT_FALSE(entry.ok()); } } // Non-parameterized tests. -TEST_F(QuotaDatabaseTest, BootstrapForEvictionFlag) { +TEST_F(QuotaDatabaseTest, BootstrapFlag) { QuotaDatabase db(DbPath()); - EXPECT_FALSE(db.IsBootstrappedForEviction()); - EXPECT_TRUE(db.SetBootstrappedForEviction(true)); - EXPECT_TRUE(db.IsBootstrappedForEviction()); - EXPECT_TRUE(db.SetBootstrappedForEviction(false)); - EXPECT_FALSE(db.IsBootstrappedForEviction()); + EXPECT_FALSE(db.IsBootstrapped()); + EXPECT_EQ(db.SetIsBootstrapped(true), QuotaError::kNone); + EXPECT_TRUE(db.IsBootstrapped()); + EXPECT_EQ(db.SetIsBootstrapped(false), QuotaError::kNone); + EXPECT_FALSE(db.IsBootstrapped()); } TEST_F(QuotaDatabaseTest, OpenCorruptedDatabase) { @@ -913,6 +890,45 @@ TEST_F(QuotaDatabaseTest, OpenCorruptedDatabase) { histograms.ExpectTotalCount("Quota.QuotaDatabaseReset", 1); histograms.ExpectBucketCount("Quota.QuotaDatabaseReset", DatabaseResetReason::kCreateSchema, 1); + + EXPECT_GE(histograms.GetTotalSum("Quota.QuotaDatabaseError"), 1); + EXPECT_GE(histograms.GetBucketCount("Quota.QuotaDatabaseError", + sql::SqliteLoggedResultCode::kCorrupt), + 1); +} + +TEST_F(QuotaDatabaseTest, GetOrCreateBucket_CorruptedDatabase) { + QuotaDatabase db(DbPath()); + StorageKey storage_key = + StorageKey::CreateFromStringForTesting("http://google/"); + std::string bucket_name = "google_bucket"; + + { + QuotaErrorOr<BucketInfo> result = + db.GetOrCreateBucket(storage_key, bucket_name); + ASSERT_TRUE(result.ok()) << "Failed to create bucket to be used in test"; + } + + // Bucket lookup uses the `buckets_by_storage_key` index. + QuotaError corruption_error = + db.CorruptForTesting(base::BindOnce([](const base::FilePath& db_path) { + ASSERT_TRUE( + sql::test::CorruptIndexRootPage(db_path, "buckets_by_storage_key")); + })); + ASSERT_EQ(QuotaError::kNone, corruption_error) + << "Failed to corrupt the database"; + + { + base::HistogramTester histograms; + + QuotaErrorOr<BucketInfo> result = + db.GetOrCreateBucket(storage_key, bucket_name); + EXPECT_FALSE(result.ok()); + + histograms.ExpectTotalCount("Quota.QuotaDatabaseError", 1); + histograms.ExpectBucketCount("Quota.QuotaDatabaseError", + sql::SqliteLoggedResultCode::kCorrupt, 1); + } } INSTANTIATE_TEST_SUITE_P(All, diff --git a/chromium/storage/browser/quota/quota_features.cc b/chromium/storage/browser/quota/quota_features.cc index 04985ab9fc0..4d6dee2ee72 100644 --- a/chromium/storage/browser/quota/quota_features.cc +++ b/chromium/storage/browser/quota/quota_features.cc @@ -39,5 +39,10 @@ constexpr base::FeatureParam<double> kShouldRemainAvailableRatio{ &kStorageQuotaSettings, "ShouldRemainAvailableRatio", 0.1 /* 10% */ }; +// Whether the StoragePolicyObserver only sends updates for modified origins. +const base::Feature kOnlySendStoragePolicyUpdatesForModifiedOrigins{ + "OnlySendStoragePolicyUpdatesForModifiedOrigins", + base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace features } // namespace storage diff --git a/chromium/storage/browser/quota/quota_features.h b/chromium/storage/browser/quota/quota_features.h index 5cf4cb0b047..23bbafab57d 100644 --- a/chromium/storage/browser/quota/quota_features.h +++ b/chromium/storage/browser/quota/quota_features.h @@ -25,6 +25,9 @@ extern const base::FeatureParam<double> kPoolSizeRatio; extern const base::FeatureParam<double> kShouldRemainAvailableBytes; extern const base::FeatureParam<double> kShouldRemainAvailableRatio; +COMPONENT_EXPORT(STORAGE_BROWSER) +extern const base::Feature kOnlySendStoragePolicyUpdatesForModifiedOrigins; + } // namespace features } // namespace storage diff --git a/chromium/storage/browser/quota/quota_internals.mojom b/chromium/storage/browser/quota/quota_internals.mojom new file mode 100644 index 00000000000..226b34d91b7 --- /dev/null +++ b/chromium/storage/browser/quota/quota_internals.mojom @@ -0,0 +1,24 @@ +// 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. + +module storage.mojom; + +import "url/mojom/origin.mojom"; + +// Interface for controlling Quota Internals. +// Hosted on chrome://quota-internals" for WebUI content::QuotaInternalsUI. +interface QuotaInternalsHandler { + // Returns the total and available disk space in bits for a user, + // which is then converted to bytes and displayed on the Quota Internals UI. + GetDiskAvailability() => (uint64 total_space, uint64 available_space); + + // Returns the following statistics: + // Errors on Getting Usage and Quota, Evicted Buckets, Evicted Rounds + // and Skipped Eviction Rounds. + GetStatistics() => (map<string, string> eviction_statistics); + + // Used by the quota-internals page to test behavior of the storage pressure + // callback and trigger a storage pressure notification. + SimulateStoragePressure(url.mojom.Origin origin_url); +};
\ No newline at end of file diff --git a/chromium/storage/browser/quota/quota_manager_impl.cc b/chromium/storage/browser/quota/quota_manager_impl.cc index 0ebb245ec41..cada5f89dd9 100644 --- a/chromium/storage/browser/quota/quota_manager_impl.cc +++ b/chromium/storage/browser/quota/quota_manager_impl.cc @@ -6,7 +6,6 @@ #include <stddef.h> #include <stdint.h> -#include <sstream> #include <algorithm> #include <functional> @@ -16,16 +15,20 @@ #include "base/barrier_closure.h" #include "base/bind.h" +#include "base/callback.h" #include "base/callback_helpers.h" #include "base/command_line.h" +#include "base/containers/contains.h" #include "base/files/file_util.h" #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" +#include "base/memory/weak_ptr.h" #include "base/metrics/histogram_functions.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/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/system/sys_info.h" #include "base/task/post_task.h" @@ -33,12 +36,14 @@ #include "base/task/single_thread_task_runner.h" #include "base/task/task_runner_util.h" #include "base/task/thread_pool.h" +#include "base/thread_annotations.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/cpp/buckets/bucket_locator.h" +#include "components/services/storage/public/cpp/buckets/constants.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" @@ -103,6 +108,16 @@ QuotaErrorOr<BucketInfo> GetOrCreateBucketOnDBThread( return database->GetOrCreateBucket(storage_key, bucket_name); } +QuotaErrorOr<BucketInfo> GetOrCreateBucketDeprecatedOnDBThread( + const StorageKey& storage_key, + const std::string& bucket_name, + blink::mojom::StorageType storage_type, + QuotaDatabase* database) { + DCHECK(database); + return database->GetOrCreateBucketDeprecated(storage_key, bucket_name, + storage_type); +} + QuotaErrorOr<BucketInfo> CreateBucketOnDBThread( const StorageKey& storage_key, const std::string& bucket_name, @@ -174,6 +189,11 @@ QuotaError SetPersistentHostQuotaOnDBThread(const std::string& host, return database->SetHostQuota(host, StorageType::kPersistent, *new_quota); } +QuotaError SetDatabaseBootstrappedOnDBThread(QuotaDatabase* database) { + DCHECK(database); + return database->SetIsBootstrapped(true); +} + QuotaErrorOr<BucketLocator> GetLRUBucketOnDBThread( StorageType type, const std::set<BucketId>& bucket_exceptions, @@ -183,46 +203,26 @@ QuotaErrorOr<BucketLocator> GetLRUBucketOnDBThread( return database->GetLRUBucket(type, bucket_exceptions, policy); } -QuotaError DeleteStorageKeyInfoOnDBThread(const StorageKey& storage_key, - StorageType type, - QuotaDatabase* database) { - DCHECK(database); - return database->DeleteStorageKeyInfo(storage_key, type); -} - QuotaError DeleteBucketInfoOnDBThread(BucketId bucket_id, - bool is_eviction, QuotaDatabase* database) { DCHECK(database); - - if (is_eviction) { - base::Time now = base::Time::Now(); - QuotaDatabase::BucketTableEntry entry; - database->GetBucketInfo(bucket_id, &entry); - base::UmaHistogramCounts1M( - QuotaManagerImpl::kEvictedBucketAccessedCountHistogram, - entry.use_count); - base::UmaHistogramCounts1000( - QuotaManagerImpl::kEvictedBucketDaysSinceAccessHistogram, - (now - entry.last_accessed).InDays()); - } - return database->DeleteBucketInfo(bucket_id); } -bool BootstrapDatabaseOnDBThread(std::set<StorageKey> storage_keys, - QuotaDatabase* database) { +QuotaErrorOr<QuotaDatabase::BucketTableEntry> +GetBucketInfoForEvictionOnDBThread(BucketId bucket_id, + QuotaDatabase* database) { DCHECK(database); - if (database->IsBootstrappedForEviction()) - return true; + return database->GetBucketInfo(bucket_id); +} +QuotaError BootstrapDatabaseOnDBThread( + base::flat_map<StorageType, std::set<StorageKey>> storage_keys_by_type, + QuotaDatabase* database) { + DCHECK(database); // Register existing storage keys with 0 last time access. - if (database->RegisterInitialStorageKeyInfo(storage_keys, - StorageType::kTemporary)) { - database->SetBootstrappedForEviction(true); - return true; - } - return false; + return database->RegisterInitialStorageKeyInfo( + std::move(storage_keys_by_type)); } QuotaError UpdateAccessTimeOnDBThread(const StorageKey& storage_key, @@ -241,15 +241,6 @@ QuotaError UpdateBucketAccessTimeOnDBThread(BucketId bucket_id, return database->SetBucketLastAccessTime(bucket_id, accessed_time); } -QuotaError UpdateModifiedTimeOnDBThread(const StorageKey& storage_key, - StorageType type, - base::Time modified_time, - QuotaDatabase* database) { - DCHECK(database); - return database->SetStorageKeyLastModifiedTime(storage_key, type, - modified_time); -} - QuotaError UpdateBucketModifiedTimeOnDBThread(BucketId bucket_id, base::Time modified_time, QuotaDatabase* database) { @@ -263,6 +254,7 @@ void DidGetUsageAndQuotaStripBreakdown( int64_t usage, int64_t quota, blink::mojom::UsageBreakdownPtr usage_breakdown) { + DCHECK(callback); std::move(callback).Run(status, usage, quota); } @@ -273,6 +265,7 @@ void DidGetUsageAndQuotaStripOverride( int64_t quota, bool is_override_enabled, blink::mojom::UsageBreakdownPtr usage_breakdown) { + DCHECK(callback); std::move(callback).Run(status, usage, quota, std::move(usage_breakdown)); } @@ -283,6 +276,7 @@ constexpr int64_t QuotaManagerImpl::kNoLimit; constexpr int64_t QuotaManagerImpl::kPerHostPersistentQuotaLimit; constexpr int QuotaManagerImpl::kEvictionIntervalInMilliSeconds; constexpr int QuotaManagerImpl::kThresholdOfErrorsToBeDenylisted; +constexpr int QuotaManagerImpl::kThresholdOfErrorsToDisableDatabase; constexpr int QuotaManagerImpl::kThresholdRandomizationPercent; constexpr char QuotaManagerImpl::kDatabaseName[]; constexpr char QuotaManagerImpl::kEvictedBucketAccessedCountHistogram[]; @@ -309,7 +303,10 @@ class QuotaManagerImpl::UsageAndQuotaInfoGatherer : public QuotaTask { is_session_only_(is_session_only), is_incognito_(is_incognito), is_override_enabled_(quota_override_size.has_value()), - quota_override_size_(quota_override_size) {} + quota_override_size_(quota_override_size) { + DCHECK(manager); + DCHECK(callback_); + } protected: void Run() override { @@ -404,14 +401,16 @@ class QuotaManagerImpl::UsageAndQuotaInfoGatherer : public QuotaTask { void OnGotSettings(base::RepeatingClosure barrier_closure, const QuotaSettings& settings) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(barrier_closure); + 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); + SetDesiredHostQuota(std::move(barrier_closure), + blink::mojom::QuotaStatusCode::kOk, host_quota); } } @@ -419,6 +418,10 @@ class QuotaManagerImpl::UsageAndQuotaInfoGatherer : public QuotaTask { int64_t total_space, int64_t available_space) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(barrier_closure); + DCHECK_GE(total_space, 0); + DCHECK_GE(available_space, 0); + total_space_ = total_space; available_space_ = available_space; std::move(barrier_closure).Run(); @@ -428,6 +431,16 @@ class QuotaManagerImpl::UsageAndQuotaInfoGatherer : public QuotaTask { int64_t usage, blink::mojom::UsageBreakdownPtr usage_breakdown) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(barrier_closure); + DCHECK_GE(usage, -1); + DCHECK(usage_breakdown); + DCHECK_GE(usage_breakdown->backgroundFetch, 0); + DCHECK_GE(usage_breakdown->fileSystem, 0); + DCHECK_GE(usage_breakdown->indexedDatabase, 0); + DCHECK_GE(usage_breakdown->serviceWorker, 0); + DCHECK_GE(usage_breakdown->serviceWorkerCache, 0); + DCHECK_GE(usage_breakdown->webSql, 0); + host_usage_ = usage; host_usage_breakdown_ = std::move(usage_breakdown); std::move(barrier_closure).Run(); @@ -437,6 +450,9 @@ class QuotaManagerImpl::UsageAndQuotaInfoGatherer : public QuotaTask { blink::mojom::QuotaStatusCode status, int64_t quota) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(barrier_closure); + DCHECK_GE(quota, 0); + desired_host_quota_ = quota; std::move(barrier_closure).Run(); } @@ -515,6 +531,8 @@ class QuotaManagerImpl::EvictionRoundInfoHelper { void OnGotSettings(base::OnceClosure barrier_closure, const QuotaSettings& settings) { + DCHECK(barrier_closure); + settings_ = settings; std::move(barrier_closure).Run(); } @@ -522,6 +540,10 @@ class QuotaManagerImpl::EvictionRoundInfoHelper { void OnGotCapacity(base::OnceClosure barrier_closure, int64_t total_space, int64_t available_space) { + DCHECK(barrier_closure); + DCHECK_GE(total_space, 0); + DCHECK_GE(available_space, 0); + total_space_ = total_space; available_space_ = available_space; std::move(barrier_closure).Run(); @@ -572,7 +594,10 @@ class QuotaManagerImpl::EvictionRoundInfoHelper { class QuotaManagerImpl::GetUsageInfoTask : public QuotaTask { public: GetUsageInfoTask(QuotaManagerImpl* manager, GetUsageInfoCallback callback) - : QuotaTask(manager), callback_(std::move(callback)) {} + : QuotaTask(manager), callback_(std::move(callback)) { + DCHECK(manager); + DCHECK(callback_); + } protected: void Run() override { @@ -601,8 +626,8 @@ class QuotaManagerImpl::GetUsageInfoTask : public QuotaTask { } private: - void AddEntries(StorageType type, UsageTracker* tracker) { - std::map<std::string, int64_t> host_usage = tracker->GetCachedHostsUsage(); + 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); @@ -612,8 +637,9 @@ class QuotaManagerImpl::GetUsageInfoTask : public QuotaTask { } void DidGetGlobalUsage(StorageType type, int64_t, int64_t) { - DCHECK(manager()->GetUsageTracker(type)); - AddEntries(type, manager()->GetUsageTracker(type)); + UsageTracker* tracker = manager()->GetUsageTracker(type); + DCHECK(tracker); + AddEntries(type, *tracker); } QuotaManagerImpl* manager() const { @@ -626,152 +652,158 @@ class QuotaManagerImpl::GetUsageInfoTask : public QuotaTask { base::WeakPtrFactory<GetUsageInfoTask> weak_factory_{this}; }; -// Calls each QuotaClient for `quota_client_types` to delete `storage_key` data. -// If `storage_key` is delete for all registered client types, and there are no -// deletion errors, StorageKeyDataDeleter will call to delete `storage_key` from -// QuotaDatabase. It will return QuotaStatusCode::kOk to the `callback` on -// success and will finish by making a call to delete itself. -class QuotaManagerImpl::StorageKeyDataDeleter : public QuotaTask { +class QuotaManagerImpl::StorageKeyGathererTask { public: - StorageKeyDataDeleter(QuotaManagerImpl* manager, - const StorageKey& storage_key, - StorageType type, - QuotaClientTypes quota_client_types, - StatusCallback callback) - : QuotaTask(manager), - storage_key_(storage_key), - type_(type), - quota_client_types_(std::move(quota_client_types)), - callback_(std::move(callback)) {} + StorageKeyGathererTask(QuotaManagerImpl* manager, + base::OnceCallback<void(StorageKeysByType)> callback) + : manager_(manager), callback_(std::move(callback)) { + DCHECK(manager_); + DCHECK(callback_); + } - protected: - void Run() override { - DCHECK(manager()->client_types_.contains(type_)); - remaining_clients_ = manager()->client_types_[type_].size(); + void Run() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +#if DCHECK_IS_ON() + DCHECK(!run_called_) << __func__ << " already called"; + run_called_ = true; +#endif // DCHECK_IS_ON() - for (const auto& client_and_type : manager()->client_types_[type_]) { - mojom::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_NESTABLE_ASYNC_BEGIN2( - "browsing_data", "QuotaManagerImpl::StorageKeyDataDeleter", - ++tracing_id, "client_type", client_type, "storage_key", - storage_key_.Serialize()); - client->DeleteStorageKeyData( - storage_key_, type_, - base::BindOnce(&StorageKeyDataDeleter::DidDeleteStorageKeyData, - weak_factory_.GetWeakPtr(), tracing_id)); - } else { - ++skipped_clients_; - --remaining_clients_; - } + size_t client_call_count = 0; + for (const auto& client_and_type : manager_->client_types_) + client_call_count += client_and_type.second.size(); + + // Registered clients can be empty in tests. + if (!client_call_count) { + Completed(); + return; } - if (remaining_clients_ == 0) - CallCompleted(); + base::RepeatingClosure barrier = base::BarrierClosure( + client_call_count, + base::BindOnce(&QuotaManagerImpl::StorageKeyGathererTask::Completed, + weak_factory_.GetWeakPtr())); + + GetStorageKeysForType(StorageType::kTemporary, barrier); + GetStorageKeysForType(StorageType::kPersistent, barrier); + GetStorageKeysForType(StorageType::kSyncable, barrier); } - void Completed() override { - if (error_count_ == 0) { - // Only remove the entire storage key if we didn't skip any client types. - if (skipped_clients_ == 0) - manager()->DeleteStorageKeyFromDatabase(storage_key_, type_); - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk); - } else { - std::move(callback_).Run( - blink::mojom::QuotaStatusCode::kErrorInvalidModification); + private: + void GetStorageKeysForType(StorageType type, base::RepeatingClosure barrier) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + auto client_map_it = manager_->client_types_.find(type); + DCHECK(client_map_it != manager_->client_types_.end()); + DCHECK(barrier); + + for (const auto& client_and_type : client_map_it->second) { + client_and_type.first->GetStorageKeysForType( + type, base::BindOnce(&StorageKeyGathererTask::DidGetStorageKeys, + weak_factory_.GetWeakPtr(), type, barrier)); } - DeleteSoon(); } - void Aborted() override { - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort); - DeleteSoon(); - } + void DidGetStorageKeys(StorageType type, + base::OnceClosure barrier, + const std::vector<StorageKey>& storage_keys) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(barrier); - private: - void DidDeleteStorageKeyData(int tracing_id, - blink::mojom::QuotaStatusCode status) { - DCHECK_GT(remaining_clients_, 0U); - TRACE_EVENT_NESTABLE_ASYNC_END0( - "browsing_data", "QuotaManagerImpl::StorageKeyDataDeleter", tracing_id); + storage_keys_by_type_[type].insert(storage_keys.begin(), + storage_keys.end()); + std::move(barrier).Run(); + } - if (status != blink::mojom::QuotaStatusCode::kOk) - ++error_count_; + void Completed() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +#if DCHECK_IS_ON() + DCHECK(!completed_called_) << __func__ << " already called"; + completed_called_ = true; +#endif // DCHECK_IS_ON() - if (--remaining_clients_ == 0) - CallCompleted(); + // Deletes `this`. + std::move(callback_).Run(storage_keys_by_type_); } - QuotaManagerImpl* manager() const { - return static_cast<QuotaManagerImpl*>(observer()); - } + SEQUENCE_CHECKER(sequence_checker_); + QuotaManagerImpl* manager_ GUARDED_BY_CONTEXT(sequence_checker_); + base::OnceCallback<void(StorageKeysByType)> callback_ + GUARDED_BY_CONTEXT(sequence_checker_); + StorageKeysByType storage_keys_by_type_ GUARDED_BY_CONTEXT(sequence_checker_); - const StorageKey storage_key_; - const StorageType type_; - const QuotaClientTypes quota_client_types_; - int error_count_ = 0; - size_t remaining_clients_ = 0; - int skipped_clients_ = 0; - StatusCallback callback_; +#if DCHECK_IS_ON() + bool run_called_ = false; + bool completed_called_ = false; +#endif // DCHECK_IS_ON() - base::WeakPtrFactory<StorageKeyDataDeleter> weak_factory_{this}; + base::WeakPtrFactory<StorageKeyGathererTask> weak_factory_ + GUARDED_BY_CONTEXT(sequence_checker_){this}; }; -// Calls all QuotaClients for the bucket StorageType to delete bucket data. -// Keeps track of operations that have failed, and will only complete the -// task if all operations have succeeded. This is currently only for the default -// bucket. If a non-default bucket is to be evicted, it will immediately -// complete the task since non-default bucket usage is not being tracked by -// QuotaClients yet. -// TODO(crbug.com/1199417): Update to call QuotaClients to clear data for -// non-default buckets when QuotaClient is migrated to operate on buckets. -class QuotaManagerImpl::BucketDataDeleter : public QuotaTask { +// Calls QuotaClients in `quota_client_types` for the `bucket` to delete bucket +// data. Will delete bucket entries from the QuotaDatabase if bucket data has +// been successfully deleted from all registered QuotaClient. +// This is currently only for the default bucket. If a non-default bucket is to +// be deleted, it will immediately complete the task since non-default bucket +// usage is not being tracked by QuotaClients yet. +class QuotaManagerImpl::BucketDataDeleter { public: - BucketDataDeleter(QuotaManagerImpl* manager, - const BucketLocator& bucket, - QuotaClientTypes quota_client_types, - bool is_eviction, - StatusCallback callback) - : QuotaTask(manager), + // `callback` will be run to report the status of the deletion on task + // completion. `callback` will only be called while this BucketDataDeleter + // instance is alive. `callback` may destroy this BucketDataDeleter instance. + BucketDataDeleter( + QuotaManagerImpl* manager, + const BucketLocator& bucket, + QuotaClientTypes quota_client_types, + base::OnceCallback<void(BucketDataDeleter*, + blink::mojom::QuotaStatusCode)> callback) + : manager_(manager), bucket_(bucket), quota_client_types_(std::move(quota_client_types)), - is_eviction_(is_eviction), - callback_(std::move(callback)) {} - - protected: - void Run() override { - DCHECK(manager()->client_types_.contains(bucket_.type)); + callback_(std::move(callback)) { + DCHECK(manager_); + // TODO(crbug/1292216): Convert back into DCHECKs once issue is resolved. + CHECK(callback_); + } - // If bucket is not the default bucket, skip calls to the QuotaClient since - // they are not being tracked yet. - // TODO(crbug.com/1199417): Update to call for all buckets once QuotaClient - // is migrated to operate on buckets. - if (!bucket_.is_default) { - CallCompleted(); - return; + ~BucketDataDeleter() { + // `callback` is non-null if the deleter gets destroyed before completing. + if (callback_) { + std::move(callback_).Run(this, + blink::mojom::QuotaStatusCode::kErrorAbort); } + } + + void Run() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +#if DCHECK_IS_ON() + // TODO(crbug/1292216): Convert back into DCHECK once issue is resolved. + CHECK(!run_called_) << __func__ << " already called"; + run_called_ = true; +#endif // DCHECK_IS_ON() + + DCHECK(manager_->client_types_.contains(bucket_.type)); - remaining_clients_ = manager()->client_types_[bucket_.type].size(); + remaining_clients_ = manager_->client_types_[bucket_.type].size(); + UsageTracker* usage_tracker = manager_->GetUsageTracker(bucket_.type); - for (const auto& client_and_type : manager()->client_types_[bucket_.type]) { + for (const auto& client_and_type : manager_->client_types_[bucket_.type]) { mojom::QuotaClient* client = client_and_type.first; QuotaClientType client_type = client_and_type.second; if (quota_client_types_.contains(client_type)) { + // Delete cached usage. + usage_tracker->DeleteBucketCache(client_type, bucket_); + static int tracing_id = 0; - std::ostringstream bucket_params; - bucket_params << "storage_key: " << bucket_.storage_key.Serialize() - << ", is_default: " << bucket_.is_default - << ", id: " << bucket_.id; + std::string bucket_params = base::StrCat( + {"storage_key: ", bucket_.storage_key.Serialize(), + ", is_default: ", bucket_.is_default ? "true" : "false", + ", id: ", base::NumberToString(bucket_.id.value())}); TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( "browsing_data", "QuotaManagerImpl::BucketDataDeleter", - ++tracing_id, "client_type", client_type, "bucket", - bucket_params.str()); - client->DeleteStorageKeyData( - bucket_.storage_key, bucket_.type, - base::BindOnce(&BucketDataDeleter::DidDeleteBucketData, - weak_factory_.GetWeakPtr(), tracing_id)); + ++tracing_id, "client_type", client_type, "bucket", bucket_params); + client->DeleteBucketData( + bucket_, base::BindOnce(&BucketDataDeleter::DidDeleteBucketData, + weak_factory_.GetWeakPtr(), tracing_id)); } else { ++skipped_clients_; --remaining_clients_; @@ -779,31 +811,19 @@ class QuotaManagerImpl::BucketDataDeleter : public QuotaTask { } if (remaining_clients_ == 0) - CallCompleted(); - } - - void Completed() override { - if (error_count_ == 0) { - // Only remove the bucket if we didn't skip any client types. - if (skipped_clients_ == 0) - manager()->DeleteBucketFromDatabase(bucket_.id, is_eviction_); - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk); - } else { - std::move(callback_).Run( - blink::mojom::QuotaStatusCode::kErrorInvalidModification); - } - DeleteSoon(); + FinishDeletion(); } - void Aborted() override { - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort); - DeleteSoon(); + bool completed() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return !callback_; } private: void DidDeleteBucketData(int tracing_id, blink::mojom::QuotaStatusCode status) { - DCHECK_GT(remaining_clients_, 0U); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_GT(remaining_clients_, 0u); TRACE_EVENT_NESTABLE_ASYNC_END0( "browsing_data", "QuotaManagerImpl::BucketDataDeleter", tracing_id); @@ -811,115 +831,184 @@ class QuotaManagerImpl::BucketDataDeleter : public QuotaTask { ++error_count_; if (--remaining_clients_ == 0) - CallCompleted(); + FinishDeletion(); } - QuotaManagerImpl* manager() const { - return static_cast<QuotaManagerImpl*>(observer()); + void FinishDeletion() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(crbug/1292216): Convert back into DCHECKs once issue is resolved. + CHECK_EQ(remaining_clients_, 0u); + CHECK(callback_) << __func__ << " called after Complete"; + + // Only remove the bucket from the database if we didn't skip any client + // types. + if (skipped_clients_ == 0 && error_count_ == 0) { + manager_->DeleteBucketFromDatabase( + bucket_.id, + base::BindOnce(&BucketDataDeleter::DidDeleteBucketFromDatabase, + weak_factory_.GetWeakPtr())); + return; + } + Complete(/*success=*/error_count_ == 0); + } + + void DidDeleteBucketFromDatabase(QuotaError result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + manager_->DidDatabaseWork(result != QuotaError::kDatabaseError); + Complete(result == QuotaError::kNone); + } + + void Complete(bool success) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(crbug/1292216): Convert back into DCHECKs once issue is resolved. + CHECK_EQ(remaining_clients_, 0u); + CHECK(callback_); + + // May delete `this`. + std::move(callback_).Run( + this, success + ? blink::mojom::QuotaStatusCode::kOk + : blink::mojom::QuotaStatusCode::kErrorInvalidModification); } + SEQUENCE_CHECKER(sequence_checker_); + QuotaManagerImpl* const manager_ GUARDED_BY_CONTEXT(sequence_checker_); const BucketLocator bucket_; const QuotaClientTypes quota_client_types_; - int error_count_ = 0; - size_t remaining_clients_ = 0; - int skipped_clients_ = 0; - const bool is_eviction_; - StatusCallback callback_; + int error_count_ GUARDED_BY_CONTEXT(sequence_checker_) = 0; + size_t remaining_clients_ GUARDED_BY_CONTEXT(sequence_checker_) = 0; + int skipped_clients_ GUARDED_BY_CONTEXT(sequence_checker_) = 0; + + // Running the callback may delete this instance. + base::OnceCallback<void(BucketDataDeleter*, blink::mojom::QuotaStatusCode)> + callback_ GUARDED_BY_CONTEXT(sequence_checker_); + +#if DCHECK_IS_ON() + bool run_called_ GUARDED_BY_CONTEXT(sequence_checker_) = false; +#endif // DCHECK_IS_ON() - base::WeakPtrFactory<BucketDataDeleter> weak_factory_{this}; + base::WeakPtrFactory<BucketDataDeleter> weak_factory_ + GUARDED_BY_CONTEXT(sequence_checker_){this}; }; -class QuotaManagerImpl::HostDataDeleter : public QuotaTask { +// Retrieves all buckets for `host` from QuotaDatabase and calls +// BucketDataDeleter for all registered QuotaClientTypes. +class QuotaManagerImpl::HostDataDeleter { public: - HostDataDeleter(QuotaManagerImpl* manager, - const std::string& host, - StorageType type, - QuotaClientTypes quota_client_types, - StatusCallback callback) - : QuotaTask(manager), + // `callback` will be run to report the status of the deletion on task + // completion. `callback` will only be called while this HostDataDeleter + // instance is alive. `callback` may destroy this HostDataDeleter instance. + HostDataDeleter( + QuotaManagerImpl* manager, + const std::string& host, + StorageType type, + base::OnceCallback<void(HostDataDeleter*, blink::mojom::QuotaStatusCode)> + callback) + : manager_(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(); + callback_(std::move(callback)) { + DCHECK(manager_); + DCHECK(callback_); + } - for (const auto& client_and_type : manager()->client_types_[type_]) { - client_and_type.first->GetStorageKeysForHost( - type_, host_, - base::BindOnce(&HostDataDeleter::DidGetStorageKeysForHost, - weak_factory_.GetWeakPtr())); + ~HostDataDeleter() { + if (callback_) { + std::move(callback_).Run(this, + blink::mojom::QuotaStatusCode::kErrorAbort); } } - 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 Run() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +#if DCHECK_IS_ON() + DCHECK(!run_called_) << __func__ << " already called"; + run_called_ = true; +#endif // DCHECK_IS_ON() + manager_->GetBucketsForHost( + host_, type_, + base::BindOnce(&HostDataDeleter::DidGetBucketsForHost, + weak_factory_.GetWeakPtr())); } - void Aborted() override { - std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort); - DeleteSoon(); + bool completed() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return !callback_; } private: - void DidGetStorageKeysForHost(const std::vector<StorageKey>& storage_keys) { - DCHECK_GT(remaining_clients_, 0U); - - storage_keys_.insert(storage_keys.begin(), storage_keys.end()); + void DidGetBucketsForHost(QuotaErrorOr<std::set<BucketLocator>> result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!result.ok()) { + Complete(/*success=*/false); + return; + } - if (--remaining_clients_ == 0) { - if (!storage_keys_.empty()) - ScheduleStorageKeysDeletion(); - else - CallCompleted(); + buckets_ = result.value(); + if (!buckets_.empty()) { + ScheduleBucketsDeletion(); + return; } + Complete(/*success=*/true); } - void ScheduleStorageKeysDeletion() { - remaining_deleters_ = storage_keys_.size(); - for (const auto& storage_key : storage_keys_) { - StorageKeyDataDeleter* deleter = new StorageKeyDataDeleter( - manager(), storage_key, type_, std::move(quota_client_types_), - base::BindOnce(&HostDataDeleter::DidDeleteStorageKeyData, - weak_factory_.GetWeakPtr())); - deleter->Start(); + void ScheduleBucketsDeletion() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + for (const auto& bucket : buckets_) { + // base::Unretained() is safe here because `this` is guaranteed to outlive + // the callback, thanks to an indirect ownership chain. `this` owns the + // BucketDataDeleter created here, which guarantees it will only use the + // callback when it's alive. + auto bucket_deleter = std::make_unique<BucketDataDeleter>( + manager_, bucket, AllQuotaClientTypes(), + base::BindOnce(&HostDataDeleter::DidDeleteBucketData, + base::Unretained(this))); + auto* bucket_deleter_ptr = bucket_deleter.get(); + bucket_deleters_[bucket_deleter_ptr] = std::move(bucket_deleter); + bucket_deleter_ptr->Run(); } } - void DidDeleteStorageKeyData(blink::mojom::QuotaStatusCode status) { - DCHECK_GT(remaining_deleters_, 0U); + void DidDeleteBucketData(BucketDataDeleter* deleter, + blink::mojom::QuotaStatusCode status) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(deleter->completed()); + + DCHECK(base::Contains(bucket_deleters_, deleter)); + bucket_deleters_.erase(deleter); if (status != blink::mojom::QuotaStatusCode::kOk) ++error_count_; - if (--remaining_deleters_ == 0) - CallCompleted(); + if (bucket_deleters_.empty()) + Complete(/*success=*/error_count_ == 0); } - QuotaManagerImpl* manager() const { - return static_cast<QuotaManagerImpl*>(observer()); + void Complete(bool success) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback_); + + // May delete `this`. + std::move(callback_).Run( + this, success + ? blink::mojom::QuotaStatusCode::kOk + : blink::mojom::QuotaStatusCode::kErrorInvalidModification); } + SEQUENCE_CHECKER(sequence_checker_); + QuotaManagerImpl* const manager_ GUARDED_BY_CONTEXT(sequence_checker_); const std::string host_; const StorageType type_; - const QuotaClientTypes quota_client_types_; - std::set<StorageKey> storage_keys_; - int error_count_; - size_t remaining_clients_; - size_t remaining_deleters_; - StatusCallback callback_; + std::map<BucketDataDeleter*, std::unique_ptr<BucketDataDeleter>> + bucket_deleters_; + std::set<BucketLocator> buckets_ GUARDED_BY_CONTEXT(sequence_checker_); + int error_count_ GUARDED_BY_CONTEXT(sequence_checker_) = 0; + base::OnceCallback<void(HostDataDeleter*, blink::mojom::QuotaStatusCode)> + callback_ GUARDED_BY_CONTEXT(sequence_checker_); + +#if DCHECK_IS_ON() + bool run_called_ GUARDED_BY_CONTEXT(sequence_checker_) = false; +#endif // DCHECK_IS_ON() base::WeakPtrFactory<HostDataDeleter> weak_factory_{this}; }; @@ -934,7 +1023,9 @@ class QuotaManagerImpl::StorageCleanupHelper : public QuotaTask { type_(type), quota_client_types_(std::move(quota_client_types)), callback_(std::move(callback)) { + DCHECK(manager); DCHECK(manager->client_types_.contains(type_)); + DCHECK(callback_); } protected: @@ -1096,22 +1187,52 @@ void QuotaManagerImpl::GetOrCreateBucket( const std::string& bucket_name, base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + if (db_disabled_) { + std::move(callback).Run(QuotaError::kDatabaseError); + return; + } PostTaskAndReplyWithResultForDBThread( base::BindOnce(&GetOrCreateBucketOnDBThread, storage_key, bucket_name), base::BindOnce(&QuotaManagerImpl::DidGetBucket, weak_factory_.GetWeakPtr(), std::move(callback))); } +void QuotaManagerImpl::GetOrCreateBucketDeprecated( + const StorageKey& storage_key, + const std::string& bucket_name, + blink::mojom::StorageType storage_type, + base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + EnsureDatabaseOpened(); + + if (db_disabled_) { + std::move(callback).Run(QuotaError::kDatabaseError); + return; + } + PostTaskAndReplyWithResultForDBThread( + base::BindOnce(&GetOrCreateBucketDeprecatedOnDBThread, storage_key, + bucket_name, storage_type), + base::BindOnce(&QuotaManagerImpl::DidGetBucket, + weak_factory_.GetWeakPtr(), std::move(callback))); +} + void QuotaManagerImpl::CreateBucketForTesting( const StorageKey& storage_key, const std::string& bucket_name, blink::mojom::StorageType storage_type, base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + if (db_disabled_) { + std::move(callback).Run(QuotaError::kDatabaseError); + return; + } PostTaskAndReplyWithResultForDBThread( base::BindOnce(&CreateBucketOnDBThread, storage_key, bucket_name, storage_type), @@ -1125,8 +1246,13 @@ void QuotaManagerImpl::GetBucket( blink::mojom::StorageType type, base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + if (db_disabled_) { + std::move(callback).Run(QuotaError::kDatabaseError); + return; + } PostTaskAndReplyWithResultForDBThread( base::BindOnce(&GetBucketOnDBThread, storage_key, bucket_name, type), base::BindOnce(&QuotaManagerImpl::DidGetBucket, @@ -1136,8 +1262,13 @@ void QuotaManagerImpl::GetBucket( void QuotaManagerImpl::GetStorageKeysForType(blink::mojom::StorageType type, GetStorageKeysCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + if (db_disabled_) { + std::move(callback).Run(std::set<StorageKey>()); + return; + } PostTaskAndReplyWithResultForDBThread( base::BindOnce(&GetStorageKeysForTypeOnDBThread, type), base::BindOnce(&QuotaManagerImpl::DidGetStorageKeys, @@ -1148,8 +1279,13 @@ void QuotaManagerImpl::GetBucketsForType( blink::mojom::StorageType type, base::OnceCallback<void(QuotaErrorOr<std::set<BucketLocator>>)> callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + if (db_disabled_) { + std::move(callback).Run(QuotaError::kDatabaseError); + return; + } PostTaskAndReplyWithResultForDBThread( base::BindOnce(&GetBucketsForTypeOnDBThread, type), base::BindOnce(&QuotaManagerImpl::DidGetBuckets, @@ -1161,8 +1297,13 @@ void QuotaManagerImpl::GetBucketsForHost( blink::mojom::StorageType type, base::OnceCallback<void(QuotaErrorOr<std::set<BucketLocator>>)> callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + if (db_disabled_) { + std::move(callback).Run(QuotaError::kDatabaseError); + return; + } PostTaskAndReplyWithResultForDBThread( base::BindOnce(&GetBucketsForHostOnDBThread, host, type), base::BindOnce(&QuotaManagerImpl::DidGetBuckets, @@ -1174,8 +1315,13 @@ void QuotaManagerImpl::GetBucketsForStorageKey( blink::mojom::StorageType type, base::OnceCallback<void(QuotaErrorOr<std::set<BucketLocator>>)> callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + if (db_disabled_) { + std::move(callback).Run(QuotaError::kDatabaseError); + return; + } PostTaskAndReplyWithResultForDBThread( base::BindOnce(&GetBucketsForStorageKeyOnDBThread, storage_key, type), base::BindOnce(&QuotaManagerImpl::DidGetBuckets, @@ -1184,7 +1330,9 @@ void QuotaManagerImpl::GetBucketsForStorageKey( void QuotaManagerImpl::GetUsageInfo(GetUsageInfoCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + GetUsageInfoTask* get_usage_info = new GetUsageInfoTask(this, std::move(callback)); get_usage_info->Start(); @@ -1195,6 +1343,8 @@ void QuotaManagerImpl::GetUsageAndQuotaForWebApps( StorageType type, UsageAndQuotaCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + GetUsageAndQuotaWithBreakdown( storage_key, type, base::BindOnce(&DidGetUsageAndQuotaStripBreakdown, std::move(callback))); @@ -1205,6 +1355,8 @@ void QuotaManagerImpl::GetUsageAndQuotaWithBreakdown( StorageType type, UsageAndQuotaWithBreakdownCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + GetUsageAndQuotaForDevtools( storage_key, type, base::BindOnce(&DidGetUsageAndQuotaStripOverride, std::move(callback))); @@ -1215,6 +1367,8 @@ void QuotaManagerImpl::GetUsageAndQuotaForDevtools( StorageType type, UsageAndQuotaForDevtoolsCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + if (!IsSupportedType(type) || (is_incognito_ && !IsSupportedIncognitoType(type))) { std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorNotSupported, @@ -1244,6 +1398,8 @@ void QuotaManagerImpl::GetUsageAndQuota(const StorageKey& storage_key, StorageType type, UsageAndQuotaCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + if (IsStorageUnlimited(storage_key, 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. @@ -1304,15 +1460,20 @@ void QuotaManagerImpl::SetUsageCacheEnabled(QuotaClientType client_id, bool enabled) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); EnsureDatabaseOpened(); - DCHECK(GetUsageTracker(type)); - GetUsageTracker(type)->SetUsageCacheEnabled(client_id, storage_key, enabled); + + UsageTracker* usage_tracker = GetUsageTracker(type); + DCHECK(usage_tracker); + + usage_tracker->SetUsageCacheEnabled(client_id, storage_key, enabled); } void QuotaManagerImpl::DeleteBucketData(const BucketLocator& bucket, QuotaClientTypes quota_client_types, StatusCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DeleteBucketDataInternal(bucket, std::move(quota_client_types), false, + DCHECK(callback); + + DeleteBucketDataInternal(bucket, std::move(quota_client_types), std::move(callback)); } @@ -1320,8 +1481,13 @@ void QuotaManagerImpl::FindAndDeleteBucketData(const StorageKey& storage_key, const std::string& bucket_name, StatusCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + if (db_disabled_) { + std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorInvalidAccess); + return; + } PostTaskAndReplyWithResultForDBThread( base::BindOnce(&GetBucketOnDBThread, storage_key, bucket_name, StorageType::kTemporary), @@ -1334,6 +1500,7 @@ void QuotaManagerImpl::PerformStorageCleanup( QuotaClientTypes quota_client_types, base::OnceClosure callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); StorageCleanupHelper* deleter = new StorageCleanupHelper( this, type, std::move(quota_client_types), std::move(callback)); deleter->Start(); @@ -1341,9 +1508,9 @@ void QuotaManagerImpl::PerformStorageCleanup( void QuotaManagerImpl::DeleteHostData(const std::string& host, StorageType type, - QuotaClientTypes quota_client_types, StatusCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); DCHECK(client_types_.contains(type)); @@ -1351,16 +1518,75 @@ void QuotaManagerImpl::DeleteHostData(const std::string& host, std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk); return; } + auto host_deleter = std::make_unique<HostDataDeleter>( + this, host, type, + base::BindOnce(&QuotaManagerImpl::DidDeleteHostData, + weak_factory_.GetWeakPtr(), std::move(callback))); + auto* host_deleter_ptr = host_deleter.get(); + host_data_deleters_[host_deleter_ptr] = std::move(host_deleter); + host_deleter_ptr->Run(); +} - HostDataDeleter* deleter = new HostDataDeleter( - this, host, type, std::move(quota_client_types), std::move(callback)); - deleter->Start(); +// static +void QuotaManagerImpl::DidDeleteHostData( + base::WeakPtr<QuotaManagerImpl> quota_manager, + StatusCallback delete_host_data_callback, + HostDataDeleter* deleter, + blink::mojom::QuotaStatusCode status_code) { + DCHECK(delete_host_data_callback); + DCHECK(deleter); + DCHECK(deleter->completed()); + + if (quota_manager) + quota_manager->host_data_deleters_.erase(deleter); + + std::move(delete_host_data_callback).Run(status_code); +} + +void QuotaManagerImpl::BindInternalsHandler( + mojo::PendingReceiver<mojom::QuotaInternalsHandler> receiver) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + internals_handlers_receivers_.Add(this, std::move(receiver)); +} + +void QuotaManagerImpl::GetDiskAvailability( + GetDiskAvailabilityCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + + GetStorageCapacity(base::BindOnce( + [](GetDiskAvailabilityCallback callback, int64_t total_space, + int64_t available_space) { + DCHECK(callback); + DCHECK_GE(total_space, 0); + DCHECK_GE(available_space, 0); + std::move(callback).Run(total_space, available_space); + }, + std::move(callback))); +} + +void QuotaManagerImpl::GetStatistics(GetStatisticsCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + + base::flat_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& storage_key_usage_pair : stats) { + statistics[storage_key_usage_pair.first] = + base::NumberToString(storage_key_usage_pair.second); + } + } + std::move(callback).Run(statistics); } void QuotaManagerImpl::GetPersistentHostQuota(const std::string& host, QuotaCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + if (host.empty()) { // This could happen if we are called on file:///. // TODO(kinuko) We may want to respect --allow-file-access-from-files @@ -1382,7 +1608,10 @@ void QuotaManagerImpl::SetPersistentHostQuota(const std::string& host, int64_t new_quota, QuotaCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_GE(new_quota, 0); + DCHECK(callback); EnsureDatabaseOpened(); + if (host.empty()) { // This could happen if we are called on file:///. std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorNotSupported, @@ -1404,7 +1633,6 @@ void QuotaManagerImpl::SetPersistentHostQuota(const std::string& host, -1); return; } - int64_t* new_quota_ptr = new int64_t(new_quota); PostTaskAndReplyWithResultForDBThread( base::BindOnce(&SetPersistentHostQuotaOnDBThread, host, @@ -1415,11 +1643,14 @@ void QuotaManagerImpl::SetPersistentHostQuota(const std::string& host, } void QuotaManagerImpl::GetGlobalUsage(StorageType type, - GlobalUsageCallback callback) { + UsageCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); - DCHECK(GetUsageTracker(type)); - GetUsageTracker(type)->GetGlobalUsage(std::move(callback)); + + UsageTracker* usage_tracker = GetUsageTracker(type); + DCHECK(usage_tracker); + usage_tracker->GetGlobalUsage(std::move(callback)); } void QuotaManagerImpl::GetHostUsageWithBreakdown( @@ -1428,27 +1659,16 @@ void QuotaManagerImpl::GetHostUsageWithBreakdown( UsageWithBreakdownCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); EnsureDatabaseOpened(); - 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& storage_key_usage_pair : stats) { - statistics[storage_key_usage_pair.first] = - base::NumberToString(storage_key_usage_pair.second); - } - } - return statistics; + UsageTracker* usage_tracker = GetUsageTracker(type); + DCHECK(usage_tracker); + usage_tracker->GetHostUsageWithBreakdown(host, std::move(callback)); } bool QuotaManagerImpl::IsStorageUnlimited(const StorageKey& storage_key, 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) @@ -1465,7 +1685,14 @@ void QuotaManagerImpl::GetBucketsModifiedBetween(StorageType type, base::Time end, GetBucketsCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + + if (db_disabled_) { + std::move(callback).Run(std::set<BucketLocator>(), type); + return; + } + PostTaskAndReplyWithResultForDBThread( base::BindOnce(&GetModifiedBetweenOnDBThread, type, begin, end), base::BindOnce(&QuotaManagerImpl::DidGetModifiedBetween, @@ -1474,12 +1701,14 @@ void QuotaManagerImpl::GetBucketsModifiedBetween(StorageType type, bool QuotaManagerImpl::ResetUsageTracker(StorageType type) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(GetUsageTracker(type)); - if (GetUsageTracker(type)->IsWorking()) + + UsageTracker* previous_usage_tracker = GetUsageTracker(type); + DCHECK(previous_usage_tracker); + if (previous_usage_tracker->IsWorking()) return false; auto usage_tracker = std::make_unique<UsageTracker>( - client_types_[type], type, special_storage_policy_.get()); + this, client_types_[type], type, special_storage_policy_.get()); switch (type) { case StorageType::kTemporary: temporary_usage_tracker_ = std::move(usage_tracker); @@ -1520,13 +1749,13 @@ void QuotaManagerImpl::EnsureDatabaseOpened() { : profile_path_.AppendASCII(kDatabaseName)); temporary_usage_tracker_ = std::make_unique<UsageTracker>( - client_types_[StorageType::kTemporary], StorageType::kTemporary, + this, client_types_[StorageType::kTemporary], StorageType::kTemporary, special_storage_policy_.get()); persistent_usage_tracker_ = std::make_unique<UsageTracker>( - client_types_[StorageType::kPersistent], StorageType::kPersistent, + this, client_types_[StorageType::kPersistent], StorageType::kPersistent, special_storage_policy_.get()); syncable_usage_tracker_ = std::make_unique<UsageTracker>( - client_types_[StorageType::kSyncable], StorageType::kSyncable, + this, client_types_[StorageType::kSyncable], StorageType::kSyncable, special_storage_policy_.get()); if (!is_incognito_) { @@ -1535,44 +1764,80 @@ void QuotaManagerImpl::EnsureDatabaseOpened() { &QuotaManagerImpl::ReportHistogram); } + if (bootstrap_disabled_for_testing_) + return; + + is_bootstrapping_database_ = true; base::PostTaskAndReplyWithResult( db_runner_.get(), FROM_HERE, - base::BindOnce(&QuotaDatabase::IsBootstrappedForEviction, + base::BindOnce(&QuotaDatabase::IsBootstrapped, base::Unretained(database_.get())), - base::BindOnce(&QuotaManagerImpl::DidOpenDatabase, + base::BindOnce(&QuotaManagerImpl::DidGetBootstrapFlag, weak_factory_.GetWeakPtr())); } -void QuotaManagerImpl::DidOpenDatabase(bool is_database_bootstrapped) { +void QuotaManagerImpl::DidGetBootstrapFlag(bool is_database_bootstrapped) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - is_database_bootstrapped_for_eviction_ = is_database_bootstrapped; + DCHECK(is_bootstrapping_database_); + if (!is_database_bootstrapped) { + BootstrapDatabase(); + return; + } + is_bootstrapping_database_ = false; + RunDatabaseCallbacks(); StartEviction(); } -void QuotaManagerImpl::BootstrapDatabaseForEviction( - GetBucketCallback did_get_bucket_callback, - int64_t usage, - int64_t unlimited_usage) { +void QuotaManagerImpl::BootstrapDatabase() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!storage_key_gatherer_); + storage_key_gatherer_ = std::make_unique<StorageKeyGathererTask>( + this, base::BindOnce(&QuotaManagerImpl::DidGetStorageKeysForBootstrap, + weak_factory_.GetWeakPtr())); + storage_key_gatherer_->Run(); +} + +void QuotaManagerImpl::DidGetStorageKeysForBootstrap( + StorageKeysByType storage_keys_by_type) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // The usage cache should be fully populated now so we can - // seed the database with storage keys we know about. - std::set<StorageKey> storage_keys = - temporary_usage_tracker_->GetCachedStorageKeys(); + DCHECK(storage_key_gatherer_); + storage_key_gatherer_.reset(); + PostTaskAndReplyWithResultForDBThread( + base::BindOnce(&BootstrapDatabaseOnDBThread, + std::move(storage_keys_by_type)), + base::BindOnce(&QuotaManagerImpl::DidBootstrapDatabase, + weak_factory_.GetWeakPtr()), FROM_HERE, - base::BindOnce(&BootstrapDatabaseOnDBThread, std::move(storage_keys)), - base::BindOnce(&QuotaManagerImpl::DidBootstrapDatabaseForEviction, - weak_factory_.GetWeakPtr(), - std::move(did_get_bucket_callback))); + /*is_bootstrap_task=*/true); } -void QuotaManagerImpl::DidBootstrapDatabaseForEviction( - GetBucketCallback did_get_bucket_callback, - bool success) { +void QuotaManagerImpl::DidBootstrapDatabase(QuotaError error) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - is_database_bootstrapped_for_eviction_ = success; - DidDatabaseWork(success); - GetLRUBucket(StorageType::kTemporary, std::move(did_get_bucket_callback)); + DidDatabaseWork(error != QuotaError::kDatabaseError); + + PostTaskAndReplyWithResultForDBThread( + base::BindOnce(&SetDatabaseBootstrappedOnDBThread), + base::BindOnce(&QuotaManagerImpl::DidSetDatabaseBootstrapped, + weak_factory_.GetWeakPtr()), + FROM_HERE, + /*is_bootstrap_task=*/true); +} + +void QuotaManagerImpl::DidSetDatabaseBootstrapped(QuotaError error) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(is_bootstrapping_database_); + is_bootstrapping_database_ = false; + DidDatabaseWork(error != QuotaError::kDatabaseError); + + RunDatabaseCallbacks(); + StartEviction(); +} + +void QuotaManagerImpl::RunDatabaseCallbacks() { + for (auto& callback : database_callbacks_) + std::move(callback).Run(); + database_callbacks_.clear(); } void QuotaManagerImpl::RegisterClient( @@ -1607,13 +1872,6 @@ UsageTracker* QuotaManagerImpl::GetUsageTracker(StorageType type) const { return nullptr; } -std::set<StorageKey> QuotaManagerImpl::GetCachedStorageKeys(StorageType type) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - EnsureDatabaseOpened(); - DCHECK(GetUsageTracker(type)); - return GetUsageTracker(type)->GetCachedStorageKeys(); -} - void QuotaManagerImpl::NotifyStorageAccessed(const StorageKey& storage_key, StorageType type, base::Time access_time) { @@ -1659,21 +1917,22 @@ void QuotaManagerImpl::NotifyStorageModified(QuotaClientType client_id, base::Time modification_time, base::OnceClosure callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); DCHECK(GetUsageTracker(type)); - GetUsageTracker(type)->UpdateUsageCache(client_id, storage_key, delta); - if (callback) - std::move(callback).Run(); - - if (db_disabled_) + if (db_disabled_) { + if (callback) + std::move(callback).Run(); return; + } PostTaskAndReplyWithResultForDBThread( - base::BindOnce(&UpdateModifiedTimeOnDBThread, storage_key, type, - modification_time), - base::BindOnce(&QuotaManagerImpl::OnComplete, - weak_factory_.GetWeakPtr())); + base::BindOnce(&GetBucketOnDBThread, storage_key, kDefaultBucketName, + type), + base::BindOnce(&QuotaManagerImpl::DidGetBucketForUsage, + weak_factory_.GetWeakPtr(), client_id, delta, + modification_time, std::move(callback))); } void QuotaManagerImpl::NotifyBucketModified(QuotaClientType client_id, @@ -1682,17 +1941,20 @@ void QuotaManagerImpl::NotifyBucketModified(QuotaClientType client_id, base::Time modification_time, base::OnceClosure callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); - // TODO(crbug.com/1199417): Update bucket usage in UsageTracker once - // QuotaClient & UsageTracker operate by bucket. - if (callback) std::move(callback).Run(); if (db_disabled_) return; + // TODO(crbug.com/1199417): Update bucket usage in UsageTracker once + // QuotaClient & UsageTracker operate by bucket. UsageTracker should be + // updated after ensuring there is a entry in the QuotaDatabase. + // Run `callback` on completion. + PostTaskAndReplyWithResultForDBThread( base::BindOnce(&UpdateBucketModifiedTimeOnDBThread, bucket_id, modification_time), @@ -1702,6 +1964,13 @@ void QuotaManagerImpl::NotifyBucketModified(QuotaClientType client_id, void QuotaManagerImpl::DumpQuotaTable(DumpQuotaTableCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + + if (db_disabled_) { + std::move(callback).Run(QuotaTableEntries()); + return; + } + DumpQuotaTableHelper* helper = new DumpQuotaTableHelper; PostTaskAndReplyWithResultForDBThread( base::BindOnce(&DumpQuotaTableHelper::DumpQuotaTableOnDBThread, @@ -1713,6 +1982,12 @@ void QuotaManagerImpl::DumpQuotaTable(DumpQuotaTableCallback callback) { void QuotaManagerImpl::DumpBucketTable(DumpBucketTableCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + + if (db_disabled_) { + std::move(callback).Run(BucketTableEntries()); + return; + } DumpBucketTableHelper* helper = new DumpBucketTableHelper; PostTaskAndReplyWithResultForDBThread( base::BindOnce(&DumpBucketTableHelper::DumpBucketTableOnDBThread, @@ -1725,6 +2000,7 @@ void QuotaManagerImpl::DumpBucketTable(DumpBucketTableCallback 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>( @@ -1732,34 +2008,25 @@ void QuotaManagerImpl::StartEviction() { temporary_storage_evictor_->Start(); } -void QuotaManagerImpl::DeleteStorageKeyFromDatabase( - const StorageKey& storage_key, - StorageType type) { +void QuotaManagerImpl::DeleteBucketFromDatabase( + BucketId bucket_id, + base::OnceCallback<void(QuotaError)> callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); - if (db_disabled_) - return; - PostTaskAndReplyWithResultForDBThread( - base::BindOnce(&DeleteStorageKeyInfoOnDBThread, storage_key, type), - base::BindOnce(&QuotaManagerImpl::OnComplete, - weak_factory_.GetWeakPtr())); -} - -void QuotaManagerImpl::DeleteBucketFromDatabase(BucketId bucket_id, - bool is_eviction) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - EnsureDatabaseOpened(); - if (db_disabled_) + if (db_disabled_) { + std::move(callback).Run(QuotaError::kDatabaseError); return; + } PostTaskAndReplyWithResultForDBThread( - base::BindOnce(&DeleteBucketInfoOnDBThread, bucket_id, is_eviction), - base::BindOnce(&QuotaManagerImpl::OnComplete, - weak_factory_.GetWeakPtr())); + base::BindOnce(&DeleteBucketInfoOnDBThread, bucket_id), + std::move(callback)); } void QuotaManagerImpl::DidBucketDataEvicted( + QuotaDatabase::BucketTableEntry entry, blink::mojom::QuotaStatusCode status) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(io_thread_->BelongsToCurrentThread()); @@ -1771,21 +2038,54 @@ void QuotaManagerImpl::DidBucketDataEvicted( if (status != blink::mojom::QuotaStatusCode::kOk) buckets_in_error_[eviction_context_.evicted_bucket.id]++; + if (status == blink::mojom::QuotaStatusCode::kOk) { + base::Time now = base::Time::Now(); + base::UmaHistogramCounts1M( + QuotaManagerImpl::kEvictedBucketAccessedCountHistogram, + entry.use_count); + base::UmaHistogramCounts1000( + QuotaManagerImpl::kEvictedBucketDaysSinceAccessHistogram, + (now - entry.last_accessed).InDays()); + } + std::move(eviction_context_.evict_bucket_data_callback).Run(status); } void QuotaManagerImpl::DeleteBucketDataInternal( const BucketLocator& bucket, QuotaClientTypes quota_client_types, - bool is_eviction, StatusCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); - BucketDataDeleter* deleter = - new BucketDataDeleter(this, bucket, std::move(quota_client_types), - is_eviction, std::move(callback)); - deleter->Start(); + if (db_disabled_) { + std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorInvalidAccess); + return; + } + auto bucket_deleter = std::make_unique<BucketDataDeleter>( + this, bucket, std::move(quota_client_types), + base::BindOnce(&QuotaManagerImpl::DidDeleteBucketData, + weak_factory_.GetWeakPtr(), std::move(callback))); + auto* bucket_deleter_ptr = bucket_deleter.get(); + bucket_data_deleters_[bucket_deleter_ptr] = std::move(bucket_deleter); + bucket_deleter_ptr->Run(); +} + +// static +void QuotaManagerImpl::DidDeleteBucketData( + base::WeakPtr<QuotaManagerImpl> quota_manager, + StatusCallback delete_bucket_data_callback, + BucketDataDeleter* deleter, + blink::mojom::QuotaStatusCode status_code) { + DCHECK(delete_bucket_data_callback); + DCHECK(deleter); + DCHECK(deleter->completed()); + + if (quota_manager) + quota_manager->bucket_data_deleters_.erase(deleter); + + std::move(delete_bucket_data_callback).Run(status_code); } void QuotaManagerImpl::MaybeRunStoragePressureCallback( @@ -1793,6 +2093,9 @@ void QuotaManagerImpl::MaybeRunStoragePressureCallback( int64_t total_space, int64_t available_space) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_GE(total_space, 0); + DCHECK_GE(available_space, 0); + // 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) @@ -1810,13 +2113,17 @@ void QuotaManagerImpl::MaybeRunStoragePressureCallback( } } -void QuotaManagerImpl::SimulateStoragePressure(const StorageKey& storage_key) { - storage_pressure_callback_.Run(storage_key); +void QuotaManagerImpl::SimulateStoragePressure(const url::Origin& origin_url) { + StorageKey key(origin_url); + storage_pressure_callback_.Run(key); } void QuotaManagerImpl::DetermineStoragePressure(int64_t total_space, int64_t free_space) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_GE(total_space, 0); + DCHECK_GE(free_space, 0); + if (!base::FeatureList::IsEnabled(features::kStoragePressureEvent)) { return; } @@ -1852,6 +2159,9 @@ void QuotaManagerImpl::OverrideQuotaForStorageKey( const StorageKey& storage_key, absl::optional<int64_t> quota_size) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_GE(quota_size.value_or(0), 0) + << "negative quota override: " << quota_size.value_or(0); + 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 @@ -1898,9 +2208,27 @@ void QuotaManagerImpl::SetQuotaChangeCallbackForTesting( quota_change_callback_ = std::move(storage_pressure_event_callback); } +void QuotaManagerImpl::SetQuotaDatabaseForTesting( + std::unique_ptr<QuotaDatabase> database) { + DCHECK(database); + database_ = std::move(database); + + // Initialize usage trackers after database is set. + temporary_usage_tracker_ = std::make_unique<UsageTracker>( + this, client_types_[StorageType::kTemporary], StorageType::kTemporary, + special_storage_policy_.get()); + persistent_usage_tracker_ = std::make_unique<UsageTracker>( + this, client_types_[StorageType::kPersistent], StorageType::kPersistent, + special_storage_policy_.get()); + syncable_usage_tracker_ = std::make_unique<UsageTracker>( + this, client_types_[StorageType::kSyncable], StorageType::kSyncable, + special_storage_policy_.get()); +} + void QuotaManagerImpl::ReportHistogram() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!is_incognito_); + GetGlobalUsage( StorageType::kTemporary, base::BindOnce(&QuotaManagerImpl::DidGetTemporaryGlobalUsageForHistogram, @@ -1910,6 +2238,9 @@ void QuotaManagerImpl::ReportHistogram() { void QuotaManagerImpl::DidGetTemporaryGlobalUsageForHistogram( int64_t usage, int64_t unlimited_usage) { + DCHECK_GE(usage, -1); + DCHECK_GE(unlimited_usage, -1); + GetStorageCapacity( base::BindOnce(&QuotaManagerImpl::DidGetStorageCapacityForHistogram, weak_factory_.GetWeakPtr(), usage)); @@ -1920,6 +2251,10 @@ void QuotaManagerImpl::DidGetStorageCapacityForHistogram( int64_t total_space, int64_t available_space) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_GE(usage, -1); + DCHECK_GE(total_space, 0); + DCHECK_GE(available_space, 0); + UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfTemporaryStorage", usage); if (total_space > 0) { UMA_HISTOGRAM_PERCENTAGE("Quota.PercentUsedForTemporaryStorage2", @@ -1940,6 +2275,9 @@ void QuotaManagerImpl::DidGetPersistentGlobalUsageForHistogram( int64_t usage, int64_t unlimited_usage) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_GE(usage, -1); + DCHECK_GE(unlimited_usage, -1); + UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfPersistentStorage", usage); // We DumpBucketTable last to ensure the trackers caches are loaded. @@ -1951,6 +2289,7 @@ void QuotaManagerImpl::DidGetPersistentGlobalUsageForHistogram( void QuotaManagerImpl::DidDumpBucketTableForHistogram( const BucketTableEntries& entries) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + std::map<StorageKey, int64_t> usage_map = GetUsageTracker(StorageType::kTemporary)->GetCachedStorageKeysUsage(); base::Time now = base::Time::Now(); @@ -1979,6 +2318,7 @@ void QuotaManagerImpl::DidDumpBucketTableForHistogram( std::set<BucketId> QuotaManagerImpl::GetEvictionBucketExceptions() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + std::set<BucketId> exceptions; for (const auto& p : buckets_in_error_) { if (p.second > QuotaManagerImpl::kThresholdOfErrorsToBeDenylisted) @@ -1992,6 +2332,8 @@ void QuotaManagerImpl::DidGetEvictionBucket( GetBucketCallback callback, const absl::optional<BucketLocator>& bucket) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + // Make sure the returned bucket has not been accessed since we posted the // eviction task. DCHECK(!bucket.has_value() || @@ -2015,29 +2357,18 @@ void QuotaManagerImpl::DidGetEvictionBucket( } void QuotaManagerImpl::GetEvictionBucket(StorageType type, - int64_t global_quota, GetBucketCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + // This must not be called while there's an in-flight task. DCHECK(!is_getting_eviction_bucket_); is_getting_eviction_bucket_ = true; - auto did_get_bucket_callback = - base::BindOnce(&QuotaManagerImpl::DidGetEvictionBucket, - weak_factory_.GetWeakPtr(), std::move(callback)); - - if (!is_database_bootstrapped_for_eviction_ && !eviction_disabled_) { - // Once bootstrapped, GetLRUBucket will be called. - GetGlobalUsage( - StorageType::kTemporary, - base::BindOnce(&QuotaManagerImpl::BootstrapDatabaseForEviction, - weak_factory_.GetWeakPtr(), - std::move(did_get_bucket_callback))); - return; - } - - GetLRUBucket(type, std::move(did_get_bucket_callback)); + GetLRUBucket(type, + base::BindOnce(&QuotaManagerImpl::DidGetEvictionBucket, + weak_factory_.GetWeakPtr(), std::move(callback))); } void QuotaManagerImpl::EvictBucketData(const BucketLocator& bucket, @@ -2045,20 +2376,39 @@ void QuotaManagerImpl::EvictBucketData(const BucketLocator& bucket, DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(io_thread_->BelongsToCurrentThread()); DCHECK_EQ(bucket.type, StorageType::kTemporary); + DCHECK(callback); eviction_context_.evicted_bucket = bucket; eviction_context_.evict_bucket_data_callback = std::move(callback); + PostTaskAndReplyWithResultForDBThread( + base::BindOnce(&GetBucketInfoForEvictionOnDBThread, bucket.id), + base::BindOnce(&QuotaManagerImpl::DidGetBucketInfoForEviction, + weak_factory_.GetWeakPtr(), bucket)); +} + +void QuotaManagerImpl::DidGetBucketInfoForEviction( + const BucketLocator& bucket, + QuotaErrorOr<QuotaDatabase::BucketTableEntry> result) { + DidDatabaseWork(result.ok() || result.error() != QuotaError::kDatabaseError); + + if (!result.ok()) { + std::move(eviction_context_.evict_bucket_data_callback) + .Run(blink::mojom::QuotaStatusCode::kErrorInvalidAccess); + return; + } + DeleteBucketDataInternal( - bucket, AllQuotaClientTypes(), true, + bucket, AllQuotaClientTypes(), base::BindOnce(&QuotaManagerImpl::DidBucketDataEvicted, - weak_factory_.GetWeakPtr())); + weak_factory_.GetWeakPtr(), result.value())); } void QuotaManagerImpl::GetEvictionRoundInfo( EvictionRoundInfoCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(io_thread_->BelongsToCurrentThread()); + DCHECK(callback); EnsureDatabaseOpened(); DCHECK(!eviction_helper_); @@ -2077,7 +2427,9 @@ void QuotaManagerImpl::DidGetEvictionRoundInfo() { void QuotaManagerImpl::GetLRUBucket(StorageType type, GetBucketCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); EnsureDatabaseOpened(); + // This must not be called while there's an in-flight task. DCHECK(lru_bucket_callback_.is_null()); lru_bucket_callback_ = std::move(callback); @@ -2114,6 +2466,8 @@ void QuotaManagerImpl::DidSetPersistentHostQuota(const std::string& host, const int64_t* new_quota, QuotaError error) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + DidDatabaseWork(error != QuotaError::kDatabaseError); if (error == QuotaError::kNone) { @@ -2147,6 +2501,8 @@ void DidGetSettingsThreadAdapter(base::TaskRunner* task_runner, void QuotaManagerImpl::GetQuotaSettings(QuotaSettingsCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + if (base::TimeTicks::Now() - settings_timestamp_ < settings_.refresh_interval) { std::move(callback).Run(settings_); @@ -2170,6 +2526,7 @@ void QuotaManagerImpl::GetQuotaSettings(QuotaSettingsCallback callback) { void QuotaManagerImpl::DidGetSettings(absl::optional<QuotaSettings> settings) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!settings) { settings = settings_; settings->refresh_interval = base::Minutes(1); @@ -2183,6 +2540,8 @@ void QuotaManagerImpl::DidGetSettings(absl::optional<QuotaSettings> settings) { void QuotaManagerImpl::GetStorageCapacity(StorageCapacityCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + if (!storage_capacity_callbacks_.Add(std::move(callback))) return; if (is_incognito_) { @@ -2202,9 +2561,16 @@ void QuotaManagerImpl::GetStorageCapacity(StorageCapacityCallback callback) { void QuotaManagerImpl::ContinueIncognitoGetStorageCapacity( const QuotaSettings& settings) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - int64_t current_usage = + + int64_t temporary_usage = GetUsageTracker(StorageType::kTemporary)->GetCachedUsage(); - current_usage += GetUsageTracker(StorageType::kPersistent)->GetCachedUsage(); + DCHECK_GE(temporary_usage, -1); + + int64_t persistent_usage = + GetUsageTracker(StorageType::kPersistent)->GetCachedUsage(); + DCHECK_GE(persistent_usage, -1); + + int64_t current_usage = temporary_usage + persistent_usage; int64_t available_space = std::max(int64_t{0}, settings.pool_size - current_usage); DidGetStorageCapacity(std::make_tuple(settings.pool_size, available_space)); @@ -2213,8 +2579,13 @@ void QuotaManagerImpl::ContinueIncognitoGetStorageCapacity( 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); + DCHECK_GE(total_space, 0); + int64_t available_space = std::get<1>(total_and_available); + DCHECK_GE(available_space, 0); + cached_disk_stats_for_storage_pressure_ = std::make_tuple(base::TimeTicks::Now(), total_space, available_space); storage_capacity_callbacks_.Run(total_space, available_space); @@ -2223,7 +2594,12 @@ void QuotaManagerImpl::DidGetStorageCapacity( void QuotaManagerImpl::DidDatabaseWork(bool success) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - db_disabled_ = !success; + if (success) + return; + + db_error_count_++; + if (db_error_count_ >= QuotaManagerImpl::kThresholdOfErrorsToDisableDatabase) + db_disabled_ = true; } void QuotaManagerImpl::OnComplete(QuotaError result) { @@ -2235,6 +2611,8 @@ void QuotaManagerImpl::DidGetBucket( base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback, QuotaErrorOr<BucketInfo> result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + DidDatabaseWork(result.ok() || result.error() != QuotaError::kDatabaseError); std::move(callback).Run(std::move(result)); } @@ -2243,6 +2621,8 @@ void QuotaManagerImpl::DidGetBucketForDeletion( StatusCallback callback, QuotaErrorOr<BucketInfo> result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + DidDatabaseWork(result.ok() || result.error() != QuotaError::kDatabaseError); if (!result.ok()) { @@ -2255,8 +2635,41 @@ void QuotaManagerImpl::DidGetBucketForDeletion( BucketLocator bucket(result->id, result->storage_key, result->type, result->name == kDefaultBucketName); - DeleteBucketDataInternal(bucket, AllQuotaClientTypes(), - /*is_eviction=*/false, std::move(callback)); + DeleteBucketDataInternal(bucket, AllQuotaClientTypes(), std::move(callback)); + return; +} + +void QuotaManagerImpl::DidGetBucketForUsage(QuotaClientType client_type, + int64_t delta, + base::Time modification_time, + base::OnceClosure callback, + QuotaErrorOr<BucketInfo> result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + + DidDatabaseWork(result.ok() || result.error() != QuotaError::kDatabaseError); + + if (!result.ok()) { + if (callback) + std::move(callback).Run(); + return; + } + + BucketLocator bucket(result->id, result->storage_key, result->type, + result->name == kDefaultBucketName); + GetUsageTracker(bucket.type) + ->UpdateBucketUsageCache(client_type, bucket, delta); + + // Return once usage cache is updated for callers waiting for quota changes to + // be reflected before querying for usage. + if (callback) + std::move(callback).Run(); + + PostTaskAndReplyWithResultForDBThread( + base::BindOnce(&UpdateBucketModifiedTimeOnDBThread, bucket.id, + modification_time), + base::BindOnce(&QuotaManagerImpl::OnComplete, + weak_factory_.GetWeakPtr())); return; } @@ -2264,6 +2677,8 @@ void QuotaManagerImpl::DidGetStorageKeys( GetStorageKeysCallback callback, QuotaErrorOr<std::set<StorageKey>> result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + DidDatabaseWork(result.ok() || result.error() != QuotaError::kDatabaseError); if (!result.ok()) { std::move(callback).Run(std::set<StorageKey>()); @@ -2276,6 +2691,8 @@ void QuotaManagerImpl::DidGetBuckets( base::OnceCallback<void(QuotaErrorOr<std::set<BucketLocator>>)> callback, QuotaErrorOr<std::set<BucketLocator>> result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + DidDatabaseWork(result.ok() || result.error() != QuotaError::kDatabaseError); std::move(callback).Run(std::move(result)); } @@ -2285,6 +2702,8 @@ void QuotaManagerImpl::DidGetModifiedBetween( StorageType type, QuotaErrorOr<std::set<BucketLocator>> result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + DidDatabaseWork(result.ok() || result.error() != QuotaError::kDatabaseError); if (!result.ok()) { std::move(callback).Run(std::set<BucketLocator>(), type); @@ -2293,29 +2712,27 @@ void QuotaManagerImpl::DidGetModifiedBetween( std::move(callback).Run(result.value(), type); } -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)); -} - template <typename ValueType> void QuotaManagerImpl::PostTaskAndReplyWithResultForDBThread( base::OnceCallback<QuotaErrorOr<ValueType>(QuotaDatabase*)> task, base::OnceCallback<void(QuotaErrorOr<ValueType>)> reply, - const base::Location& from_here) { + const base::Location& from_here, + bool is_bootstrap_task) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(task); + DCHECK(reply); // 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. + + if (!is_bootstrap_task && is_bootstrapping_database_) { + database_callbacks_.push_back(base::BindOnce( + &QuotaManagerImpl::PostTaskAndReplyWithResultForDBThread<ValueType>, + weak_factory_.GetWeakPtr(), std::move(task), std::move(reply), + from_here, is_bootstrap_task)); + return; + } + base::PostTaskAndReplyWithResult( db_runner_.get(), from_here, base::BindOnce(std::move(task), base::Unretained(database_.get())), @@ -2325,11 +2742,26 @@ void QuotaManagerImpl::PostTaskAndReplyWithResultForDBThread( void QuotaManagerImpl::PostTaskAndReplyWithResultForDBThread( base::OnceCallback<QuotaError(QuotaDatabase*)> task, base::OnceCallback<void(QuotaError)> reply, - const base::Location& from_here) { + const base::Location& from_here, + bool is_bootstrap_task) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(task); + DCHECK(reply); // 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. + + if (!is_bootstrap_task && is_bootstrapping_database_) { + database_callbacks_.push_back(base::BindOnce( + static_cast<void (QuotaManagerImpl::*)( + base::OnceCallback<QuotaError(QuotaDatabase*)>, + base::OnceCallback<void(QuotaError)>, const base::Location&, bool)>( + &QuotaManagerImpl::PostTaskAndReplyWithResultForDBThread), + weak_factory_.GetWeakPtr(), std::move(task), std::move(reply), + from_here, is_bootstrap_task)); + return; + } + base::PostTaskAndReplyWithResult( db_runner_.get(), from_here, base::BindOnce(std::move(task), base::Unretained(database_.get())), @@ -2344,13 +2776,15 @@ std::tuple<int64_t, int64_t> QuotaManagerImpl::CallGetVolumeInfo( 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); + + const auto [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); } + DCHECK_GE(total, 0); + DCHECK_GE(available, 0); + UMA_HISTOGRAM_MBYTES("Quota.TotalDiskSpace", total); UMA_HISTOGRAM_MBYTES("Quota.AvailableDiskSpace", available); if (total > 0) { diff --git a/chromium/storage/browser/quota/quota_manager_impl.h b/chromium/storage/browser/quota/quota_manager_impl.h index 8762f546418..bc2eb685441 100644 --- a/chromium/storage/browser/quota/quota_manager_impl.h +++ b/chromium/storage/browser/quota/quota_manager_impl.h @@ -7,7 +7,6 @@ #include <stdint.h> -#include <list> #include <map> #include <memory> #include <set> @@ -30,10 +29,12 @@ #include "components/services/storage/public/cpp/quota_error_or.h" #include "components/services/storage/public/mojom/quota_client.mojom.h" #include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/remote.h" #include "storage/browser/quota/quota_callbacks.h" #include "storage/browser/quota/quota_client_type.h" #include "storage/browser/quota/quota_database.h" +#include "storage/browser/quota/quota_internals.mojom.h" #include "storage/browser/quota/quota_settings.h" #include "storage/browser/quota/quota_task.h" #include "storage/browser/quota/special_storage_policy.h" @@ -48,10 +49,6 @@ class SingleThreadTaskRunner; class TaskRunner; } // namespace base -namespace quota_internals { -class QuotaInternalsProxy; -} // namespace quota_internals - namespace storage { class QuotaManagerProxy; @@ -79,7 +76,6 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaEvictionHandler { // Returns the next bucket to evict, or nullopt if there are no evictable // buckets. virtual void GetEvictionBucket(blink::mojom::StorageType type, - int64_t global_quota, GetBucketCallback callback) = 0; // Called to evict a bucket. @@ -125,7 +121,8 @@ struct UsageInfo { class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl : public QuotaTaskObserver, public QuotaEvictionHandler, - public base::RefCountedDeleteOnSequence<QuotaManagerImpl> { + public base::RefCountedDeleteOnSequence<QuotaManagerImpl>, + public storage::mojom::QuotaInternalsHandler { public: using UsageAndQuotaCallback = base::OnceCallback< void(blink::mojom::QuotaStatusCode, int64_t usage, int64_t quota)>; @@ -169,6 +166,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl // Returns a proxy object that can be used on any thread. QuotaManagerProxy* proxy() { return proxy_.get(); } + void BindInternalsHandler( + mojo::PendingReceiver<mojom::QuotaInternalsHandler> receiver); + // Gets the bucket with `bucket_name` for the `storage_key` for StorageType // kTemporary and returns the BucketInfo. If one doesn't exist, it creates // a new bucket with the specified policies. Returns a QuotaError if the @@ -178,6 +178,15 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl const blink::StorageKey& storage_key, const std::string& bucket_name, base::OnceCallback<void(QuotaErrorOr<BucketInfo>)>); + // Same as GetOrCreateBucket but takes in StorageType. This should only be + // used by FileSystem, and is expected to be removed when + // StorageType::kSyncable and StorageType::kPersistent are deprecated. + // (crbug.com/1233525, crbug.com/1286964). + virtual void GetOrCreateBucketDeprecated( + const blink::StorageKey& storage_key, + const std::string& bucket_name, + blink::mojom::StorageType storage_type, + base::OnceCallback<void(QuotaErrorOr<BucketInfo>)>); // Creates a bucket for `origin` with `bucket_name` and returns BucketInfo // to the callback. Will return a QuotaError to the callback on operation @@ -309,19 +318,22 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl blink::mojom::StorageType type, bool enabled); - // DeleteHostData (surprisingly enough) deletes data of a particular - // blink::mojom::StorageType associated with a set of storage keys. - // DeleteBucketData will only delete the specified bucket. - // Each method additionally requires a `quota_client_types` which specifies - // the types of QuotaClients to delete from the storage key. - // Pass in QuotaClientType::AllClients() to remove all clients from the - // storage key, regardless of type. + // Deletes `bucket` data for the specified `quota_client_types`. Pass in + // QuotaClientType::AllClients() to remove bucket data for all quota clients. + // + // `callback` is always called. If this QuotaManager gets destroyed during + // deletion, `callback` may be called with a kErrorAbort status. virtual void DeleteBucketData(const BucketLocator& bucket, QuotaClientTypes quota_client_types, StatusCallback callback); + + // Deletes buckets of a particular blink::mojom::StorageType with storage keys + // that match the specified host. + // + // `callback` is always called. If this QuotaManager gets destroyed during + // deletion, `callback` may be called with a kErrorAbort status. void DeleteHostData(const std::string& host, blink::mojom::StorageType type, - QuotaClientTypes quota_client_types, StatusCallback callback); // Queries QuotaDatabase for the bucket with `storage_key` and `bucket_name` @@ -339,19 +351,20 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl QuotaClientTypes quota_client_types, base::OnceClosure callback); + // storage::mojom::QuotaInternalsHandler implementation + void GetDiskAvailability(GetDiskAvailabilityCallback callback) override; + void GetStatistics(GetStatisticsCallback callback) override; + // 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 GetGlobalUsage(blink::mojom::StorageType type, UsageCallback callback); void GetHostUsageWithBreakdown(const std::string& host, blink::mojom::StorageType type, UsageWithBreakdownCallback callback); - std::map<std::string, std::string> GetStatistics(); - bool IsStorageUnlimited(const blink::StorageKey& storage_key, blink::mojom::StorageType type) const; @@ -388,6 +401,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl static constexpr int kEvictionIntervalInMilliSeconds = 30 * kMinutesInMilliSeconds; static constexpr int kThresholdOfErrorsToBeDenylisted = 3; + static constexpr int kThresholdOfErrorsToDisableDatabase = 3; static constexpr int kThresholdRandomizationPercent = 5; static constexpr char kDatabaseName[] = "QuotaManager"; @@ -410,6 +424,12 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl eviction_disabled_ = disable; } + void SetQuotaDatabaseForTesting(std::unique_ptr<QuotaDatabase> database); + + void SetBootstrapDisabledForTesting(bool disable) { + bootstrap_disabled_for_testing_ = disable; + } + protected: ~QuotaManagerImpl() override; void SetQuotaChangeCallbackForTesting( @@ -418,18 +438,18 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl 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; + friend class UsageTrackerTest; class EvictionRoundInfoHelper; class UsageAndQuotaInfoGatherer; class GetUsageInfoTask; + class StorageKeyGathererTask; class BucketDataDeleter; - class StorageKeyDataDeleter; class HostDataDeleter; class DumpQuotaTableHelper; class DumpBucketTableHelper; @@ -452,6 +472,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl using BucketTableEntry = QuotaDatabase::BucketTableEntry; using QuotaTableEntries = std::vector<QuotaTableEntry>; using BucketTableEntries = std::vector<BucketTableEntry>; + using StorageKeysByType = + base::flat_map<blink::mojom::StorageType, std::set<blink::StorageKey>>; using QuotaSettingsCallback = base::OnceCallback<void(const QuotaSettings&)>; @@ -475,13 +497,19 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl // Initialize() must be called after all quota clients are added to the // manager by RegisterClient(). void EnsureDatabaseOpened(); - void DidOpenDatabase(bool is_database_bootstraped); - void BootstrapDatabaseForEviction(GetBucketCallback did_get_bucket_callback, - int64_t unused_usage, - int64_t unused_unlimited_usage); - void DidBootstrapDatabaseForEviction( - GetBucketCallback did_get_bucket_callback, - bool success); + + // Bootstraps database with storage keys that may not have been registered. + // Bootstrapping ensures that there is a bucket entry in the buckets table for + // all storage keys that have stored data by quota managed Storage APIs. Will + // queue calls to QuotaDatabase during bootstrap to be run after bootstrapping + // is complete. + void BootstrapDatabase(); + void DidGetBootstrapFlag(bool is_database_bootstrapped); + void DidGetStorageKeysForBootstrap(StorageKeysByType storage_keys_by_type); + void DidBootstrapDatabase(QuotaError error); + void DidSetDatabaseBootstrapped(QuotaError error); + // Runs all callbacks to QuotaDatabase that have been queued during bootstrap. + void RunDatabaseCallbacks(); // Called by clients via proxy. // Registers a quota client to the manager. @@ -492,11 +520,6 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl UsageTracker* GetUsageTracker(blink::mojom::StorageType type) const; - // Extract cached storage keys list from the usage tracker. - // (Might return empty list if no storage key is tracked by the tracker.) - std::set<blink::StorageKey> GetCachedStorageKeys( - blink::mojom::StorageType type); - void DumpQuotaTable(DumpQuotaTableCallback callback); void DumpBucketTable(DumpBucketTableCallback callback); @@ -505,16 +528,33 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl // bucket from the bucket table. void DeleteBucketDataInternal(const BucketLocator& bucket, QuotaClientTypes quota_client_types, - bool is_eviction, StatusCallback callback); + // Removes the HostDataDeleter that completed its work. + // + // This method is static because it must call `delete_host_data_callback` even + // if the QuotaManagerImpl was destroyed. + static void DidDeleteHostData(base::WeakPtr<QuotaManagerImpl> quota_manager, + StatusCallback delete_host_data_callback, + HostDataDeleter* deleter, + blink::mojom::QuotaStatusCode status_code); + + // Removes the BucketDataDeleter that completed its work. + // + // This method is static because it must call `delete_bucket_data_callback` + // even if the QuotaManagerImpl was destroyed. + static void DidDeleteBucketData(base::WeakPtr<QuotaManagerImpl> quota_manager, + StatusCallback delete_bucket_data_callback, + BucketDataDeleter* deleter, + blink::mojom::QuotaStatusCode status_code); + // Methods for eviction logic. void StartEviction(); - void DeleteStorageKeyFromDatabase(const blink::StorageKey& storage_key, - blink::mojom::StorageType type); - void DeleteBucketFromDatabase(BucketId bucket_id, bool is_eviction); + void DeleteBucketFromDatabase(BucketId bucket_id, + base::OnceCallback<void(QuotaError)> callback); - void DidBucketDataEvicted(blink::mojom::QuotaStatusCode status); + void DidBucketDataEvicted(QuotaDatabase::BucketTableEntry entry, + blink::mojom::QuotaStatusCode status); void ReportHistogram(); void DidGetTemporaryGlobalUsageForHistogram(int64_t usage, @@ -534,12 +574,15 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl // QuotaEvictionHandler. void GetEvictionBucket(blink::mojom::StorageType type, - int64_t global_quota, GetBucketCallback callback) override; void EvictBucketData(const BucketLocator& bucket, StatusCallback callback) override; void GetEvictionRoundInfo(EvictionRoundInfoCallback callback) override; + void DidGetBucketInfoForEviction( + const BucketLocator& bucket, + QuotaErrorOr<QuotaDatabase::BucketTableEntry> result); + void DidGetEvictionRoundInfo(); void GetLRUBucket(blink::mojom::StorageType type, GetBucketCallback callback); @@ -565,6 +608,11 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl QuotaErrorOr<BucketInfo> result); void DidGetBucketForDeletion(StatusCallback callback, QuotaErrorOr<BucketInfo> result); + void DidGetBucketForUsage(QuotaClientType client_type, + int64_t delta, + base::Time modification_time, + base::OnceClosure callback, + QuotaErrorOr<BucketInfo> result); void DidGetStorageKeys(GetStorageKeysCallback callback, QuotaErrorOr<std::set<blink::StorageKey>> result); void DidGetBuckets( @@ -581,7 +629,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl int64_t available_space); // Used from quota-internals page to test behavior of the storage pressure // callback. - void SimulateStoragePressure(const blink::StorageKey& storage_key); + void SimulateStoragePressure(const url::Origin& origin_url) override; // Evaluates disk statistics to identify storage pressure // (low disk space availability) and starts the storage @@ -594,28 +642,28 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl absl::optional<int64_t> GetQuotaOverrideForStorageKey( const blink::StorageKey&); - // TODO(ayui): Replace instances to use result with QuotaErrorOr. - void PostTaskAndReplyWithResultForDBThread( - const base::Location& from_here, - base::OnceCallback<bool(QuotaDatabase*)> task, - base::OnceCallback<void(bool)> reply); - template <typename ValueType> void PostTaskAndReplyWithResultForDBThread( base::OnceCallback<QuotaErrorOr<ValueType>(QuotaDatabase*)> task, base::OnceCallback<void(QuotaErrorOr<ValueType>)> reply, - const base::Location& from_here = base::Location::Current()); + const base::Location& from_here = base::Location::Current(), + bool is_bootstrap_task = false); void PostTaskAndReplyWithResultForDBThread( base::OnceCallback<QuotaError(QuotaDatabase*)> task, base::OnceCallback<void(QuotaError)> reply, - const base::Location& from_here = base::Location::Current()); + const base::Location& from_here = base::Location::Current(), + bool is_bootstrap_task = false); 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); + bool is_bootstrapping_database_for_testing() { + return is_bootstrapping_database_; + } + bool is_db_disabled_for_testing() { return db_disabled_; } const bool is_incognito_; @@ -625,14 +673,20 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl // points to never changes), and the underlying object is thread-safe. const scoped_refptr<QuotaManagerProxy> proxy_; + int db_error_count_ = 0; bool db_disabled_ = false; bool eviction_disabled_ = false; + bool bootstrap_disabled_for_testing_ = false; + absl::optional<blink::StorageKey> storage_key_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_for_eviction_ = false; + bool is_bootstrapping_database_ = false; + // Queued callbacks to QuotaDatabase that will run after database bootstrap is + // complete. + std::vector<base::OnceClosure> database_callbacks_; GetQuotaSettingsFunc get_settings_function_; scoped_refptr<base::TaskRunner> get_settings_task_runner_; @@ -659,6 +713,10 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl std::map<blink::StorageKey, QuotaOverride> devtools_overrides_; int next_override_handle_id_ = 0; + // Serve mojo connections for chrome://quota-internals pages. + mojo::ReceiverSet<mojom::QuotaInternalsHandler> internals_handlers_receivers_ + GUARDED_BY_CONTEXT(sequence_checker_); + // Owns the QuotaClient remotes registered via RegisterClient(). // // Iterating over this list is almost always incorrect. Most algorithms should @@ -707,6 +765,11 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl GetVolumeInfoFn get_volume_info_fn_; std::unique_ptr<EvictionRoundInfoHelper> eviction_helper_; + std::map<HostDataDeleter*, std::unique_ptr<HostDataDeleter>> + host_data_deleters_; + std::map<BucketDataDeleter*, std::unique_ptr<BucketDataDeleter>> + bucket_data_deleters_; + std::unique_ptr<StorageKeyGathererTask> storage_key_gatherer_; SEQUENCE_CHECKER(sequence_checker_); diff --git a/chromium/storage/browser/quota/quota_manager_proxy.cc b/chromium/storage/browser/quota/quota_manager_proxy.cc index 196f97ca311..8cce82b9af9 100644 --- a/chromium/storage/browser/quota/quota_manager_proxy.cc +++ b/chromium/storage/browser/quota/quota_manager_proxy.cc @@ -35,6 +35,9 @@ namespace { void DidGetBucket(scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback, QuotaErrorOr<BucketInfo> result) { + DCHECK(callback_task_runner); + DCHECK(callback); + if (callback_task_runner->RunsTasksInCurrentSequence()) { std::move(callback).Run(std::move(result)); return; @@ -47,6 +50,9 @@ void DidGetStatus( scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceCallback<void(blink::mojom::QuotaStatusCode)> callback, blink::mojom::QuotaStatusCode status) { + DCHECK(callback_task_runner); + DCHECK(callback); + if (callback_task_runner->RunsTasksInCurrentSequence()) { std::move(callback).Run(std::move(status)); return; @@ -87,11 +93,27 @@ void QuotaManagerProxy::RegisterClient( } } +void QuotaManagerProxy::BindInternalsHandler( + mojo::PendingReceiver<mojom::QuotaInternalsHandler> receiver) { + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&QuotaManagerProxy::BindInternalsHandler, + this, std::move(receiver))); + return; + } + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (quota_manager_impl_) + quota_manager_impl_->BindInternalsHandler(std::move(receiver)); +} + void QuotaManagerProxy::GetOrCreateBucket( const StorageKey& storage_key, const std::string& bucket_name, scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { + DCHECK(callback_task_runner); + DCHECK(callback); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { quota_manager_impl_task_runner_->PostTask( FROM_HERE, @@ -114,12 +136,46 @@ void QuotaManagerProxy::GetOrCreateBucket( std::move(callback))); } +void QuotaManagerProxy::GetOrCreateBucketDeprecated( + const StorageKey& storage_key, + const std::string& bucket_name, + blink::mojom::StorageType storage_type, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { + DCHECK(callback_task_runner); + DCHECK(callback); + + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { + quota_manager_impl_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&QuotaManagerProxy::GetOrCreateBucketDeprecated, this, + storage_key, bucket_name, storage_type, + std::move(callback_task_runner), std::move(callback))); + return; + } + + DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_); + if (!quota_manager_impl_) { + DidGetBucket(std::move(callback_task_runner), std::move(callback), + QuotaErrorOr<BucketInfo>(QuotaError::kUnknownError)); + return; + } + + quota_manager_impl_->GetOrCreateBucketDeprecated( + storage_key, bucket_name, storage_type, + base::BindOnce(&DidGetBucket, std::move(callback_task_runner), + std::move(callback))); +} + void QuotaManagerProxy::CreateBucketForTesting( const StorageKey& storage_key, const std::string& bucket_name, blink::mojom::StorageType storage_type, scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { + DCHECK(callback_task_runner); + DCHECK(callback); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { quota_manager_impl_task_runner_->PostTask( FROM_HERE, @@ -148,6 +204,9 @@ void QuotaManagerProxy::GetBucket( blink::mojom::StorageType type, scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { + DCHECK(callback_task_runner); + DCHECK(callback); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { quota_manager_impl_task_runner_->PostTask( FROM_HERE, @@ -175,6 +234,9 @@ void QuotaManagerProxy::DeleteBucket( const std::string& bucket_name, scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceCallback<void(blink::mojom::QuotaStatusCode)> callback) { + DCHECK(callback_task_runner); + DCHECK(callback); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { quota_manager_impl_task_runner_->PostTask( FROM_HERE, @@ -234,7 +296,9 @@ void QuotaManagerProxy::NotifyStorageModified( base::Time modification_time, scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceClosure callback) { - DCHECK(!callback || callback_task_runner); + DCHECK(callback_task_runner); + DCHECK(callback); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { quota_manager_impl_task_runner_->PostTask( FROM_HERE, @@ -272,7 +336,9 @@ void QuotaManagerProxy::NotifyBucketModified( base::Time modification_time, scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceClosure callback) { - DCHECK(!callback || callback_task_runner); + DCHECK(callback_task_runner); + DCHECK(callback); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { quota_manager_impl_task_runner_->PostTask( FROM_HERE, @@ -341,6 +407,9 @@ void DidGetUsageAndQuota( blink::mojom::QuotaStatusCode status, int64_t usage, int64_t quota) { + DCHECK(callback_task_runner); + DCHECK(callback); + if (callback_task_runner->RunsTasksInCurrentSequence()) { std::move(callback).Run(status, usage, quota); return; @@ -356,6 +425,9 @@ void QuotaManagerProxy::GetUsageAndQuota( blink::mojom::StorageType type, scoped_refptr<base::SequencedTaskRunner> callback_task_runner, UsageAndQuotaCallback callback) { + DCHECK(callback_task_runner); + DCHECK(callback); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { quota_manager_impl_task_runner_->PostTask( FROM_HERE, @@ -383,6 +455,9 @@ void QuotaManagerProxy::IsStorageUnlimited( blink::mojom::StorageType type, scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceCallback<void(bool)> callback) { + DCHECK(callback_task_runner); + DCHECK(callback); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { quota_manager_impl_task_runner_->PostTask( FROM_HERE, @@ -417,6 +492,9 @@ void QuotaManagerProxy::OverrideQuotaForStorageKey( absl::optional<int64_t> quota_size, scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceClosure callback) { + DCHECK(callback_task_runner); + DCHECK(callback); + if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) { quota_manager_impl_task_runner_->PostTask( FROM_HERE, diff --git a/chromium/storage/browser/quota/quota_manager_proxy.h b/chromium/storage/browser/quota/quota_manager_proxy.h index a6f0648c1c7..1b68cc3e359 100644 --- a/chromium/storage/browser/quota/quota_manager_proxy.h +++ b/chromium/storage/browser/quota/quota_manager_proxy.h @@ -67,6 +67,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerProxy QuotaClientType client_type, const std::vector<blink::mojom::StorageType>& storage_types); + virtual void BindInternalsHandler( + mojo::PendingReceiver<mojom::QuotaInternalsHandler> receiver); + // Gets the bucket with `bucket_name` for the `storage_key` for StorageType // kTemporary and returns the BucketInfo. If one doesn't exist, it creates // a new bucket with the specified policies. Returns a QuotaError if the @@ -77,6 +80,17 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerProxy scoped_refptr<base::SequencedTaskRunner> callback_task_runner, base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback); + // Same as GetOrCreateBucket but takes in StorageType. This should only be + // used by FileSystem, and is expected to be removed when + // StorageType::kSyncable and StorageType::kPersistent are deprecated. + // (crbug.com/1233525, crbug.com/1286964). + virtual void GetOrCreateBucketDeprecated( + const blink::StorageKey& storage_key, + const std::string& bucket_name, + blink::mojom::StorageType storage_type, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback); + // Creates a bucket for `origin` with `bucket_name` and returns the // BucketInfo to the callback. Returns a QuotaError to the callback // on operation failure. @@ -132,8 +146,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerProxy blink::mojom::StorageType type, int64_t delta, base::Time modification_time, - scoped_refptr<base::SequencedTaskRunner> callback_task_runner = nullptr, - base::OnceClosure callback = base::OnceClosure()); + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceClosure callback); // Notifies the quota manager that a bucket has been modified for the given // client. A `callback` may be optionally provided to be invoked on the @@ -146,8 +160,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerProxy BucketId bucket_id, int64_t delta, base::Time modification_time, - scoped_refptr<base::SequencedTaskRunner> callback_task_runner = nullptr, - base::OnceClosure callback = base::OnceClosure()); + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + base::OnceClosure callback); virtual void NotifyWriteFailed(const blink::StorageKey& storage_key); diff --git a/chromium/storage/browser/quota/quota_manager_unittest.cc b/chromium/storage/browser/quota/quota_manager_unittest.cc index 113ce82329e..96ab2a51517 100644 --- a/chromium/storage/browser/quota/quota_manager_unittest.cc +++ b/chromium/storage/browser/quota/quota_manager_unittest.cc @@ -6,6 +6,7 @@ #include <stdint.h> #include <algorithm> +#include <cstdint> #include <memory> #include <set> #include <sstream> @@ -27,6 +28,7 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "base/test/test_future.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "components/services/storage/public/cpp/buckets/bucket_locator.h" @@ -41,6 +43,7 @@ #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_quota_database.h" #include "storage/browser/test/mock_special_storage_policy.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -68,6 +71,29 @@ const int64_t kDefaultPoolSize = 1000; const int64_t kDefaultPerHostQuota = 200; const int64_t kGigabytes = QuotaManagerImpl::kGBytes; +struct UsageAndQuotaResult { + QuotaStatusCode status; + int64_t usage; + int64_t quota; +}; + +struct GlobalUsageResult { + int64_t usage; + int64_t unlimited_usage; +}; + +struct StorageCapacityResult { + int64_t total_space; + int64_t available_space; +}; + +struct ClientBucketData { + const char* origin; + std::string name; + StorageType type; + int64_t usage; +}; + // Returns a deterministic value for the amount of available disk space. int64_t GetAvailableDiskSpaceForTest() { return kAvailableSpaceForApp + kMustRemainAvailableForSystem; @@ -91,15 +117,6 @@ MATCHER_P3(MatchesBucketTableEntry, storage_key, type, use_count, "") { testing::ExplainMatchResult(use_count, arg.use_count, result_listener); } -bool ContainsBucket(const std::set<BucketLocator>& buckets, - const BucketInfo& target_bucket) { - BucketLocator target_bucket_locator( - target_bucket.id, target_bucket.storage_key, target_bucket.type, - target_bucket.name == kDefaultBucketName); - auto it = buckets.find(target_bucket_locator); - return it != buckets.end(); -} - } // namespace class QuotaManagerImplTest : public testing::Test { @@ -145,11 +162,12 @@ class QuotaManagerImplTest : public testing::Test { } MockQuotaClient* CreateAndRegisterClient( - base::span<const MockStorageKeyData> mock_data, QuotaClientType client_type, - const std::vector<blink::mojom::StorageType> storage_types) { + const std::vector<blink::mojom::StorageType> storage_types, + base::span<const UnmigratedStorageKeyData> unmigrated_data = + base::span<const UnmigratedStorageKeyData>()) { auto mock_quota_client = std::make_unique<storage::MockQuotaClient>( - quota_manager_impl_->proxy(), mock_data, client_type); + quota_manager_impl_->proxy(), client_type, unmigrated_data); MockQuotaClient* mock_quota_client_ptr = mock_quota_client.get(); mojo::PendingRemote<storage::mojom::QuotaClient> quota_client; @@ -160,115 +178,92 @@ class QuotaManagerImplTest : public testing::Test { return mock_quota_client_ptr; } + // Creates buckets in QuotaDatabase if they don't exist yet, and sets usage + // to the `client`. + void RegisterClientBucketData(MockQuotaClient* client, + base::span<const ClientBucketData> mock_data) { + std::map<BucketLocator, int64_t> buckets_data; + for (const ClientBucketData& data : mock_data) { + base::test::TestFuture<QuotaErrorOr<BucketInfo>> future; + quota_manager_impl_->GetOrCreateBucketDeprecated( + ToStorageKey(data.origin), data.name, data.type, + future.GetCallback()); + auto bucket = future.Take(); + EXPECT_TRUE(bucket.ok()); + buckets_data.insert(std::pair<BucketLocator, int64_t>( + bucket->ToBucketLocator(), data.usage)); + } + client->AddBucketsData(buckets_data); + } + void OpenDatabase() { quota_manager_impl_->EnsureDatabaseOpened(); } - void GetOrCreateBucket(const StorageKey& storage_key, - const std::string& bucket_name) { - base::RunLoop run_loop; - quota_manager_impl_->GetOrCreateBucket( - storage_key, bucket_name, - base::BindOnce(&QuotaManagerImplTest::DidGetBucket, - weak_factory_.GetWeakPtr(), run_loop.QuitClosure())); - run_loop.Run(); + QuotaErrorOr<BucketInfo> GetOrCreateBucket(const StorageKey& storage_key, + const std::string& bucket_name) { + base::test::TestFuture<QuotaErrorOr<BucketInfo>> future; + quota_manager_impl_->GetOrCreateBucket(storage_key, bucket_name, + future.GetCallback()); + return future.Take(); } - void CreateBucketForTesting(const StorageKey& storage_key, - const std::string& bucket_name, - blink::mojom::StorageType storage_type) { - base::RunLoop run_loop; + QuotaErrorOr<BucketInfo> CreateBucketForTesting( + const StorageKey& storage_key, + const std::string& bucket_name, + blink::mojom::StorageType storage_type) { + base::test::TestFuture<QuotaErrorOr<BucketInfo>> future; quota_manager_impl_->CreateBucketForTesting( - storage_key, bucket_name, storage_type, - base::BindOnce(&QuotaManagerImplTest::DidGetBucket, - weak_factory_.GetWeakPtr(), run_loop.QuitClosure())); - run_loop.Run(); + storage_key, bucket_name, storage_type, future.GetCallback()); + return future.Take(); } - void GetBucket(const StorageKey& storage_key, - const std::string& bucket_name, - blink::mojom::StorageType storage_type) { - base::RunLoop run_loop; - quota_manager_impl_->GetBucket( - storage_key, bucket_name, storage_type, - base::BindOnce(&QuotaManagerImplTest::DidGetBucket, - weak_factory_.GetWeakPtr(), run_loop.QuitClosure())); - run_loop.Run(); + QuotaErrorOr<BucketInfo> GetBucket(const StorageKey& storage_key, + const std::string& bucket_name, + blink::mojom::StorageType storage_type) { + base::test::TestFuture<QuotaErrorOr<BucketInfo>> future; + quota_manager_impl_->GetBucket(storage_key, bucket_name, storage_type, + future.GetCallback()); + return future.Take(); } - void GetStorageKeysForType(blink::mojom::StorageType storage_type) { - base::RunLoop run_loop; + std::set<StorageKey> GetStorageKeysForType( + blink::mojom::StorageType storage_type) { + base::test::TestFuture<std::set<StorageKey>> future; quota_manager_impl_->GetStorageKeysForType( - storage_type, - base::BindOnce(&QuotaManagerImplTest::DidGetStorageKeys, - weak_factory_.GetWeakPtr(), run_loop.QuitClosure())); - run_loop.Run(); + storage_type, future.GetCallback<const std::set<StorageKey>&>()); + return future.Take(); } QuotaErrorOr<std::set<BucketLocator>> GetBucketsForType( blink::mojom::StorageType storage_type) { - base::RunLoop run_loop; - QuotaErrorOr<std::set<BucketLocator>> buckets; - quota_manager_impl_->GetBucketsForType( - storage_type, base::BindLambdaForTesting( - [&](QuotaErrorOr<std::set<BucketLocator>> result) { - buckets = std::move(result); - run_loop.Quit(); - })); - run_loop.Run(); - return buckets; + base::test::TestFuture<QuotaErrorOr<std::set<BucketLocator>>> future; + quota_manager_impl_->GetBucketsForType(storage_type, future.GetCallback()); + return future.Take(); } QuotaErrorOr<std::set<BucketLocator>> GetBucketsForHost( const std::string& host, blink::mojom::StorageType storage_type) { - base::RunLoop run_loop; - QuotaErrorOr<std::set<BucketLocator>> buckets; - quota_manager_impl_->GetBucketsForHost( - host, storage_type, - base::BindLambdaForTesting( - [&](QuotaErrorOr<std::set<BucketLocator>> result) { - buckets = std::move(result); - run_loop.Quit(); - })); - run_loop.Run(); - return buckets; + base::test::TestFuture<QuotaErrorOr<std::set<BucketLocator>>> future; + quota_manager_impl_->GetBucketsForHost(host, storage_type, + future.GetCallback()); + return future.Take(); } QuotaErrorOr<std::set<BucketLocator>> GetBucketsForStorageKey( const StorageKey& storage_key, blink::mojom::StorageType storage_type) { - base::RunLoop run_loop; - QuotaErrorOr<std::set<BucketLocator>> buckets; - quota_manager_impl_->GetBucketsForStorageKey( - storage_key, storage_type, - base::BindLambdaForTesting( - [&](QuotaErrorOr<std::set<BucketLocator>> result) { - buckets = std::move(result); - run_loop.Quit(); - })); - run_loop.Run(); - return buckets; - } - - void GetUsageInfo() { - usage_info_.clear(); - base::RunLoop run_loop; - quota_manager_impl_->GetUsageInfo( - base::BindOnce(&QuotaManagerImplTest::DidGetUsageInfo, - weak_factory_.GetWeakPtr(), run_loop.QuitClosure())); - run_loop.Run(); + base::test::TestFuture<QuotaErrorOr<std::set<BucketLocator>>> future; + quota_manager_impl_->GetBucketsForStorageKey(storage_key, storage_type, + future.GetCallback()); + return future.Take(); } - void GetUsageAndQuotaForWebApps(const StorageKey& storage_key, - StorageType type) { - base::RunLoop run_loop; - quota_status_ = QuotaStatusCode::kUnknown; - usage_ = -1; - quota_ = -1; - quota_manager_impl_->GetUsageAndQuotaForWebApps( - storage_key, type, - base::BindOnce(&QuotaManagerImplTest::DidGetUsageAndQuota, - weak_factory_.GetWeakPtr(), run_loop.QuitClosure())); - run_loop.Run(); + UsageAndQuotaResult GetUsageAndQuotaForWebApps(const StorageKey& storage_key, + StorageType type) { + base::test::TestFuture<QuotaStatusCode, int64_t, int64_t> future; + quota_manager_impl_->GetUsageAndQuotaForWebApps(storage_key, type, + future.GetCallback()); + return {future.Get<0>(), future.Get<1>(), future.Get<2>()}; } void GetUsageAndQuotaWithBreakdown(const StorageKey& storage_key, @@ -285,17 +280,13 @@ class QuotaManagerImplTest : public testing::Test { run_loop.Run(); } - void GetUsageAndQuotaForStorageClient(const StorageKey& storage_key, - StorageType type) { - base::RunLoop run_loop; - quota_status_ = QuotaStatusCode::kUnknown; - usage_ = -1; - quota_ = -1; - quota_manager_impl_->GetUsageAndQuota( - storage_key, type, - base::BindOnce(&QuotaManagerImplTest::DidGetUsageAndQuota, - weak_factory_.GetWeakPtr(), run_loop.QuitClosure())); - run_loop.Run(); + UsageAndQuotaResult GetUsageAndQuotaForStorageClient( + const StorageKey& storage_key, + StorageType type) { + base::test::TestFuture<QuotaStatusCode, int64_t, int64_t> future; + quota_manager_impl_->GetUsageAndQuota(storage_key, type, + future.GetCallback()); + return {future.Get<0>(), future.Get<1>(), future.Get<2>()}; } void SetQuotaSettings(int64_t pool_size, @@ -318,32 +309,25 @@ class QuotaManagerImplTest : public testing::Test { quota_manager_impl_->SetGetVolumeInfoFnForTesting(fn); } - void GetPersistentHostQuota(const std::string& host) { - quota_status_ = QuotaStatusCode::kUnknown; - quota_ = -1; - quota_manager_impl_->GetPersistentHostQuota( - host, base::BindOnce(&QuotaManagerImplTest::DidGetHostQuota, - weak_factory_.GetWeakPtr())); + int64_t GetPersistentHostQuota(const std::string& host) { + base::test::TestFuture<QuotaStatusCode, int64_t> future; + quota_manager_impl_->GetPersistentHostQuota(host, future.GetCallback()); + EXPECT_EQ(future.Get<0>(), QuotaStatusCode::kOk); + return future.Get<1>(); } - void SetPersistentHostQuota(const std::string& host, int64_t new_quota) { - quota_status_ = QuotaStatusCode::kUnknown; - quota_ = -1; - quota_manager_impl_->SetPersistentHostQuota( - host, new_quota, - base::BindOnce(&QuotaManagerImplTest::DidGetHostQuota, - weak_factory_.GetWeakPtr())); + int64_t SetPersistentHostQuota(const std::string& host, int64_t new_quota) { + base::test::TestFuture<QuotaStatusCode, int64_t> future; + quota_manager_impl_->SetPersistentHostQuota(host, new_quota, + future.GetCallback()); + EXPECT_EQ(future.Get<0>(), QuotaStatusCode::kOk); + return future.Get<1>(); } - void GetGlobalUsage(StorageType type) { - usage_ = -1; - unlimited_usage_ = -1; - base::RunLoop run_loop; - quota_manager_impl_->GetGlobalUsage( - type, - base::BindOnce(&QuotaManagerImplTest::DidGetGlobalUsage, - weak_factory_.GetWeakPtr(), run_loop.QuitClosure())); - run_loop.Run(); + GlobalUsageResult GetGlobalUsage(StorageType type) { + base::test::TestFuture<int64_t, int64_t> future; + quota_manager_impl_->GetGlobalUsage(type, future.GetCallback()); + return {future.Get<0>(), future.Get<1>()}; } void GetHostUsageWithBreakdown(const std::string& host, StorageType type) { @@ -364,60 +348,38 @@ class QuotaManagerImplTest : public testing::Test { weak_factory_.GetWeakPtr())); } - void DeleteClientStorageKeyData(mojom::QuotaClient* client, - const StorageKey& storage_key, - StorageType type) { - DCHECK(client); - quota_status_ = QuotaStatusCode::kUnknown; - client->DeleteStorageKeyData( - storage_key, type, - base::BindOnce(&QuotaManagerImplTest::StatusCallback, - weak_factory_.GetWeakPtr())); + QuotaStatusCode EvictBucketData(const BucketLocator& bucket) { + base::test::TestFuture<QuotaStatusCode> future; + quota_manager_impl_->EvictBucketData(bucket, future.GetCallback()); + return future.Get(); } - void EvictBucketData(const BucketLocator& bucket) { - quota_status_ = QuotaStatusCode::kUnknown; - quota_manager_impl_->EvictBucketData( - bucket, base::BindOnce(&QuotaManagerImplTest::StatusCallback, - weak_factory_.GetWeakPtr())); + QuotaStatusCode DeleteBucketData(const BucketLocator& bucket, + QuotaClientTypes quota_client_types) { + base::test::TestFuture<QuotaStatusCode> future; + quota_manager_impl_->DeleteBucketData(bucket, std::move(quota_client_types), + future.GetCallback()); + return future.Get(); } - void DeleteBucketData(const BucketLocator& bucket, - QuotaClientTypes quota_client_types) { - quota_status_ = QuotaStatusCode::kUnknown; - quota_manager_impl_->DeleteBucketData( - bucket, std::move(quota_client_types), - base::BindOnce(&QuotaManagerImplTest::StatusCallback, - weak_factory_.GetWeakPtr())); - } - - void DeleteHostData(const std::string& host, - StorageType type, - QuotaClientTypes quota_client_types) { - quota_status_ = QuotaStatusCode::kUnknown; - quota_manager_impl_->DeleteHostData( - host, type, std::move(quota_client_types), - base::BindOnce(&QuotaManagerImplTest::StatusCallback, - weak_factory_.GetWeakPtr())); + QuotaStatusCode DeleteHostData(const std::string& host, StorageType type) { + base::test::TestFuture<QuotaStatusCode> future; + quota_manager_impl_->DeleteHostData(host, type, future.GetCallback()); + return future.Get(); } - void FindAndDeleteBucketData(const StorageKey& storage_key, - const std::string& bucket_name) { - base::RunLoop run_loop; - quota_status_ = QuotaStatusCode::kUnknown; - quota_manager_impl_->FindAndDeleteBucketData( - storage_key, bucket_name, - base::BindOnce(&QuotaManagerImplTest::StatusCallbackSync, - weak_factory_.GetWeakPtr(), run_loop.QuitClosure())); - run_loop.Run(); + QuotaStatusCode FindAndDeleteBucketData(const StorageKey& storage_key, + const std::string& bucket_name) { + base::test::TestFuture<QuotaStatusCode> future; + quota_manager_impl_->FindAndDeleteBucketData(storage_key, bucket_name, + future.GetCallback()); + return future.Get(); } - void GetStorageCapacity() { - available_space_ = -1; - total_space_ = -1; - quota_manager_impl_->GetStorageCapacity( - base::BindOnce(&QuotaManagerImplTest::DidGetStorageCapacity, - weak_factory_.GetWeakPtr())); + StorageCapacityResult GetStorageCapacity() { + base::test::TestFuture<int64_t, int64_t> future; + quota_manager_impl_->GetStorageCapacity(future.GetCallback()); + return {future.Get<0>(), future.Get<1>()}; } void GetEvictionRoundInfo() { @@ -431,10 +393,6 @@ class QuotaManagerImplTest : public testing::Test { weak_factory_.GetWeakPtr())); } - std::set<StorageKey> GetCachedStorageKeys(StorageType type) { - return quota_manager_impl_->GetCachedStorageKeys(type); - } - void NotifyStorageAccessed(const StorageKey& storage_key, StorageType type) { quota_manager_impl_->NotifyStorageAccessed(storage_key, type, IncrementMockTime()); @@ -444,71 +402,38 @@ class QuotaManagerImplTest : public testing::Test { quota_manager_impl_->NotifyBucketAccessed(bucket_id, IncrementMockTime()); } - void DeleteBucketFromDatabase(BucketId bucket_id) { - quota_manager_impl_->DeleteBucketFromDatabase(bucket_id, false); - } - void GetEvictionBucket(StorageType type) { eviction_bucket_.reset(); // The quota manager's default eviction policy is to use an LRU eviction // policy. quota_manager_impl_->GetEvictionBucket( - type, 0, - base::BindOnce(&QuotaManagerImplTest::DidGetEvictionBucket, - weak_factory_.GetWeakPtr())); + type, base::BindOnce(&QuotaManagerImplTest::DidGetEvictionBucket, + weak_factory_.GetWeakPtr())); } - void GetBucketsModifiedBetween(StorageType type, - base::Time begin, - base::Time end) { - modified_buckets_.clear(); - modified_buckets_type_ = StorageType::kUnknown; - base::RunLoop run_loop; + std::set<BucketLocator> GetBucketsModifiedBetween(StorageType type, + base::Time begin, + base::Time end) { + base::test::TestFuture<std::set<BucketLocator>, StorageType> future; quota_manager_impl_->GetBucketsModifiedBetween( type, begin, end, - base::BindOnce(&QuotaManagerImplTest::DidGetModifiedBuckets, - weak_factory_.GetWeakPtr(), run_loop.QuitClosure())); - run_loop.Run(); + future.GetCallback<const std::set<BucketLocator>&, StorageType>()); + EXPECT_EQ(future.Get<1>(), type); + return future.Get<0>(); } - void DumpQuotaTable() { - quota_entries_.clear(); - quota_manager_impl_->DumpQuotaTable(base::BindOnce( - &QuotaManagerImplTest::DidDumpQuotaTable, weak_factory_.GetWeakPtr())); + QuotaTableEntries DumpQuotaTable() { + base::test::TestFuture<QuotaTableEntries> future; + quota_manager_impl_->DumpQuotaTable( + future.GetCallback<const QuotaTableEntries&>()); + return future.Get(); } - void DumpBucketTable() { - bucket_entries_.clear(); - quota_manager_impl_->DumpBucketTable(base::BindOnce( - &QuotaManagerImplTest::DidDumpBucketTable, weak_factory_.GetWeakPtr())); - } - - void DidGetBucket(base::OnceClosure quit_closure, - QuotaErrorOr<BucketInfo> result) { - bucket_ = std::move(result); - std::move(quit_closure).Run(); - } - - void DidGetStorageKeys(base::OnceClosure quit_closure, - const std::set<StorageKey>& storage_keys) { - storage_keys_ = std::move(storage_keys); - std::move(quit_closure).Run(); - } - - void DidGetUsageInfo(base::OnceClosure quit_closure, - UsageInfoEntries entries) { - usage_info_ = std::move(entries); - std::move(quit_closure).Run(); - } - - void DidGetUsageAndQuota(base::OnceClosure quit_closure, - QuotaStatusCode status, - int64_t usage, - int64_t quota) { - quota_status_ = status; - usage_ = usage; - quota_ = quota; - std::move(quit_closure).Run(); + BucketTableEntries DumpBucketTable() { + base::test::TestFuture<BucketTableEntries> future; + quota_manager_impl_->DumpBucketTable( + future.GetCallback<const BucketTableEntries&>()); + return future.Get(); } void DidGetUsageAndQuotaWithBreakdown( @@ -524,43 +449,6 @@ class QuotaManagerImplTest : public testing::Test { std::move(quit_closure).Run(); } - void DidGetQuota(QuotaStatusCode status, int64_t quota) { - quota_status_ = status; - quota_ = quota; - } - - void DidGetStorageCapacity(int64_t total_space, int64_t available_space) { - total_space_ = total_space; - available_space_ = available_space; - } - - void DidGetHostQuota(QuotaStatusCode status, int64_t quota) { - quota_status_ = status; - quota_ = quota; - } - - void DidGetGlobalUsage(base::OnceClosure quit_closure, - int64_t usage, - int64_t unlimited_usage) { - usage_ = usage; - unlimited_usage_ = unlimited_usage; - std::move(quit_closure).Run(); - } - - void DidGetHostUsage(int64_t usage) { usage_ = usage; } - - void StatusCallback(QuotaStatusCode status) { - ++status_callback_count_; - quota_status_ = status; - } - - void StatusCallbackSync(base::OnceClosure quit_closure, - QuotaStatusCode status) { - ++status_callback_count_; - quota_status_ = status; - std::move(quit_closure).Run(); - } - void DidGetHostUsageBreakdown( base::OnceClosure quit_closure, int64_t usage, @@ -589,22 +477,6 @@ class QuotaManagerImplTest : public testing::Test { !bucket->storage_key.origin().GetURL().is_empty()); } - void DidGetModifiedBuckets(base::OnceClosure quit_closure, - const std::set<BucketLocator>& buckets, - StorageType type) { - modified_buckets_ = buckets; - modified_buckets_type_ = type; - std::move(quit_closure).Run(); - } - - void DidDumpQuotaTable(const QuotaTableEntries& entries) { - quota_entries_ = entries; - } - - void DidDumpBucketTable(const BucketTableEntries& entries) { - bucket_entries_ = entries; - } - void GetUsage_WithModifyTestBody(const StorageType type); void SetStoragePressureCallback( @@ -630,6 +502,7 @@ class QuotaManagerImplTest : public testing::Test { 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; } @@ -646,6 +519,14 @@ class QuotaManagerImplTest : public testing::Test { quota_manager_impl_->SetQuotaChangeCallbackForTesting(std::move(cb)); } + void SetQuotaDatabase(std::unique_ptr<QuotaDatabase> database) { + quota_manager_impl_->SetQuotaDatabaseForTesting(std::move(database)); + } + + bool is_db_bootstrapping() { + return quota_manager_impl_->is_bootstrapping_database_for_testing(); + } + bool is_db_disabled() { return quota_manager_impl_->is_db_disabled_for_testing(); } @@ -654,34 +535,28 @@ class QuotaManagerImplTest : public testing::Test { quota_manager_impl_->database_->SetDisabledForTesting(disable); } + void disable_database_bootstrap(bool disable) { + quota_manager_impl_->SetBootstrapDisabledForTesting(disable); + } + QuotaStatusCode status() const { return quota_status_; } - const UsageInfoEntries& usage_info() const { return usage_info_; } int64_t usage() const { return usage_; } const blink::mojom::UsageBreakdown& usage_breakdown() const { return *usage_breakdown_; } - int64_t unlimited_usage() const { return unlimited_usage_; } int64_t quota() const { return quota_; } int64_t total_space() const { return total_space_; } int64_t available_space() const { return available_space_; } const absl::optional<BucketLocator>& eviction_bucket() const { return eviction_bucket_; } - const std::set<BucketLocator>& modified_buckets() const { - return modified_buckets_; - } - StorageType modified_buckets_type() const { return modified_buckets_type_; } - const QuotaTableEntries& quota_entries() const { return quota_entries_; } - const BucketTableEntries& bucket_entries() const { return bucket_entries_; } const QuotaSettings& settings() const { return settings_; } - int status_callback_count() const { return status_callback_count_; } - void reset_status_callback_count() { status_callback_count_ = 0; } protected: base::test::ScopedFeatureList scoped_feature_list_; base::test::TaskEnvironment task_environment_; - QuotaErrorOr<BucketInfo> bucket_; - QuotaErrorOr<std::set<StorageKey>> storage_keys_; + base::ScopedTempDir data_dir_; + scoped_refptr<QuotaManagerImpl> quota_manager_impl_; private: base::Time IncrementMockTime() { @@ -689,26 +564,16 @@ class QuotaManagerImplTest : public testing::Test { return base::Time::FromDoubleT(mock_time_counter_ * 10.0); } - base::ScopedTempDir data_dir_; - - scoped_refptr<QuotaManagerImpl> quota_manager_impl_; scoped_refptr<MockSpecialStoragePolicy> mock_special_storage_policy_; QuotaStatusCode quota_status_; - UsageInfoEntries usage_info_; int64_t usage_; blink::mojom::UsageBreakdownPtr usage_breakdown_; - int64_t unlimited_usage_; int64_t quota_; int64_t total_space_; int64_t available_space_; absl::optional<BucketLocator> eviction_bucket_; - std::set<BucketLocator> modified_buckets_; - StorageType modified_buckets_type_; - QuotaTableEntries quota_entries_; - BucketTableEntries bucket_entries_; QuotaSettings settings_; - int status_callback_count_; int additional_callback_count_; @@ -717,65 +582,136 @@ class QuotaManagerImplTest : public testing::Test { base::WeakPtrFactory<QuotaManagerImplTest> weak_factory_{this}; }; -TEST_F(QuotaManagerImplTest, GetUsageInfo) { - static const MockStorageKeyData kData1[] = { +TEST_F(QuotaManagerImplTest, QuotaDatabaseBootstrap) { + static const UnmigratedStorageKeyData kData1[] = { {"http://foo.com/", kTemp, 10}, {"http://foo.com:8080/", kTemp, 15}, - {"http://bar.com/", kTemp, 20}, {"http://bar.com/", kPerm, 50}, }; - static const MockStorageKeyData kData2[] = { + static const UnmigratedStorageKeyData kData2[] = { {"https://foo.com/", kTemp, 30}, {"https://foo.com:8081/", kTemp, 35}, - {"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}); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}, kData1); + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp, kPerm}, kData2); + + // OpenDatabase should trigger database bootstrapping. + OpenDatabase(); + EXPECT_TRUE(is_db_bootstrapping()); - GetUsageInfo(); + // When bootstrapping is complete, queued calls to the QuotaDatabase + // should return successfully and buckets for registered storage keys should + // already exist. + auto bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); + EXPECT_FALSE(is_db_bootstrapping()); + ASSERT_TRUE(bucket.ok()); - EXPECT_THAT(usage_info(), testing::UnorderedElementsAre( - UsageInfo("foo.com", kTemp, 10 + 15 + 30 + 35), - UsageInfo("bar.com", kTemp, 20), - UsageInfo("bar.com", kPerm, 40 + 50), - UsageInfo("example.com", kPerm, 40))); + bucket = GetBucket(ToStorageKey("http://foo.com:8080/"), kDefaultBucketName, + kTemp); + ASSERT_TRUE(bucket.ok()); + + bucket = GetBucket(ToStorageKey("https://foo.com:8081/"), kDefaultBucketName, + kTemp); + ASSERT_TRUE(bucket.ok()); + + bucket = + GetBucket(ToStorageKey("http://bar.com/"), kDefaultBucketName, kPerm); + ASSERT_TRUE(bucket.ok()); + + bucket = + GetBucket(ToStorageKey("http://example.com/"), kDefaultBucketName, kPerm); + ASSERT_TRUE(bucket.ok()); +} + +TEST_F(QuotaManagerImplTest, GetUsageInfo) { + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 10}, + {"http://foo.com:8080/", kDefaultBucketName, kTemp, 15}, + {"http://bar.com/", "logs", kTemp, 20}, + {"http://bar.com/", kDefaultBucketName, kPerm, 50}, + }; + static const ClientBucketData kData2[] = { + {"https://foo.com/", kDefaultBucketName, kTemp, 30}, + {"https://foo.com:8081/", kDefaultBucketName, kTemp, 35}, + {"http://bar.com/", kDefaultBucketName, kPerm, 40}, + {"http://example.com/", kDefaultBucketName, kPerm, 40}, + }; + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + MockQuotaClient* database_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(database_client, kData2); + + base::test::TestFuture<UsageInfoEntries> future; + quota_manager_impl()->GetUsageInfo(future.GetCallback()); + auto entries = future.Get(); + + EXPECT_THAT(entries, testing::UnorderedElementsAre( + UsageInfo("foo.com", kTemp, 10 + 15 + 30 + 35), + UsageInfo("bar.com", kTemp, 20), + UsageInfo("bar.com", kPerm, 40 + 50), + UsageInfo("example.com", kPerm, 40))); +} + +TEST_F(QuotaManagerImplTest, DatabaseDisabledAfterThreshold) { + disable_database_bootstrap(true); + OpenDatabase(); + + // Disable quota database for database error behavior. + disable_quota_database(true); + + ASSERT_FALSE(is_db_disabled()); + + StorageKey storage_key = ToStorageKey("http://a.com/"); + std::string bucket_name = "bucket_a"; + + auto bucket = GetOrCreateBucket(storage_key, bucket_name); + ASSERT_FALSE(bucket.ok()); + ASSERT_FALSE(is_db_disabled()); + + bucket = GetOrCreateBucket(storage_key, bucket_name); + ASSERT_FALSE(bucket.ok()); + ASSERT_FALSE(is_db_disabled()); + + // Disables access to QuotaDatabase after error counts passes threshold. + bucket = GetBucket(storage_key, bucket_name, kTemp); + ASSERT_FALSE(bucket.ok()); + ASSERT_TRUE(is_db_disabled()); } TEST_F(QuotaManagerImplTest, GetOrCreateBucket) { StorageKey storage_key = ToStorageKey("http://a.com/"); std::string bucket_name = "bucket_a"; - GetOrCreateBucket(storage_key, bucket_name); - ASSERT_TRUE(bucket_.ok()); + auto bucket = GetOrCreateBucket(storage_key, bucket_name); + ASSERT_TRUE(bucket.ok()); - BucketId created_bucket_id = bucket_.value().id; + BucketId created_bucket_id = bucket.value().id; - GetOrCreateBucket(storage_key, bucket_name); - EXPECT_TRUE(bucket_.ok()); - EXPECT_EQ(bucket_.value().id, created_bucket_id); + bucket = GetOrCreateBucket(storage_key, bucket_name); + EXPECT_TRUE(bucket.ok()); + EXPECT_EQ(bucket.value().id, created_bucket_id); } TEST_F(QuotaManagerImplTest, GetBucket) { StorageKey storage_key = ToStorageKey("http://a.com/"); std::string bucket_name = "bucket_a"; - CreateBucketForTesting(storage_key, bucket_name, kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo created_bucket = bucket_.value(); + auto bucket = CreateBucketForTesting(storage_key, bucket_name, kTemp); + ASSERT_TRUE(bucket.ok()); + BucketInfo created_bucket = bucket.value(); - GetBucket(storage_key, bucket_name, kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo retrieved_bucket = bucket_.value(); + bucket = GetBucket(storage_key, bucket_name, kTemp); + ASSERT_TRUE(bucket.ok()); + BucketInfo retrieved_bucket = bucket.value(); EXPECT_EQ(created_bucket.id, retrieved_bucket.id); - GetBucket(storage_key, "bucket_b", kTemp); - ASSERT_FALSE(bucket_.ok()); - EXPECT_EQ(bucket_.error(), QuotaError::kNotFound); + bucket = GetBucket(storage_key, "bucket_b", kTemp); + ASSERT_FALSE(bucket.ok()); + EXPECT_EQ(bucket.error(), QuotaError::kNotFound); ASSERT_FALSE(is_db_disabled()); } @@ -784,36 +720,36 @@ TEST_F(QuotaManagerImplTest, GetStorageKeysForType) { StorageKey storage_key_b = ToStorageKey("http://b.com/"); StorageKey storage_key_c = ToStorageKey("http://c.com/"); - CreateBucketForTesting(storage_key_a, "bucket_a", kTemp); - EXPECT_TRUE(bucket_.ok()); - BucketInfo bucket_a = bucket_.value(); + auto bucket = CreateBucketForTesting(storage_key_a, "bucket_a", kTemp); + EXPECT_TRUE(bucket.ok()); + BucketInfo bucket_a = bucket.value(); - CreateBucketForTesting(storage_key_b, "bucket_b", kTemp); - EXPECT_TRUE(bucket_.ok()); - BucketInfo bucket_b = bucket_.value(); + bucket = CreateBucketForTesting(storage_key_b, "bucket_b", kTemp); + EXPECT_TRUE(bucket.ok()); + BucketInfo bucket_b = bucket.value(); - CreateBucketForTesting(storage_key_c, "bucket_c", kPerm); - EXPECT_TRUE(bucket_.ok()); - BucketInfo bucket_c = bucket_.value(); + bucket = CreateBucketForTesting(storage_key_c, "bucket_c", kPerm); + EXPECT_TRUE(bucket.ok()); + BucketInfo bucket_c = bucket.value(); - GetStorageKeysForType(kTemp); - EXPECT_THAT(storage_keys_.value(), + std::set<StorageKey> storage_keys = GetStorageKeysForType(kTemp); + EXPECT_THAT(storage_keys, testing::UnorderedElementsAre(storage_key_a, storage_key_b)); - GetStorageKeysForType(kPerm); - EXPECT_THAT(storage_keys_.value(), - testing::UnorderedElementsAre(storage_key_c)); + storage_keys = GetStorageKeysForType(kPerm); + EXPECT_THAT(storage_keys, testing::UnorderedElementsAre(storage_key_c)); } TEST_F(QuotaManagerImplTest, GetStorageKeysForTypeWithDatabaseError) { + disable_database_bootstrap(true); OpenDatabase(); // Disable quota database for database error behavior. disable_quota_database(true); // Return empty set when error is encountered. - GetStorageKeysForType(kTemp); - EXPECT_TRUE(storage_keys_.value().empty()); + std::set<StorageKey> storage_keys = GetStorageKeysForType(kTemp); + EXPECT_TRUE(storage_keys.empty()); } TEST_F(QuotaManagerImplTest, GetBucketsForType) { @@ -821,30 +757,30 @@ TEST_F(QuotaManagerImplTest, GetBucketsForType) { StorageKey storage_key_b = ToStorageKey("http://b.com/"); StorageKey storage_key_c = ToStorageKey("http://c.com/"); - CreateBucketForTesting(storage_key_a, "bucket_a", kTemp); - EXPECT_TRUE(bucket_.ok()); - BucketInfo bucket_a = bucket_.value(); + auto bucket = CreateBucketForTesting(storage_key_a, "bucket_a", kTemp); + EXPECT_TRUE(bucket.ok()); + BucketInfo bucket_a = bucket.value(); - CreateBucketForTesting(storage_key_b, "bucket_b", kTemp); - EXPECT_TRUE(bucket_.ok()); - BucketInfo bucket_b = bucket_.value(); + bucket = CreateBucketForTesting(storage_key_b, "bucket_b", kTemp); + EXPECT_TRUE(bucket.ok()); + BucketInfo bucket_b = bucket.value(); - CreateBucketForTesting(storage_key_c, "bucket_c", kPerm); - EXPECT_TRUE(bucket_.ok()); - BucketInfo bucket_c = bucket_.value(); + bucket = CreateBucketForTesting(storage_key_c, kDefaultBucketName, kPerm); + EXPECT_TRUE(bucket.ok()); + BucketInfo bucket_c = bucket.value(); QuotaErrorOr<std::set<BucketLocator>> result = GetBucketsForType(kTemp); EXPECT_TRUE(result.ok()); std::set<BucketLocator> buckets = result.value(); EXPECT_EQ(2U, buckets.size()); - EXPECT_TRUE(ContainsBucket(buckets, bucket_a)); - EXPECT_TRUE(ContainsBucket(buckets, bucket_b)); + EXPECT_THAT(buckets, testing::Contains(bucket_a.ToBucketLocator())); + EXPECT_THAT(buckets, testing::Contains(bucket_b.ToBucketLocator())); result = GetBucketsForType(kPerm); buckets = result.value(); EXPECT_EQ(1U, buckets.size()); - EXPECT_TRUE(ContainsBucket(buckets, bucket_c)); + EXPECT_THAT(buckets, testing::Contains(bucket_c.ToBucketLocator())); } TEST_F(QuotaManagerImplTest, GetBucketsForHost) { @@ -852,17 +788,19 @@ TEST_F(QuotaManagerImplTest, GetBucketsForHost) { StorageKey host_a_storage_key_2 = ToStorageKey("https://a.com:123/"); StorageKey host_b_storage_key = ToStorageKey("http://b.com/"); - CreateBucketForTesting(host_a_storage_key_1, kDefaultBucketName, kTemp); - EXPECT_TRUE(bucket_.ok()); - BucketInfo host_a_bucket_1 = bucket_.value(); + auto bucket = + CreateBucketForTesting(host_a_storage_key_1, kDefaultBucketName, kTemp); + EXPECT_TRUE(bucket.ok()); + BucketInfo host_a_bucket_1 = bucket.value(); - CreateBucketForTesting(host_a_storage_key_2, "test", kTemp); - EXPECT_TRUE(bucket_.ok()); - BucketInfo host_a_bucket_2 = bucket_.value(); + bucket = CreateBucketForTesting(host_a_storage_key_2, "test", kTemp); + EXPECT_TRUE(bucket.ok()); + BucketInfo host_a_bucket_2 = bucket.value(); - CreateBucketForTesting(host_b_storage_key, kDefaultBucketName, kPerm); - EXPECT_TRUE(bucket_.ok()); - BucketInfo host_b_bucket = bucket_.value(); + bucket = + CreateBucketForTesting(host_b_storage_key, kDefaultBucketName, kPerm); + EXPECT_TRUE(bucket.ok()); + BucketInfo host_b_bucket = bucket.value(); QuotaErrorOr<std::set<BucketLocator>> result = GetBucketsForHost("a.com", kTemp); @@ -870,13 +808,13 @@ TEST_F(QuotaManagerImplTest, GetBucketsForHost) { std::set<BucketLocator> buckets = result.value(); EXPECT_EQ(2U, buckets.size()); - EXPECT_TRUE(ContainsBucket(buckets, host_a_bucket_1)); - EXPECT_TRUE(ContainsBucket(buckets, host_a_bucket_2)); + EXPECT_THAT(buckets, testing::Contains(host_a_bucket_1.ToBucketLocator())); + EXPECT_THAT(buckets, testing::Contains(host_a_bucket_2.ToBucketLocator())); result = GetBucketsForHost("b.com", kPerm); buckets = result.value(); EXPECT_EQ(1U, buckets.size()); - EXPECT_TRUE(ContainsBucket(buckets, host_b_bucket)); + EXPECT_THAT(buckets, testing::Contains(host_b_bucket.ToBucketLocator())); } TEST_F(QuotaManagerImplTest, GetBucketsForStorageKey) { @@ -884,21 +822,21 @@ TEST_F(QuotaManagerImplTest, GetBucketsForStorageKey) { StorageKey storage_key_b = ToStorageKey("http://b.com/"); StorageKey storage_key_c = ToStorageKey("http://c.com/"); - CreateBucketForTesting(storage_key_a, "bucket_a1", kTemp); - EXPECT_TRUE(bucket_.ok()); - BucketInfo bucket_a1 = bucket_.value(); + auto bucket = CreateBucketForTesting(storage_key_a, "bucket_a1", kTemp); + EXPECT_TRUE(bucket.ok()); + BucketInfo bucket_a1 = bucket.value(); - CreateBucketForTesting(storage_key_a, "bucket_a2", kTemp); - EXPECT_TRUE(bucket_.ok()); - BucketInfo bucket_a2 = bucket_.value(); + bucket = CreateBucketForTesting(storage_key_a, "bucket_a2", kTemp); + EXPECT_TRUE(bucket.ok()); + BucketInfo bucket_a2 = bucket.value(); - CreateBucketForTesting(storage_key_b, "bucket_b", kTemp); - EXPECT_TRUE(bucket_.ok()); - BucketInfo bucket_b = bucket_.value(); + bucket = CreateBucketForTesting(storage_key_b, "bucket_b", kTemp); + EXPECT_TRUE(bucket.ok()); + BucketInfo bucket_b = bucket.value(); - CreateBucketForTesting(storage_key_c, "bucket_c", kPerm); - EXPECT_TRUE(bucket_.ok()); - BucketInfo bucket_c = bucket_.value(); + bucket = CreateBucketForTesting(storage_key_c, kDefaultBucketName, kPerm); + EXPECT_TRUE(bucket.ok()); + BucketInfo bucket_c = bucket.value(); QuotaErrorOr<std::set<BucketLocator>> result = GetBucketsForStorageKey(storage_key_a, kTemp); @@ -906,8 +844,8 @@ TEST_F(QuotaManagerImplTest, GetBucketsForStorageKey) { std::set<BucketLocator> buckets = result.value(); EXPECT_EQ(2U, buckets.size()); - EXPECT_TRUE(ContainsBucket(buckets, bucket_a1)); - EXPECT_TRUE(ContainsBucket(buckets, bucket_a2)); + EXPECT_THAT(buckets, testing::Contains(bucket_a1.ToBucketLocator())); + EXPECT_THAT(buckets, testing::Contains(bucket_a2.ToBucketLocator())); result = GetBucketsForStorageKey(storage_key_a, kPerm); EXPECT_TRUE(result.ok()); @@ -918,43 +856,45 @@ TEST_F(QuotaManagerImplTest, GetBucketsForStorageKey) { buckets = result.value(); EXPECT_EQ(1U, buckets.size()); - EXPECT_TRUE(ContainsBucket(buckets, bucket_c)); + EXPECT_THAT(buckets, testing::Contains(bucket_c.ToBucketLocator())); } TEST_F(QuotaManagerImplTest, GetUsageAndQuota_Simple) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 10}, - {"http://foo.com/", kPerm, 80}, + static const ClientBucketData kData[] = { + {"http://foo.com/", "logs", kTemp, 10}, + {"http://foo.com/", kDefaultBucketName, kPerm, 80}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(80, usage()); - EXPECT_EQ(0, quota()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 80); + EXPECT_EQ(result.quota, 0); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10, usage()); - EXPECT_LE(0, quota()); - int64_t quota_returned_for_foo = quota(); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10); + EXPECT_GT(result.quota, 0); + int64_t quota_returned_for_foo = result.quota; - GetUsageAndQuotaForWebApps(ToStorageKey("http://bar.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); - EXPECT_EQ(quota_returned_for_foo, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://bar.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); + EXPECT_EQ(result.quota, quota_returned_for_foo); } TEST_F(QuotaManagerImplTest, GetUsage_NoClient) { - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(0, usage()); @@ -962,28 +902,26 @@ TEST_F(QuotaManagerImplTest, GetUsage_NoClient) { GetHostUsageWithBreakdown("foo.com", kPerm); EXPECT_EQ(0, usage()); - GetGlobalUsage(kTemp); - EXPECT_EQ(0, usage()); - EXPECT_EQ(0, unlimited_usage()); + auto global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, 0); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); - GetGlobalUsage(kPerm); - EXPECT_EQ(0, usage()); - EXPECT_EQ(0, unlimited_usage()); + global_usage_result = GetGlobalUsage(kPerm); + EXPECT_EQ(global_usage_result.usage, 0); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); } TEST_F(QuotaManagerImplTest, GetUsage_EmptyClient) { - CreateAndRegisterClient(base::span<MockStorageKeyData>(), - QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(0, usage()); @@ -991,121 +929,127 @@ TEST_F(QuotaManagerImplTest, GetUsage_EmptyClient) { GetHostUsageWithBreakdown("foo.com", kPerm); EXPECT_EQ(0, usage()); - GetGlobalUsage(kTemp); - EXPECT_EQ(0, usage()); - EXPECT_EQ(0, unlimited_usage()); + auto global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, 0); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); - GetGlobalUsage(kPerm); - EXPECT_EQ(0, usage()); - EXPECT_EQ(0, unlimited_usage()); + global_usage_result = GetGlobalUsage(kPerm); + EXPECT_EQ(global_usage_result.usage, 0); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); } TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_MultiStorageKeys) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 10}, {"http://foo.com:8080/", kTemp, 20}, - {"http://bar.com/", kTemp, 5}, {"https://bar.com/", kTemp, 7}, - {"http://baz.com/", kTemp, 30}, {"http://foo.com/", kPerm, 40}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 10}, + {"http://foo.com:8080/", kDefaultBucketName, kTemp, 20}, + {"http://bar.com/", "logs", kTemp, 5}, + {"https://bar.com/", "notes", kTemp, 7}, + {"http://baz.com/", "songs", kTemp, 30}, + {"http://foo.com/", kDefaultBucketName, kPerm, 40}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); // This time explicitly sets a temporary global quota. const int kPoolSize = 100; const int kPerHostQuota = 20; SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10 + 20, usage()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10 + 20); // The host's quota should be its full portion of the global quota // since there's plenty of diskspace. - EXPECT_EQ(kPerHostQuota, quota()); + EXPECT_EQ(result.quota, kPerHostQuota); - GetUsageAndQuotaForWebApps(ToStorageKey("http://bar.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(5 + 7, usage()); - EXPECT_EQ(kPerHostQuota, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://bar.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 5 + 7); + EXPECT_EQ(result.quota, kPerHostQuota); } TEST_F(QuotaManagerImplTest, GetUsage_MultipleClients) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kTemp, 1}, - {"http://bar.com/", kTemp, 2}, - {"http://bar.com/", kPerm, 4}, - {"http://unlimited/", kPerm, 8}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://bar.com/", kDefaultBucketName, kTemp, 2}, + {"http://bar.com/", kDefaultBucketName, kPerm, 4}, + {"http://unlimited/", kDefaultBucketName, kPerm, 8}, }; - static const MockStorageKeyData kData2[] = { - {"https://foo.com/", kTemp, 128}, - {"http://example.com/", kPerm, 256}, - {"http://unlimited/", kTemp, 512}, + static const ClientBucketData kData2[] = { + {"https://foo.com/", kDefaultBucketName, kTemp, 128}, + {"http://example.com/", kDefaultBucketName, kPerm, 256}, + {"http://unlimited/", "logs", kTemp, 512}, }; mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/")); - GetStorageCapacity(); - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + auto storage_capacity = GetStorageCapacity(); + + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + MockQuotaClient* database_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(database_client, kData2); const int64_t kPoolSize = GetAvailableDiskSpaceForTest(); const int64_t kPerHostQuota = kPoolSize / 5; SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(1 + 128, usage()); - EXPECT_EQ(kPerHostQuota, quota()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 1 + 128); + EXPECT_EQ(result.quota, kPerHostQuota); - GetUsageAndQuotaForWebApps(ToStorageKey("http://bar.com/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(4, usage()); - EXPECT_EQ(0, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://bar.com/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 4); + EXPECT_EQ(result.quota, 0); - GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(512, usage()); - EXPECT_EQ(available_space() + usage(), quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 512); + EXPECT_EQ(result.quota, storage_capacity.available_space + result.usage); - GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(8, usage()); - EXPECT_EQ(available_space() + usage(), quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 8); + EXPECT_EQ(result.quota, storage_capacity.available_space + result.usage); - GetGlobalUsage(kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(1 + 2 + 128 + 512, usage()); - EXPECT_EQ(512, unlimited_usage()); + auto global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, 1 + 2 + 128 + 512); + EXPECT_EQ(global_usage_result.unlimited_usage, 512); - GetGlobalUsage(kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(4 + 8 + 256, usage()); - EXPECT_EQ(8, unlimited_usage()); + global_usage_result = GetGlobalUsage(kPerm); + EXPECT_EQ(global_usage_result.usage, 4 + 8 + 256); + EXPECT_EQ(global_usage_result.unlimited_usage, 8); } TEST_F(QuotaManagerImplTest, GetUsageWithBreakdown_Simple) { - blink::mojom::UsageBreakdown usage_breakdown_expected = - blink::mojom::UsageBreakdown(); - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kTemp, 1}, - {"http://foo.com/", kPerm, 80}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://foo.com/", kDefaultBucketName, kPerm, 80}, }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com/", kTemp, 4}, + static const ClientBucketData kData2[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 4}, }; - static const MockStorageKeyData kData3[] = { - {"http://foo.com/", kTemp, 8}, + static const ClientBucketData kData3[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 8}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData3, QuotaClientType::kServiceWorkerCache, - {blink::mojom::StorageType::kTemporary}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + MockQuotaClient* db_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp}); + MockQuotaClient* sw_client = + CreateAndRegisterClient(QuotaClientType::kServiceWorkerCache, {kTemp}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(db_client, kData2); + RegisterClientBucketData(sw_client, kData3); + blink::mojom::UsageBreakdown usage_breakdown_expected = + blink::mojom::UsageBreakdown(); GetUsageAndQuotaWithBreakdown(ToStorageKey("http://foo.com/"), kPerm); EXPECT_EQ(QuotaStatusCode::kOk, status()); EXPECT_EQ(80, usage()); @@ -1155,17 +1099,20 @@ TEST_F(QuotaManagerImplTest, GetUsageWithBreakdown_NoClient) { } TEST_F(QuotaManagerImplTest, GetUsageWithBreakdown_MultiStorageKeys) { - blink::mojom::UsageBreakdown usage_breakdown_expected = - blink::mojom::UsageBreakdown(); - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 10}, {"http://foo.com:8080/", kTemp, 20}, - {"http://bar.com/", kTemp, 5}, {"https://bar.com/", kTemp, 7}, - {"http://baz.com/", kTemp, 30}, {"http://foo.com/", kPerm, 40}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 10}, + {"http://foo.com:8080/", "logs", kTemp, 20}, + {"http://bar.com/", kDefaultBucketName, kTemp, 5}, + {"https://bar.com/", kDefaultBucketName, kTemp, 7}, + {"http://baz.com/", "logs", kTemp, 30}, + {"http://foo.com/", kDefaultBucketName, kPerm, 40}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); + blink::mojom::UsageBreakdown usage_breakdown_expected = + blink::mojom::UsageBreakdown(); GetUsageAndQuotaWithBreakdown(ToStorageKey("http://foo.com/"), kTemp); EXPECT_EQ(QuotaStatusCode::kOk, status()); EXPECT_EQ(10 + 20, usage()); @@ -1180,27 +1127,27 @@ TEST_F(QuotaManagerImplTest, GetUsageWithBreakdown_MultiStorageKeys) { } TEST_F(QuotaManagerImplTest, GetUsageWithBreakdown_MultipleClients) { - blink::mojom::UsageBreakdown usage_breakdown_expected = - blink::mojom::UsageBreakdown(); - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kTemp, 1}, - {"http://bar.com/", kTemp, 2}, - {"http://bar.com/", kPerm, 4}, - {"http://unlimited/", kPerm, 8}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://bar.com/", kDefaultBucketName, kTemp, 2}, + {"http://bar.com/", kDefaultBucketName, kPerm, 4}, + {"http://unlimited/", kDefaultBucketName, kPerm, 8}, }; - static const MockStorageKeyData kData2[] = { - {"https://foo.com/", kTemp, 128}, - {"http://example.com/", kPerm, 256}, - {"http://unlimited/", kTemp, 512}, + static const ClientBucketData kData2[] = { + {"https://foo.com/", kDefaultBucketName, kTemp, 128}, + {"http://example.com/", kDefaultBucketName, kPerm, 256}, + {"http://unlimited/", "logs", kTemp, 512}, }; mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/")); - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + MockQuotaClient* db_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(db_client, kData2); + blink::mojom::UsageBreakdown usage_breakdown_expected = + blink::mojom::UsageBreakdown(); GetUsageAndQuotaWithBreakdown(ToStorageKey("http://foo.com/"), kTemp); EXPECT_EQ(QuotaStatusCode::kOk, status()); EXPECT_EQ(1 + 128, usage()); @@ -1231,35 +1178,46 @@ TEST_F(QuotaManagerImplTest, GetUsageWithBreakdown_MultipleClients) { } void QuotaManagerImplTest::GetUsage_WithModifyTestBody(const StorageType type) { - const MockStorageKeyData data[] = { - {"http://foo.com/", type, 10}, - {"http://foo.com:1/", type, 20}, + const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, type, 10}, + {"http://bar.com/", kDefaultBucketName, type, 0}, + {"http://foo.com:1/", kDefaultBucketName, type, 20}, + {"https://foo.com/", kDefaultBucketName, type, 0}, }; MockQuotaClient* client = - CreateAndRegisterClient(data, QuotaClientType::kFileSystem, {type}); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {type}); + RegisterClientBucketData(client, kData); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), type); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10 + 20, usage()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), type); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10 + 20); client->ModifyStorageKeyAndNotify(ToStorageKey("http://foo.com/"), type, 30); client->ModifyStorageKeyAndNotify(ToStorageKey("http://foo.com:1/"), type, -5); - client->AddStorageKeyAndNotify(ToStorageKey("https://foo.com/"), type, 1); + client->ModifyStorageKeyAndNotify(ToStorageKey("https://foo.com/"), type, 1); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), type); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10 + 20 + 30 - 5 + 1, usage()); - int foo_usage = usage(); + // Database call to ensure modification calls have completed. + GetBucket(ToStorageKey("http://foo.com"), kDefaultBucketName, kTemp); - client->AddStorageKeyAndNotify(ToStorageKey("http://bar.com/"), type, 40); - GetUsageAndQuotaForWebApps(ToStorageKey("http://bar.com/"), type); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(40, usage()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), type); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10 + 20 + 30 - 5 + 1); + int foo_usage = result.usage; - GetGlobalUsage(type); - EXPECT_EQ(foo_usage + 40, usage()); - EXPECT_EQ(0, unlimited_usage()); + client->ModifyStorageKeyAndNotify(ToStorageKey("http://bar.com/"), type, 40); + + // Database call to ensure modification calls have completed. + GetBucket(ToStorageKey("http://foo.com"), kDefaultBucketName, kTemp); + + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://bar.com/"), type); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 40); + + auto global_usage_result = GetGlobalUsage(type); + EXPECT_EQ(global_usage_result.usage, foo_usage + 40); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); } TEST_F(QuotaManagerImplTest, GetTemporaryUsage_WithModify) { @@ -1267,15 +1225,15 @@ TEST_F(QuotaManagerImplTest, GetTemporaryUsage_WithModify) { } TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_WithAdditionalTasks) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 10}, - {"http://foo.com:8080/", kTemp, 20}, - {"http://bar.com/", kTemp, 13}, - {"http://foo.com/", kPerm, 40}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 10}, + {"http://foo.com:8080/", kDefaultBucketName, kTemp, 20}, + {"http://bar.com/", "logs", kTemp, 13}, + {"http://foo.com/", "inbox", kPerm, 40}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); const int kPoolSize = 100; const int kPerHostQuota = 20; @@ -1283,32 +1241,34 @@ TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_WithAdditionalTasks) { GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10 + 20, usage()); - EXPECT_EQ(kPerHostQuota, quota()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10 + 20); + EXPECT_EQ(result.quota, kPerHostQuota); set_additional_callback_count(0); RunAdditionalUsageAndQuotaTask(ToStorageKey("http://foo.com/"), kTemp); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); RunAdditionalUsageAndQuotaTask(ToStorageKey("http://bar.com/"), kTemp); task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10 + 20, usage()); - EXPECT_EQ(kPerHostQuota, quota()); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10 + 20); + EXPECT_EQ(result.quota, kPerHostQuota); EXPECT_EQ(2, additional_callback_count()); } TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_NukeManager) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 10}, - {"http://foo.com:8080/", kTemp, 20}, - {"http://bar.com/", kTemp, 13}, - {"http://foo.com/", kPerm, 40}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 10}, + {"http://foo.com:8080/", kDefaultBucketName, kTemp, 20}, + {"http://bar.com/", kDefaultBucketName, kTemp, 13}, + {"http://foo.com/", kDefaultBucketName, kPerm, 40}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); + const int kPoolSize = 100; const int kPerHostQuota = 20; SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem); @@ -1319,23 +1279,30 @@ TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_NukeManager) { RunAdditionalUsageAndQuotaTask(ToStorageKey("http://foo.com/"), kTemp); RunAdditionalUsageAndQuotaTask(ToStorageKey("http://bar.com/"), kTemp); - DeleteHostData("foo.com", kTemp, AllQuotaClientTypes()); - DeleteHostData("bar.com", kTemp, AllQuotaClientTypes()); + base::test::TestFuture<QuotaStatusCode> future_foo; + base::test::TestFuture<QuotaStatusCode> future_bar; + quota_manager_impl()->DeleteHostData("foo.com", kTemp, + future_foo.GetCallback()); + quota_manager_impl()->DeleteHostData("bar.com", kTemp, + future_bar.GetCallback()); // Nuke before waiting for callbacks. set_quota_manager_impl(nullptr); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kErrorAbort, status()); + + EXPECT_EQ(QuotaStatusCode::kErrorAbort, future_foo.Get()); + EXPECT_EQ(QuotaStatusCode::kErrorAbort, future_bar.Get()); } TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_Overbudget) { - static const MockStorageKeyData kData[] = { - {"http://usage1/", kTemp, 1}, - {"http://usage10/", kTemp, 10}, - {"http://usage200/", kTemp, 200}, + static const ClientBucketData kData[] = { + {"http://usage1/", kDefaultBucketName, kTemp, 1}, + {"http://usage10/", kDefaultBucketName, kTemp, 10}, + {"http://usage200/", kDefaultBucketName, kTemp, 200}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp}); + RegisterClientBucketData(fs_client, kData); + const int kPoolSize = 100; const int kPerHostQuota = 20; SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem); @@ -1343,187 +1310,187 @@ TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_Overbudget) { // Provided diskspace is not tight, global usage does not affect the // quota calculations for an individual storage key, so despite global usage // in excess of our poolsize, we still get the nominal quota value. - GetStorageCapacity(); - task_environment_.RunUntilIdle(); - EXPECT_LE(kMustRemainAvailableForSystem, available_space()); + auto storage_capacity = GetStorageCapacity(); + EXPECT_LE(kMustRemainAvailableForSystem, storage_capacity.available_space); - GetUsageAndQuotaForWebApps(ToStorageKey("http://usage1/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(1, usage()); - EXPECT_EQ(kPerHostQuota, quota()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://usage1/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 1); + EXPECT_EQ(result.quota, kPerHostQuota); - GetUsageAndQuotaForWebApps(ToStorageKey("http://usage10/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10, usage()); - EXPECT_EQ(kPerHostQuota, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://usage10/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10); + EXPECT_EQ(result.quota, kPerHostQuota); - GetUsageAndQuotaForWebApps(ToStorageKey("http://usage200/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(200, usage()); - EXPECT_EQ(kPerHostQuota, quota()); // should be clamped to the nominal quota + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://usage200/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 200); + // Should be clamped to the nominal quota. + EXPECT_EQ(result.quota, kPerHostQuota); } TEST_F(QuotaManagerImplTest, GetTemporaryUsageAndQuota_Unlimited) { - static const MockStorageKeyData kData[] = { - {"http://usage10/", kTemp, 10}, - {"http://usage50/", kTemp, 50}, - {"http://unlimited/", kTemp, 4000}, + static const ClientBucketData kData[] = { + {"http://usage10/", kDefaultBucketName, kTemp, 10}, + {"http://usage50/", kDefaultBucketName, kTemp, 50}, + {"http://unlimited/", "inbox", kTemp, 4000}, }; mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/")); - GetStorageCapacity(); - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); + auto storage_capacity = GetStorageCapacity(); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp}); + RegisterClientBucketData(fs_client, kData); // Test when not overbugdet. const int kPerHostQuotaFor1000 = 200; SetQuotaSettings(1000, kPerHostQuotaFor1000, kMustRemainAvailableForSystem); - GetGlobalUsage(kTemp); - EXPECT_EQ(10 + 50 + 4000, usage()); - EXPECT_EQ(4000, unlimited_usage()); - - GetUsageAndQuotaForWebApps(ToStorageKey("http://usage10/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10, usage()); - EXPECT_EQ(kPerHostQuotaFor1000, quota()); - - GetUsageAndQuotaForWebApps(ToStorageKey("http://usage50/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(50, usage()); - EXPECT_EQ(kPerHostQuotaFor1000, quota()); - - GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(4000, usage()); - EXPECT_EQ(available_space() + usage(), quota()); - - GetUsageAndQuotaForStorageClient(ToStorageKey("http://unlimited/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); - EXPECT_EQ(QuotaManagerImpl::kNoLimit, quota()); - - // Test when overbugdet. + auto global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, 10 + 50 + 4000); + EXPECT_EQ(global_usage_result.unlimited_usage, 4000); + + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://usage10/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10); + EXPECT_EQ(result.quota, kPerHostQuotaFor1000); + + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://usage50/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 50); + EXPECT_EQ(result.quota, kPerHostQuotaFor1000); + + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 4000); + EXPECT_EQ(result.quota, storage_capacity.available_space + result.usage); + + result = GetUsageAndQuotaForStorageClient(ToStorageKey("http://unlimited/"), + kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); + EXPECT_EQ(result.quota, QuotaManagerImpl::kNoLimit); + + // Test when overbudgeted. const int kPerHostQuotaFor100 = 20; SetQuotaSettings(100, kPerHostQuotaFor100, kMustRemainAvailableForSystem); - GetUsageAndQuotaForWebApps(ToStorageKey("http://usage10/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10, usage()); - EXPECT_EQ(kPerHostQuotaFor100, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://usage10/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10); + EXPECT_EQ(result.quota, kPerHostQuotaFor100); - GetUsageAndQuotaForWebApps(ToStorageKey("http://usage50/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(50, usage()); - EXPECT_EQ(kPerHostQuotaFor100, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://usage50/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 50); + EXPECT_EQ(result.quota, kPerHostQuotaFor100); - GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(4000, usage()); - EXPECT_EQ(available_space() + usage(), quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 4000); + EXPECT_EQ(result.quota, storage_capacity.available_space + result.usage); - GetUsageAndQuotaForStorageClient(ToStorageKey("http://unlimited/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); - EXPECT_EQ(QuotaManagerImpl::kNoLimit, quota()); + result = GetUsageAndQuotaForStorageClient(ToStorageKey("http://unlimited/"), + kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); + EXPECT_EQ(result.quota, QuotaManagerImpl::kNoLimit); // Revoke the unlimited rights and make sure the change is noticed. mock_special_storage_policy()->Reset(); mock_special_storage_policy()->NotifyCleared(); - GetGlobalUsage(kTemp); - EXPECT_EQ(10 + 50 + 4000, usage()); - EXPECT_EQ(0, unlimited_usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, 10 + 50 + 4000); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); - GetUsageAndQuotaForWebApps(ToStorageKey("http://usage10/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10, usage()); - EXPECT_EQ(kPerHostQuotaFor100, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://usage10/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10); + EXPECT_EQ(result.quota, kPerHostQuotaFor100); - GetUsageAndQuotaForWebApps(ToStorageKey("http://usage50/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(50, usage()); - EXPECT_EQ(kPerHostQuotaFor100, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://usage50/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 50); + EXPECT_EQ(result.quota, kPerHostQuotaFor100); - GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(4000, usage()); - EXPECT_EQ(kPerHostQuotaFor100, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 4000); + EXPECT_EQ(result.quota, kPerHostQuotaFor100); - GetUsageAndQuotaForStorageClient(ToStorageKey("http://unlimited/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(4000, usage()); - EXPECT_EQ(kPerHostQuotaFor100, quota()); + result = GetUsageAndQuotaForStorageClient(ToStorageKey("http://unlimited/"), + kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 4000); + EXPECT_EQ(result.quota, kPerHostQuotaFor100); } TEST_F(QuotaManagerImplTest, GetAndSetPerststentHostQuota) { - CreateAndRegisterClient(base::span<MockStorageKeyData>(), - QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - - GetPersistentHostQuota("foo.com"); - task_environment_.RunUntilIdle(); - EXPECT_EQ(0, quota()); - - SetPersistentHostQuota("foo.com", 100); - task_environment_.RunUntilIdle(); - EXPECT_EQ(100, quota()); - - GetPersistentHostQuota("foo.com"); - SetPersistentHostQuota("foo.com", 200); - GetPersistentHostQuota("foo.com"); - SetPersistentHostQuota("foo.com", - QuotaManagerImpl::kPerHostPersistentQuotaLimit); - GetPersistentHostQuota("foo.com"); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaManagerImpl::kPerHostPersistentQuotaLimit, quota()); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + + EXPECT_EQ(GetPersistentHostQuota("foo.com"), 0); + EXPECT_EQ(SetPersistentHostQuota("foo.com", 100), 100); + + // Should still succeed after multiple calls at once. + base::test::TestFuture<QuotaStatusCode, int64_t> future1; + base::test::TestFuture<QuotaStatusCode, int64_t> future2; + base::test::TestFuture<QuotaStatusCode, int64_t> future3; + quota_manager_impl()->SetPersistentHostQuota("foo.com", 200, + future1.GetCallback()); + quota_manager_impl()->SetPersistentHostQuota("foo.com", 300, + future2.GetCallback()); + quota_manager_impl()->SetPersistentHostQuota( + "foo.com", QuotaManagerImpl::kPerHostPersistentQuotaLimit, + future3.GetCallback()); + EXPECT_EQ(GetPersistentHostQuota("foo.com"), + QuotaManagerImpl::kPerHostPersistentQuotaLimit); // Persistent quota should be capped at the per-host quota limit. SetPersistentHostQuota("foo.com", QuotaManagerImpl::kPerHostPersistentQuotaLimit + 100); - GetPersistentHostQuota("foo.com"); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaManagerImpl::kPerHostPersistentQuotaLimit, quota()); + EXPECT_EQ(GetPersistentHostQuota("foo.com"), + QuotaManagerImpl::kPerHostPersistentQuotaLimit); } TEST_F(QuotaManagerImplTest, GetAndSetPersistentUsageAndQuota) { - GetStorageCapacity(); - CreateAndRegisterClient(base::span<MockStorageKeyData>(), - QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + auto storage_capacity = GetStorageCapacity(); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); - EXPECT_EQ(0, quota()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); + EXPECT_EQ(result.quota, 0); SetPersistentHostQuota("foo.com", 100); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); - EXPECT_EQ(100, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); + EXPECT_EQ(result.quota, 100); // The actual space available is given to 'unlimited' storage keys as their // quota. mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/")); - GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kPerm); - EXPECT_EQ(available_space() + usage(), quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kPerm); + EXPECT_EQ(result.quota, storage_capacity.available_space + result.usage); - // GetUsageAndQuotaForStorageClient should just return 0 usage and - // kNoLimit quota. - GetUsageAndQuotaForStorageClient(ToStorageKey("http://unlimited/"), kPerm); - EXPECT_EQ(0, usage()); - EXPECT_EQ(QuotaManagerImpl::kNoLimit, quota()); + result = GetUsageAndQuotaForStorageClient(ToStorageKey("http://unlimited/"), + kPerm); + EXPECT_EQ(result.usage, 0); + EXPECT_EQ(result.quota, QuotaManagerImpl::kNoLimit); } TEST_F(QuotaManagerImplTest, GetQuotaLowAvailableDiskSpace) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 100000}, - {"http://unlimited/", kTemp, 4000000}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 100000}, + {"http://unlimited/", kDefaultBucketName, kTemp, 4000000}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp}); + RegisterClientBucketData(fs_client, kData); const int kPoolSize = 10000000; const int kPerHostQuota = kPoolSize / 5; @@ -1536,17 +1503,15 @@ TEST_F(QuotaManagerImplTest, GetQuotaLowAvailableDiskSpace) { static_cast<int>(GetAvailableDiskSpaceForTest() - 65536); SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailable); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(100000, usage()); - EXPECT_EQ(kPerHostQuota, quota()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 100000); + EXPECT_EQ(result.quota, kPerHostQuota); } TEST_F(QuotaManagerImplTest, GetSyncableQuota) { - CreateAndRegisterClient(base::span<MockStorageKeyData>(), - QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kSyncable}); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kSync}); // Pre-condition check: available disk space (for testing) is less than // the default quota for syncable storage. @@ -1560,28 +1525,34 @@ TEST_F(QuotaManagerImplTest, GetSyncableQuota) { // storage, shall have their quota calculation take into account the amount of // available disk space. mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/")); - GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kSync); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); - EXPECT_EQ(QuotaManagerImpl::kSyncableStorageDefaultHostQuota, quota()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://unlimited/"), kSync); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); + EXPECT_EQ(result.quota, QuotaManagerImpl::kSyncableStorageDefaultHostQuota); } TEST_F(QuotaManagerImplTest, GetPersistentUsageAndQuota_MultiStorageKeys) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kPerm, 10}, {"http://foo.com:8080/", kPerm, 20}, - {"https://foo.com/", kPerm, 13}, {"https://foo.com:8081/", kPerm, 19}, - {"http://bar.com/", kPerm, 5}, {"https://bar.com/", kPerm, 7}, - {"http://baz.com/", kPerm, 30}, {"http://foo.com/", kTemp, 40}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kPerm, 10}, + {"http://foo.com:8080/", kDefaultBucketName, kPerm, 20}, + {"https://foo.com/", kDefaultBucketName, kPerm, 13}, + {"https://foo.com:8081/", kDefaultBucketName, kPerm, 19}, + {"http://bar.com/", kDefaultBucketName, kPerm, 5}, + {"https://bar.com/", kDefaultBucketName, kPerm, 7}, + {"http://baz.com/", kDefaultBucketName, kPerm, 30}, + {"http://foo.com/", kDefaultBucketName, kTemp, 40}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); SetPersistentHostQuota("foo.com", 100); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10 + 20 + 13 + 19, usage()); - EXPECT_EQ(100, quota()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10 + 20 + 13 + 19); + EXPECT_EQ(result.quota, 100); } TEST_F(QuotaManagerImplTest, GetPersistentUsage_WithModify) { @@ -1589,44 +1560,45 @@ TEST_F(QuotaManagerImplTest, GetPersistentUsage_WithModify) { } TEST_F(QuotaManagerImplTest, GetPersistentUsageAndQuota_WithAdditionalTasks) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kPerm, 10}, - {"http://foo.com:8080/", kPerm, 20}, - {"http://bar.com/", kPerm, 13}, - {"http://foo.com/", kTemp, 40}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kPerm, 10}, + {"http://foo.com:8080/", kDefaultBucketName, kPerm, 20}, + {"http://bar.com/", kDefaultBucketName, kPerm, 13}, + {"http://foo.com/", kDefaultBucketName, kTemp, 40}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); SetPersistentHostQuota("foo.com", 100); GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10 + 20, usage()); - EXPECT_EQ(100, quota()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10 + 20); + EXPECT_EQ(result.quota, 100); set_additional_callback_count(0); RunAdditionalUsageAndQuotaTask(ToStorageKey("http://foo.com/"), kPerm); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); RunAdditionalUsageAndQuotaTask(ToStorageKey("http://bar.com/"), kPerm); task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10 + 20, usage()); - EXPECT_EQ(2, additional_callback_count()); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10 + 20); + EXPECT_EQ(additional_callback_count(), 2); } TEST_F(QuotaManagerImplTest, GetPersistentUsageAndQuota_NukeManager) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kPerm, 10}, - {"http://foo.com:8080/", kPerm, 20}, - {"http://bar.com/", kPerm, 13}, - {"http://foo.com/", kTemp, 40}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kPerm, 10}, + {"http://foo.com:8080/", kDefaultBucketName, kPerm, 20}, + {"http://bar.com/", kDefaultBucketName, kPerm, 13}, + {"http://foo.com/", kDefaultBucketName, kTemp, 40}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); SetPersistentHostQuota("foo.com", 100); set_additional_callback_count(0); @@ -1649,23 +1621,26 @@ TEST_F(QuotaManagerImplTest, GetPersistentUsageAndQuota_NukeManager) { } TEST_F(QuotaManagerImplTest, GetUsage_Simple) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kPerm, 1}, {"http://foo.com:1/", kPerm, 20}, - {"http://bar.com/", kTemp, 300}, {"https://buz.com/", kTemp, 4000}, - {"http://buz.com/", kTemp, 50000}, {"http://bar.com:1/", kPerm, 600000}, - {"http://foo.com/", kTemp, 7000000}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kPerm, 1}, + {"http://foo.com:1/", kDefaultBucketName, kPerm, 20}, + {"http://bar.com/", kDefaultBucketName, kTemp, 300}, + {"https://buz.com/", kDefaultBucketName, kTemp, 4000}, + {"http://buz.com/", kDefaultBucketName, kTemp, 50000}, + {"http://bar.com:1/", kDefaultBucketName, kPerm, 600000}, + {"http://foo.com/", kDefaultBucketName, kTemp, 7000000}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); - GetGlobalUsage(kPerm); - EXPECT_EQ(usage(), 1 + 20 + 600000); - EXPECT_EQ(0, unlimited_usage()); + auto global_usage_result = GetGlobalUsage(kPerm); + EXPECT_EQ(global_usage_result.usage, 1 + 20 + 600000); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); - GetGlobalUsage(kTemp); - EXPECT_EQ(usage(), 300 + 4000 + 50000 + 7000000); - EXPECT_EQ(0, unlimited_usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(300 + 4000 + 50000 + 7000000, global_usage_result.usage); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); GetHostUsageWithBreakdown("foo.com", kPerm); EXPECT_EQ(usage(), 1 + 20); @@ -1675,38 +1650,40 @@ TEST_F(QuotaManagerImplTest, GetUsage_Simple) { } TEST_F(QuotaManagerImplTest, GetUsage_WithModification) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kPerm, 1}, {"http://foo.com:1/", kPerm, 20}, - {"http://bar.com/", kTemp, 300}, {"https://buz.com/", kTemp, 4000}, - {"http://buz.com/", kTemp, 50000}, {"http://bar.com:1/", kPerm, 600000}, - {"http://foo.com/", kTemp, 7000000}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kPerm, 1}, + {"http://foo.com:1/", kDefaultBucketName, kPerm, 20}, + {"http://bar.com/", kDefaultBucketName, kTemp, 300}, + {"https://buz.com/", kDefaultBucketName, kTemp, 4000}, + {"http://buz.com/", kDefaultBucketName, kTemp, 50000}, + {"http://bar.com:1/", kDefaultBucketName, kPerm, 600000}, + {"http://foo.com/", kDefaultBucketName, kTemp, 7000000}, }; MockQuotaClient* client = - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(client, kData); - GetGlobalUsage(kPerm); - EXPECT_EQ(usage(), 1 + 20 + 600000); - EXPECT_EQ(0, unlimited_usage()); + auto global_usage_result = GetGlobalUsage(kPerm); + EXPECT_EQ(global_usage_result.usage, 1 + 20 + 600000); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); client->ModifyStorageKeyAndNotify(ToStorageKey("http://foo.com/"), kPerm, 80000000); - GetGlobalUsage(kPerm); - EXPECT_EQ(usage(), 1 + 20 + 600000 + 80000000); - EXPECT_EQ(0, unlimited_usage()); + global_usage_result = GetGlobalUsage(kPerm); + EXPECT_EQ(global_usage_result.usage, 1 + 20 + 600000 + 80000000); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); - GetGlobalUsage(kTemp); - EXPECT_EQ(usage(), 300 + 4000 + 50000 + 7000000); - EXPECT_EQ(0, unlimited_usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, 300 + 4000 + 50000 + 7000000); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); client->ModifyStorageKeyAndNotify(ToStorageKey("http://foo.com/"), kTemp, 1); - GetGlobalUsage(kTemp); - EXPECT_EQ(usage(), 300 + 4000 + 50000 + 7000000 + 1); - EXPECT_EQ(0, unlimited_usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, 300 + 4000 + 50000 + 7000000 + 1); + EXPECT_EQ(global_usage_result.unlimited_usage, 0); GetHostUsageWithBreakdown("buz.com", kTemp); EXPECT_EQ(usage(), 4000 + 50000); @@ -1718,20 +1695,19 @@ TEST_F(QuotaManagerImplTest, GetUsage_WithModification) { EXPECT_EQ(usage(), 4000 + 50000 + 900000000); } -TEST_F(QuotaManagerImplTest, GetUsage_WithDeleteStorageKey) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 1}, - {"http://foo.com:1/", kTemp, 20}, - {"http://foo.com/", kPerm, 300}, - {"http://bar.com/", kTemp, 4000}, +TEST_F(QuotaManagerImplTest, GetUsage_WithDeleteBucket) { + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://foo.com:1/", kDefaultBucketName, kTemp, 20}, + {"http://foo.com/", kDefaultBucketName, kPerm, 300}, + {"http://bar.com/", kDefaultBucketName, kTemp, 4000}, }; MockQuotaClient* client = - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(client, kData); - GetGlobalUsage(kTemp); - int64_t predelete_global_tmp = usage(); + auto global_usage_result = GetGlobalUsage(kTemp); + int64_t predelete_global_tmp = global_usage_result.usage; GetHostUsageWithBreakdown("foo.com", kTemp); int64_t predelete_host_tmp = usage(); @@ -1739,12 +1715,16 @@ TEST_F(QuotaManagerImplTest, GetUsage_WithDeleteStorageKey) { GetHostUsageWithBreakdown("foo.com", kPerm); int64_t predelete_host_pers = usage(); - DeleteClientStorageKeyData(client, ToStorageKey("http://foo.com/"), kTemp); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kOk, status()); + auto bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); + EXPECT_TRUE(bucket.ok()); - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp - 1, usage()); + auto status = DeleteBucketData(bucket->ToBucketLocator(), + {QuotaClientType::kFileSystem}); + EXPECT_EQ(status, QuotaStatusCode::kOk); + + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, predelete_global_tmp - 1); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_host_tmp - 1, usage()); @@ -1754,33 +1734,34 @@ TEST_F(QuotaManagerImplTest, GetUsage_WithDeleteStorageKey) { } TEST_F(QuotaManagerImplTest, GetStorageCapacity) { - GetStorageCapacity(); - task_environment_.RunUntilIdle(); - EXPECT_LE(0, total_space()); - EXPECT_LE(0, available_space()); + auto storage_capacity = GetStorageCapacity(); + EXPECT_GE(storage_capacity.total_space, 0); + EXPECT_GE(storage_capacity.available_space, 0); } TEST_F(QuotaManagerImplTest, EvictBucketData) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kTemp, 1}, - {"http://foo.com:1/", kTemp, 20}, - {"http://foo.com/", kPerm, 300}, - {"http://bar.com/", kTemp, 4000}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://foo.com:1/", "logs", kTemp, 20}, + {"http://foo.com/", kDefaultBucketName, kPerm, 300}, + {"http://bar.com/", kDefaultBucketName, kTemp, 4000}, }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com/", kTemp, 50000}, {"http://foo.com:1/", kTemp, 6000}, - {"http://foo.com/", kPerm, 700}, {"https://foo.com/", kTemp, 80}, - {"http://bar.com/", kTemp, 9}, + static const ClientBucketData kData2[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 50000}, + {"http://foo.com:1/", "logs", kTemp, 6000}, + {"http://foo.com/", kDefaultBucketName, kPerm, 700}, + {"https://foo.com/", kDefaultBucketName, kTemp, 80}, + {"http://bar.com/", kDefaultBucketName, kTemp, 9}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + MockQuotaClient* db_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(db_client, kData2); - GetGlobalUsage(kTemp); - int64_t predelete_global_tmp = usage(); + auto global_usage_result = GetGlobalUsage(kTemp); + int64_t predelete_global_tmp = global_usage_result.usage; GetHostUsageWithBreakdown("foo.com", kTemp); int64_t predelete_host_tmp = usage(); @@ -1788,116 +1769,74 @@ TEST_F(QuotaManagerImplTest, EvictBucketData) { GetHostUsageWithBreakdown("foo.com", kPerm); int64_t predelete_host_pers = usage(); - for (const MockStorageKeyData& data : kData1) { + for (const ClientBucketData& data : kData1) { quota_manager_impl()->NotifyStorageAccessed(ToStorageKey(data.origin), data.type, base::Time::Now()); } - for (const MockStorageKeyData& data : kData2) { + for (const ClientBucketData& data : kData2) { quota_manager_impl()->NotifyStorageAccessed(ToStorageKey(data.origin), data.type, base::Time::Now()); } task_environment_.RunUntilIdle(); - GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); - ASSERT_TRUE(bucket_.ok()); - - EvictBucketData(bucket_->ToBucketLocator()); - task_environment_.RunUntilIdle(); + // Default bucket eviction. + auto bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); + ASSERT_TRUE(bucket.ok()); - DumpBucketTable(); - task_environment_.RunUntilIdle(); + ASSERT_EQ(EvictBucketData(bucket->ToBucketLocator()), QuotaStatusCode::kOk); - for (const auto& entry : bucket_entries()) { - if (entry.type == kTemp) { - EXPECT_NE(std::string("http://foo.com/"), - entry.storage_key.origin().GetURL().spec()); - } - } + bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); + ASSERT_FALSE(bucket.ok()); + ASSERT_EQ(bucket.error(), QuotaError::kNotFound); - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp - (1 + 50000), usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(predelete_global_tmp - (1 + 50000), global_usage_result.usage); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_host_tmp - (1 + 50000), usage()); GetHostUsageWithBreakdown("foo.com", kPerm); EXPECT_EQ(predelete_host_pers, usage()); -} - -TEST_F(QuotaManagerImplTest, EvictNonDefaultBucketData) { - static const MockStorageKeyData kData[] = {{"http://foo.com/", kTemp, 100}}; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, {kTemp}); - GetGlobalUsage(kTemp); - int64_t predelete_global_tmp = usage(); + // Non default bucket eviction. + bucket = GetBucket(ToStorageKey("http://foo.com:1"), "logs", kTemp); + ASSERT_TRUE(bucket.ok()); - GetHostUsageWithBreakdown("foo.com", kTemp); - int64_t predelete_host_tmp = usage(); + ASSERT_EQ(EvictBucketData(bucket->ToBucketLocator()), QuotaStatusCode::kOk); - StorageKey storage_key = ToStorageKey("http://foo.com/"); - quota_manager_impl()->NotifyStorageAccessed(storage_key, kTemp, - base::Time::Now()); - task_environment_.RunUntilIdle(); + bucket = GetBucket(ToStorageKey("http://foo.com:1"), "logs", kTemp); + EXPECT_EQ(bucket.error(), QuotaError::kNotFound); - CreateBucketForTesting(storage_key, "foo_bucket", kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo created_bucket = bucket_.value(); - - EvictBucketData(created_bucket.ToBucketLocator()); - task_environment_.RunUntilIdle(); - - EXPECT_EQ(QuotaStatusCode::kOk, status()); - - DumpBucketTable(); - task_environment_.RunUntilIdle(); - - for (const auto& entry : bucket_entries()) { - if (entry.type == kTemp) - EXPECT_NE(created_bucket.id, entry.bucket_id); - } - - // Evicting non-default bucket should not change usage. - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp, usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(predelete_global_tmp - (1 + 20 + 50000 + 6000), + global_usage_result.usage); GetHostUsageWithBreakdown("foo.com", kTemp); - EXPECT_EQ(predelete_host_tmp, usage()); - - GetBucket(storage_key, kDefaultBucketName, kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo default_bucket = bucket_.value(); - - EvictBucketData(default_bucket.ToBucketLocator()); - task_environment_.RunUntilIdle(); - - EXPECT_EQ(QuotaStatusCode::kOk, status()); - - // Evicting default bucket should remove usage. - GetGlobalUsage(kTemp); - EXPECT_EQ(usage(), 0); + EXPECT_EQ(predelete_host_tmp - (1 + 20 + 50000 + 6000), usage()); - GetHostUsageWithBreakdown("foo.com", kTemp); - EXPECT_EQ(usage(), 0); + GetHostUsageWithBreakdown("foo.com", kPerm); + EXPECT_EQ(predelete_host_pers, usage()); } TEST_F(QuotaManagerImplTest, EvictBucketDataHistogram) { - const StorageKey kStorageKey = ToStorageKey("http://foo.com/"); - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 1}, - }; - base::HistogramTester histograms; + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://bar.com/", kDefaultBucketName, kTemp, 1}, + }; MockQuotaClient* client = - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp}); + RegisterClientBucketData(client, kData); GetGlobalUsage(kTemp); - CreateBucketForTesting(kStorageKey, kDefaultBucketName, kTemp); - ASSERT_TRUE(bucket_.ok()); + auto bucket = + GetBucket(ToStorageKey("http://foo.com"), kDefaultBucketName, kTemp); + ASSERT_TRUE(bucket.ok()); - EvictBucketData(bucket_->ToBucketLocator()); - task_environment_.RunUntilIdle(); + ASSERT_EQ(EvictBucketData(bucket->ToBucketLocator()), QuotaStatusCode::kOk); // Ensure use count and time since access are recorded. histograms.ExpectTotalCount( @@ -1907,17 +1846,17 @@ TEST_F(QuotaManagerImplTest, EvictBucketDataHistogram) { histograms.ExpectTotalCount( QuotaManagerImpl::kEvictedBucketDaysSinceAccessHistogram, 1); - client->AddStorageKeyAndNotify(kStorageKey, kTemp, 100); - - // Change the use count of the storage key. - quota_manager_impl()->NotifyStorageAccessed(kStorageKey, kTemp, - base::Time::Now()); + // Change the use count. + quota_manager_impl()->NotifyStorageAccessed(ToStorageKey("http://bar.com/"), + kTemp, base::Time::Now()); task_environment_.RunUntilIdle(); GetGlobalUsage(kTemp); - EvictBucketData(bucket_->ToBucketLocator()); - task_environment_.RunUntilIdle(); + bucket = GetBucket(ToStorageKey("http://bar.com"), kDefaultBucketName, kTemp); + ASSERT_TRUE(bucket.ok()); + + ASSERT_EQ(EvictBucketData(bucket->ToBucketLocator()), QuotaStatusCode::kOk); // The new use count should be logged. histograms.ExpectTotalCount( @@ -1929,56 +1868,45 @@ TEST_F(QuotaManagerImplTest, EvictBucketDataHistogram) { } TEST_F(QuotaManagerImplTest, EvictBucketDataWithDeletionError) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 1}, - {"http://foo.com:1/", kTemp, 20}, - {"http://foo.com/", kPerm, 300}, - {"http://bar.com/", kTemp, 4000}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://foo.com:1/", kDefaultBucketName, kTemp, 20}, + {"http://foo.com/", kDefaultBucketName, kPerm, 300}, + {"http://bar.com/", kDefaultBucketName, kTemp, 4000}, }; static const int kNumberOfTemporaryBuckets = 3; MockQuotaClient* client = - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(client, kData); - GetGlobalUsage(kTemp); - int64_t predelete_global_tmp = usage(); + auto global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, (1 + 20 + 4000)); GetHostUsageWithBreakdown("foo.com", kTemp); - int64_t predelete_host_tmp = usage(); + EXPECT_EQ((1 + 20), usage()); GetHostUsageWithBreakdown("foo.com", kPerm); - int64_t predelete_host_pers = usage(); + EXPECT_EQ(300, usage()); - for (const MockStorageKeyData& data : kData) + for (const ClientBucketData& data : kData) NotifyStorageAccessed(ToStorageKey(data.origin), data.type); task_environment_.RunUntilIdle(); - client->AddStorageKeyToErrorSet(ToStorageKey("http://foo.com/"), kTemp); - - GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); - ASSERT_TRUE(bucket_.ok()); + auto bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); + ASSERT_TRUE(bucket.ok()); + client->AddBucketToErrorSet(bucket->ToBucketLocator()); for (int i = 0; i < QuotaManagerImpl::kThresholdOfErrorsToBeDenylisted + 1; ++i) { - EvictBucketData(bucket_->ToBucketLocator()); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kErrorInvalidModification, status()); + ASSERT_EQ(EvictBucketData(bucket->ToBucketLocator()), + QuotaStatusCode::kErrorInvalidModification); } - DumpBucketTable(); - task_environment_.RunUntilIdle(); - - bool found_storage_key_in_database = false; - for (const auto& entry : bucket_entries()) { - if (entry.type == kTemp && entry.name == kDefaultBucketName && - entry.storage_key == ToStorageKey("http://foo.com/")) { - found_storage_key_in_database = true; - break; - } - } - // The default bucket for "http://foo.com/" should be in the database. - EXPECT_TRUE(found_storage_key_in_database); + // The default bucket for "http://foo.com/" should still be in the database. + bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); + EXPECT_TRUE(bucket.ok()); for (size_t i = 0; i < kNumberOfTemporaryBuckets - 1; ++i) { GetEvictionBucket(kTemp); @@ -1987,8 +1915,7 @@ TEST_F(QuotaManagerImplTest, EvictBucketDataWithDeletionError) { // "http://foo.com/" should not be in the LRU list. EXPECT_NE(std::string("http://foo.com/"), eviction_bucket()->storage_key.origin().GetURL().spec()); - DeleteBucketFromDatabase(eviction_bucket()->id); - task_environment_.RunUntilIdle(); + DeleteBucketData(*eviction_bucket(), AllQuotaClientTypes()); } // Now the LRU list must be empty. @@ -1996,30 +1923,28 @@ TEST_F(QuotaManagerImplTest, EvictBucketDataWithDeletionError) { task_environment_.RunUntilIdle(); EXPECT_FALSE(eviction_bucket().has_value()); - // Deleting buckets from the database should not affect the results of the - // following checks. - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp, usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, 1); GetHostUsageWithBreakdown("foo.com", kTemp); - EXPECT_EQ(predelete_host_tmp, usage()); + EXPECT_EQ(1, usage()); GetHostUsageWithBreakdown("foo.com", kPerm); - EXPECT_EQ(predelete_host_pers, usage()); + EXPECT_EQ(300, usage()); } TEST_F(QuotaManagerImplTest, GetEvictionRoundInfo) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 1}, - {"http://foo.com:1/", kTemp, 20}, - {"http://foo.com/", kPerm, 300}, - {"http://unlimited/", kTemp, 4000}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://foo.com:1/", kDefaultBucketName, kTemp, 20}, + {"http://foo.com/", kDefaultBucketName, kPerm, 300}, + {"http://unlimited/", kDefaultBucketName, kTemp, 4000}, }; mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/")); - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(client, kData); const int kPoolSize = 10000000; const int kPerHostQuota = kPoolSize / 5; @@ -2034,21 +1959,19 @@ TEST_F(QuotaManagerImplTest, GetEvictionRoundInfo) { } TEST_F(QuotaManagerImplTest, DeleteHostDataNoClients) { - DeleteHostData(std::string(), kTemp, AllQuotaClientTypes()); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kOk, status()); + EXPECT_EQ(DeleteHostData(std::string(), kTemp), QuotaStatusCode::kOk); } TEST_F(QuotaManagerImplTest, DeleteHostDataSimple) { - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 1}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(client, kData); - GetGlobalUsage(kTemp); - const int64_t predelete_global_tmp = usage(); + auto global_usage_result = GetGlobalUsage(kTemp); + const int64_t predelete_global_tmp = global_usage_result.usage; GetHostUsageWithBreakdown("foo.com", kTemp); int64_t predelete_host_tmp = usage(); @@ -2056,12 +1979,10 @@ TEST_F(QuotaManagerImplTest, DeleteHostDataSimple) { GetHostUsageWithBreakdown("foo.com", kPerm); int64_t predelete_host_pers = usage(); - DeleteHostData(std::string(), kTemp, AllQuotaClientTypes()); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kOk, status()); + EXPECT_EQ(DeleteHostData(std::string(), kTemp), QuotaStatusCode::kOk); - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp, usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, predelete_global_tmp); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_host_tmp, usage()); @@ -2069,12 +1990,10 @@ TEST_F(QuotaManagerImplTest, DeleteHostDataSimple) { GetHostUsageWithBreakdown("foo.com", kPerm); EXPECT_EQ(predelete_host_pers, usage()); - DeleteHostData("foo.com", kTemp, AllQuotaClientTypes()); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kOk, status()); + EXPECT_EQ(DeleteHostData("foo.com", kTemp), QuotaStatusCode::kOk); - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp - 1, usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(predelete_global_tmp - 1, global_usage_result.usage); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_host_tmp - 1, usage()); @@ -2084,26 +2003,28 @@ TEST_F(QuotaManagerImplTest, DeleteHostDataSimple) { } TEST_F(QuotaManagerImplTest, DeleteHostDataMultiple) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kTemp, 1}, - {"http://foo.com:1/", kTemp, 20}, - {"http://foo.com/", kPerm, 300}, - {"http://bar.com/", kTemp, 4000}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://foo.com:1/", kDefaultBucketName, kTemp, 20}, + {"http://foo.com/", kDefaultBucketName, kPerm, 300}, + {"http://bar.com/", kDefaultBucketName, kTemp, 4000}, }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com/", kTemp, 50000}, {"http://foo.com:1/", kTemp, 6000}, - {"http://foo.com/", kPerm, 700}, {"https://foo.com/", kTemp, 80}, - {"http://bar.com/", kTemp, 9}, + static const ClientBucketData kData2[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 50000}, + {"http://foo.com:1/", kDefaultBucketName, kTemp, 6000}, + {"http://foo.com/", kDefaultBucketName, kPerm, 700}, + {"https://foo.com/", kDefaultBucketName, kTemp, 80}, + {"http://bar.com/", kDefaultBucketName, kTemp, 9}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + MockQuotaClient* db_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(db_client, kData2); - GetGlobalUsage(kTemp); - const int64_t predelete_global_tmp = usage(); + auto global_usage_result = GetGlobalUsage(kTemp); + const int64_t predelete_global_tmp = global_usage_result.usage; GetHostUsageWithBreakdown("foo.com", kTemp); const int64_t predelete_foo_tmp = usage(); @@ -2117,18 +2038,12 @@ TEST_F(QuotaManagerImplTest, DeleteHostDataMultiple) { GetHostUsageWithBreakdown("bar.com", kPerm); const int64_t predelete_bar_pers = usage(); - reset_status_callback_count(); - DeleteHostData("foo.com", kTemp, AllQuotaClientTypes()); - DeleteHostData("bar.com", kTemp, AllQuotaClientTypes()); - DeleteHostData("foo.com", kTemp, AllQuotaClientTypes()); - task_environment_.RunUntilIdle(); + EXPECT_EQ(DeleteHostData("foo.com", kTemp), QuotaStatusCode::kOk); + EXPECT_EQ(DeleteHostData("bar.com", kTemp), QuotaStatusCode::kOk); + EXPECT_EQ(DeleteHostData("foo.com", kTemp), QuotaStatusCode::kOk); - EXPECT_EQ(3, status_callback_count()); - - DumpBucketTable(); - task_environment_.RunUntilIdle(); - - for (const auto& entry : bucket_entries()) { + const BucketTableEntries& entries = DumpBucketTable(); + for (const auto& entry : entries) { if (entry.type != kTemp) continue; @@ -2142,9 +2057,9 @@ TEST_F(QuotaManagerImplTest, DeleteHostDataMultiple) { entry.storage_key.origin().GetURL().spec()); } - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp - (1 + 20 + 4000 + 50000 + 6000 + 80 + 9), - usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, + predelete_global_tmp - (1 + 20 + 4000 + 50000 + 6000 + 80 + 9)); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_foo_tmp - (1 + 20 + 50000 + 6000 + 80), usage()); @@ -2160,26 +2075,27 @@ TEST_F(QuotaManagerImplTest, DeleteHostDataMultiple) { } TEST_F(QuotaManagerImplTest, DeleteHostDataMultipleClientsDifferentTypes) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kPerm, 1}, - {"http://foo.com:1/", kPerm, 10}, - {"http://foo.com/", kTemp, 100}, - {"http://bar.com/", kPerm, 1000}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kPerm, 1}, + {"http://foo.com:1/", kDefaultBucketName, kPerm, 10}, + {"http://foo.com/", kDefaultBucketName, kTemp, 100}, + {"http://bar.com/", kDefaultBucketName, kPerm, 1000}, }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com/", kTemp, 10000}, - {"http://foo.com:1/", kTemp, 100000}, - {"https://foo.com/", kTemp, 1000000}, - {"http://bar.com/", kTemp, 10000000}, + static const ClientBucketData kData2[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 10000}, + {"http://foo.com:1/", kDefaultBucketName, kTemp, 100000}, + {"https://foo.com/", kDefaultBucketName, kTemp, 1000000}, + {"http://bar.com/", kDefaultBucketName, kTemp, 10000000}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + MockQuotaClient* db_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(db_client, kData2); - GetGlobalUsage(kTemp); - const int64_t predelete_global_tmp = usage(); + auto global_usage_result = GetGlobalUsage(kTemp); + const int64_t predelete_global_tmp = global_usage_result.usage; GetHostUsageWithBreakdown("foo.com", kTemp); const int64_t predelete_foo_tmp = usage(); @@ -2187,27 +2103,21 @@ TEST_F(QuotaManagerImplTest, DeleteHostDataMultipleClientsDifferentTypes) { GetHostUsageWithBreakdown("bar.com", kTemp); const int64_t predelete_bar_tmp = usage(); - GetGlobalUsage(kPerm); - const int64_t predelete_global_pers = usage(); + global_usage_result = GetGlobalUsage(kPerm); + EXPECT_EQ(global_usage_result.usage, (1000 + 10 + 1)); GetHostUsageWithBreakdown("foo.com", kPerm); - const int64_t predelete_foo_pers = usage(); + EXPECT_EQ((10 + 1), usage()); GetHostUsageWithBreakdown("bar.com", kPerm); - const int64_t predelete_bar_pers = usage(); - - reset_status_callback_count(); - DeleteHostData("foo.com", kPerm, AllQuotaClientTypes()); - DeleteHostData("bar.com", kPerm, AllQuotaClientTypes()); - task_environment_.RunUntilIdle(); - - EXPECT_EQ(2, status_callback_count()); + EXPECT_EQ(1000, usage()); - DumpBucketTable(); - task_environment_.RunUntilIdle(); + EXPECT_EQ(DeleteHostData("foo.com", kPerm), QuotaStatusCode::kOk); + EXPECT_EQ(DeleteHostData("bar.com", kPerm), QuotaStatusCode::kOk); - for (const auto& entry : bucket_entries()) { - if (entry.type != kTemp) + const BucketTableEntries& entries = DumpBucketTable(); + for (const auto& entry : entries) { + if (entry.type != kPerm) continue; EXPECT_NE(std::string("http://foo.com/"), @@ -2220,8 +2130,8 @@ TEST_F(QuotaManagerImplTest, DeleteHostDataMultipleClientsDifferentTypes) { entry.storage_key.origin().GetURL().spec()); } - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp, usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, predelete_global_tmp); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_foo_tmp, usage()); @@ -2229,57 +2139,56 @@ TEST_F(QuotaManagerImplTest, DeleteHostDataMultipleClientsDifferentTypes) { GetHostUsageWithBreakdown("bar.com", kTemp); EXPECT_EQ(predelete_bar_tmp, usage()); - GetGlobalUsage(kPerm); - EXPECT_EQ(predelete_global_pers - (1 + 10 + 1000), usage()); + global_usage_result = GetGlobalUsage(kPerm); + EXPECT_EQ(global_usage_result.usage, 0); GetHostUsageWithBreakdown("foo.com", kPerm); - EXPECT_EQ(predelete_foo_pers - (1 + 10), usage()); + EXPECT_EQ(0, usage()); GetHostUsageWithBreakdown("bar.com", kPerm); - EXPECT_EQ(predelete_bar_pers - 1000, usage()); + EXPECT_EQ(0, usage()); } TEST_F(QuotaManagerImplTest, DeleteBucketNoClients) { - CreateBucketForTesting(ToStorageKey("http://foo.com"), kDefaultBucketName, - kTemp); - ASSERT_TRUE(bucket_.ok()); + auto bucket = CreateBucketForTesting(ToStorageKey("http://foo.com"), + kDefaultBucketName, kTemp); + ASSERT_TRUE(bucket.ok()); - DeleteBucketData(bucket_->ToBucketLocator(), AllQuotaClientTypes()); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kOk, status()); + EXPECT_EQ(DeleteBucketData(bucket->ToBucketLocator(), AllQuotaClientTypes()), + QuotaStatusCode::kOk); } TEST_F(QuotaManagerImplTest, DeleteBucketDataMultiple) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kTemp, 1}, - {"http://foo.com:1/", kTemp, 20}, - {"http://foo.com/", kPerm, 300}, - {"http://bar.com/", kTemp, 4000}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://foo.com:1/", kDefaultBucketName, kTemp, 20}, + {"http://foo.com/", kDefaultBucketName, kPerm, 300}, + {"http://bar.com/", kDefaultBucketName, kTemp, 4000}, }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com/", kTemp, 50000}, {"http://foo.com:1/", kTemp, 6000}, - {"http://foo.com/", kPerm, 700}, {"https://foo.com/", kTemp, 80}, - {"http://bar.com/", kTemp, 9}, + static const ClientBucketData kData2[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 50000}, + {"http://foo.com:1/", kDefaultBucketName, kTemp, 6000}, + {"http://foo.com/", kDefaultBucketName, kPerm, 700}, + {"https://foo.com/", kDefaultBucketName, kTemp, 80}, + {"http://bar.com/", kDefaultBucketName, kTemp, 9}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - - CreateBucketForTesting(ToStorageKey("http://foo.com"), kDefaultBucketName, - kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo foo_temp_bucket = bucket_.value(); - - CreateBucketForTesting(ToStorageKey("http://bar.com"), kDefaultBucketName, - kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo bar_temp_bucket = bucket_.value(); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + MockQuotaClient* db_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(db_client, kData2); - GetGlobalUsage(kTemp); - const int64_t predelete_global_tmp = usage(); + auto foo_temp_bucket = + GetBucket(ToStorageKey("http://foo.com"), kDefaultBucketName, kTemp); + ASSERT_TRUE(foo_temp_bucket.ok()); + + auto bar_temp_bucket = + GetBucket(ToStorageKey("http://bar.com"), kDefaultBucketName, kTemp); + ASSERT_TRUE(bar_temp_bucket.ok()); + + auto global_usage_result = GetGlobalUsage(kTemp); + const int64_t predelete_global_tmp = global_usage_result.usage; GetHostUsageWithBreakdown("foo.com", kTemp); const int64_t predelete_foo_tmp = usage(); @@ -2293,39 +2202,35 @@ TEST_F(QuotaManagerImplTest, DeleteBucketDataMultiple) { GetHostUsageWithBreakdown("bar.com", kPerm); const int64_t predelete_bar_pers = usage(); - for (const MockStorageKeyData& data : kData1) { + for (const ClientBucketData& data : kData1) { quota_manager_impl()->NotifyStorageAccessed(ToStorageKey(data.origin), data.type, base::Time::Now()); } - for (const MockStorageKeyData& data : kData2) { + for (const ClientBucketData& data : kData2) { quota_manager_impl()->NotifyStorageAccessed(ToStorageKey(data.origin), data.type, base::Time::Now()); } task_environment_.RunUntilIdle(); - reset_status_callback_count(); - DeleteBucketData(foo_temp_bucket.ToBucketLocator(), AllQuotaClientTypes()); - DeleteBucketData(bar_temp_bucket.ToBucketLocator(), AllQuotaClientTypes()); - DeleteBucketData(foo_temp_bucket.ToBucketLocator(), AllQuotaClientTypes()); - task_environment_.RunUntilIdle(); + EXPECT_EQ(DeleteBucketData(foo_temp_bucket->ToBucketLocator(), + AllQuotaClientTypes()), + QuotaStatusCode::kOk); + EXPECT_EQ(DeleteBucketData(bar_temp_bucket->ToBucketLocator(), + AllQuotaClientTypes()), + QuotaStatusCode::kOk); - EXPECT_EQ(3, status_callback_count()); + QuotaErrorOr<BucketInfo> bucket; + bucket = GetBucket(foo_temp_bucket->storage_key, foo_temp_bucket->name, + foo_temp_bucket->type); + EXPECT_EQ(bucket.error(), QuotaError::kNotFound); - DumpBucketTable(); - task_environment_.RunUntilIdle(); - - for (const auto& entry : bucket_entries()) { - if (entry.type != kTemp) - continue; - - EXPECT_NE(std::string("http://foo.com/"), - entry.storage_key.origin().GetURL().spec()); - EXPECT_NE(std::string("http://bar.com/"), - entry.storage_key.origin().GetURL().spec()); - } + bucket = GetBucket(bar_temp_bucket->storage_key, bar_temp_bucket->name, + bar_temp_bucket->type); + EXPECT_EQ(bucket.error(), QuotaError::kNotFound); - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp - (1 + 4000 + 50000 + 9), usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, + predelete_global_tmp - (1 + 4000 + 50000 + 9)); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_foo_tmp - (1 + 50000), usage()); @@ -2341,36 +2246,35 @@ TEST_F(QuotaManagerImplTest, DeleteBucketDataMultiple) { } TEST_F(QuotaManagerImplTest, DeleteBucketDataMultipleClientsDifferentTypes) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kPerm, 1}, - {"http://foo.com:1/", kPerm, 10}, - {"http://foo.com/", kTemp, 100}, - {"http://bar.com/", kPerm, 1000}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kPerm, 1}, + {"http://foo.com:1/", kDefaultBucketName, kPerm, 10}, + {"http://foo.com/", kDefaultBucketName, kTemp, 100}, + {"http://bar.com/", kDefaultBucketName, kPerm, 1000}, }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com/", kTemp, 10000}, - {"http://foo.com:1/", kTemp, 100000}, - {"https://foo.com/", kTemp, 1000000}, - {"http://bar.com/", kTemp, 10000000}, + static const ClientBucketData kData2[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 10000}, + {"http://foo.com:1/", kDefaultBucketName, kTemp, 100000}, + {"https://foo.com/", kDefaultBucketName, kTemp, 1000000}, + {"http://bar.com/", kDefaultBucketName, kTemp, 10000000}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); - - CreateBucketForTesting(ToStorageKey("http://foo.com/"), kDefaultBucketName, - kPerm); - ASSERT_TRUE(bucket_.ok()); - BucketInfo foo_perm_bucket = bucket_.value(); - - CreateBucketForTesting(ToStorageKey("http://bar.com/"), kDefaultBucketName, - kPerm); - ASSERT_TRUE(bucket_.ok()); - BucketInfo bar_perm_bucket = bucket_.value(); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + MockQuotaClient* db_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(db_client, kData2); - GetGlobalUsage(kTemp); - const int64_t predelete_global_tmp = usage(); + auto foo_perm_bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kPerm); + ASSERT_TRUE(foo_perm_bucket.ok()); + + auto bar_perm_bucket = + GetBucket(ToStorageKey("http://bar.com/"), kDefaultBucketName, kPerm); + ASSERT_TRUE(bar_perm_bucket.ok()); + + auto global_usage_result = GetGlobalUsage(kTemp); + const int64_t predelete_global_tmp = global_usage_result.usage; GetHostUsageWithBreakdown("foo.com", kTemp); const int64_t predelete_foo_tmp = usage(); @@ -2378,8 +2282,8 @@ TEST_F(QuotaManagerImplTest, DeleteBucketDataMultipleClientsDifferentTypes) { GetHostUsageWithBreakdown("bar.com", kTemp); const int64_t predelete_bar_tmp = usage(); - GetGlobalUsage(kPerm); - const int64_t predelete_global_pers = usage(); + global_usage_result = GetGlobalUsage(kPerm); + const int64_t predelete_global_pers = global_usage_result.usage; GetHostUsageWithBreakdown("foo.com", kPerm); const int64_t predelete_foo_pers = usage(); @@ -2387,38 +2291,34 @@ TEST_F(QuotaManagerImplTest, DeleteBucketDataMultipleClientsDifferentTypes) { GetHostUsageWithBreakdown("bar.com", kPerm); const int64_t predelete_bar_pers = usage(); - for (const MockStorageKeyData& data : kData1) { + for (const ClientBucketData& data : kData1) { quota_manager_impl()->NotifyStorageAccessed(ToStorageKey(data.origin), data.type, base::Time::Now()); } - for (const MockStorageKeyData& data : kData2) { + for (const ClientBucketData& data : kData2) { quota_manager_impl()->NotifyStorageAccessed(ToStorageKey(data.origin), data.type, base::Time::Now()); } task_environment_.RunUntilIdle(); - reset_status_callback_count(); - DeleteBucketData(foo_perm_bucket.ToBucketLocator(), AllQuotaClientTypes()); - DeleteBucketData(bar_perm_bucket.ToBucketLocator(), AllQuotaClientTypes()); - task_environment_.RunUntilIdle(); + EXPECT_EQ(DeleteBucketData(foo_perm_bucket->ToBucketLocator(), + AllQuotaClientTypes()), + QuotaStatusCode::kOk); + EXPECT_EQ(DeleteBucketData(bar_perm_bucket->ToBucketLocator(), + AllQuotaClientTypes()), + QuotaStatusCode::kOk); - EXPECT_EQ(2, status_callback_count()); + QuotaErrorOr<BucketInfo> bucket; + bucket = GetBucket(foo_perm_bucket->storage_key, foo_perm_bucket->name, + foo_perm_bucket->type); + EXPECT_EQ(bucket.error(), QuotaError::kNotFound); - DumpBucketTable(); - task_environment_.RunUntilIdle(); + bucket = GetBucket(bar_perm_bucket->storage_key, bar_perm_bucket->name, + bar_perm_bucket->type); + EXPECT_EQ(bucket.error(), QuotaError::kNotFound); - for (const auto& entry : bucket_entries()) { - if (entry.type != kPerm) - continue; - - EXPECT_NE(std::string("http://foo.com/"), - entry.storage_key.origin().GetURL().spec()); - EXPECT_NE(std::string("http://bar.com/"), - entry.storage_key.origin().GetURL().spec()); - } - - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp, usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, predelete_global_tmp); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_foo_tmp, usage()); @@ -2426,8 +2326,8 @@ TEST_F(QuotaManagerImplTest, DeleteBucketDataMultipleClientsDifferentTypes) { GetHostUsageWithBreakdown("bar.com", kTemp); EXPECT_EQ(predelete_bar_tmp, usage()); - GetGlobalUsage(kPerm); - EXPECT_EQ(predelete_global_pers - (1 + 1000), usage()); + global_usage_result = GetGlobalUsage(kPerm); + EXPECT_EQ(global_usage_result.usage, predelete_global_pers - (1 + 1000)); GetHostUsageWithBreakdown("foo.com", kPerm); EXPECT_EQ(predelete_foo_pers - 1, usage()); @@ -2437,35 +2337,33 @@ TEST_F(QuotaManagerImplTest, DeleteBucketDataMultipleClientsDifferentTypes) { } TEST_F(QuotaManagerImplTest, FindAndDeleteBucketData) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kTemp, 1}, - {"http://bar.com/", kTemp, 4000}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + {"http://bar.com/", kDefaultBucketName, kTemp, 4000}, }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com/", kTemp, 50000}, - {"http://bar.com/", kTemp, 9}, + static const ClientBucketData kData2[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 50000}, + {"http://bar.com/", kDefaultBucketName, kTemp, 9}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - CreateAndRegisterClient(kData2, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); - - CreateBucketForTesting(ToStorageKey("http://foo.com"), kDefaultBucketName, - kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo foo_bucket = bucket_.value(); - - CreateBucketForTesting(ToStorageKey("http://bar.com"), kDefaultBucketName, - kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo bar_bucket = bucket_.value(); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + MockQuotaClient* db_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(db_client, kData2); + + auto foo_bucket = + GetBucket(ToStorageKey("http://foo.com"), kDefaultBucketName, kTemp); + ASSERT_TRUE(foo_bucket.ok()); + + auto bar_bucket = + GetBucket(ToStorageKey("http://bar.com"), kDefaultBucketName, kTemp); + ASSERT_TRUE(bar_bucket.ok()); // Check usage data before deletion. - GetGlobalUsage(kTemp); - const int64_t predelete_global_tmp = usage(); - ASSERT_EQ((1 + 9 + 4000 + 50000), usage()); + auto global_usage_result = GetGlobalUsage(kTemp); + ASSERT_EQ((1 + 9 + 4000 + 50000), global_usage_result.usage); + const int64_t predelete_global_tmp = global_usage_result.usage; GetHostUsageWithBreakdown("foo.com", kTemp); ASSERT_EQ((1 + 50000), usage()); @@ -2474,110 +2372,101 @@ TEST_F(QuotaManagerImplTest, FindAndDeleteBucketData) { ASSERT_EQ((9 + 4000), usage()); // Delete bucket for "http://foo.com/". - reset_status_callback_count(); - FindAndDeleteBucketData(foo_bucket.storage_key, foo_bucket.name); - EXPECT_EQ(1, status_callback_count()); + EXPECT_EQ(FindAndDeleteBucketData(foo_bucket->storage_key, foo_bucket->name), + QuotaStatusCode::kOk); - GetBucket(foo_bucket.storage_key, foo_bucket.name, foo_bucket.type); - ASSERT_FALSE(bucket_.ok()); - EXPECT_EQ(bucket_.error(), QuotaError::kNotFound); + auto bucket = + GetBucket(foo_bucket->storage_key, foo_bucket->name, foo_bucket->type); + ASSERT_FALSE(bucket.ok()); + EXPECT_EQ(bucket.error(), QuotaError::kNotFound); - GetGlobalUsage(kTemp); - EXPECT_EQ(predelete_global_tmp - (1 + 50000), usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, predelete_global_tmp - (1 + 50000)); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(0, usage()); // Delete bucket for "http://bar.com/". - reset_status_callback_count(); - FindAndDeleteBucketData(bar_bucket.storage_key, bar_bucket.name); - EXPECT_EQ(1, status_callback_count()); + EXPECT_EQ(FindAndDeleteBucketData(bar_bucket->storage_key, bar_bucket->name), + QuotaStatusCode::kOk); - GetBucket(bar_bucket.storage_key, bar_bucket.name, bar_bucket.type); - ASSERT_FALSE(bucket_.ok()); - EXPECT_EQ(bucket_.error(), QuotaError::kNotFound); + bucket = + GetBucket(bar_bucket->storage_key, bar_bucket->name, bar_bucket->type); + ASSERT_FALSE(bucket.ok()); + EXPECT_EQ(bucket.error(), QuotaError::kNotFound); - GetGlobalUsage(kTemp); - EXPECT_EQ(0, usage()); + global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, 0); GetHostUsageWithBreakdown("bar.com", kTemp); EXPECT_EQ(0, usage()); } -TEST_F(QuotaManagerImplTest, GetCachedStorageKeys) { - static const MockStorageKeyData kData[] = { - {"http://a.com/", kTemp, 1}, - {"http://a.com:1/", kTemp, 20}, - {"http://b.com/", kPerm, 300}, - {"http://c.com/", kTemp, 4000}, +TEST_F(QuotaManagerImplTest, FindAndDeleteBucketDataWithDBError) { + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 123}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); - // TODO(kinuko): Be careful when we add cache pruner. + auto quota_db = std::make_unique<MockQuotaDatabase>( + data_dir_.GetPath().AppendASCII("QuotaManager")); + MockQuotaDatabase* mock_database = quota_db.get(); + SetQuotaDatabase(std::move(quota_db)); - std::set<StorageKey> storage_keys = GetCachedStorageKeys(kTemp); - EXPECT_TRUE(storage_keys.empty()); + RegisterClientBucketData(fs_client, kData); - GetHostUsageWithBreakdown("a.com", kTemp); - storage_keys = GetCachedStorageKeys(kTemp); - EXPECT_EQ(2U, storage_keys.size()); + // Check usage data before deletion. + GetHostUsageWithBreakdown("foo.com", kTemp); + ASSERT_EQ(123, usage()); - GetHostUsageWithBreakdown("b.com", kTemp); - storage_keys = GetCachedStorageKeys(kTemp); - EXPECT_EQ(2U, storage_keys.size()); + EXPECT_CALL(*mock_database, DeleteBucketInfo) + .Times(1) + .WillOnce(testing::Return(QuotaError::kDatabaseError)); - GetHostUsageWithBreakdown("c.com", kTemp); - storage_keys = GetCachedStorageKeys(kTemp); - EXPECT_EQ(3U, storage_keys.size()); + // Trying to delete bucket for "http://foo.com/" should return error. + EXPECT_EQ(FindAndDeleteBucketData(ToStorageKey("http://foo.com"), + kDefaultBucketName), + QuotaStatusCode::kErrorInvalidModification); - storage_keys = GetCachedStorageKeys(kPerm); - EXPECT_TRUE(storage_keys.empty()); + auto global_usage_result = GetGlobalUsage(kTemp); + EXPECT_EQ(global_usage_result.usage, 0); - GetGlobalUsage(kTemp); - storage_keys = GetCachedStorageKeys(kTemp); - EXPECT_THAT(storage_keys, - testing::UnorderedElementsAre(ToStorageKey("http://a.com"), - ToStorageKey("http://c.com"), - ToStorageKey("http://a.com:1"))); + GetHostUsageWithBreakdown("foo.com", kTemp); + EXPECT_EQ(0, usage()); } TEST_F(QuotaManagerImplTest, NotifyAndLRUBucket) { - static const MockStorageKeyData kData[] = { - {"http://a.com/", kTemp, 0}, {"http://a.com:1/", kTemp, 0}, - {"https://a.com/", kTemp, 0}, {"http://b.com/", kPerm, 0}, // persistent - {"http://c.com/", kTemp, 0}, + static const ClientBucketData kData[] = { + {"http://a.com/", kDefaultBucketName, kTemp, 0}, + {"http://a.com:1/", kDefaultBucketName, kTemp, 0}, + {"http://b.com/", kDefaultBucketName, kPerm, 0}, + {"http://c.com/", kDefaultBucketName, kTemp, 0}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); - GetEvictionBucket(kTemp); + quota_manager_impl()->NotifyStorageAccessed(ToStorageKey("http://b.com/"), + kPerm, base::Time::Now()); + quota_manager_impl()->NotifyStorageAccessed(ToStorageKey("http://a.com/"), + kTemp, base::Time::Now()); + quota_manager_impl()->NotifyStorageAccessed(ToStorageKey("http://c.com/"), + kTemp, base::Time::Now()); task_environment_.RunUntilIdle(); - EXPECT_FALSE(eviction_bucket().has_value()); - NotifyStorageAccessed(ToStorageKey("http://a.com/"), kTemp); GetEvictionBucket(kTemp); task_environment_.RunUntilIdle(); - EXPECT_EQ("http://a.com/", + EXPECT_EQ("http://a.com:1/", eviction_bucket()->storage_key.origin().GetURL().spec()); - NotifyStorageAccessed(ToStorageKey("http://b.com/"), kPerm); - NotifyStorageAccessed(ToStorageKey("https://a.com/"), kTemp); - NotifyStorageAccessed(ToStorageKey("http://c.com/"), kTemp); + DeleteBucketData(*eviction_bucket(), AllQuotaClientTypes()); GetEvictionBucket(kTemp); task_environment_.RunUntilIdle(); EXPECT_EQ("http://a.com/", eviction_bucket()->storage_key.origin().GetURL().spec()); - DeleteBucketFromDatabase(eviction_bucket()->id); - GetEvictionBucket(kTemp); - task_environment_.RunUntilIdle(); - EXPECT_EQ("https://a.com/", - eviction_bucket()->storage_key.origin().GetURL().spec()); - - DeleteBucketFromDatabase(eviction_bucket()->id); + DeleteBucketData(*eviction_bucket(), AllQuotaClientTypes()); GetEvictionBucket(kTemp); task_environment_.RunUntilIdle(); EXPECT_EQ("http://c.com/", @@ -2589,18 +2478,19 @@ TEST_F(QuotaManagerImplTest, GetLRUBucket) { StorageKey storage_key_b = ToStorageKey("http://b.com/"); StorageKey storage_key_c = ToStorageKey("http://c.com/"); - CreateBucketForTesting(storage_key_a, kDefaultBucketName, kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo bucket_a = bucket_.value(); + auto bucket = + CreateBucketForTesting(storage_key_a, kDefaultBucketName, kTemp); + ASSERT_TRUE(bucket.ok()); + BucketInfo bucket_a = bucket.value(); - CreateBucketForTesting(storage_key_b, kDefaultBucketName, kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo bucket_b = bucket_.value(); + bucket = CreateBucketForTesting(storage_key_b, kDefaultBucketName, kTemp); + ASSERT_TRUE(bucket.ok()); + BucketInfo bucket_b = bucket.value(); // Persistent bucket. - CreateBucketForTesting(storage_key_c, kDefaultBucketName, kPerm); - ASSERT_TRUE(bucket_.ok()); - BucketInfo bucket_c = bucket_.value(); + bucket = CreateBucketForTesting(storage_key_c, kDefaultBucketName, kPerm); + ASSERT_TRUE(bucket.ok()); + BucketInfo bucket_c = bucket.value(); NotifyBucketAccessed(bucket_a.id); NotifyBucketAccessed(bucket_b.id); @@ -2626,19 +2516,20 @@ TEST_F(QuotaManagerImplTest, GetLRUBucket) { } TEST_F(QuotaManagerImplTest, GetBucketsModifiedBetween) { - static const MockStorageKeyData kData[] = { - {"http://a.com/", kTemp, 0}, {"http://a.com:1/", kTemp, 0}, - {"https://a.com/", kTemp, 0}, {"http://b.com/", kPerm, 0}, // persistent - {"http://c.com/", kTemp, 0}, + static const ClientBucketData kData[] = { + {"http://a.com/", kDefaultBucketName, kTemp, 0}, + {"http://a.com:1/", kDefaultBucketName, kTemp, 0}, + {"https://a.com/", kDefaultBucketName, kTemp, 0}, + {"http://b.com/", kDefaultBucketName, kPerm, 0}, // persistent + {"http://c.com/", kDefaultBucketName, kTemp, 0}, }; MockQuotaClient* client = - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(client, kData); - GetBucketsModifiedBetween(kTemp, base::Time(), base::Time::Max()); - EXPECT_TRUE(modified_buckets().empty()); - EXPECT_EQ(modified_buckets_type(), kTemp); + auto buckets = + GetBucketsModifiedBetween(kTemp, base::Time(), base::Time::Max()); + EXPECT_EQ(4U, buckets.size()); base::Time time1 = client->IncrementMockTime(); client->ModifyStorageKeyAndNotify(ToStorageKey("http://a.com/"), kTemp, 10); @@ -2649,46 +2540,49 @@ TEST_F(QuotaManagerImplTest, GetBucketsModifiedBetween) { client->ModifyStorageKeyAndNotify(ToStorageKey("http://c.com/"), kTemp, 10); base::Time time3 = client->IncrementMockTime(); - GetBucketsModifiedBetween(kTemp, time1, base::Time::Max()); - EXPECT_EQ(modified_buckets_type(), kTemp); - EXPECT_THAT(modified_buckets(), - testing::UnorderedElementsAre( - testing::Field(&BucketLocator::storage_key, - ToStorageKey("http://a.com")), - testing::Field(&BucketLocator::storage_key, - ToStorageKey("http://a.com:1")), - testing::Field(&BucketLocator::storage_key, - ToStorageKey("https://a.com")), - testing::Field(&BucketLocator::storage_key, - ToStorageKey("http://c.com")))); - - GetBucketsModifiedBetween(kTemp, time2, base::Time::Max()); - EXPECT_EQ(2U, modified_buckets().size()); - - GetBucketsModifiedBetween(kTemp, time3, base::Time::Max()); - EXPECT_TRUE(modified_buckets().empty()); - EXPECT_EQ(modified_buckets_type(), kTemp); + // Database call to ensure modification calls have completed. + GetBucket(ToStorageKey("http://a.com"), kDefaultBucketName, kTemp); + + buckets = GetBucketsModifiedBetween(kTemp, time1, base::Time::Max()); + EXPECT_THAT(buckets, testing::UnorderedElementsAre( + testing::Field(&BucketLocator::storage_key, + ToStorageKey("http://a.com")), + testing::Field(&BucketLocator::storage_key, + ToStorageKey("http://a.com:1")), + testing::Field(&BucketLocator::storage_key, + ToStorageKey("https://a.com")), + testing::Field(&BucketLocator::storage_key, + ToStorageKey("http://c.com")))); + + buckets = GetBucketsModifiedBetween(kTemp, time2, base::Time::Max()); + EXPECT_EQ(2U, buckets.size()); + + buckets = GetBucketsModifiedBetween(kTemp, time3, base::Time::Max()); + EXPECT_TRUE(buckets.empty()); client->ModifyStorageKeyAndNotify(ToStorageKey("http://a.com/"), kTemp, 10); - GetBucketsModifiedBetween(kTemp, time3, base::Time::Max()); - EXPECT_THAT(modified_buckets(), + // Database call to ensure modification calls have completed. + GetBucket(ToStorageKey("http://a.com"), kDefaultBucketName, kTemp); + + buckets = GetBucketsModifiedBetween(kTemp, time3, base::Time::Max()); + EXPECT_THAT(buckets, testing::UnorderedElementsAre(testing::Field( &BucketLocator::storage_key, ToStorageKey("http://a.com/")))); - EXPECT_EQ(modified_buckets_type(), kTemp); } TEST_F(QuotaManagerImplTest, GetBucketsModifiedBetweenWithDatabaseError) { + disable_database_bootstrap(true); OpenDatabase(); // Disable quota database for database error behavior. disable_quota_database(true); - GetBucketsModifiedBetween(kTemp, base::Time(), base::Time::Max()); + auto buckets = + GetBucketsModifiedBetween(kTemp, base::Time(), base::Time::Max()); // Return empty set when error is encountered. - EXPECT_TRUE(modified_buckets().empty()); - EXPECT_EQ(modified_buckets_type(), kTemp); + EXPECT_TRUE(buckets.empty()); } TEST_F(QuotaManagerImplTest, DumpQuotaTable) { @@ -2697,11 +2591,9 @@ TEST_F(QuotaManagerImplTest, DumpQuotaTable) { SetPersistentHostQuota("example3.com", 300); task_environment_.RunUntilIdle(); - DumpQuotaTable(); - task_environment_.RunUntilIdle(); - + const QuotaTableEntries& entries = DumpQuotaTable(); EXPECT_THAT( - quota_entries(), + entries, testing::UnorderedElementsAre( QuotaTableEntry{.host = "example1.com", .type = kPerm, .quota = 1}, QuotaTableEntry{.host = "example2.com", .type = kPerm, .quota = 20}, @@ -2710,216 +2602,127 @@ TEST_F(QuotaManagerImplTest, DumpQuotaTable) { } TEST_F(QuotaManagerImplTest, DumpBucketTable) { - quota_manager_impl()->NotifyStorageAccessed( - ToStorageKey("http://example.com/"), kTemp, base::Time::Now()); - quota_manager_impl()->NotifyStorageAccessed( - ToStorageKey("http://example.com/"), kPerm, base::Time::Now()); - quota_manager_impl()->NotifyStorageAccessed( - ToStorageKey("http://example.com/"), kPerm, base::Time::Now()); - task_environment_.RunUntilIdle(); + const StorageKey kStorageKey = ToStorageKey("http://example.com/"); + CreateBucketForTesting(kStorageKey, kDefaultBucketName, kTemp); + CreateBucketForTesting(kStorageKey, kDefaultBucketName, kPerm); - DumpBucketTable(); + quota_manager_impl()->NotifyStorageAccessed(kStorageKey, kTemp, + base::Time::Now()); + quota_manager_impl()->NotifyStorageAccessed(kStorageKey, kPerm, + base::Time::Now()); + quota_manager_impl()->NotifyStorageAccessed(kStorageKey, kPerm, + base::Time::Now()); task_environment_.RunUntilIdle(); - EXPECT_THAT(bucket_entries(), - testing::UnorderedElementsAre( - MatchesBucketTableEntry(ToStorageKey("http://example.com/"), - kTemp, 1), - MatchesBucketTableEntry(ToStorageKey("http://example.com/"), - kPerm, 2))); + const BucketTableEntries& entries = DumpBucketTable(); + EXPECT_THAT(entries, testing::UnorderedElementsAre( + MatchesBucketTableEntry(kStorageKey, kTemp, 1), + MatchesBucketTableEntry(kStorageKey, kPerm, 2))); } TEST_F(QuotaManagerImplTest, QuotaForEmptyHost) { - GetPersistentHostQuota(std::string()); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, quota()); + EXPECT_EQ(GetPersistentHostQuota(std::string()), 0); - SetPersistentHostQuota(std::string(), 10); - task_environment_.RunUntilIdle(); - EXPECT_EQ(QuotaStatusCode::kErrorNotSupported, status()); + base::test::TestFuture<QuotaStatusCode, int64_t> future; + quota_manager_impl()->SetPersistentHostQuota(std::string(), 10, + future.GetCallback()); + EXPECT_EQ(future.Get<0>(), QuotaStatusCode::kErrorNotSupported); } TEST_F(QuotaManagerImplTest, DeleteSpecificClientTypeSingleBucket) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kTemp, 1}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com/", kTemp, 2}, + static const ClientBucketData kData2[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 2}, }; - static const MockStorageKeyData kData3[] = { - {"http://foo.com/", kTemp, 4}, + static const ClientBucketData kData3[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 4}, }; - static const MockStorageKeyData kData4[] = { - {"http://foo.com/", kTemp, 8}, + static const ClientBucketData kData4[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 8}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData2, QuotaClientType::kServiceWorkerCache, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData3, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData4, QuotaClientType::kIndexedDatabase, - {blink::mojom::StorageType::kTemporary}); - - CreateBucketForTesting(ToStorageKey("http://foo.com"), kDefaultBucketName, - kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo foo_bucket = bucket_.value(); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp}); + MockQuotaClient* sw_client = + CreateAndRegisterClient(QuotaClientType::kServiceWorkerCache, {kTemp}); + MockQuotaClient* db_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp}); + MockQuotaClient* idb_client = + CreateAndRegisterClient(QuotaClientType::kIndexedDatabase, {kTemp}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(sw_client, kData2); + RegisterClientBucketData(db_client, kData3); + RegisterClientBucketData(idb_client, kData4); + + auto foo_bucket = + GetBucket(ToStorageKey("http://foo.com"), kDefaultBucketName, kTemp); + ASSERT_TRUE(foo_bucket.ok()); GetHostUsageWithBreakdown("foo.com", kTemp); const int64_t predelete_foo_tmp = usage(); - DeleteBucketData(foo_bucket.ToBucketLocator(), + DeleteBucketData(foo_bucket->ToBucketLocator(), {QuotaClientType::kFileSystem}); - task_environment_.RunUntilIdle(); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_foo_tmp - 1, usage()); - DeleteBucketData(foo_bucket.ToBucketLocator(), + DeleteBucketData(foo_bucket->ToBucketLocator(), {QuotaClientType::kServiceWorkerCache}); - task_environment_.RunUntilIdle(); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_foo_tmp - 2 - 1, usage()); - DeleteBucketData(foo_bucket.ToBucketLocator(), {QuotaClientType::kDatabase}); - task_environment_.RunUntilIdle(); + DeleteBucketData(foo_bucket->ToBucketLocator(), {QuotaClientType::kDatabase}); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_foo_tmp - 4 - 2 - 1, usage()); - DeleteBucketData(foo_bucket.ToBucketLocator(), + DeleteBucketData(foo_bucket->ToBucketLocator(), {QuotaClientType::kIndexedDatabase}); - task_environment_.RunUntilIdle(); - GetHostUsageWithBreakdown("foo.com", kTemp); - EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage()); -} - -TEST_F(QuotaManagerImplTest, DeleteSpecificClientTypeSingleHost) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com:1111/", kTemp, 1}, - }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com:2222/", kTemp, 2}, - }; - static const MockStorageKeyData kData3[] = { - {"http://foo.com:3333/", kTemp, 4}, - }; - static const MockStorageKeyData kData4[] = { - {"http://foo.com:4444/", kTemp, 8}, - }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData2, QuotaClientType::kServiceWorkerCache, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData3, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData4, QuotaClientType::kIndexedDatabase, - {blink::mojom::StorageType::kTemporary}); - - GetHostUsageWithBreakdown("foo.com", kTemp); - const int64_t predelete_foo_tmp = usage(); - - DeleteHostData("foo.com", kTemp, {QuotaClientType::kFileSystem}); - task_environment_.RunUntilIdle(); - GetHostUsageWithBreakdown("foo.com", kTemp); - EXPECT_EQ(predelete_foo_tmp - 1, usage()); - - DeleteHostData("foo.com", kTemp, {QuotaClientType::kServiceWorkerCache}); - task_environment_.RunUntilIdle(); - GetHostUsageWithBreakdown("foo.com", kTemp); - EXPECT_EQ(predelete_foo_tmp - 2 - 1, usage()); - - DeleteHostData("foo.com", kTemp, {QuotaClientType::kDatabase}); - task_environment_.RunUntilIdle(); - GetHostUsageWithBreakdown("foo.com", kTemp); - EXPECT_EQ(predelete_foo_tmp - 4 - 2 - 1, usage()); - - DeleteHostData("foo.com", kTemp, {QuotaClientType::kIndexedDatabase}); - task_environment_.RunUntilIdle(); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage()); } TEST_F(QuotaManagerImplTest, DeleteMultipleClientTypesSingleBucket) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com/", kTemp, 1}, + static const ClientBucketData kData1[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com/", kTemp, 2}, + static const ClientBucketData kData2[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 2}, }; - static const MockStorageKeyData kData3[] = { - {"http://foo.com/", kTemp, 4}, + static const ClientBucketData kData3[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 4}, }; - static const MockStorageKeyData kData4[] = { - {"http://foo.com/", kTemp, 8}, + static const ClientBucketData kData4[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 8}, }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData2, QuotaClientType::kServiceWorkerCache, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData3, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData4, QuotaClientType::kIndexedDatabase, - {blink::mojom::StorageType::kTemporary}); - - CreateBucketForTesting(ToStorageKey("http://foo.com/"), kDefaultBucketName, - kTemp); - ASSERT_TRUE(bucket_.ok()); - BucketInfo foo_bucket = bucket_.value(); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp}); + MockQuotaClient* sw_client = + CreateAndRegisterClient(QuotaClientType::kServiceWorkerCache, {kTemp}); + MockQuotaClient* db_client = + CreateAndRegisterClient(QuotaClientType::kDatabase, {kTemp}); + MockQuotaClient* idb_client = + CreateAndRegisterClient(QuotaClientType::kIndexedDatabase, {kTemp}); + RegisterClientBucketData(fs_client, kData1); + RegisterClientBucketData(sw_client, kData2); + RegisterClientBucketData(db_client, kData3); + RegisterClientBucketData(idb_client, kData4); + + auto foo_bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); + ASSERT_TRUE(foo_bucket.ok()); GetHostUsageWithBreakdown("foo.com", kTemp); const int64_t predelete_foo_tmp = usage(); - DeleteBucketData(foo_bucket.ToBucketLocator(), + DeleteBucketData(foo_bucket->ToBucketLocator(), {QuotaClientType::kFileSystem, QuotaClientType::kDatabase}); - task_environment_.RunUntilIdle(); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_foo_tmp - 4 - 1, usage()); - DeleteBucketData(foo_bucket.ToBucketLocator(), + DeleteBucketData(foo_bucket->ToBucketLocator(), {QuotaClientType::kServiceWorkerCache, QuotaClientType::kIndexedDatabase}); - task_environment_.RunUntilIdle(); - GetHostUsageWithBreakdown("foo.com", kTemp); - EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage()); -} - -TEST_F(QuotaManagerImplTest, DeleteMultipleClientTypesSingleHost) { - static const MockStorageKeyData kData1[] = { - {"http://foo.com:1111/", kTemp, 1}, - }; - static const MockStorageKeyData kData2[] = { - {"http://foo.com:2222/", kTemp, 2}, - }; - static const MockStorageKeyData kData3[] = { - {"http://foo.com:3333/", kTemp, 4}, - }; - static const MockStorageKeyData kData4[] = { - {"http://foo.com:4444/", kTemp, 8}, - }; - CreateAndRegisterClient(kData1, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData2, QuotaClientType::kServiceWorkerCache, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData3, QuotaClientType::kDatabase, - {blink::mojom::StorageType::kTemporary}); - CreateAndRegisterClient(kData4, QuotaClientType::kIndexedDatabase, - {blink::mojom::StorageType::kTemporary}); - - GetHostUsageWithBreakdown("foo.com", kTemp); - const int64_t predelete_foo_tmp = usage(); - - DeleteHostData( - "foo.com", kTemp, - {QuotaClientType::kFileSystem, QuotaClientType::kServiceWorkerCache}); - task_environment_.RunUntilIdle(); - GetHostUsageWithBreakdown("foo.com", kTemp); - EXPECT_EQ(predelete_foo_tmp - 2 - 1, usage()); - - DeleteHostData( - "foo.com", kTemp, - {QuotaClientType::kDatabase, QuotaClientType::kIndexedDatabase}); - task_environment_.RunUntilIdle(); GetHostUsageWithBreakdown("foo.com", kTemp); EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage()); } @@ -2927,47 +2730,47 @@ TEST_F(QuotaManagerImplTest, DeleteMultipleClientTypesSingleHost) { TEST_F(QuotaManagerImplTest, GetUsageAndQuota_Incognito) { ResetQuotaManagerImpl(true); - static const MockStorageKeyData kData[] = { - {"http://foo.com/", kTemp, 10}, - {"http://foo.com/", kPerm, 80}, + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 10}, + {"http://foo.com/", kDefaultBucketName, kPerm, 80}, }; - CreateAndRegisterClient(kData, QuotaClientType::kFileSystem, - {blink::mojom::StorageType::kTemporary, - blink::mojom::StorageType::kPersistent}); + MockQuotaClient* fs_client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(fs_client, kData); // Query global usage to warmup the usage tracker caching. GetGlobalUsage(kTemp); GetGlobalUsage(kPerm); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(80, usage()); - EXPECT_EQ(0, quota()); + auto result = + GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 80); + EXPECT_EQ(result.quota, 0); const int kPoolSize = 1000; const int kPerHostQuota = kPoolSize / 5; SetQuotaSettings(kPoolSize, kPerHostQuota, INT64_C(0)); - GetStorageCapacity(); - task_environment_.RunUntilIdle(); - EXPECT_EQ(kPoolSize, total_space()); - EXPECT_EQ(kPoolSize - 80 - 10, available_space()); + auto storage_capacity = GetStorageCapacity(); + EXPECT_EQ(storage_capacity.total_space, kPoolSize); + EXPECT_EQ(storage_capacity.available_space, kPoolSize - 80 - 10); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10, usage()); - EXPECT_LE(kPerHostQuota, quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10); + EXPECT_GE(result.quota, kPerHostQuota); mock_special_storage_policy()->AddUnlimited(GURL("http://foo.com/")); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(80, usage()); - EXPECT_EQ(available_space() + usage(), quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kPerm); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 80); + EXPECT_EQ(result.quota, storage_capacity.available_space + result.usage); - GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(10, usage()); - EXPECT_EQ(available_space() + usage(), quota()); + result = GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 10); + EXPECT_EQ(result.quota, storage_capacity.available_space + result.usage); } TEST_F(QuotaManagerImplTest, GetUsageAndQuota_SessionOnly) { @@ -2975,12 +2778,12 @@ TEST_F(QuotaManagerImplTest, GetUsageAndQuota_SessionOnly) { mock_special_storage_policy()->AddSessionOnly( kEpheremalStorageKey.origin().GetURL()); - GetUsageAndQuotaForWebApps(kEpheremalStorageKey, kTemp); + auto result = GetUsageAndQuotaForWebApps(kEpheremalStorageKey, kTemp); EXPECT_EQ(quota_manager_impl()->settings().session_only_per_host_quota, - quota()); + result.quota); - GetUsageAndQuotaForWebApps(kEpheremalStorageKey, kPerm); - EXPECT_EQ(0, quota()); + result = GetUsageAndQuotaForWebApps(kEpheremalStorageKey, kPerm); + EXPECT_EQ(0, result.quota); } TEST_F(QuotaManagerImplTest, MaybeRunStoragePressureCallback) { @@ -3011,10 +2814,10 @@ TEST_F(QuotaManagerImplTest, OverrideQuotaForStorageKey) { base::BindLambdaForTesting([&]() { run_loop.Quit(); })); run_loop.Run(); - GetUsageAndQuotaForWebApps(storage_key, kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(0, usage()); - EXPECT_EQ(5000, quota()); + auto result = GetUsageAndQuotaForWebApps(storage_key, kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.usage, 0); + EXPECT_EQ(result.quota, 5000); } TEST_F(QuotaManagerImplTest, OverrideQuotaForStorageKey_Disable) { @@ -3028,9 +2831,9 @@ TEST_F(QuotaManagerImplTest, OverrideQuotaForStorageKey_Disable) { base::BindLambdaForTesting([&]() { run_loop1.Quit(); })); run_loop1.Run(); - GetUsageAndQuotaForWebApps(storage_key, kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(5000, quota()); + auto result = GetUsageAndQuotaForWebApps(storage_key, kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.quota, 5000); base::RunLoop run_loop2; handle2->OverrideQuotaForStorageKey( @@ -3038,9 +2841,9 @@ TEST_F(QuotaManagerImplTest, OverrideQuotaForStorageKey_Disable) { base::BindLambdaForTesting([&]() { run_loop2.Quit(); })); run_loop2.Run(); - GetUsageAndQuotaForWebApps(storage_key, kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(9000, quota()); + result = GetUsageAndQuotaForWebApps(storage_key, kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.quota, 9000); base::RunLoop run_loop3; handle2->OverrideQuotaForStorageKey( @@ -3048,9 +2851,9 @@ TEST_F(QuotaManagerImplTest, OverrideQuotaForStorageKey_Disable) { base::BindLambdaForTesting([&]() { run_loop3.Quit(); })); run_loop3.Run(); - GetUsageAndQuotaForWebApps(storage_key, kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(kDefaultPerHostQuota, quota()); + result = GetUsageAndQuotaForWebApps(storage_key, kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.quota, kDefaultPerHostQuota); } TEST_F(QuotaManagerImplTest, WithdrawQuotaOverride) { @@ -3064,9 +2867,9 @@ TEST_F(QuotaManagerImplTest, WithdrawQuotaOverride) { base::BindLambdaForTesting([&]() { run_loop1.Quit(); })); run_loop1.Run(); - GetUsageAndQuotaForWebApps(storage_key, kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(5000, quota()); + auto result = GetUsageAndQuotaForWebApps(storage_key, kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.quota, 5000); base::RunLoop run_loop2; handle1->OverrideQuotaForStorageKey( @@ -3074,22 +2877,22 @@ TEST_F(QuotaManagerImplTest, WithdrawQuotaOverride) { base::BindLambdaForTesting([&]() { run_loop2.Quit(); })); run_loop2.Run(); - GetUsageAndQuotaForWebApps(storage_key, kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(8000, quota()); + result = GetUsageAndQuotaForWebApps(storage_key, kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.quota, 8000); // Quota should remain overridden if only one of the two handles withdraws // it's overrides handle2.reset(); - GetUsageAndQuotaForWebApps(storage_key, kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(8000, quota()); + result = GetUsageAndQuotaForWebApps(storage_key, kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.quota, 8000); handle1.reset(); task_environment_.RunUntilIdle(); - GetUsageAndQuotaForWebApps(storage_key, kTemp); - EXPECT_EQ(QuotaStatusCode::kOk, status()); - EXPECT_EQ(kDefaultPerHostQuota, quota()); + result = GetUsageAndQuotaForWebApps(storage_key, kTemp); + EXPECT_EQ(result.status, QuotaStatusCode::kOk); + EXPECT_EQ(result.quota, kDefaultPerHostQuota); } TEST_F(QuotaManagerImplTest, QuotaChangeEvent_LargePartitionPressure) { @@ -3104,7 +2907,6 @@ TEST_F(QuotaManagerImplTest, QuotaChangeEvent_LargePartitionPressure) { 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> { @@ -3113,7 +2915,6 @@ TEST_F(QuotaManagerImplTest, QuotaChangeEvent_LargePartitionPressure) { return std::make_tuple(total, available); }); GetStorageCapacity(); - task_environment_.RunUntilIdle(); EXPECT_TRUE(quota_change_dispatched); } @@ -3129,7 +2930,6 @@ TEST_F(QuotaManagerImplTest, QuotaChangeEvent_SmallPartitionPressure) { 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> { @@ -3141,8 +2941,83 @@ TEST_F(QuotaManagerImplTest, QuotaChangeEvent_SmallPartitionPressure) { return std::make_tuple(total, available); }); GetStorageCapacity(); - task_environment_.RunUntilIdle(); EXPECT_TRUE(quota_change_dispatched); } +TEST_F(QuotaManagerImplTest, DeleteBucketData_QuotaManagerDeletedImmediately) { + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + }; + MockQuotaClient* client = + CreateAndRegisterClient(QuotaClientType::kIndexedDatabase, {kTemp}); + RegisterClientBucketData(client, kData); + + QuotaErrorOr<BucketInfo> bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); + ASSERT_TRUE(bucket.ok()); + + base::test::TestFuture<QuotaStatusCode> delete_bucket_data_future; + quota_manager_impl_->DeleteBucketData( + bucket->ToBucketLocator(), {QuotaClientType::kIndexedDatabase}, + delete_bucket_data_future.GetCallback()); + quota_manager_impl_.reset(); + EXPECT_EQ(QuotaStatusCode::kErrorAbort, delete_bucket_data_future.Get()); +} + +TEST_F(QuotaManagerImplTest, DeleteBucketData_CallbackDeletesQuotaManager) { + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + }; + MockQuotaClient* client = + CreateAndRegisterClient(QuotaClientType::kIndexedDatabase, {kTemp}); + RegisterClientBucketData(client, kData); + + QuotaErrorOr<BucketInfo> bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); + ASSERT_TRUE(bucket.ok()); + + base::RunLoop run_loop; + QuotaStatusCode delete_bucket_data_result = QuotaStatusCode::kUnknown; + quota_manager_impl_->DeleteBucketData( + bucket->ToBucketLocator(), {QuotaClientType::kIndexedDatabase}, + base::BindLambdaForTesting([&](QuotaStatusCode status_code) { + quota_manager_impl_.reset(); + delete_bucket_data_result = status_code; + run_loop.Quit(); + })); + run_loop.Run(); + + EXPECT_EQ(QuotaStatusCode::kOk, delete_bucket_data_result); +} + +TEST_F(QuotaManagerImplTest, DeleteHostData_CallbackDeletesQuotaManager) { + static const ClientBucketData kData[] = { + {"http://foo.com/", kDefaultBucketName, kTemp, 1}, + }; + MockQuotaClient* client = + CreateAndRegisterClient(QuotaClientType::kIndexedDatabase, {kTemp}); + RegisterClientBucketData(client, kData); + + QuotaErrorOr<BucketInfo> bucket = + GetBucket(ToStorageKey("http://foo.com/"), kDefaultBucketName, kTemp); + ASSERT_TRUE(bucket.ok()); + + auto status = DeleteBucketData(bucket->ToBucketLocator(), + {QuotaClientType::kFileSystem}); + EXPECT_EQ(status, QuotaStatusCode::kOk); + + base::RunLoop run_loop; + QuotaStatusCode delete_host_data_result = QuotaStatusCode::kUnknown; + quota_manager_impl_->DeleteHostData( + "foo.com", kTemp, + base::BindLambdaForTesting([&](QuotaStatusCode status_code) { + quota_manager_impl_.reset(); + delete_host_data_result = status_code; + run_loop.Quit(); + })); + run_loop.Run(); + + EXPECT_EQ(QuotaStatusCode::kOk, delete_host_data_result); +} + } // namespace storage diff --git a/chromium/storage/browser/quota/quota_temporary_storage_evictor.cc b/chromium/storage/browser/quota/quota_temporary_storage_evictor.cc index 38faa8a0da4..8962a7d0c96 100644 --- a/chromium/storage/browser/quota/quota_temporary_storage_evictor.cc +++ b/chromium/storage/browser/quota/quota_temporary_storage_evictor.cc @@ -196,7 +196,7 @@ void QuotaTemporaryStorageEvictor::OnGotEvictionRoundInfo( // TODO(michaeln): if the reason for eviction is low physical disk space, // make 'unlimited' storage keys subject to eviction too. quota_eviction_handler_->GetEvictionBucket( - blink::mojom::StorageType::kTemporary, settings.pool_size, + blink::mojom::StorageType::kTemporary, base::BindOnce(&QuotaTemporaryStorageEvictor::OnGotEvictionBucket, weak_factory_.GetWeakPtr())); return; 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 ab8e9fc940f..9abddf3253d 100644 --- a/chromium/storage/browser/quota/quota_temporary_storage_evictor_unittest.cc +++ b/chromium/storage/browser/quota/quota_temporary_storage_evictor_unittest.cc @@ -63,7 +63,6 @@ class MockQuotaEvictionHandler : public QuotaEvictionHandler { } void GetEvictionBucket(StorageType type, - int64_t global_quota, GetBucketCallback callback) override { if (bucket_order_.empty()) { std::move(callback).Run(absl::nullopt); diff --git a/chromium/storage/browser/quota/storage_policy_observer.cc b/chromium/storage/browser/quota/storage_policy_observer.cc index 5fe45ed8ffe..884ade794c5 100644 --- a/chromium/storage/browser/quota/storage_policy_observer.cc +++ b/chromium/storage/browser/quota/storage_policy_observer.cc @@ -6,7 +6,9 @@ #include <utility> +#include "base/feature_list.h" #include "base/task/post_task.h" +#include "storage/browser/quota/quota_features.h" #include "url/origin.h" namespace storage { @@ -69,25 +71,23 @@ StoragePolicyObserver::~StoragePolicyObserver() { 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(); + StartTrackingOrigins({origin}); } void StoragePolicyObserver::StartTrackingOrigins( const std::vector<url::Origin>& origins) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + std::vector<std::pair<const GURL, OriginState>*> updates; 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()); + auto& entry = + *origin_state_.emplace(std::move(origin_url), OriginState()).first; + updates.push_back(&entry); } - OnPolicyChanged(); + OnPolicyChangedForOrigins(updates); } void StoragePolicyObserver::StopTrackingOrigin(const url::Origin& origin) { @@ -100,17 +100,8 @@ 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)); - } - } + for (auto& entry : origin_state_) + AddPolicyUpdate(&entry, &policy_updates); if (policy_updates.empty()) return; callback_.Run(std::move(policy_updates)); @@ -133,4 +124,34 @@ bool StoragePolicyObserver::ShouldPurgeOnShutdown(const GURL& origin) { return true; } +void StoragePolicyObserver::OnPolicyChangedForOrigins( + const std::vector<std::pair<const GURL, OriginState>*>& updated_origins) { + if (!base::FeatureList::IsEnabled( + features::kOnlySendStoragePolicyUpdatesForModifiedOrigins)) { + OnPolicyChanged(); + return; + } + + std::vector<storage::mojom::StoragePolicyUpdatePtr> policy_updates; + for (auto* entry : updated_origins) + AddPolicyUpdate(entry, &policy_updates); + if (policy_updates.empty()) + return; + callback_.Run(std::move(policy_updates)); +} + +void StoragePolicyObserver::AddPolicyUpdate( + std::pair<const GURL, OriginState>* entry, + std::vector<storage::mojom::StoragePolicyUpdatePtr>* policy_updates) { + 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)); + } +} + } // namespace storage diff --git a/chromium/storage/browser/quota/storage_policy_observer.h b/chromium/storage/browser/quota/storage_policy_observer.h index 01990226bb6..a9a1f01eb97 100644 --- a/chromium/storage/browser/quota/storage_policy_observer.h +++ b/chromium/storage/browser/quota/storage_policy_observer.h @@ -67,6 +67,13 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) StoragePolicyObserver { // Indicates the last value for `purge_on_shutdown` that was communicated. bool will_purge_on_shutdown = false; }; + + void OnPolicyChangedForOrigins( + const std::vector<std::pair<const GURL, OriginState>*>& updated_origins); + void AddPolicyUpdate( + std::pair<const GURL, OriginState>* entry, + std::vector<storage::mojom::StoragePolicyUpdatePtr>* policy_updates); + // 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. diff --git a/chromium/storage/browser/quota/storage_policy_observer_unittest.cc b/chromium/storage/browser/quota/storage_policy_observer_unittest.cc new file mode 100644 index 00000000000..8b879a5fb27 --- /dev/null +++ b/chromium/storage/browser/quota/storage_policy_observer_unittest.cc @@ -0,0 +1,92 @@ +// Copyright 2022 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 "base/test/task_environment.h" +#include "storage/browser/test/mock_special_storage_policy.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace storage { +namespace { + +constexpr char kOrigin1[] = "http://origin1.com"; +constexpr char kOrigin2[] = "http://origin2.com"; +constexpr char kSessionOrigin1[] = "http://session-origin1.com"; +constexpr char kSessionOrigin2[] = "http://session-origin2.com"; + +class StoragePolicyObserverTest : public testing::Test { + public: + StoragePolicyObserverTest() + : mock_policy_(base::MakeRefCounted<MockSpecialStoragePolicy>()), + observer_(std::make_unique<StoragePolicyObserver>( + base::BindRepeating(&StoragePolicyObserverTest::OnPolicyUpdates, + base::Unretained(this)), + task_environment_.GetMainThreadTaskRunner(), + mock_policy_)) { + mock_policy_->AddSessionOnly(GURL(kSessionOrigin1)); + mock_policy_->AddSessionOnly(GURL(kSessionOrigin2)); + // Make sure the IO thread observer is created. + task_environment_.RunUntilIdle(); + } + + ~StoragePolicyObserverTest() override { + observer_.reset(); + // Make sure the IO thread observer is destroyed. + task_environment_.RunUntilIdle(); + } + + protected: + void OnPolicyUpdates( + std::vector<storage::mojom::StoragePolicyUpdatePtr> updates) { + ASSERT_TRUE(latest_updates_.empty()); + latest_updates_ = std::move(updates); + } + + std::vector<storage::mojom::StoragePolicyUpdatePtr> TakeLatestUpdates() { + return std::move(latest_updates_); + } + + void ExpectUpdates(std::map<url::Origin, bool> expectations) { + auto updates = TakeLatestUpdates(); + EXPECT_EQ(updates.size(), expectations.size()); + for (const auto& update : updates) { + auto it = expectations.find(update->origin); + ASSERT_NE(it, expectations.end()); + EXPECT_EQ(it->second, update->purge_on_shutdown); + } + } + + base::test::TaskEnvironment task_environment_; + scoped_refptr<MockSpecialStoragePolicy> mock_policy_; + std::unique_ptr<StoragePolicyObserver> observer_; + + private: + std::vector<storage::mojom::StoragePolicyUpdatePtr> latest_updates_; +}; + +TEST_F(StoragePolicyObserverTest, NonSessionOnlyOriginDoesNotCreateUpdate) { + observer_->StartTrackingOrigins({url::Origin::Create(GURL(kOrigin1)), + url::Origin::Create(GURL(kOrigin2))}); + auto updates = TakeLatestUpdates(); + EXPECT_TRUE(updates.empty()); +} + +TEST_F(StoragePolicyObserverTest, SessionOnlyOriginCreateUpdate) { + url::Origin origin1 = url::Origin::Create(GURL(kSessionOrigin1)); + observer_->StartTrackingOrigin(origin1); + ExpectUpdates({{origin1, true}}); + + url::Origin origin2 = url::Origin::Create(GURL(kSessionOrigin2)); + observer_->StartTrackingOrigin(origin2); + ExpectUpdates({{origin2, true}}); + + mock_policy_->RemoveSessionOnly(GURL(kSessionOrigin1)); + mock_policy_->RemoveSessionOnly(GURL(kSessionOrigin2)); + observer_->OnPolicyChanged(); + ExpectUpdates({{origin1, false}, {origin2, false}}); +} + +} // namespace +} // namespace storage diff --git a/chromium/storage/browser/quota/usage_tracker.cc b/chromium/storage/browser/quota/usage_tracker.cc index 87624191bd2..f6018b20d7d 100644 --- a/chromium/storage/browser/quota/usage_tracker.cc +++ b/chromium/storage/browser/quota/usage_tracker.cc @@ -11,6 +11,7 @@ #include "base/barrier_closure.h" #include "base/bind.h" +#include "components/services/storage/public/cpp/buckets/constants.h" #include "storage/browser/quota/client_usage_tracker.h" #include "storage/browser/quota/quota_client_type.h" #include "third_party/blink/public/common/storage_key/storage_key.h" @@ -18,10 +19,6 @@ namespace storage { struct UsageTracker::AccumulateInfo { - AccumulateInfo() = default; - ~AccumulateInfo() = default; - - size_t pending_clients = 0; int64_t usage = 0; int64_t unlimited_usage = 0; blink::mojom::UsageBreakdownPtr usage_breakdown = @@ -29,11 +26,12 @@ struct UsageTracker::AccumulateInfo { }; UsageTracker::UsageTracker( + QuotaManagerImpl* quota_manager_impl, const base::flat_map<mojom::QuotaClient*, QuotaClientType>& client_types, blink::mojom::StorageType type, scoped_refptr<SpecialStoragePolicy> special_storage_policy) - : type_(type) { - size_t client_count = 0; + : quota_manager_impl_(quota_manager_impl), type_(type) { + DCHECK(quota_manager_impl_); for (const auto& client_and_type : client_types) { mojom::QuotaClient* client = client_and_type.first; @@ -41,41 +39,22 @@ UsageTracker::UsageTracker( client_tracker_map_[client_type].push_back( std::make_unique<ClientUsageTracker>(this, client, type, special_storage_policy)); - ++client_count; } - client_count_ = client_count; } UsageTracker::~UsageTracker() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -void UsageTracker::GetGlobalUsage(GlobalUsageCallback callback) { +void UsageTracker::GetGlobalUsage(UsageCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); global_usage_callbacks_.emplace_back(std::move(callback)); if (global_usage_callbacks_.size() > 1) return; - AccumulateInfo* info = new AccumulateInfo; - // Calling GetGlobalUsage(accumulator) may synchronously - // return if the usage is cached, which may in turn dispatch - // the completion callback before we finish looping over - // all clients (because info->pending_clients may reach 0 - // during the loop). - // To avoid this, we add one more pending client as a sentinel - // and fire the sentinel callback at the end. - info->pending_clients = client_tracker_map_.size() + 1; - auto accumulator = - base::BindRepeating(&UsageTracker::AccumulateClientGlobalUsage, - weak_factory_.GetWeakPtr(), base::Owned(info)); - - for (const auto& client_type_and_trackers : client_tracker_map_) { - for (const auto& client_tracker : client_type_and_trackers.second) - client_tracker->GetGlobalUsage(accumulator); - } - - // Fire the sentinel as we've now called GetGlobalUsage for all clients. - accumulator.Run(0, 0); + quota_manager_impl_->GetBucketsForType( + type_, base::BindOnce(&UsageTracker::DidGetBucketsForType, + weak_factory_.GetWeakPtr())); } void UsageTracker::GetHostUsageWithBreakdown( @@ -88,30 +67,30 @@ void UsageTracker::GetHostUsageWithBreakdown( if (host_callbacks.size() > 1) return; - AccumulateInfo* info = new AccumulateInfo; - // We use BarrierClosure here instead of manually counting pending_clients. - base::RepeatingClosure barrier = base::BarrierClosure( - client_tracker_map_.size(), - base::BindOnce(&UsageTracker::FinallySendHostUsageWithBreakdown, - weak_factory_.GetWeakPtr(), base::Owned(info), host)); + quota_manager_impl_->GetBucketsForHost( + host, type_, + base::BindOnce(&UsageTracker::DidGetBucketsForHost, + weak_factory_.GetWeakPtr(), host)); +} - for (const auto& client_type_and_trackers : client_tracker_map_) { - for (const auto& client_tracker : client_type_and_trackers.second) { - client_tracker->GetHostUsage( - host, base::BindOnce(&UsageTracker::AccumulateClientHostUsage, - weak_factory_.GetWeakPtr(), barrier, info, host, - client_type_and_trackers.first)); - } - } +void UsageTracker::UpdateBucketUsageCache(QuotaClientType client_type, + const BucketLocator& bucket, + int64_t delta) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(client_tracker_map_.count(client_type)); + + for (const auto& client_tracker : client_tracker_map_[client_type]) + client_tracker->UpdateBucketUsageCache(bucket, delta); } -void UsageTracker::UpdateUsageCache(QuotaClientType client_type, - const blink::StorageKey& storage_key, - int64_t delta) { +void UsageTracker::DeleteBucketCache(QuotaClientType client_type, + const BucketLocator& bucket) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(client_tracker_map_.count(client_type)); + DCHECK_EQ(bucket.type, type_); + for (const auto& client_tracker : client_tracker_map_[client_type]) - client_tracker->UpdateUsageCache(storage_key, delta); + client_tracker->DeleteBucketCache(bucket); } int64_t UsageTracker::GetCachedUsage() const { @@ -154,20 +133,6 @@ std::map<blink::StorageKey, int64_t> UsageTracker::GetCachedStorageKeysUsage() return storage_key_usage; } -std::set<blink::StorageKey> UsageTracker::GetCachedStorageKeys() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::set<blink::StorageKey> storage_keys; - for (const auto& client_type_and_trackers : client_tracker_map_) { - for (const auto& client_tracker : client_type_and_trackers.second) { - std::set<blink::StorageKey> client_storage_keys = - client_tracker->GetCachedStorageKeys(); - for (const auto& client_storage_key : client_storage_keys) - storage_keys.insert(client_storage_key); - } - } - return storage_keys; -} - void UsageTracker::SetUsageCacheEnabled(QuotaClientType client_type, const blink::StorageKey& storage_key, bool enabled) { @@ -177,75 +142,157 @@ void UsageTracker::SetUsageCacheEnabled(QuotaClientType client_type, client_tracker->SetUsageCacheEnabled(storage_key, enabled); } -void UsageTracker::AccumulateClientGlobalUsage(AccumulateInfo* info, - int64_t usage, - int64_t unlimited_usage) { - DCHECK_GT(info->pending_clients, 0U); +void UsageTracker::DidGetBucketsForType( + QuotaErrorOr<std::set<BucketLocator>> result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - info->usage += usage; - info->unlimited_usage += unlimited_usage; - if (--info->pending_clients) + auto info = std::make_unique<AccumulateInfo>(); + if (!result.ok()) { + // Return with invalid values on error. + info->usage = -1; + info->unlimited_usage = -1; + FinallySendGlobalUsage(std::move(info)); return; + } - // Defend against confusing inputs from clients. - if (info->usage < 0) - info->usage = 0; + const std::set<BucketLocator>& buckets = result.value(); + if (buckets.empty()) { + FinallySendGlobalUsage(std::move(info)); + return; + } - // TODO(michaeln): The unlimited number is not trustworthy, it - // can get out of whack when apps are installed or uninstalled. - if (info->unlimited_usage > info->usage) - info->unlimited_usage = info->usage; - else if (info->unlimited_usage < 0) - info->unlimited_usage = 0; + auto* info_ptr = info.get(); + base::RepeatingClosure barrier = base::BarrierClosure( + client_tracker_map_.size(), + base::BindOnce(&UsageTracker::FinallySendGlobalUsage, + weak_factory_.GetWeakPtr(), std::move(info))); - // Moving callbacks out of the original vector early handles the case where a - // callback makes a new quota call. - std::vector<GlobalUsageCallback> pending_callbacks; - pending_callbacks.swap(global_usage_callbacks_); - for (auto& callback : pending_callbacks) - std::move(callback).Run(info->usage, info->unlimited_usage); + for (const auto& client_type_and_trackers : client_tracker_map_) { + for (const auto& client_tracker : client_type_and_trackers.second) { + client_tracker->GetBucketsUsage( + buckets, + // base::Unretained usage is safe here because BarrierClosure holds + // the std::unque_ptr that keeps AccumulateInfo alive, and the + // BarrierClosure will outlive all the AccumulateClientGlobalUsage + // closures. + base::BindOnce(&UsageTracker::AccumulateClientGlobalUsage, + weak_factory_.GetWeakPtr(), barrier, + base::Unretained(info_ptr))); + } + } +} + +void UsageTracker::DidGetBucketsForHost( + const std::string& host, + QuotaErrorOr<std::set<BucketLocator>> result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + auto info = std::make_unique<AccumulateInfo>(); + if (!result.ok()) { + // Return with invalid values on error. + info->usage = -1; + info->unlimited_usage = -1; + FinallySendHostUsageWithBreakdown(std::move(info), host); + return; + } + + const std::set<BucketLocator>& buckets = result.value(); + if (buckets.empty()) { + FinallySendHostUsageWithBreakdown(std::move(info), host); + return; + } + + auto* info_ptr = info.get(); + base::RepeatingClosure barrier = base::BarrierClosure( + client_tracker_map_.size(), + base::BindOnce(&UsageTracker::FinallySendHostUsageWithBreakdown, + weak_factory_.GetWeakPtr(), std::move(info), host)); + + for (const auto& client_type_and_trackers : client_tracker_map_) { + for (const auto& client_tracker : client_type_and_trackers.second) { + client_tracker->GetBucketsUsage( + buckets, + // base::Unretained usage is safe here because BarrierClosure holds + // the std::unque_ptr that keeps AccumulateInfo alive, and the + // BarrierClosure will outlive all the AccumulateClientGlobalUsage + // closures. + base::BindOnce(&UsageTracker::AccumulateClientHostUsage, + weak_factory_.GetWeakPtr(), barrier, + base::Unretained(info_ptr), host, + client_type_and_trackers.first)); + } + } +} + +void UsageTracker::AccumulateClientGlobalUsage( + base::OnceClosure barrier_callback, + AccumulateInfo* info, + int64_t total_usage, + int64_t unlimited_usage) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_GE(unlimited_usage, 0); + DCHECK_GE(total_usage, unlimited_usage); + + info->usage += total_usage; + info->unlimited_usage += unlimited_usage; + + std::move(barrier_callback).Run(); } -void UsageTracker::AccumulateClientHostUsage(base::OnceClosure callback, +void UsageTracker::AccumulateClientHostUsage(base::OnceClosure barrier_callback, AccumulateInfo* info, const std::string& host, QuotaClientType client, - int64_t usage) { + int64_t total_usage, + int64_t unlimited_usage) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - info->usage += usage; - // Defend against confusing inputs from clients. - if (info->usage < 0) - info->usage = 0; + DCHECK_GE(unlimited_usage, 0); + DCHECK_GE(total_usage, unlimited_usage); + + info->usage += total_usage; switch (client) { case QuotaClientType::kFileSystem: - info->usage_breakdown->fileSystem += usage; + info->usage_breakdown->fileSystem += total_usage; break; case QuotaClientType::kDatabase: - info->usage_breakdown->webSql += usage; + info->usage_breakdown->webSql += total_usage; break; case QuotaClientType::kIndexedDatabase: - info->usage_breakdown->indexedDatabase += usage; + info->usage_breakdown->indexedDatabase += total_usage; break; case QuotaClientType::kServiceWorkerCache: - info->usage_breakdown->serviceWorkerCache += usage; + info->usage_breakdown->serviceWorkerCache += total_usage; break; case QuotaClientType::kServiceWorker: - info->usage_breakdown->serviceWorker += usage; + info->usage_breakdown->serviceWorker += total_usage; break; case QuotaClientType::kBackgroundFetch: - info->usage_breakdown->backgroundFetch += usage; + info->usage_breakdown->backgroundFetch += total_usage; break; case QuotaClientType::kNativeIO: - info->usage_breakdown->fileSystem += usage; + info->usage_breakdown->fileSystem += total_usage; break; } - std::move(callback).Run(); + std::move(barrier_callback).Run(); } -void UsageTracker::FinallySendHostUsageWithBreakdown(AccumulateInfo* info, - const std::string& host) { +void UsageTracker::FinallySendGlobalUsage( + std::unique_ptr<AccumulateInfo> info) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_GE(info->unlimited_usage, -1); + DCHECK_GE(info->usage, info->unlimited_usage); + + // Moving callbacks out of the original vector early handles the case where a + // callback makes a new quota call. + std::vector<UsageCallback> pending_callbacks; + pending_callbacks.swap(global_usage_callbacks_); + for (auto& callback : pending_callbacks) + std::move(callback).Run(info->usage, info->unlimited_usage); +} + +void UsageTracker::FinallySendHostUsageWithBreakdown( + std::unique_ptr<AccumulateInfo> info, + const std::string& host) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto it = host_usage_callbacks_.find(host); if (it == host_usage_callbacks_.end()) @@ -257,9 +304,8 @@ void UsageTracker::FinallySendHostUsageWithBreakdown(AccumulateInfo* info, << "host_usage_callbacks_ should only have non-empty callback lists"; host_usage_callbacks_.erase(it); - for (auto& callback : pending_callbacks) { + for (auto& callback : pending_callbacks) std::move(callback).Run(info->usage, info->usage_breakdown->Clone()); - } } } // namespace storage diff --git a/chromium/storage/browser/quota/usage_tracker.h b/chromium/storage/browser/quota/usage_tracker.h index 7c300eae9cb..b95cdd47bcb 100644 --- a/chromium/storage/browser/quota/usage_tracker.h +++ b/chromium/storage/browser/quota/usage_tracker.h @@ -18,9 +18,12 @@ #include "base/containers/flat_map.h" #include "base/memory/scoped_refptr.h" #include "base/sequence_checker.h" +#include "components/services/storage/public/cpp/buckets/bucket_info.h" +#include "components/services/storage/public/cpp/buckets/bucket_locator.h" #include "components/services/storage/public/mojom/quota_client.mojom.h" #include "storage/browser/quota/quota_callbacks.h" #include "storage/browser/quota/quota_client_type.h" +#include "storage/browser/quota/quota_manager_impl.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.h" @@ -45,6 +48,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) UsageTracker // The caller must ensure that all mojo::QuotaClient instances outlive this // instance. UsageTracker( + QuotaManagerImpl* quota_manager_impl, const base::flat_map<mojom::QuotaClient*, QuotaClientType>& client_types, blink::mojom::StorageType type, scoped_refptr<SpecialStoragePolicy> special_storage_policy); @@ -54,26 +58,48 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) UsageTracker ~UsageTracker() override; - blink::mojom::StorageType type() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return type_; - } + // Retrieves all buckets for type from QuotaDatabase and requests bucket usage + // from each registered client. Returns cached bucket usage if one exists for + // a bucket. + void GetGlobalUsage(UsageCallback callback); - void GetGlobalUsage(GlobalUsageCallback callback); + // Retrieves all buckets for host from QuotaDatabase and requests bucket usage + // from each registered client. Returns cached bucket usage if one exists for + // a bucket. void GetHostUsageWithBreakdown(const std::string& host, UsageWithBreakdownCallback callback); - void UpdateUsageCache(QuotaClientType client_type, - const blink::StorageKey& storage_key, - int64_t delta); + + // Updates usage for `bucket` in the ClientUsageTracker for `client_type`. + void UpdateBucketUsageCache(QuotaClientType client_type, + const BucketLocator& bucket, + int64_t delta); + + // Deletes `bucket` from the cache for `client_type` if it exists. + // Called by QuotaManagerImpl::BucketDataDeleter. + void DeleteBucketCache(QuotaClientType client_type, + const BucketLocator& bucket); + + // Returns accumulated usage for all cached buckets from registered + // ClientUsageTrackers. Used to determine storage pressure. int64_t GetCachedUsage() const; + + // Retrieves all cached usage organized by host. Expected to be called after + // GetGlobalUsage which retrieves and caches host usage. std::map<std::string, int64_t> GetCachedHostsUsage() const; + + // Returns all cached usage organized by StorageKey. Used for histogram + // recording. std::map<blink::StorageKey, int64_t> GetCachedStorageKeysUsage() const; - std::set<blink::StorageKey> GetCachedStorageKeys() const; + + // Checks if there are ongoing tasks to get global or host usage. Used to + // prevent a UsageTracker reset from happening before a task is complete. bool IsWorking() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return !global_usage_callbacks_.empty() || !host_usage_callbacks_.empty(); } + // Sets if a `storage_key` for `client_type` should / should not be excluded + // from quota restrictions. void SetUsageCacheEnabled(QuotaClientType client_type, const blink::StorageKey& storage_key, bool enabled); @@ -82,29 +108,42 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) UsageTracker struct AccumulateInfo; friend class ClientUsageTracker; - void AccumulateClientGlobalUsage(AccumulateInfo* info, - int64_t usage, + void DidGetBucketsForType(QuotaErrorOr<std::set<BucketLocator>> result); + void DidGetBucketsForHost(const std::string& host, + QuotaErrorOr<std::set<BucketLocator>> result); + void DidGetBucketForUsage(QuotaClientType client_type, + int64_t delta, + QuotaErrorOr<BucketInfo> result); + + void AccumulateClientGlobalUsage(base::OnceClosure barrier_callback, + AccumulateInfo* info, + int64_t total_usage, int64_t unlimited_usage); - void AccumulateClientHostUsage(base::OnceClosure callback, + void AccumulateClientHostUsage(base::OnceClosure barrier_callback, AccumulateInfo* info, const std::string& host, QuotaClientType client, - int64_t usage); - void FinallySendHostUsageWithBreakdown(AccumulateInfo* info, + int64_t total_usage, + int64_t unlimited_usage); + + void FinallySendGlobalUsage(std::unique_ptr<AccumulateInfo> info); + void FinallySendHostUsageWithBreakdown(std::unique_ptr<AccumulateInfo> info, const std::string& host); + SEQUENCE_CHECKER(sequence_checker_); + + // Raw pointer usage is safe because `quota_manager_impl_` owns `this` and + // is therefore valid throughout its lifetime. + QuotaManagerImpl* const quota_manager_impl_; const blink::mojom::StorageType type_; base::flat_map<QuotaClientType, std::vector<std::unique_ptr<ClientUsageTracker>>> client_tracker_map_; - size_t client_count_; - std::vector<GlobalUsageCallback> global_usage_callbacks_; + std::vector<UsageCallback> global_usage_callbacks_; std::map<std::string, std::vector<UsageWithBreakdownCallback>> host_usage_callbacks_; - SEQUENCE_CHECKER(sequence_checker_); - base::WeakPtrFactory<UsageTracker> weak_factory_{this}; }; diff --git a/chromium/storage/browser/quota/usage_tracker_unittest.cc b/chromium/storage/browser/quota/usage_tracker_unittest.cc index 28e09638fe2..477d8fc4455 100644 --- a/chromium/storage/browser/quota/usage_tracker_unittest.cc +++ b/chromium/storage/browser/quota/usage_tracker_unittest.cc @@ -4,18 +4,25 @@ #include <stdint.h> +#include <cstdint> #include <utility> #include <vector> #include "base/bind.h" +#include "base/files/scoped_temp_dir.h" #include "base/location.h" #include "base/memory/scoped_refptr.h" #include "base/run_loop.h" #include "base/task/single_thread_task_runner.h" #include "base/test/task_environment.h" +#include "base/test/test_future.h" #include "base/threading/thread_task_runner_handle.h" +#include "components/services/storage/public/cpp/buckets/bucket_info.h" +#include "components/services/storage/public/cpp/buckets/constants.h" +#include "components/services/storage/public/cpp/quota_error_or.h" #include "components/services/storage/public/mojom/quota_client.mojom.h" #include "storage/browser/quota/quota_client_type.h" +#include "storage/browser/quota/quota_manager_impl.h" #include "storage/browser/quota/usage_tracker.h" #include "storage/browser/test/mock_special_storage_policy.h" #include "testing/gtest/include/gtest/gtest.h" @@ -29,17 +36,6 @@ namespace storage { namespace { -void DidGetGlobalUsage(bool* done, - int64_t* usage_out, - int64_t* unlimited_usage_out, - int64_t usage, - int64_t unlimited_usage) { - EXPECT_FALSE(*done); - *done = true; - *usage_out = usage; - *unlimited_usage_out = unlimited_usage; -} - class UsageTrackerTestQuotaClient : public mojom::QuotaClient { public: UsageTrackerTestQuotaClient() = default; @@ -48,11 +44,10 @@ class UsageTrackerTestQuotaClient : public mojom::QuotaClient { UsageTrackerTestQuotaClient& operator=(const UsageTrackerTestQuotaClient&) = delete; - void GetStorageKeyUsage(const StorageKey& storage_key, - StorageType type, - GetStorageKeyUsageCallback callback) override { - EXPECT_EQ(StorageType::kTemporary, type); - int64_t usage = GetUsage(storage_key); + void GetBucketUsage(const BucketLocator& bucket, + GetBucketUsageCallback callback) override { + EXPECT_EQ(StorageType::kTemporary, bucket.type); + int64_t usage = GetUsage(bucket); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), usage)); } @@ -60,33 +55,19 @@ class UsageTrackerTestQuotaClient : public mojom::QuotaClient { void GetStorageKeysForType(StorageType type, GetStorageKeysForTypeCallback callback) override { EXPECT_EQ(StorageType::kTemporary, type); - std::vector<StorageKey> storage_keys; - for (const auto& storage_key_usage_pair : storage_key_usage_map_) - storage_keys.push_back(storage_key_usage_pair.first); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(std::move(callback), std::move(storage_keys))); - } - - void GetStorageKeysForHost(StorageType type, - const std::string& host, - GetStorageKeysForHostCallback callback) override { - EXPECT_EQ(StorageType::kTemporary, type); - std::vector<StorageKey> storage_keys; - for (const auto& storage_key_usage_pair : storage_key_usage_map_) { - if (storage_key_usage_pair.first.origin().host() == host) - storage_keys.push_back(storage_key_usage_pair.first); - } + std::set<StorageKey> storage_keys; + for (const auto& bucket_usage_pair : bucket_usage_map_) + storage_keys.emplace(bucket_usage_pair.first.storage_key); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(std::move(callback), std::move(storage_keys))); + FROM_HERE, base::BindOnce(std::move(callback), + std::vector<StorageKey>(storage_keys.begin(), + storage_keys.end()))); } - void DeleteStorageKeyData(const StorageKey& storage_key, - StorageType type, - DeleteStorageKeyDataCallback callback) override { - EXPECT_EQ(StorageType::kTemporary, type); - storage_key_usage_map_.erase(storage_key); + void DeleteBucketData(const BucketLocator& bucket, + DeleteBucketDataCallback callback) override { + EXPECT_EQ(StorageType::kTemporary, bucket.type); + bucket_usage_map_.erase(bucket); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), QuotaStatusCode::kOk)); } @@ -96,23 +77,19 @@ class UsageTrackerTestQuotaClient : public mojom::QuotaClient { std::move(callback).Run(); } - int64_t GetUsage(const StorageKey& storage_key) { - auto it = storage_key_usage_map_.find(storage_key); - if (it == storage_key_usage_map_.end()) + int64_t GetUsage(const BucketLocator& bucket) { + auto it = bucket_usage_map_.find(bucket); + if (it == bucket_usage_map_.end()) return 0; return it->second; } - void SetUsage(const StorageKey& storage_key, int64_t usage) { - storage_key_usage_map_[storage_key] = usage; - } - - int64_t UpdateUsage(const StorageKey& storage_key, int64_t delta) { - return storage_key_usage_map_[storage_key] += delta; + int64_t UpdateUsage(const BucketLocator& bucket, int64_t delta) { + return bucket_usage_map_[bucket] += delta; } private: - std::map<StorageKey, int64_t> storage_key_usage_map_; + std::map<BucketLocator, int64_t> bucket_usage_map_; }; } // namespace @@ -121,65 +98,47 @@ class UsageTrackerTest : public testing::Test { public: UsageTrackerTest() : storage_policy_(base::MakeRefCounted<MockSpecialStoragePolicy>()), - quota_client_(std::make_unique<UsageTrackerTestQuotaClient>()), - usage_tracker_(GetQuotaClientMap(), - StorageType::kTemporary, - storage_policy_.get()) {} + quota_client_(std::make_unique<UsageTrackerTestQuotaClient>()) { + EXPECT_TRUE(base_.CreateUniqueTempDir()); + quota_manager_ = base::MakeRefCounted<QuotaManagerImpl>( + /*is_incognito=*/false, base_.GetPath(), + base::ThreadTaskRunnerHandle::Get().get(), + /*quota_change_callback=*/base::DoNothing(), storage_policy_.get(), + GetQuotaSettingsFunc()); + usage_tracker_ = std::make_unique<UsageTracker>( + quota_manager_.get(), GetQuotaClientMap(), StorageType::kTemporary, + storage_policy_.get()); + } UsageTrackerTest(const UsageTrackerTest&) = delete; UsageTrackerTest& operator=(const UsageTrackerTest&) = delete; ~UsageTrackerTest() override = default; - UsageTracker* usage_tracker() { - return &usage_tracker_; - } - - static void DidGetUsageBreakdown( - bool* done, - int64_t* usage_out, - blink::mojom::UsageBreakdownPtr* usage_breakdown_out, - int64_t usage, - blink::mojom::UsageBreakdownPtr usage_breakdown) { - EXPECT_FALSE(*done); - *usage_out = usage; - *usage_breakdown_out = std::move(usage_breakdown); - *done = true; - } - - void UpdateUsage(const StorageKey& storage_key, int64_t delta) { - quota_client_->UpdateUsage(storage_key, delta); - usage_tracker_.UpdateUsageCache(QuotaClientType::kFileSystem, storage_key, - delta); + void UpdateUsage(const BucketInfo& bucket, int64_t delta) { + quota_client_->UpdateUsage(bucket.ToBucketLocator(), delta); + usage_tracker_->UpdateBucketUsageCache(QuotaClientType::kFileSystem, + bucket.ToBucketLocator(), delta); base::RunLoop().RunUntilIdle(); } - void UpdateUsageWithoutNotification(const StorageKey& storage_key, - int64_t delta) { - quota_client_->UpdateUsage(storage_key, delta); + void UpdateUsageWithoutNotification(const BucketInfo& bucket, int64_t delta) { + quota_client_->UpdateUsage(bucket.ToBucketLocator(), delta); } void GetGlobalUsage(int64_t* usage, int64_t* unlimited_usage) { - bool done = false; - usage_tracker_.GetGlobalUsage( - base::BindOnce(&DidGetGlobalUsage, &done, usage, unlimited_usage)); - base::RunLoop().RunUntilIdle(); - - EXPECT_TRUE(done); + base::test::TestFuture<int64_t, int64_t> future; + usage_tracker_->GetGlobalUsage(future.GetCallback()); + *usage = future.Get<0>(); + *unlimited_usage = future.Get<1>(); } std::pair<int64_t, blink::mojom::UsageBreakdownPtr> GetHostUsageWithBreakdown( const std::string& host) { - int64_t usage; - blink::mojom::UsageBreakdownPtr usage_breakdown; - bool done = false; - - usage_tracker_.GetHostUsageWithBreakdown( - host, base::BindOnce(&UsageTrackerTest::DidGetUsageBreakdown, &done, - &usage, &usage_breakdown)); - base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(done); - return std::make_pair(usage, std::move(usage_breakdown)); + base::test::TestFuture<int64_t, blink::mojom::UsageBreakdownPtr> future; + usage_tracker_->GetHostUsageWithBreakdown(host, future.GetCallback()); + return std::make_pair(future.Get<0>(), + std::move(std::get<1>(future.Take()))); } void GrantUnlimitedStoragePolicy(const StorageKey& storage_key) { @@ -199,8 +158,29 @@ class UsageTrackerTest : public testing::Test { } void SetUsageCacheEnabled(const StorageKey& storage_key, bool enabled) { - usage_tracker_.SetUsageCacheEnabled(QuotaClientType::kFileSystem, - storage_key, enabled); + usage_tracker_->SetUsageCacheEnabled(QuotaClientType::kFileSystem, + storage_key, enabled); + } + + BucketInfo CreateBucket(const StorageKey& storage_key, + const std::string& bucket_name) { + base::test::TestFuture<QuotaErrorOr<BucketInfo>> future; + quota_manager_->CreateBucketForTesting(storage_key, bucket_name, + StorageType::kTemporary, + future.GetCallback()); + QuotaErrorOr<BucketInfo> bucket_result = future.Take(); + DCHECK(bucket_result.ok()); + return bucket_result.value(); + } + + void OpenDatabase() { quota_manager_->EnsureDatabaseOpened(); } + + void disable_quota_database(bool disable) { + quota_manager_->database_->SetDisabledForTesting(disable); + } + + void disable_database_bootstrap(bool disable) { + quota_manager_->SetBootstrapDisabledForTesting(disable); } private: @@ -215,7 +195,10 @@ class UsageTrackerTest : public testing::Test { scoped_refptr<MockSpecialStoragePolicy> storage_policy_; std::unique_ptr<UsageTrackerTestQuotaClient> quota_client_; - UsageTracker usage_tracker_; + + scoped_refptr<QuotaManagerImpl> quota_manager_; + std::unique_ptr<UsageTracker> usage_tracker_; + base::ScopedTempDir base_; }; TEST_F(UsageTrackerTest, GrantAndRevokeUnlimitedStorage) { @@ -231,7 +214,9 @@ TEST_F(UsageTrackerTest, GrantAndRevokeUnlimitedStorage) { StorageKey::CreateFromStringForTesting("http://example.com"); const std::string& host = storage_key.origin().host(); - UpdateUsage(storage_key, 100); + BucketInfo bucket = CreateBucket(storage_key, kDefaultBucketName); + + UpdateUsage(bucket, 100); GetGlobalUsage(&usage, &unlimited_usage); EXPECT_EQ(100, usage); EXPECT_EQ(0, unlimited_usage); @@ -268,7 +253,9 @@ TEST_F(UsageTrackerTest, CacheDisabledClientTest) { StorageKey::CreateFromStringForTesting("http://example.com"); const std::string& host = storage_key.origin().host(); - UpdateUsage(storage_key, 100); + BucketInfo bucket = CreateBucket(storage_key, kDefaultBucketName); + + UpdateUsage(bucket, 100); GetGlobalUsage(&usage, &unlimited_usage); EXPECT_EQ(100, usage); EXPECT_EQ(0, unlimited_usage); @@ -278,7 +265,7 @@ TEST_F(UsageTrackerTest, CacheDisabledClientTest) { EXPECT_EQ(100, host_usage_breakdown.first); EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second); - UpdateUsageWithoutNotification(storage_key, 100); + UpdateUsageWithoutNotification(bucket, 100); GetGlobalUsage(&usage, &unlimited_usage); EXPECT_EQ(100, usage); EXPECT_EQ(0, unlimited_usage); @@ -287,9 +274,9 @@ TEST_F(UsageTrackerTest, CacheDisabledClientTest) { EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second); GrantUnlimitedStoragePolicy(storage_key); - UpdateUsageWithoutNotification(storage_key, 100); + UpdateUsageWithoutNotification(bucket, 100); SetUsageCacheEnabled(storage_key, false); - UpdateUsageWithoutNotification(storage_key, 100); + UpdateUsageWithoutNotification(bucket, 100); GetGlobalUsage(&usage, &unlimited_usage); EXPECT_EQ(400, usage); @@ -308,7 +295,7 @@ TEST_F(UsageTrackerTest, CacheDisabledClientTest) { EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second); SetUsageCacheEnabled(storage_key, true); - UpdateUsage(storage_key, 100); + UpdateUsage(bucket, 100); GetGlobalUsage(&usage, &unlimited_usage); EXPECT_EQ(500, usage); @@ -329,16 +316,22 @@ TEST_F(UsageTrackerTest, GlobalUsageUnlimitedUncached) { const StorageKey kNonCachedUnlimited = StorageKey::CreateFromStringForTesting("http://non_cached-unlimited"); + BucketInfo bucket_normal = CreateBucket(kNormal, kDefaultBucketName); + BucketInfo bucket_unlimited = CreateBucket(kUnlimited, kDefaultBucketName); + BucketInfo bucket_noncached = CreateBucket(kNonCached, kDefaultBucketName); + BucketInfo bucket_noncached_unlimited = + CreateBucket(kNonCachedUnlimited, kDefaultBucketName); + GrantUnlimitedStoragePolicy(kUnlimited); GrantUnlimitedStoragePolicy(kNonCachedUnlimited); SetUsageCacheEnabled(kNonCached, false); SetUsageCacheEnabled(kNonCachedUnlimited, false); - UpdateUsageWithoutNotification(kNormal, 1); - UpdateUsageWithoutNotification(kUnlimited, 2); - UpdateUsageWithoutNotification(kNonCached, 4); - UpdateUsageWithoutNotification(kNonCachedUnlimited, 8); + UpdateUsageWithoutNotification(bucket_normal, 1); + UpdateUsageWithoutNotification(bucket_unlimited, 2); + UpdateUsageWithoutNotification(bucket_noncached, 4); + UpdateUsageWithoutNotification(bucket_noncached_unlimited, 8); int64_t total_usage = 0; int64_t unlimited_usage = 0; @@ -347,8 +340,8 @@ TEST_F(UsageTrackerTest, GlobalUsageUnlimitedUncached) { EXPECT_EQ(1 + 2 + 4 + 8, total_usage); EXPECT_EQ(2 + 8, unlimited_usage); - UpdateUsageWithoutNotification(kNonCached, 16 - 4); - UpdateUsageWithoutNotification(kNonCachedUnlimited, 32 - 8); + UpdateUsageWithoutNotification(bucket_noncached, 16 - 4); + UpdateUsageWithoutNotification(bucket_noncached_unlimited, 32 - 8); GetGlobalUsage(&total_usage, &unlimited_usage); EXPECT_EQ(1 + 2 + 16 + 32, total_usage); @@ -363,8 +356,11 @@ TEST_F(UsageTrackerTest, GlobalUsageMultipleStorageKeysPerHostCachedInit) { ASSERT_EQ(kStorageKey1.origin().host(), kStorageKey2.origin().host()) << "The test assumes that the two storage keys have the same host"; - UpdateUsageWithoutNotification(kStorageKey1, 100); - UpdateUsageWithoutNotification(kStorageKey2, 200); + BucketInfo bucket1 = CreateBucket(kStorageKey1, kDefaultBucketName); + BucketInfo bucket2 = CreateBucket(kStorageKey2, kDefaultBucketName); + + UpdateUsageWithoutNotification(bucket1, 100); + UpdateUsageWithoutNotification(bucket2, 200); int64_t total_usage = 0; int64_t unlimited_usage = 0; @@ -384,6 +380,9 @@ TEST_F(UsageTrackerTest, GlobalUsageMultipleStorageKeysPerHostCachedUpdate) { ASSERT_EQ(kStorageKey1.origin().host(), kStorageKey2.origin().host()) << "The test assumes that the two storage keys have the same host"; + BucketInfo bucket1 = CreateBucket(kStorageKey1, kDefaultBucketName); + BucketInfo bucket2 = CreateBucket(kStorageKey2, kDefaultBucketName); + int64_t total_usage = 0; int64_t unlimited_usage = 0; // GetGlobalUsage() takes different code paths on the first call and on @@ -393,8 +392,8 @@ TEST_F(UsageTrackerTest, GlobalUsageMultipleStorageKeysPerHostCachedUpdate) { EXPECT_EQ(0, total_usage); EXPECT_EQ(0, unlimited_usage); - UpdateUsage(kStorageKey1, 100); - UpdateUsage(kStorageKey2, 200); + UpdateUsage(bucket1, 100); + UpdateUsage(bucket2, 200); GetGlobalUsage(&total_usage, &unlimited_usage); EXPECT_EQ(100 + 200, total_usage); @@ -409,11 +408,14 @@ TEST_F(UsageTrackerTest, GlobalUsageMultipleStorageKeysPerHostUncachedInit) { ASSERT_EQ(kStorageKey1.origin().host(), kStorageKey2.origin().host()) << "The test assumes that the two storage keys have the same host"; + BucketInfo bucket1 = CreateBucket(kStorageKey1, kDefaultBucketName); + BucketInfo bucket2 = CreateBucket(kStorageKey2, kDefaultBucketName); + SetUsageCacheEnabled(kStorageKey1, false); SetUsageCacheEnabled(kStorageKey2, false); - UpdateUsageWithoutNotification(kStorageKey1, 100); - UpdateUsageWithoutNotification(kStorageKey2, 200); + UpdateUsageWithoutNotification(bucket1, 100); + UpdateUsageWithoutNotification(bucket2, 200); int64_t total_usage = 0; int64_t unlimited_usage = 0; @@ -433,6 +435,9 @@ TEST_F(UsageTrackerTest, GlobalUsageMultipleStorageKeysPerHostUncachedUpdate) { ASSERT_EQ(kStorageKey1.origin().host(), kStorageKey2.origin().host()) << "The test assumes that the two storage keys have the same host"; + BucketInfo bucket1 = CreateBucket(kStorageKey1, kDefaultBucketName); + BucketInfo bucket2 = CreateBucket(kStorageKey2, kDefaultBucketName); + int64_t total_usage = 0; int64_t unlimited_usage = 0; // GetGlobalUsage() takes different code paths on the first call and on @@ -445,12 +450,32 @@ TEST_F(UsageTrackerTest, GlobalUsageMultipleStorageKeysPerHostUncachedUpdate) { SetUsageCacheEnabled(kStorageKey1, false); SetUsageCacheEnabled(kStorageKey2, false); - UpdateUsageWithoutNotification(kStorageKey1, 100); - UpdateUsageWithoutNotification(kStorageKey2, 200); + UpdateUsageWithoutNotification(bucket1, 100); + UpdateUsageWithoutNotification(bucket2, 200); GetGlobalUsage(&total_usage, &unlimited_usage); EXPECT_EQ(100 + 200, total_usage); EXPECT_EQ(0, unlimited_usage); } +TEST_F(UsageTrackerTest, QuotaDatabaseDisabled) { + disable_database_bootstrap(true); + OpenDatabase(); + + disable_quota_database(true); + + int64_t total_usage = 0; + int64_t unlimited_usage = 0; + GetGlobalUsage(&total_usage, &unlimited_usage); + EXPECT_EQ(total_usage, -1); + EXPECT_EQ(unlimited_usage, -1); + + const StorageKey kStorageKey = + StorageKey::CreateFromStringForTesting("http://example.com"); + std::pair<int64_t, blink::mojom::UsageBreakdownPtr> host_usage_breakdown = + GetHostUsageWithBreakdown(kStorageKey.origin().host()); + EXPECT_EQ(host_usage_breakdown.first, -1); + EXPECT_EQ(host_usage_breakdown.second, blink::mojom::UsageBreakdown::New()); +} + } // namespace storage diff --git a/chromium/storage/common/file_system/file_system_types.h b/chromium/storage/common/file_system/file_system_types.h index 0677b34cdb9..e5630d1279f 100644 --- a/chromium/storage/common/file_system/file_system_types.h +++ b/chromium/storage/common/file_system/file_system_types.h @@ -122,7 +122,10 @@ enum FileSystemType { // Indicates an SmbFs filesystem which provides access to SMB file shares. kFileSystemTypeSmbFs, - kFileSystemTypeLast = kFileSystemTypeSmbFs, + // Indicates a FUSE filesystem which provides access to virtual files. + kFileSystemTypeFuseBox, + + kFileSystemTypeLast = kFileSystemTypeFuseBox, // -------------------------------------------------------------------- // Marks the end of internal type enum. (This is not the actual fs type) diff --git a/chromium/storage/common/file_system/file_system_util.cc b/chromium/storage/common/file_system/file_system_util.cc index 34aa58cfd30..1d91dc7aff0 100644 --- a/chromium/storage/common/file_system/file_system_util.cc +++ b/chromium/storage/common/file_system/file_system_util.cc @@ -277,10 +277,12 @@ std::string GetFileSystemTypeString(FileSystemType type) { return "DriveFs"; case kFileSystemTypeSmbFs: return "SmbFs"; + case kFileSystemTypeFuseBox: + return "FuseBox"; case kFileSystemInternalTypeEnumStart: case kFileSystemInternalTypeEnumEnd: NOTREACHED(); - FALLTHROUGH; + [[fallthrough]]; case kFileSystemTypeUnknown: return "Unknown"; } @@ -290,18 +292,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) +#if BUILDFLAG(IS_WIN) return file_path.AsUTF8Unsafe(); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_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) +#if BUILDFLAG(IS_WIN) return base::FilePath::FromUTF8Unsafe(file_path_string); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) return base::FilePath(file_path_string); #endif } |