| // Copyright 2013 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "storage/browser/quota/quota_manager_impl.h" | 
 |  | 
 | #include <stddef.h> | 
 | #include <stdint.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <cstdint> | 
 | #include <functional> | 
 | #include <limits> | 
 | #include <memory> | 
 | #include <utility> | 
 |  | 
 | #include "base/barrier_callback.h" | 
 | #include "base/barrier_closure.h" | 
 | #include "base/check_op.h" | 
 | #include "base/command_line.h" | 
 | #include "base/containers/contains.h" | 
 | #include "base/files/file_util.h" | 
 | #include "base/functional/bind.h" | 
 | #include "base/functional/callback.h" | 
 | #include "base/functional/callback_forward.h" | 
 | #include "base/functional/callback_helpers.h" | 
 | #include "base/functional/concurrent_callbacks.h" | 
 | #include "base/functional/concurrent_closures.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/not_fatal_until.h" | 
 | #include "base/numerics/clamped_math.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/bind_post_task.h" | 
 | #include "base/task/sequenced_task_runner.h" | 
 | #include "base/task/single_thread_task_runner.h" | 
 | #include "base/task/thread_pool.h" | 
 | #include "base/thread_annotations.h" | 
 | #include "base/threading/thread_restrictions.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 "mojo/public/cpp/bindings/remote.h" | 
 | #include "sql/error_delegate_util.h" | 
 | #include "sql/sqlite_result_code.h" | 
 | #include "sql/sqlite_result_code_values.h" | 
 | #include "storage/browser/quota/client_usage_tracker.h" | 
 | #include "storage/browser/quota/quota_availability.h" | 
 | #include "storage/browser/quota/quota_callbacks.h" | 
 | #include "storage/browser/quota/quota_client_type.h" | 
 | #include "storage/browser/quota/quota_features.h" | 
 | #include "storage/browser/quota/quota_macros.h" | 
 | #include "storage/browser/quota/quota_manager_observer.mojom-forward.h" | 
 | #include "storage/browser/quota/quota_manager_observer.mojom.h" | 
 | #include "storage/browser/quota/quota_manager_proxy.h" | 
 | #include "storage/browser/quota/quota_override_handle.h" | 
 | #include "storage/browser/quota/quota_temporary_storage_evictor.h" | 
 | #include "storage/browser/quota/storage_directory_util.h" | 
 | #include "storage/browser/quota/usage_tracker.h" | 
 | #include "third_party/blink/public/common/storage_key/storage_key.h" | 
 | #include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h" | 
 | #include "third_party/blink/public/mojom/storage_key/storage_key.mojom.h" | 
 | #include "url/gurl.h" | 
 | #include "url/origin.h" | 
 |  | 
 | using ::blink::StorageKey; | 
 |  | 
 | namespace storage { | 
 |  | 
 | namespace { | 
 |  | 
 | // These values are used in UMA, so the list should be append-only. | 
 | enum class DatabaseDisabledReason { | 
 |   kRegisterStorageKeyFailed = 0, | 
 |   kSetIsBootstrappedFailed = 1, | 
 |   kRazeFailed = 2, | 
 |   kMaxValue = kRazeFailed, | 
 | }; | 
 |  | 
 | constexpr int64_t kReportHistogramInterval = 60 * 60 * 1000;  // 1 hour | 
 |  | 
 | // Take action on write errors if there is <= 2% disk space | 
 | // available. | 
 | constexpr double kStoragePressureThresholdRatio = 0.02; | 
 |  | 
 | // Limit how frequently QuotaManagerImpl polls for free disk space when | 
 | // only using that information to identify storage pressure. | 
 | constexpr base::TimeDelta kStoragePressureCheckDiskStatsInterval = | 
 |     base::Minutes(5); | 
 |  | 
 | // The path where media license data is persisted on disk, relative to the path | 
 | // for the respective storage bucket. | 
 | constexpr base::FilePath::CharType kMediaLicenseDirectory[] = | 
 |     FILE_PATH_LITERAL("Media Licenses"); | 
 |  | 
 | void ReportDatabaseDisabledReason(DatabaseDisabledReason reason) { | 
 |   base::UmaHistogramEnumeration("Quota.QuotaDatabaseDisabled", reason); | 
 | } | 
 |  | 
 | void DidGetUsageAndQuotaStripBreakdown( | 
 |     QuotaManagerImpl::UsageAndQuotaCallback callback, | 
 |     blink::mojom::QuotaStatusCode status, | 
 |     int64_t usage, | 
 |     int64_t quota, | 
 |     blink::mojom::UsageBreakdownPtr usage_breakdown) { | 
 |   DCHECK(callback); | 
 |   std::move(callback).Run(status, usage, quota); | 
 | } | 
 |  | 
 | void DidGetUsageAndQuotaStripOverride( | 
 |     QuotaManagerImpl::UsageAndQuotaWithBreakdownCallback callback, | 
 |     blink::mojom::QuotaStatusCode status, | 
 |     int64_t usage, | 
 |     int64_t quota, | 
 |     bool is_override_enabled, | 
 |     blink::mojom::UsageBreakdownPtr usage_breakdown) { | 
 |   DCHECK(callback); | 
 |   std::move(callback).Run(status, usage, quota, std::move(usage_breakdown)); | 
 | } | 
 |  | 
 | base::FilePath CreateMediaLicenseBucketPath(const base::FilePath& profile_path, | 
 |                                             const BucketLocator& bucket) { | 
 |   base::FilePath bucket_directory = CreateBucketPath(profile_path, bucket); | 
 |   return bucket_directory.Append(kMediaLicenseDirectory); | 
 | } | 
 |  | 
 | int64_t CalculateReportedQuota(int64_t total_space_bytes, int64_t usage_bytes) { | 
 |   base::ClampedNumeric<int64_t> clamped_total_space_bytes(total_space_bytes); | 
 |   base::ClampedNumeric<int64_t> clamped_usage_bytes(usage_bytes); | 
 |   if (clamped_total_space_bytes >= 10 * QuotaManagerImpl::kGBytes) { | 
 |     return clamped_usage_bytes + 10 * QuotaManagerImpl::kGBytes; | 
 |   } | 
 |  | 
 |   // Generally, we report a quota value of 10 GiB. However, if there is less | 
 |   // than 10 GiB of disk space, the quota value we report is disk space rounded | 
 |   // up to the nearest 1 GiB. | 
 |   base::ClampedNumeric<int64_t> rounded_total_space_gib = | 
 |       (clamped_total_space_bytes / QuotaManagerImpl::kGBytes) + | 
 |       (clamped_total_space_bytes % QuotaManagerImpl::kGBytes != 0); | 
 |   return clamped_usage_bytes + | 
 |          rounded_total_space_gib * QuotaManagerImpl::kGBytes; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // Heuristics: assuming average cloud server allows a few Gigs storage | 
 | // on the server side and the storage needs to be shared for user data | 
 | // and by multiple apps. | 
 | int64_t QuotaManagerImpl::kSyncableStorageDefaultStorageKeyQuota = | 
 |     500 * kMBytes; | 
 |  | 
 | QuotaManagerImpl::QuotaOverride::QuotaOverride() = default; | 
 | QuotaManagerImpl::QuotaOverride::~QuotaOverride() = default; | 
 |  | 
 | class QuotaManagerImpl::UsageAndQuotaInfoGatherer : public QuotaTask { | 
 |  public: | 
 |   UsageAndQuotaInfoGatherer( | 
 |       QuotaManagerImpl* manager, | 
 |       const StorageKey& storage_key, | 
 |       bool is_incognito, | 
 |       UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback) | 
 |       : QuotaTask(manager), | 
 |         storage_key_(storage_key), | 
 |         callback_(std::move(callback)), | 
 |         is_unlimited_(manager->IsStorageUnlimited(storage_key_)), | 
 |         is_incognito_(is_incognito) { | 
 |     DCHECK(manager); | 
 |     DCHECK(callback_); | 
 |   } | 
 |  | 
 |   UsageAndQuotaInfoGatherer( | 
 |       QuotaManagerImpl* manager, | 
 |       const BucketInfo& bucket_info, | 
 |       bool is_incognito, | 
 |       UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback) | 
 |       : UsageAndQuotaInfoGatherer(manager, | 
 |                                   bucket_info.storage_key, | 
 |                                   is_incognito, | 
 |                                   std::move(callback)) { | 
 |     bucket_info_ = bucket_info; | 
 |   } | 
 |  | 
 |  protected: | 
 |   void Run() override { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     // Start the async process of gathering the info we need. | 
 |     // Gather info before computing an answer: | 
 |     // settings, host_usage, storage_key_quota and device_storage_capacity if | 
 |     // unlimited. | 
 |     base::ConcurrentClosures concurrent; | 
 |     manager()->GetQuotaSettings( | 
 |         base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotSettings, | 
 |                        weak_factory_.GetWeakPtr(), concurrent.CreateClosure())); | 
 |  | 
 |     if (bucket_info_) { | 
 |       manager()->GetBucketUsageWithBreakdown( | 
 |           bucket_info_->ToBucketLocator(), | 
 |           base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotUsage, | 
 |                          weak_factory_.GetWeakPtr(), | 
 |                          concurrent.CreateClosure())); | 
 |     } else { | 
 |       manager()->GetStorageKeyUsageWithBreakdown( | 
 |           storage_key_, base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotUsage, | 
 |                                        weak_factory_.GetWeakPtr(), | 
 |                                        concurrent.CreateClosure())); | 
 |     } | 
 |  | 
 |     // Determine storage_key_quota differently depending on type. | 
 |     if (is_unlimited_) { | 
 |       SetDesiredStorageKeyQuota(kNoLimit); | 
 |       manager()->GetStorageCapacity(base::BindOnce( | 
 |           &UsageAndQuotaInfoGatherer::OnGotCapacity, weak_factory_.GetWeakPtr(), | 
 |           concurrent.CreateClosure())); | 
 |     } else { | 
 |       // For limited storage,  OnGotSettings will set the host quota. | 
 |     } | 
 |  | 
 |     std::move(concurrent) | 
 |         .Done(base::BindOnce(&UsageAndQuotaInfoGatherer::CallCompleted, | 
 |                              weak_factory_.GetWeakPtr())); | 
 |   } | 
 |  | 
 |   void Aborted() override { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     weak_factory_.InvalidateWeakPtrs(); | 
 |     std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort, | 
 |                              /*usage=*/0, | 
 |                              /*quota=*/0, | 
 |                              /*is_override_enabled=*/false, | 
 |                              /*usage_breakdown=*/nullptr); | 
 |     DeleteSoon(); | 
 |   } | 
 |  | 
 |   void Completed() override { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     weak_factory_.InvalidateWeakPtrs(); | 
 |  | 
 |     int64_t quota = desired_storage_key_quota_; | 
 |     std::optional<int64_t> quota_override_size = | 
 |         manager()->GetQuotaOverrideForStorageKey(storage_key_); | 
 |     if (quota_override_size) { | 
 |       quota = *quota_override_size; | 
 |     } | 
 |  | 
 |     // For an individual bucket, the quota is the minimum of the requested quota | 
 |     // and the StorageKey quota. | 
 |     if (bucket_info_ && bucket_info_->quota > 0) { | 
 |       quota = std::min(quota, bucket_info_->quota); | 
 |     } | 
 |  | 
 |     if (is_unlimited_) { | 
 |       int64_t temp_pool_free_space = | 
 |           available_space_ - settings_.must_remain_available; | 
 |       // Constrain the desired quota to something that fits. | 
 |       if (quota > temp_pool_free_space) { | 
 |         quota = available_space_ + usage_; | 
 |       } | 
 |     } | 
 |  | 
 |     std::move(callback_).Run(usage_ >= 0 | 
 |                                  ? blink::mojom::QuotaStatusCode::kOk | 
 |                                  : blink::mojom::QuotaStatusCode::kUnknown, | 
 |                              usage_, quota, quota_override_size.has_value(), | 
 |                              std::move(usage_breakdown_)); | 
 |     if (!is_incognito_ && !is_unlimited_ && !bucket_info_) { | 
 |       UMA_HISTOGRAM_MBYTES("Quota.QuotaForOrigin", quota); | 
 |       UMA_HISTOGRAM_MBYTES("Quota.UsageByOrigin", usage_); | 
 |       if (quota > 0) { | 
 |         UMA_HISTOGRAM_PERCENTAGE( | 
 |             "Quota.PercentUsedByOrigin", | 
 |             std::min(100, static_cast<int>((usage_ * 100) / quota))); | 
 |       } | 
 |     } | 
 |     DeleteSoon(); | 
 |   } | 
 |  | 
 |  private: | 
 |   QuotaManagerImpl* manager() const { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     return static_cast<QuotaManagerImpl*>(observer()); | 
 |   } | 
 |  | 
 |   void OnGotSettings(base::OnceClosure callback, | 
 |                      const QuotaSettings& settings) { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     DCHECK(callback); | 
 |  | 
 |     settings_ = settings; | 
 |     const int64_t quota = | 
 |         manager()->GetQuotaForStorageKey(storage_key_, settings); | 
 |     if (quota != kNoLimit) { | 
 |       SetDesiredStorageKeyQuota(quota); | 
 |     } | 
 |  | 
 |     std::move(callback).Run(); | 
 |   } | 
 |  | 
 |   void OnGotCapacity(base::OnceClosure callback, | 
 |                      int64_t total_space, | 
 |                      int64_t available_space) { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     DCHECK(callback); | 
 |     DCHECK_GE(total_space, 0); | 
 |     DCHECK_GE(available_space, 0); | 
 |  | 
 |     total_space_ = total_space; | 
 |     available_space_ = available_space; | 
 |     std::move(callback).Run(); | 
 |   } | 
 |  | 
 |   void OnGotUsage(base::OnceClosure callback, | 
 |                   int64_t usage, | 
 |                   blink::mojom::UsageBreakdownPtr usage_breakdown) { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     DCHECK(callback); | 
 |     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); | 
 |  | 
 |     usage_ = usage; | 
 |     usage_breakdown_ = std::move(usage_breakdown); | 
 |     std::move(callback).Run(); | 
 |   } | 
 |  | 
 |   void SetDesiredStorageKeyQuota(int64_t quota) { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     DCHECK_GE(quota, 0); | 
 |  | 
 |     desired_storage_key_quota_ = quota; | 
 |   } | 
 |  | 
 |   // These fields are passed at construction time. | 
 |   const StorageKey storage_key_; | 
 |   // Non-null iff usage info is to be gathered for an individual bucket. If | 
 |   // null, usage is gathered for all buckets in the given host/StorageKey. | 
 |   std::optional<BucketInfo> bucket_info_; | 
 |   QuotaManagerImpl::UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback_; | 
 |   const bool is_unlimited_; | 
 |   const bool is_incognito_; | 
 |  | 
 |   // Fields retrieved while running. | 
 |   int64_t available_space_ = 0; | 
 |   int64_t total_space_ = 0; | 
 |   int64_t desired_storage_key_quota_ = 0; | 
 |   int64_t usage_ = 0; | 
 |   blink::mojom::UsageBreakdownPtr usage_breakdown_; | 
 |   QuotaSettings settings_; | 
 |  | 
 |   SEQUENCE_CHECKER(sequence_checker_); | 
 |  | 
 |   // Weak pointers are used to support cancelling work. | 
 |   base::WeakPtrFactory<UsageAndQuotaInfoGatherer> weak_factory_{this}; | 
 | }; | 
 |  | 
 | class QuotaManagerImpl::EvictionRoundInfoHelper { | 
 |  public: | 
 |   // `callback` is called when the helper successfully retrieves quota settings | 
 |   // and capacity data. `completion_closure` is called after data has been | 
 |   // retrieved and `callback` has been run and to clean up itself and delete | 
 |   // this EvictionRoundInfoHelper instance. | 
 |   EvictionRoundInfoHelper(QuotaManagerImpl* manager, | 
 |                           EvictionRoundInfoCallback callback, | 
 |                           base::OnceClosure completion_closure) | 
 |       : manager_(manager), | 
 |         callback_(std::move(callback)), | 
 |         completion_closure_(std::move(completion_closure)) { | 
 |     DCHECK(manager_); | 
 |     DCHECK(callback_); | 
 |     DCHECK(completion_closure_); | 
 |   } | 
 |  | 
 |   void Run() { | 
 | #if DCHECK_IS_ON() | 
 |     DCHECK(!run_called_) << __func__ << " already called"; | 
 |     run_called_ = true; | 
 | #endif  // DCHECK_IS_ON() | 
 |  | 
 |     // Gather 2 pieces of info before deciding if we need to get GlobalUsage: | 
 |     // settings and device_storage_capacity. | 
 |     base::RepeatingClosure barrier = base::BarrierClosure( | 
 |         2, base::BindOnce(&EvictionRoundInfoHelper::OnBarrierComplete, | 
 |                           weak_factory_.GetWeakPtr())); | 
 |  | 
 |     manager_->GetQuotaSettings( | 
 |         base::BindOnce(&EvictionRoundInfoHelper::OnGotSettings, | 
 |                        weak_factory_.GetWeakPtr(), barrier)); | 
 |     manager_->GetStorageCapacity( | 
 |         base::BindOnce(&EvictionRoundInfoHelper::OnGotCapacity, | 
 |                        weak_factory_.GetWeakPtr(), barrier)); | 
 |   } | 
 |  | 
 |  private: | 
 |   void Completed() { | 
 | #if DCHECK_IS_ON() | 
 |     DCHECK(!completed_called_) << __func__ << " already called"; | 
 |     completed_called_ = true; | 
 | #endif  // DCHECK_IS_ON() | 
 |     std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk, settings_, | 
 |                              available_space_, total_space_, global_usage_, | 
 |                              global_usage_is_complete_); | 
 |     // May delete `this`. | 
 |     std::move(completion_closure_).Run(); | 
 |   } | 
 |  | 
 |   void OnGotSettings(base::OnceClosure barrier_closure, | 
 |                      const QuotaSettings& settings) { | 
 |     DCHECK(barrier_closure); | 
 |  | 
 |     settings_ = settings; | 
 |     std::move(barrier_closure).Run(); | 
 |   } | 
 |  | 
 |   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(); | 
 |   } | 
 |  | 
 |   void OnBarrierComplete() { | 
 |     // Avoid computing the full current_usage when there's no pressure. | 
 |     int64_t consumed_space = total_space_ - available_space_; | 
 |     if (consumed_space < settings_.pool_size && | 
 |         available_space_ > settings_.should_remain_available) { | 
 |       DCHECK(!global_usage_is_complete_); | 
 |       global_usage_ = manager_->GetUsageTracker()->GetCachedUsage(); | 
 |       // `this` may be deleted during this Complete() call. | 
 |       Completed(); | 
 |       return; | 
 |     } | 
 |     manager_->GetGlobalUsage( | 
 |         base::BindOnce(&EvictionRoundInfoHelper::OnGotGlobalUsage, | 
 |                        weak_factory_.GetWeakPtr())); | 
 |   } | 
 |  | 
 |   void OnGotGlobalUsage(int64_t usage, int64_t unlimited_usage) { | 
 |     global_usage_ = std::max(int64_t{0}, usage - unlimited_usage); | 
 |     global_usage_is_complete_ = true; | 
 |     // `this` may be deleted during this Complete() call. | 
 |     Completed(); | 
 |   } | 
 |  | 
 |   const raw_ptr<QuotaManagerImpl> manager_; | 
 |   EvictionRoundInfoCallback callback_; | 
 |   base::OnceClosure completion_closure_; | 
 |   QuotaSettings settings_; | 
 |   int64_t available_space_ = 0; | 
 |   int64_t total_space_ = 0; | 
 |   int64_t global_usage_ = 0; | 
 |   bool global_usage_is_complete_ = false; | 
 |  | 
 | #if DCHECK_IS_ON() | 
 |   bool run_called_ = false; | 
 |   bool completed_called_ = false; | 
 | #endif  // DCHECK_IS_ON() | 
 |  | 
 |   base::WeakPtrFactory<EvictionRoundInfoHelper> weak_factory_{this}; | 
 | }; | 
 |  | 
 | class QuotaManagerImpl::GetUsageInfoTask : public QuotaTask { | 
 |  public: | 
 |   GetUsageInfoTask(QuotaManagerImpl* manager, GetUsageInfoCallback callback) | 
 |       : QuotaTask(manager), callback_(std::move(callback)) { | 
 |     DCHECK(manager); | 
 |     DCHECK(callback_); | 
 |   } | 
 |  | 
 |  protected: | 
 |   void Run() override { | 
 |     // This will populate cached hosts and usage info. | 
 |     manager()->GetUsageTracker()->GetGlobalUsage(base::BindOnce( | 
 |         &GetUsageInfoTask::DidGetGlobalUsage, weak_factory_.GetWeakPtr())); | 
 |   } | 
 |  | 
 |   void Completed() override { | 
 |     std::move(callback_).Run(std::move(entries_)); | 
 |     DeleteSoon(); | 
 |   } | 
 |  | 
 |  private: | 
 |   void AddEntries(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, host_usage_pair.second); | 
 |     } | 
 |     CallCompleted(); | 
 |   } | 
 |  | 
 |   void DidGetGlobalUsage(int64_t, int64_t) { | 
 |     UsageTracker* tracker = manager()->GetUsageTracker(); | 
 |     DCHECK(tracker); | 
 |     AddEntries(*tracker); | 
 |   } | 
 |  | 
 |   QuotaManagerImpl* manager() const { | 
 |     return static_cast<QuotaManagerImpl*>(observer()); | 
 |   } | 
 |  | 
 |   GetUsageInfoCallback callback_; | 
 |   UsageInfoEntries entries_; | 
 |   base::WeakPtrFactory<GetUsageInfoTask> weak_factory_{this}; | 
 | }; | 
 |  | 
 | class QuotaManagerImpl::StorageKeyGathererTask { | 
 |  public: | 
 |   StorageKeyGathererTask( | 
 |       QuotaManagerImpl* manager, | 
 |       base::OnceCallback<void(std::set<StorageKey>)> callback) | 
 |       : manager_(manager), callback_(std::move(callback)) { | 
 |     DCHECK(manager_); | 
 |     DCHECK(callback_); | 
 |   } | 
 |  | 
 |   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() | 
 |  | 
 |     base::ConcurrentClosures concurrent; | 
 |     for (const auto& client_and_type : manager_->client_types_) { | 
 |       client_and_type.first->GetDefaultStorageKeys(base::BindOnce( | 
 |           &StorageKeyGathererTask::DidGetStorageKeys, | 
 |           weak_factory_.GetWeakPtr(), concurrent.CreateClosure())); | 
 |     } | 
 |     std::move(concurrent) | 
 |         .Done( | 
 |             base::BindOnce(&QuotaManagerImpl::StorageKeyGathererTask::Completed, | 
 |                            weak_factory_.GetWeakPtr())); | 
 |   } | 
 |  | 
 |  private: | 
 |   void DidGetStorageKeys(base::OnceClosure callback, | 
 |                          const std::vector<StorageKey>& storage_keys) { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     DCHECK(callback); | 
 |     storage_keys_.insert(storage_keys.begin(), storage_keys.end()); | 
 |     std::move(callback).Run(); | 
 |   } | 
 |  | 
 |   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() | 
 |  | 
 |     // Deletes `this`. | 
 |     std::move(callback_).Run(storage_keys_); | 
 |   } | 
 |  | 
 |   SEQUENCE_CHECKER(sequence_checker_); | 
 |   raw_ptr<QuotaManagerImpl> manager_ GUARDED_BY_CONTEXT(sequence_checker_); | 
 |   base::OnceCallback<void(std::set<StorageKey>)> callback_ | 
 |       GUARDED_BY_CONTEXT(sequence_checker_); | 
 |   std::set<StorageKey> storage_keys_ GUARDED_BY_CONTEXT(sequence_checker_); | 
 |  | 
 | #if DCHECK_IS_ON() | 
 |   bool run_called_ = false; | 
 |   bool completed_called_ = false; | 
 | #endif  // DCHECK_IS_ON() | 
 |  | 
 |   base::WeakPtrFactory<StorageKeyGathererTask> weak_factory_ | 
 |       GUARDED_BY_CONTEXT(sequence_checker_){this}; | 
 | }; | 
 |  | 
 | // 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: | 
 |   // `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, | 
 |       bool commit_immediately, | 
 |       base::OnceCallback<void(BucketDataDeleter*, | 
 |                               QuotaErrorOr<mojom::BucketTableEntryPtr>)> | 
 |           callback) | 
 |       : manager_(manager), | 
 |         bucket_(bucket), | 
 |         quota_client_types_(std::move(quota_client_types)), | 
 |         commit_immediately_(commit_immediately), | 
 |         callback_(std::move(callback)) { | 
 |     DCHECK(manager_); | 
 |     // TODO(crbug.com/40058632): Convert back into DCHECKs once issue is | 
 |     // resolved. | 
 |     CHECK(callback_); | 
 |   } | 
 |  | 
 |   ~BucketDataDeleter() { | 
 |     // `callback` is non-null if the deleter gets destroyed before completing. | 
 |     if (callback_) { | 
 |       std::move(callback_).Run(this, | 
 |                                base::unexpected(QuotaError::kUnknownError)); | 
 |     } | 
 |   } | 
 |  | 
 |   void Run() { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 | #if DCHECK_IS_ON() | 
 |     // TODO(crbug.com/40058632): Convert back into DCHECK once issue is | 
 |     // resolved. | 
 |     CHECK(!run_called_) << __func__ << " already called"; | 
 |     run_called_ = true; | 
 | #endif  // DCHECK_IS_ON() | 
 |  | 
 |     remaining_clients_ = manager_->client_types_.size(); | 
 |     UsageTracker* usage_tracker = manager_->GetUsageTracker(); | 
 |  | 
 |     for (const auto& client_and_type : manager_->client_types_) { | 
 |       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::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); | 
 |         client->DeleteBucketData( | 
 |             bucket_, base::BindOnce(&BucketDataDeleter::DidDeleteBucketData, | 
 |                                     weak_factory_.GetWeakPtr(), tracing_id)); | 
 |       } else { | 
 |         ++skipped_clients_; | 
 |         --remaining_clients_; | 
 |       } | 
 |     } | 
 |  | 
 |     if (remaining_clients_ == 0) { | 
 |       FinishDeletion(); | 
 |     } | 
 |   } | 
 |  | 
 |   bool completed() { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     return !callback_; | 
 |   } | 
 |  | 
 |  private: | 
 |   void DidDeleteBucketData(int tracing_id, | 
 |                            blink::mojom::QuotaStatusCode status) { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     DCHECK_GT(remaining_clients_, 0u); | 
 |     TRACE_EVENT_NESTABLE_ASYNC_END0( | 
 |         "browsing_data", "QuotaManagerImpl::BucketDataDeleter", tracing_id); | 
 |  | 
 |     if (status != blink::mojom::QuotaStatusCode::kOk) { | 
 |       ++error_count_; | 
 |     } | 
 |  | 
 |     if (--remaining_clients_ == 0) { | 
 |       FinishDeletion(); | 
 |     } | 
 |   } | 
 |  | 
 |   void FinishDeletion() { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     // TODO(crbug.com/40058632): 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_, commit_immediately_, | 
 |           base::BindOnce(&BucketDataDeleter::DidDeleteBucketFromDatabase, | 
 |                          weak_factory_.GetWeakPtr())); | 
 |       return; | 
 |     } | 
 |     if (error_count_ == 0) { | 
 |       Complete(base::ok(nullptr)); | 
 |     } else { | 
 |       Complete(base::unexpected(QuotaError::kUnknownError)); | 
 |     } | 
 |   } | 
 |  | 
 |   void DidDeleteBucketFromDatabase( | 
 |       QuotaErrorOr<mojom::BucketTableEntryPtr> result) { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     Complete(std::move(result)); | 
 |   } | 
 |  | 
 |   void Complete(QuotaErrorOr<mojom::BucketTableEntryPtr> result) { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     // TODO(crbug.com/40058632): Convert back into DCHECKs once issue is | 
 |     // resolved. | 
 |     CHECK_EQ(remaining_clients_, 0u); | 
 |     CHECK(callback_); | 
 |  | 
 |     // May delete `this`. | 
 |     std::move(callback_).Run(this, std::move(result)); | 
 |   } | 
 |  | 
 |   SEQUENCE_CHECKER(sequence_checker_); | 
 |   const raw_ptr<QuotaManagerImpl> manager_ | 
 |       GUARDED_BY_CONTEXT(sequence_checker_); | 
 |   const BucketLocator bucket_; | 
 |   const QuotaClientTypes quota_client_types_; | 
 |   // Whether the update to the database should be committed immediately (if not, | 
 |   // it will be scheduled to be committed as part of a batch). | 
 |   const bool commit_immediately_; | 
 |   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*, | 
 |                           QuotaErrorOr<mojom::BucketTableEntryPtr>)> | 
 |       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_ | 
 |       GUARDED_BY_CONTEXT(sequence_checker_){this}; | 
 | }; | 
 |  | 
 | // Deletes a set of buckets. | 
 | class QuotaManagerImpl::BucketSetDataDeleter { | 
 |  public: | 
 |   // `callback` will be run to report the status of the deletion on task | 
 |   // completion. `callback` will only be called while this BucketSetDataDeleter | 
 |   // instance is alive. `callback` may destroy this BucketSetDataDeleter | 
 |   // instance. | 
 |   BucketSetDataDeleter( | 
 |       QuotaManagerImpl* manager, | 
 |       base::OnceCallback<void(BucketSetDataDeleter*, | 
 |                               blink::mojom::QuotaStatusCode)> callback) | 
 |       : manager_(manager), callback_(std::move(callback)) { | 
 |     DCHECK(manager_); | 
 |     DCHECK(callback_); | 
 |   } | 
 |  | 
 |   ~BucketSetDataDeleter() { | 
 |     if (callback_) { | 
 |       std::move(callback_).Run(this, | 
 |                                blink::mojom::QuotaStatusCode::kErrorAbort); | 
 |     } | 
 |   } | 
 |  | 
 |   base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> | 
 |   GetBucketDeletionCallback() { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 | #if DCHECK_IS_ON() | 
 |     DCHECK(!started_) << __func__ << " already called"; | 
 |     started_ = true; | 
 | #endif  // DCHECK_IS_ON() | 
 |     return base::BindOnce(&BucketSetDataDeleter::DidGetBuckets, | 
 |                           weak_factory_.GetWeakPtr()); | 
 |   } | 
 |  | 
 |   bool completed() { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     return !callback_; | 
 |   } | 
 |  | 
 |  private: | 
 |   void DidGetBuckets(QuotaErrorOr<std::set<BucketInfo>> result) { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     if (result.has_value()) { | 
 |       buckets_ = BucketInfosToBucketLocators(result.value()); | 
 |       if (!buckets_.empty()) { | 
 |         ScheduleBucketsDeletion(); | 
 |         return; | 
 |       } | 
 |     } | 
 |     Complete(/*success=*/result.has_value()); | 
 |   } | 
 |  | 
 |   void ScheduleBucketsDeletion() { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     for (const auto& bucket : buckets_) { | 
 |       // Weak pointer is used here because the callback will be invoked when | 
 |       // `bucket_deleters_` is being destroyed during `BucketSetDataDeleter` | 
 |       // destruction. In this case, we don't need `DidDeleteBucketData` to be | 
 |       // called. See crbug.com/373754859 | 
 |       auto bucket_deleter = std::make_unique<BucketDataDeleter>( | 
 |           manager_, bucket, AllQuotaClientTypes(), /*commit_immediately=*/false, | 
 |           base::BindOnce(&BucketSetDataDeleter::DidDeleteBucketData, | 
 |                          weak_factory_.GetWeakPtr())); | 
 |       auto* bucket_deleter_ptr = bucket_deleter.get(); | 
 |       bucket_deleters_[bucket_deleter_ptr] = std::move(bucket_deleter); | 
 |       bucket_deleter_ptr->Run(); | 
 |     } | 
 |   } | 
 |  | 
 |   void DidDeleteBucketData(BucketDataDeleter* deleter, | 
 |                            QuotaErrorOr<mojom::BucketTableEntryPtr> entry) { | 
 |     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |     DCHECK(deleter->completed()); | 
 |  | 
 |     DCHECK(base::Contains(bucket_deleters_, deleter)); | 
 |     bucket_deleters_.erase(deleter); | 
 |  | 
 |     if (!entry.has_value()) { | 
 |       ++error_count_; | 
 |     } | 
 |  | 
 |     if (bucket_deleters_.empty()) { | 
 |       Complete(/*success=*/error_count_ == 0); | 
 |     } | 
 |   } | 
 |  | 
 |   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_); | 
 |   const raw_ptr<QuotaManagerImpl> manager_ | 
 |       GUARDED_BY_CONTEXT(sequence_checker_); | 
 |   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(BucketSetDataDeleter*, blink::mojom::QuotaStatusCode)> | 
 |       callback_ GUARDED_BY_CONTEXT(sequence_checker_); | 
 |  | 
 | #if DCHECK_IS_ON() | 
 |   bool started_ GUARDED_BY_CONTEXT(sequence_checker_) = false; | 
 | #endif  // DCHECK_IS_ON() | 
 |  | 
 |   base::WeakPtrFactory<BucketSetDataDeleter> weak_factory_{this}; | 
 | }; | 
 |  | 
 | class QuotaManagerImpl::StorageCleanupHelper : public QuotaTask { | 
 |  public: | 
 |   StorageCleanupHelper(QuotaManagerImpl* manager, | 
 |                        QuotaClientTypes quota_client_types, | 
 |                        base::OnceClosure callback) | 
 |       : QuotaTask(manager), | 
 |         quota_client_types_(std::move(quota_client_types)), | 
 |         callback_(std::move(callback)) { | 
 |     DCHECK(manager); | 
 |     DCHECK(callback_); | 
 |   } | 
 |  | 
 |  protected: | 
 |   void Run() override { | 
 |     base::ConcurrentClosures concurrent; | 
 |     for (const auto& client_and_type : manager()->client_types_) { | 
 |       mojom::QuotaClient* client = client_and_type.first; | 
 |       QuotaClientType client_type = client_and_type.second; | 
 |       if (quota_client_types_.contains(client_type)) { | 
 |         client->PerformStorageCleanup(concurrent.CreateClosure()); | 
 |       } | 
 |     } | 
 |     std::move(concurrent) | 
 |         .Done(base::BindOnce(&StorageCleanupHelper::CallCompleted, | 
 |                              weak_factory_.GetWeakPtr())); | 
 |   } | 
 |  | 
 |   void Aborted() override { | 
 |     weak_factory_.InvalidateWeakPtrs(); | 
 |     std::move(callback_).Run(); | 
 |     DeleteSoon(); | 
 |   } | 
 |  | 
 |   void Completed() override { | 
 |     weak_factory_.InvalidateWeakPtrs(); | 
 |     std::move(callback_).Run(); | 
 |     DeleteSoon(); | 
 |   } | 
 |  | 
 |  private: | 
 |   QuotaManagerImpl* manager() const { | 
 |     return static_cast<QuotaManagerImpl*>(observer()); | 
 |   } | 
 |  | 
 |   const QuotaClientTypes quota_client_types_; | 
 |   base::OnceClosure callback_; | 
 |   base::WeakPtrFactory<StorageCleanupHelper> weak_factory_{this}; | 
 | }; | 
 |  | 
 | // Gather storage key info table for quota-internals page. | 
 | // | 
 | // This class is granted ownership of itself when it is passed to | 
 | // DidDumpBucketTable() via base::Owned(). When the closure for said function | 
 | // goes out of scope, the object is deleted. | 
 | // This class is not thread-safe because there can be races when entries_ is | 
 | // modified. | 
 | class QuotaManagerImpl::DumpBucketTableHelper { | 
 |  public: | 
 |   QuotaError DumpBucketTableOnDBThread(QuotaDatabase* database) { | 
 |     DCHECK(database); | 
 |     return database->DumpBucketTable(base::BindRepeating( | 
 |         &DumpBucketTableHelper::AppendEntry, base::Unretained(this))); | 
 |   } | 
 |  | 
 |   void DidDumpBucketTable(const base::WeakPtr<QuotaManagerImpl>& manager, | 
 |                           DumpBucketTableCallback callback, | 
 |                           QuotaError error) { | 
 |     if (!manager) { | 
 |       // The operation was aborted. | 
 |       std::move(callback).Run(BucketTableEntries()); | 
 |       return; | 
 |     } | 
 |     std::move(callback).Run(std::move(entries_)); | 
 |   } | 
 |  | 
 |  private: | 
 |   bool AppendEntry(mojom::BucketTableEntryPtr entry) { | 
 |     entries_.push_back(std::move(entry)); | 
 |     return true; | 
 |   } | 
 |  | 
 |   BucketTableEntries entries_; | 
 | }; | 
 |  | 
 | // QuotaManagerImpl ----------------------------------------------------------- | 
 |  | 
 | QuotaManagerImpl::QuotaManagerImpl( | 
 |     bool is_incognito, | 
 |     const base::FilePath& profile_path, | 
 |     scoped_refptr<base::SingleThreadTaskRunner> io_thread, | 
 |     scoped_refptr<SpecialStoragePolicy> special_storage_policy, | 
 |     const GetQuotaSettingsFunc& get_settings_function) | 
 |     : RefCountedDeleteOnSequence<QuotaManagerImpl>(io_thread), | 
 |       is_incognito_(is_incognito), | 
 |       profile_path_(profile_path), | 
 |       proxy_(base::MakeRefCounted<QuotaManagerProxy>(this, | 
 |                                                      io_thread, | 
 |                                                      profile_path)), | 
 |       io_thread_(std::move(io_thread)), | 
 |       db_runner_(base::ThreadPool::CreateSequencedTaskRunner( | 
 |           {base::MayBlock(), base::TaskPriority::USER_VISIBLE, | 
 |            base::TaskShutdownBehavior::BLOCK_SHUTDOWN})), | 
 |       get_settings_function_(get_settings_function), | 
 |       special_storage_policy_(std::move(special_storage_policy)), | 
 |       get_volume_info_fn_(&QuotaManagerImpl::GetVolumeInfo) { | 
 |   DCHECK_EQ(settings_.refresh_interval, base::TimeDelta::Max()); | 
 |   if (!get_settings_function.is_null()) { | 
 |     // Reset the interval to ensure we use the get_settings_function | 
 |     // the first times settings_ is needed. | 
 |     settings_.refresh_interval = base::TimeDelta(); | 
 |     get_settings_task_runner_ = | 
 |         base::SingleThreadTaskRunner::GetCurrentDefault(); | 
 |   } | 
 |   DETACH_FROM_SEQUENCE(sequence_checker_); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::SetQuotaSettings(const QuotaSettings& settings) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   settings_ = settings; | 
 |   settings_timestamp_ = base::TimeTicks::Now(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::UpdateOrCreateBucket( | 
 |     const BucketInitParams& bucket_params, | 
 |     base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |   if (!bucket_params.expiration.is_null() && | 
 |       (bucket_params.expiration <= QuotaDatabase::GetNow())) { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kInvalidExpiration)); | 
 |     return; | 
 |   } | 
 |  | 
 |   last_opened_bucket_site_ = bucket_params.storage_key; | 
 |  | 
 |   // The default bucket skips the quota check. | 
 |   if (bucket_params.name == kDefaultBucketName) { | 
 |     PostTaskAndReplyWithResultForDBThread( | 
 |         base::BindOnce( | 
 |             [](const BucketInitParams& params, QuotaDatabase* database) { | 
 |               DCHECK(database); | 
 |               return database->UpdateOrCreateBucket(params, | 
 |                                                     /*max_bucket_count=*/0); | 
 |             }, | 
 |             bucket_params), | 
 |         base::BindOnce(&QuotaManagerImpl::DidGetBucketCheckExpiration, | 
 |                        weak_factory_.GetWeakPtr(), bucket_params, | 
 |                        std::move(callback))); | 
 |     return; | 
 |   } | 
 |  | 
 |   GetQuotaSettings( | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetQuotaSettingsForBucketCreation, | 
 |                      weak_factory_.GetWeakPtr(), std::move(bucket_params), | 
 |                      std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::CreateBucketForTesting( | 
 |     const StorageKey& storage_key, | 
 |     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(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](const StorageKey& storage_key, const std::string& bucket_name, | 
 |              QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->CreateBucketForTesting(  // IN-TEST | 
 |                 storage_key, bucket_name); | 
 |           }, | 
 |           storage_key, bucket_name), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBucket, | 
 |                      weak_factory_.GetWeakPtr(), /*notify_update_bucket=*/true, | 
 |                      std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetBucketByNameUnsafe( | 
 |     const StorageKey& storage_key, | 
 |     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(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](const StorageKey& storage_key, const std::string& bucket_name, | 
 |              QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->GetBucket(storage_key, bucket_name); | 
 |           }, | 
 |           storage_key, bucket_name), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBucket, | 
 |                      weak_factory_.GetWeakPtr(), /*notify_update_bucket=*/false, | 
 |                      std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetBucketById( | 
 |     const BucketId& bucket_id, | 
 |     base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](const BucketId bucket_id, QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->GetBucketById(bucket_id); | 
 |           }, | 
 |           bucket_id), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBucket, | 
 |                      weak_factory_.GetWeakPtr(), /*notify_update_bucket=*/false, | 
 |                      std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetAllStorageKeys(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([](QuotaDatabase* database) { | 
 |         DCHECK(database); | 
 |         return database->GetAllStorageKeys(); | 
 |       }), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetStorageKeys, | 
 |                      weak_factory_.GetWeakPtr(), std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetAllBuckets( | 
 |     base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce([](QuotaDatabase* database) { | 
 |         DCHECK(database); | 
 |         return database->GetAllBuckets(); | 
 |       }), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBuckets, | 
 |                      weak_factory_.GetWeakPtr(), std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetBucketsForHost( | 
 |     const std::string& host, | 
 |     base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](const std::string& host, QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->GetBucketsForHost(host); | 
 |           }, | 
 |           host), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBuckets, | 
 |                      weak_factory_.GetWeakPtr(), std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetBucketsForStorageKey( | 
 |     const StorageKey& storage_key, | 
 |     base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback, | 
 |     bool delete_expired) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |  | 
 |   base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> reply; | 
 |   if (delete_expired) { | 
 |     reply = base::BindOnce(&QuotaManagerImpl::DidGetBucketsCheckExpiration, | 
 |                            weak_factory_.GetWeakPtr(), std::move(callback)); | 
 |   } else { | 
 |     reply = base::BindOnce(&QuotaManagerImpl::DidGetBuckets, | 
 |                            weak_factory_.GetWeakPtr(), std::move(callback)); | 
 |   } | 
 |  | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](const StorageKey& storage_key, QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->GetBucketsForStorageKey(storage_key); | 
 |           }, | 
 |           storage_key), | 
 |       std::move(reply)); | 
 | } | 
 |  | 
 | 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(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetUsageAndQuotaForWebApps( | 
 |     const StorageKey& storage_key, | 
 |     UsageAndQuotaCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   GetUsageAndQuotaWithBreakdown( | 
 |       storage_key, | 
 |       base::BindOnce(&DidGetUsageAndQuotaStripBreakdown, std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetUsageAndQuotaWithBreakdown( | 
 |     const StorageKey& storage_key, | 
 |     UsageAndQuotaWithBreakdownCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   HandleGetUsageAndQuotaRequest( | 
 |       storage_key, | 
 |       base::BindOnce(&DidGetUsageAndQuotaStripOverride, std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetUsageAndReportedQuotaWithBreakdown( | 
 |     const StorageKey& storage_key, | 
 |     UsageAndQuotaWithBreakdownCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   if (base::FeatureList::IsEnabled(storage::features::kStaticStorageQuota) && | 
 |       !IsStorageUnlimited(storage_key)) { | 
 |     HandleGetUsageAndQuotaRequest( | 
 |         storage_key, | 
 |         base::BindOnce( | 
 |             [](base::WeakPtr<QuotaManagerImpl> weak_this, | 
 |                UsageAndQuotaWithBreakdownCallback callback, | 
 |                blink::mojom::QuotaStatusCode status, int64_t usage, | 
 |                int64_t quota, bool is_override_enabled, | 
 |                blink::mojom::UsageBreakdownPtr usage_breakdown) { | 
 |               DCHECK(callback); | 
 |               if (!weak_this) { | 
 |                 std::move(callback).Run(blink::mojom::QuotaStatusCode::kUnknown, | 
 |                                         0, 0, std::move(usage_breakdown)); | 
 |                 return; | 
 |               } | 
 |               if (status != blink::mojom::QuotaStatusCode::kOk) { | 
 |                 std::move(callback).Run(status, 0, 0, | 
 |                                         std::move(usage_breakdown)); | 
 |                 return; | 
 |               } | 
 |               weak_this->GetDiskAvailabilityAndTempPoolSize(base::BindOnce( | 
 |                   [](UsageAndQuotaWithBreakdownCallback callback, | 
 |                      blink::mojom::QuotaStatusCode status, int64_t usage, | 
 |                      blink::mojom::UsageBreakdownPtr usage_breakdown, | 
 |                      int64_t total_space, int64_t available_space, | 
 |                      int64_t temp_pool_size) { | 
 |                     int64_t reported_quota = | 
 |                         CalculateReportedQuota(total_space, usage); | 
 |  | 
 |                     std::move(callback).Run(status, usage, reported_quota, | 
 |                                             std::move(usage_breakdown)); | 
 |                   }, | 
 |                   std::move(callback), status, usage, | 
 |                   std::move(usage_breakdown))); | 
 |             }, | 
 |             weak_factory_.GetWeakPtr(), std::move(callback))); | 
 |     return; | 
 |   } | 
 |  | 
 |   HandleGetUsageAndQuotaRequest( | 
 |       storage_key, | 
 |       base::BindOnce(&DidGetUsageAndQuotaStripOverride, std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetUsageAndQuotaForDevtools( | 
 |     const StorageKey& storage_key, | 
 |     UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   HandleGetUsageAndQuotaRequest(storage_key, std::move(callback)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::HandleGetUsageAndQuotaRequest( | 
 |     const StorageKey& storage_key, | 
 |     UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   UsageAndQuotaInfoGatherer* helper = new UsageAndQuotaInfoGatherer( | 
 |       this, storage_key, is_incognito_, std::move(callback)); | 
 |   helper->Start(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetUsageAndQuota(const StorageKey& storage_key, | 
 |                                         UsageAndQuotaCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   if (IsStorageUnlimited(storage_key)) { | 
 |     // TODO(michaeln): This seems like a non-obvious odd behavior, probably for | 
 |     // apps/extensions, but it would be good to eliminate this special case. | 
 |     std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk, 0, kNoLimit); | 
 |     return; | 
 |   } | 
 |  | 
 |   GetUsageAndQuotaWithBreakdown( | 
 |       storage_key, | 
 |       base::BindOnce(&DidGetUsageAndQuotaStripBreakdown, std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetBucketUsageAndQuota(BucketId id, | 
 |                                               UsageAndQuotaCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   GetBucketById( | 
 |       id, base::BindOnce(&QuotaManagerImpl::DidGetBucketForUsageAndQuota, | 
 |                          weak_factory_.GetWeakPtr(), std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetBucketUsageAndReportedQuota( | 
 |     BucketId id, | 
 |     UsageAndQuotaCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   if (base::FeatureList::IsEnabled(storage::features::kStaticStorageQuota)) { | 
 |     GetBucketById( | 
 |         id, | 
 |         base::BindOnce( | 
 |             [](base::WeakPtr<QuotaManagerImpl> weak_this, | 
 |                UsageAndQuotaCallback callback, | 
 |                QuotaErrorOr<BucketInfo> result) { | 
 |               if (!weak_this || !result.has_value()) { | 
 |                 std::move(callback).Run(blink::mojom::QuotaStatusCode::kUnknown, | 
 |                                         0, 0); | 
 |                 return; | 
 |               } | 
 |  | 
 |               const BucketInfo& bucket = result.value(); | 
 |               bool is_storage_unlimited = | 
 |                   weak_this->IsStorageUnlimited(bucket.storage_key); | 
 |  | 
 |               UsageAndQuotaInfoGatherer* helper = new UsageAndQuotaInfoGatherer( | 
 |                   weak_this.get(), bucket, weak_this->is_incognito_, | 
 |                   base::BindOnce( | 
 |                       [](base::WeakPtr<QuotaManagerImpl> weak_this, | 
 |                          UsageAndQuotaCallback callback, | 
 |                          const BucketInfo& bucket, bool is_storage_unlimited, | 
 |                          blink::mojom::QuotaStatusCode status, int64_t usage, | 
 |                          int64_t quota, bool is_override_enabled, | 
 |                          blink::mojom::UsageBreakdownPtr usage_breakdown) { | 
 |                         DCHECK(callback); | 
 |  | 
 |                         if (!weak_this) { | 
 |                           std::move(callback).Run( | 
 |                               blink::mojom::QuotaStatusCode::kUnknown, 0, 0); | 
 |                           return; | 
 |                         } | 
 |  | 
 |                         // If storage is unlimited, return the real quota value. | 
 |                         if (is_storage_unlimited) { | 
 |                           std::move(callback).Run(status, usage, quota); | 
 |                           return; | 
 |                         } | 
 |  | 
 |                         // If there was a requested bucket quota, return that | 
 |                         // value regardless of whether it was capped at the | 
 |                         // StorageKey quota or not. | 
 |                         if (bucket.quota > 0) { | 
 |                           std::move(callback).Run(status, usage, bucket.quota); | 
 |                           return; | 
 |                         } | 
 |  | 
 |                         if (status != blink::mojom::QuotaStatusCode::kOk) { | 
 |                           std::move(callback).Run(status, 0, 0); | 
 |                           return; | 
 |                         } | 
 |  | 
 |                         weak_this->GetDiskAvailabilityAndTempPoolSize( | 
 |                             base::BindOnce( | 
 |                                 [](UsageAndQuotaCallback callback, | 
 |                                    blink::mojom::QuotaStatusCode status, | 
 |                                    int64_t usage, int64_t total_space, | 
 |                                    int64_t available_space, | 
 |                                    int64_t temp_pool_size) { | 
 |                                   int64_t reported_quota = | 
 |                                       CalculateReportedQuota(total_space, | 
 |                                                              usage); | 
 |  | 
 |                                   std::move(callback).Run(status, usage, | 
 |                                                           reported_quota); | 
 |                                 }, | 
 |                                 std::move(callback), status, usage)); | 
 |                       }, | 
 |                       weak_this, std::move(callback), bucket, | 
 |                       is_storage_unlimited)); | 
 |               helper->Start(); | 
 |             }, | 
 |             weak_factory_.GetWeakPtr(), std::move(callback))); | 
 |     return; | 
 |   } | 
 |  | 
 |   GetBucketUsageAndQuota(id, std::move(callback)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetBucketSpaceRemaining( | 
 |     const BucketLocator& bucket, | 
 |     base::OnceCallback<void(QuotaErrorOr<int64_t>)> callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   // ConcurrentCallbacks is run once with each space restriction --- the | 
 |   // StorageKey usage/quota and the bucket's usage/quota (if it exists). The | 
 |   // final value is the more restrictive of the two. | 
 |   auto aggregator = base::BindOnce( | 
 |       [](base::OnceCallback<void(QuotaErrorOr<int64_t>)> final_space_remaining, | 
 |          std::vector<int64_t> space_checks) { | 
 |         int64_t space_left = | 
 |             *std::min_element(space_checks.begin(), space_checks.end()); | 
 |         if (space_left == std::numeric_limits<int64_t>::min()) { | 
 |           std::move(final_space_remaining) | 
 |               .Run(base::unexpected(QuotaError::kUnknownError)); | 
 |         } else { | 
 |           std::move(final_space_remaining).Run(space_left); | 
 |         } | 
 |       }, | 
 |       std::move(callback)); | 
 |   base::ConcurrentCallbacks<int64_t> concurrent; | 
 |  | 
 |   // Translates a UsageAndQuota result into a single number for the aggregator. | 
 |   auto on_got_usage = | 
 |       [](base::OnceCallback<void(int64_t)> report_space_remaining, | 
 |          blink::mojom::QuotaStatusCode code, int64_t usage, int64_t quota) { | 
 |         // Report the amount of allocated space remaining, or min() for an | 
 |         // error, or max() if there's no limit. | 
 |         int64_t leftover_space = 0; | 
 |         if (code != blink::mojom::QuotaStatusCode::kOk) { | 
 |           leftover_space = std::numeric_limits<int64_t>::min(); | 
 |         } else if (quota == 0) { | 
 |           leftover_space = kNoLimit; | 
 |         } else { | 
 |           leftover_space = quota - usage; | 
 |         } | 
 |         std::move(report_space_remaining).Run(leftover_space); | 
 |       }; | 
 |  | 
 |   // Check the usage for the whole StorageKey. | 
 |   GetUsageAndQuota(bucket.storage_key, | 
 |                    base::BindOnce(on_got_usage, concurrent.CreateCallback())); | 
 |  | 
 |   // If this is the default bucket, we're done. Otherwise, additionally check | 
 |   // the usage of the specific bucket against its quota. | 
 |   if (!bucket.is_default) { | 
 |     GetBucketUsageAndQuota( | 
 |         bucket.id, base::BindOnce(on_got_usage, concurrent.CreateCallback())); | 
 |   } | 
 |   std::move(concurrent).Done(std::move(aggregator)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::OnClientWriteFailed(const StorageKey& storage_key) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   OnFullDiskError(storage_key); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::SetUsageCacheEnabled(QuotaClientType client_id, | 
 |                                             const StorageKey& storage_key, | 
 |                                             bool enabled) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   UsageTracker* usage_tracker = GetUsageTracker(); | 
 |   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_); | 
 |   DCHECK(callback); | 
 |  | 
 |   auto result_callback = base::BindOnce( | 
 |       [](StatusCallback callback, | 
 |          QuotaErrorOr<mojom::BucketTableEntryPtr> result) { | 
 |         std::move(callback).Run(result.has_value() | 
 |                                     ? blink::mojom::QuotaStatusCode::kOk | 
 |                                     : blink::mojom::QuotaStatusCode::kUnknown); | 
 |       }, | 
 |       std::move(callback)); | 
 |   DeleteBucketDataInternal(bucket, std::move(quota_client_types), | 
 |                            std::move(result_callback)); | 
 | } | 
 |  | 
 | 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( | 
 |           [](const StorageKey& storage_key, const std::string& bucket_name, | 
 |              QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->GetBucket(storage_key, bucket_name); | 
 |           }, | 
 |           storage_key, bucket_name), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBucketForDeletion, | 
 |                      weak_factory_.GetWeakPtr(), std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::UpdateBucketExpiration( | 
 |     BucketId bucket, | 
 |     const base::Time& expiration, | 
 |     base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](BucketId bucket, const base::Time& expiration, | 
 |              QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->UpdateBucketExpiration(bucket, expiration); | 
 |           }, | 
 |           bucket, expiration), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBucket, | 
 |                      weak_factory_.GetWeakPtr(), | 
 |                      /*notify_update_bucket=*/true, std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::UpdateBucketPersistence( | 
 |     BucketId bucket, | 
 |     bool persistent, | 
 |     base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](BucketId bucket, bool persistent, QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->UpdateBucketPersistence(bucket, persistent); | 
 |           }, | 
 |           bucket, persistent), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBucket, | 
 |                      weak_factory_.GetWeakPtr(), | 
 |                      /*notify_update_bucket=*/true, std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::PerformStorageCleanup( | 
 |     QuotaClientTypes quota_client_types, | 
 |     base::OnceClosure callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   StorageCleanupHelper* deleter = new StorageCleanupHelper( | 
 |       this, std::move(quota_client_types), std::move(callback)); | 
 |   deleter->Start(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DeleteHostData(const std::string& host, | 
 |                                       StatusCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (host.empty() || client_types_.empty()) { | 
 |     std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk); | 
 |     return; | 
 |   } | 
 |   auto buckets_deleter = std::make_unique<BucketSetDataDeleter>( | 
 |       this, base::BindOnce(&QuotaManagerImpl::DidDeleteBuckets, | 
 |                            weak_factory_.GetWeakPtr(), std::move(callback))); | 
 |   auto* buckets_deleter_ptr = buckets_deleter.get(); | 
 |   bucket_set_data_deleters_[buckets_deleter_ptr] = std::move(buckets_deleter); | 
 |   GetBucketsForHost(host, buckets_deleter_ptr->GetBucketDeletionCallback()); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DeleteStorageKeyData( | 
 |     const blink::StorageKey& storage_key, | 
 |     StatusCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (client_types_.empty()) { | 
 |     std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk); | 
 |     return; | 
 |   } | 
 |   auto buckets_deleter = std::make_unique<BucketSetDataDeleter>( | 
 |       this, base::BindOnce(&QuotaManagerImpl::DidDeleteBuckets, | 
 |                            weak_factory_.GetWeakPtr(), std::move(callback))); | 
 |   auto* buckets_deleter_ptr = buckets_deleter.get(); | 
 |   bucket_set_data_deleters_[buckets_deleter_ptr] = std::move(buckets_deleter); | 
 |   GetBucketsForStorageKey(storage_key, | 
 |                           buckets_deleter_ptr->GetBucketDeletionCallback()); | 
 | } | 
 |  | 
 | // static | 
 | void QuotaManagerImpl::DidDeleteBuckets( | 
 |     base::WeakPtr<QuotaManagerImpl> quota_manager, | 
 |     StatusCallback callback, | 
 |     BucketSetDataDeleter* deleter, | 
 |     blink::mojom::QuotaStatusCode status_code) { | 
 |   DCHECK(callback); | 
 |   DCHECK(deleter); | 
 |   DCHECK(deleter->completed()); | 
 |  | 
 |   if (quota_manager) { | 
 |     quota_manager->bucket_set_data_deleters_.erase(deleter); | 
 |   } | 
 |  | 
 |   std::move(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::GetDiskAvailabilityAndTempPoolSize( | 
 |     GetDiskAvailabilityAndTempPoolSizeCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   auto info = std::make_unique<AccumulateQuotaInternalsInfo>(); | 
 |   auto* info_ptr = info.get(); | 
 |  | 
 |   base::RepeatingClosure barrier = base::BarrierClosure( | 
 |       2, base::BindOnce( | 
 |              &QuotaManagerImpl::FinallySendDiskAvailabilityAndTempPoolSize, | 
 |              weak_factory_.GetWeakPtr(), std::move(callback), std::move(info))); | 
 |  | 
 |   // base::Unretained usage is safe here because BarrierClosure holds | 
 |   // the std::unque_ptr that keeps AccumulateQuotaInternalsInfo alive, and the | 
 |   // BarrierClosure will outlive the UpdateQuotaInternalsDiskAvailability | 
 |   // and UpdateQuotaInternalsTempPoolSpace closures. | 
 |   GetStorageCapacity(base::BindOnce( | 
 |       &QuotaManagerImpl::UpdateQuotaInternalsDiskAvailability, | 
 |       weak_factory_.GetWeakPtr(), barrier, base::Unretained(info_ptr))); | 
 |   GetQuotaSettings(base::BindOnce( | 
 |       &QuotaManagerImpl::UpdateQuotaInternalsTempPoolSpace, | 
 |       weak_factory_.GetWeakPtr(), barrier, base::Unretained(info_ptr))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::UpdateQuotaInternalsDiskAvailability( | 
 |     base::OnceClosure barrier_callback, | 
 |     AccumulateQuotaInternalsInfo* info, | 
 |     int64_t total_space, | 
 |     int64_t available_space) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK_GE(total_space, 0); | 
 |   DCHECK_GE(total_space, available_space); | 
 |  | 
 |   info->total_space = total_space; | 
 |   info->available_space = available_space; | 
 |  | 
 |   std::move(barrier_callback).Run(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::UpdateQuotaInternalsTempPoolSpace( | 
 |     base::OnceClosure barrier_callback, | 
 |     AccumulateQuotaInternalsInfo* info, | 
 |     const QuotaSettings& settings) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK_GE(settings.pool_size, 0); | 
 |   info->temp_pool_size = settings.pool_size; | 
 |  | 
 |   std::move(barrier_callback).Run(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::FinallySendDiskAvailabilityAndTempPoolSize( | 
 |     GetDiskAvailabilityAndTempPoolSizeCallback callback, | 
 |     std::unique_ptr<AccumulateQuotaInternalsInfo> info) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK_GE(info->total_space, 0); | 
 |   DCHECK_GE(info->total_space, info->available_space); | 
 |   DCHECK_GE(info->temp_pool_size, 0); | 
 |  | 
 |   std::move(callback).Run(info->total_space, info->available_space, | 
 |                           info->temp_pool_size); | 
 | } | 
 |  | 
 | 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::GetGlobalUsage(UsageCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   UsageTracker* usage_tracker = GetUsageTracker(); | 
 |   DCHECK(usage_tracker); | 
 |   usage_tracker->GetGlobalUsage(std::move(callback)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetGlobalUsageForInternals( | 
 |     GetGlobalUsageForInternalsCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   UsageTracker* usage_tracker = GetUsageTracker(); | 
 |   DCHECK(usage_tracker); | 
 |   usage_tracker->GetGlobalUsage(std::move(callback)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetStorageKeyUsageWithBreakdown( | 
 |     const blink::StorageKey& storage_key, | 
 |     UsageWithBreakdownCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   UsageTracker* usage_tracker = GetUsageTracker(); | 
 |   DCHECK(usage_tracker); | 
 |   usage_tracker->GetStorageKeyUsageWithBreakdown(storage_key, | 
 |                                                  std::move(callback)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetBucketUsageWithBreakdown( | 
 |     const BucketLocator& bucket, | 
 |     UsageWithBreakdownCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   UsageTracker* usage_tracker = GetUsageTracker(); | 
 |   DCHECK(usage_tracker); | 
 |   usage_tracker->GetBucketUsageWithBreakdown(bucket, std::move(callback)); | 
 | } | 
 |  | 
 | bool QuotaManagerImpl::IsStorageUnlimited(const StorageKey& storage_key) const { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   return special_storage_policy_.get() && | 
 |          special_storage_policy_->IsStorageUnlimited( | 
 |              storage_key.origin().GetURL()); | 
 | } | 
 |  | 
 | int64_t QuotaManagerImpl::GetQuotaForStorageKey( | 
 |     const StorageKey& storage_key, | 
 |     const QuotaSettings& settings) const { | 
 |   if (IsStorageUnlimited(storage_key)) { | 
 |     return kNoLimit; | 
 |   } | 
 |  | 
 |   if (special_storage_policy_ && special_storage_policy_->IsStorageSessionOnly( | 
 |                                      storage_key.origin().GetURL())) { | 
 |     return settings.session_only_per_storage_key_quota; | 
 |   } | 
 |  | 
 |   return settings.per_storage_key_quota; | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetBucketsModifiedBetween(base::Time begin, | 
 |                                                  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>()); | 
 |     return; | 
 |   } | 
 |  | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](base::Time begin, base::Time end, QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->GetBucketsModifiedBetween(begin, end); | 
 |           }, | 
 |           begin, end), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetModifiedBetween, | 
 |                      weak_factory_.GetWeakPtr(), std::move(callback))); | 
 | } | 
 |  | 
 | bool QuotaManagerImpl::ResetUsageTracker() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   UsageTracker* previous_usage_tracker = GetUsageTracker(); | 
 |   DCHECK(previous_usage_tracker); | 
 |   if (previous_usage_tracker->IsWorking()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   usage_tracker_ = std::make_unique<UsageTracker>( | 
 |       this, client_types_, special_storage_policy_.get()); | 
 |   return true; | 
 | } | 
 |  | 
 | QuotaManagerImpl::~QuotaManagerImpl() { | 
 |   // Delete this now because otherwise it may call back into `this` after the | 
 |   // `sequence_checker_` has been destroyed. | 
 |   temporary_storage_evictor_.reset(); | 
 |  | 
 |   proxy_->InvalidateQuotaManagerImpl(base::PassKey<QuotaManagerImpl>()); | 
 |  | 
 |   if (database_) { | 
 |     db_runner_->DeleteSoon(FROM_HERE, std::move(database_)); | 
 |   } | 
 | } | 
 |  | 
 | void QuotaManagerImpl::EnsureDatabaseOpened() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(io_thread_->BelongsToCurrentThread()); | 
 |   if (database_) { | 
 |     // Already initialized. | 
 |     return; | 
 |   } | 
 |  | 
 |   // Use an empty path to open an in-memory only database for incognito. | 
 |   database_ = std::make_unique<QuotaDatabase>(is_incognito_ ? base::FilePath() | 
 |                                                             : profile_path_); | 
 |  | 
 |   database_->SetDbErrorCallback( | 
 |       base::BindPostTaskToCurrentDefault(base::BindRepeating( | 
 |           &QuotaManagerImpl::OnDbError, weak_factory_.GetWeakPtr()))); | 
 |  | 
 |   usage_tracker_ = std::make_unique<UsageTracker>( | 
 |       this, client_types_, special_storage_policy_.get()); | 
 |  | 
 |   if (!is_incognito_) { | 
 |     histogram_timer_.Start(FROM_HERE, | 
 |                            base::Milliseconds(kReportHistogramInterval), this, | 
 |                            &QuotaManagerImpl::ReportHistogram); | 
 |   } | 
 |  | 
 |   if (bootstrap_disabled_for_testing_) { | 
 |     return; | 
 |   } | 
 |  | 
 |   MaybeBootstrapDatabase(); | 
 |   MaybeRemoveMediaLicenseDatabases(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::MaybeRemoveMediaLicenseDatabases() { | 
 |   db_runner_->PostTaskAndReplyWithResult( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&QuotaDatabase::IsMediaLicenseDatabaseRemoved, | 
 |                      base::Unretained(database_.get())), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetMediaLicenseDatabaseRemovalFlag, | 
 |                      weak_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetMediaLicenseDatabaseRemovalFlag( | 
 |     bool is_media_license_database_removed) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   if (!is_media_license_database_removed) { | 
 |     RemoveMediaLicenseDatabases(); | 
 |   } | 
 | } | 
 |  | 
 | void QuotaManagerImpl::RemoveMediaLicenseDatabases() { | 
 |   base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&QuotaManagerImpl::DeleteMediaLicenseDatabase, | 
 |                      weak_factory_.GetWeakPtr()), | 
 |       kMinutesAfterStartupToBeginMediaLicenseDatabaseDeletion); | 
 |  | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce([](QuotaDatabase* database) { | 
 |         DCHECK(database); | 
 |         return database->SetIsMediaLicenseDatabaseRemoved(true); | 
 |       }), | 
 |       base::DoNothing(), FROM_HERE); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::MaybeBootstrapDatabase() { | 
 |   is_bootstrapping_database_ = true; | 
 |   db_runner_->PostTaskAndReplyWithResult( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&QuotaDatabase::IsBootstrapped, | 
 |                      base::Unretained(database_.get())), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBootstrapFlag, | 
 |                      weak_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetBootstrapFlag(bool is_database_bootstrapped) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(is_bootstrapping_database_); | 
 |   if (!is_database_bootstrapped) { | 
 |     BootstrapDatabase(); | 
 |     return; | 
 |   } | 
 |   is_bootstrapping_database_ = false; | 
 |   RunDatabaseCallbacks(); | 
 |   base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&QuotaManagerImpl::StartEviction, | 
 |                      weak_factory_.GetWeakPtr()), | 
 |       kMinutesAfterStartupToBeginEviction); | 
 | } | 
 |  | 
 | 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( | 
 |     std::set<StorageKey> storage_keys) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(storage_key_gatherer_); | 
 |   storage_key_gatherer_.reset(); | 
 |  | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](std::set<StorageKey> storage_keys, QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->RegisterInitialStorageKeyInfo( | 
 |                 std::move(storage_keys)); | 
 |           }, | 
 |           std::move(storage_keys)), | 
 |       base::BindOnce(&QuotaManagerImpl::DidBootstrapDatabase, | 
 |                      weak_factory_.GetWeakPtr()), | 
 |       FROM_HERE, | 
 |       /*is_bootstrap_task=*/true); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidBootstrapDatabase(QuotaError error) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   if (error == QuotaError::kDatabaseError) { | 
 |     // If we got an error during bootstrapping there is no point in | 
 |     // trying again. Disable the database instead. | 
 |     db_disabled_ = true; | 
 |     ReportDatabaseDisabledReason( | 
 |         DatabaseDisabledReason::kRegisterStorageKeyFailed); | 
 |   } | 
 |  | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce([](QuotaDatabase* database) { | 
 |         DCHECK(database); | 
 |         return database->SetIsBootstrapped(true); | 
 |       }), | 
 |       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; | 
 |   if (error == QuotaError::kDatabaseError) { | 
 |     // If we got an error during bootstrapping there is no point in | 
 |     // trying again. Disable the database instead. | 
 |     db_disabled_ = true; | 
 |     ReportDatabaseDisabledReason( | 
 |         DatabaseDisabledReason::kSetIsBootstrappedFailed); | 
 |   } | 
 |  | 
 |   RunDatabaseCallbacks(); | 
 |   base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&QuotaManagerImpl::StartEviction, | 
 |                      weak_factory_.GetWeakPtr()), | 
 |       kMinutesAfterStartupToBeginEviction); | 
 |  | 
 |   // Schedule the MediaLicenseDatabase deletion task. | 
 |   base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&QuotaManagerImpl::DeleteMediaLicenseDatabase, | 
 |                      weak_factory_.GetWeakPtr()), | 
 |       kMinutesAfterStartupToBeginMediaLicenseDatabaseDeletion); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::RunDatabaseCallbacks() { | 
 |   for (auto& callback : database_callbacks_) { | 
 |     std::move(callback).Run(); | 
 |   } | 
 |   database_callbacks_.clear(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::RegisterClient( | 
 |     mojo::PendingRemote<mojom::QuotaClient> client, | 
 |     QuotaClientType client_type) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(!database_.get()) | 
 |       << "All clients must be registered before the database is initialized"; | 
 |  | 
 |   clients_for_ownership_.emplace_back(std::move(client)); | 
 |   mojom::QuotaClient* client_ptr = clients_for_ownership_.back().get(); | 
 |   client_types_.insert({client_ptr, client_type}); | 
 | } | 
 |  | 
 | UsageTracker* QuotaManagerImpl::GetUsageTracker() const { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   return usage_tracker_.get(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::NotifyBucketAccessed(const BucketLocator& bucket, | 
 |                                             base::Time access_time) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   EnsureDatabaseOpened(); | 
 |   if (is_getting_eviction_bucket_) { | 
 |     // Record the accessed buckets while GetLRUStorageKey task is running | 
 |     // to filter out them from eviction. | 
 |     access_notified_buckets_.insert(bucket); | 
 |   } | 
 |  | 
 |   if (db_disabled_) { | 
 |     return; | 
 |   } | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](BucketLocator bucket, base::Time accessed_time, | 
 |              QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             if (bucket.is_default) { | 
 |               return database->SetStorageKeyLastAccessTime(bucket.storage_key, | 
 |                                                            accessed_time); | 
 |             } else { | 
 |               return database->SetBucketLastAccessTime(bucket.id, | 
 |                                                        accessed_time); | 
 |             } | 
 |           }, | 
 |           bucket, access_time), | 
 |       base::DoNothing()); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::NotifyBucketModified(QuotaClientType client_id, | 
 |                                             const BucketLocator& bucket, | 
 |                                             std::optional<int64_t> delta, | 
 |                                             base::Time modification_time, | 
 |                                             base::OnceClosure callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   GetUsageTracker()->UpdateBucketUsageCache(client_id, bucket, delta); | 
 |   // Return once usage cache is updated for callers waiting for quota changes to | 
 |   // be reflected before querying for usage. | 
 |   std::move(callback).Run(); | 
 |  | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](BucketLocator bucket, base::Time modification_time, | 
 |              QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             BucketId id = bucket.id; | 
 |             if (!id) { | 
 |               CHECK(bucket.is_default); | 
 |               QuotaErrorOr<BucketInfo> result = | 
 |                   database->GetBucket(bucket.storage_key, kDefaultBucketName); | 
 |               if (!result.has_value()) { | 
 |                 return QuotaError::kNotFound; | 
 |               } | 
 |  | 
 |               id = result->id; | 
 |             } | 
 |             return database->SetBucketLastModifiedTime(id, modification_time); | 
 |           }, | 
 |           bucket, modification_time), | 
 |       base::DoNothing()); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DumpBucketTable(DumpBucketTableCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   if (db_disabled_ || !database_) { | 
 |     std::move(callback).Run(BucketTableEntries()); | 
 |     return; | 
 |   } | 
 |   DumpBucketTableHelper* helper = new DumpBucketTableHelper; | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce(&DumpBucketTableHelper::DumpBucketTableOnDBThread, | 
 |                      base::Unretained(helper)), | 
 |       base::BindOnce(&DumpBucketTableHelper::DidDumpBucketTable, | 
 |                      base::Owned(helper), weak_factory_.GetWeakPtr(), | 
 |                      std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::RetrieveBucketsTable( | 
 |     RetrieveBucketsTableCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run({}); | 
 |     return; | 
 |   } | 
 |  | 
 |   DumpBucketTable( | 
 |       base::BindOnce(&QuotaManagerImpl::RetrieveBucketUsageForBucketTable, | 
 |                      weak_factory_.GetWeakPtr(), std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::RetrieveBucketUsageForBucketTable( | 
 |     RetrieveBucketsTableCallback callback, | 
 |     BucketTableEntries entries) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   base::ConcurrentCallbacks<mojom::BucketTableEntryPtr> concurrent; | 
 |  | 
 |   for (auto& entry : entries) { | 
 |     std::optional<StorageKey> storage_key = | 
 |         StorageKey::Deserialize(entry->storage_key); | 
 |     // If the serialization format changes keys may not deserialize. | 
 |     if (!storage_key) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     BucketId bucket_id = BucketId(entry->bucket_id); | 
 |  | 
 |     BucketLocator bucket_locator = | 
 |         BucketLocator(bucket_id, std::move(storage_key).value(), | 
 |                       entry->name == kDefaultBucketName); | 
 |  | 
 |     GetBucketUsageWithBreakdown( | 
 |         bucket_locator, | 
 |         base::BindOnce(&QuotaManagerImpl::AddBucketTableEntry, | 
 |                        weak_factory_.GetWeakPtr(), std::move(entry), | 
 |                        concurrent.CreateCallback())); | 
 |   } | 
 |   std::move(concurrent).Done(std::move(callback)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::AddBucketTableEntry( | 
 |     mojom::BucketTableEntryPtr entry, | 
 |     base::OnceCallback<void(mojom::BucketTableEntryPtr)> callback, | 
 |     int64_t usage, | 
 |     blink::mojom::UsageBreakdownPtr bucket_usage_breakdown) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   entry->usage = usage; | 
 |  | 
 |   std::move(callback).Run(std::move(entry)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::OnDbError(int error_code) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   sql::UmaHistogramSqliteResult("Quota.QuotaDatabaseError", error_code); | 
 |  | 
 |   // Start the storage eviction routine on a full disk error. | 
 |   if (static_cast<sql::SqliteErrorCode>(error_code) == | 
 |       sql::SqliteErrorCode::kFullDisk) { | 
 |     OnFullDiskError(std::nullopt); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!sql::IsErrorCatastrophic(error_code)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // Db will be set to disabled after a bootstrapping failure. | 
 |   if (db_disabled_) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // Ignore any errors that happen while a new bootstrap attempt is already in | 
 |   // progress or queued. | 
 |   if (is_bootstrapping_database_) { | 
 |     return; | 
 |   } | 
 |  | 
 |   if (bootstrap_disabled_for_testing_) { | 
 |     db_disabled_ = true; | 
 |     return; | 
 |   } | 
 |  | 
 |   // Start another bootstrapping process. Pause eviction while bootstrapping | 
 |   // is in progress. When bootstrapping finishes a new Evictor will be | 
 |   // created. | 
 |   is_bootstrapping_database_ = true; | 
 |   temporary_storage_evictor_ = nullptr; | 
 |  | 
 |   // Wipe the database before triggering another bootstrap. | 
 |   db_runner_->PostTaskAndReplyWithResult( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&QuotaDatabase::RecoverOrRaze, | 
 |                      base::Unretained(database_.get()), error_code), | 
 |       base::BindOnce(&QuotaManagerImpl::DidRecoverOrRazeForReBootstrap, | 
 |                      weak_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::OnFullDiskError(std::optional<StorageKey> storage_key) { | 
 |   if ((base::TimeTicks::Now() - last_full_disk_eviction_time_) > | 
 |       base::Minutes(15)) { | 
 |     last_full_disk_eviction_time_ = base::TimeTicks::Now(); | 
 |     StartEviction(); | 
 |   } | 
 |  | 
 |   // We may already be evicting, either due to the above or just by chance. In | 
 |   // either case, do nothing more for now. | 
 |   if (temporary_storage_evictor_ && temporary_storage_evictor_->in_round()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   if (storage_key) { | 
 |     NotifyWriteFailed(*storage_key); | 
 |   } else if (last_opened_bucket_site_) { | 
 |     NotifyWriteFailed(*last_opened_bucket_site_); | 
 |   } | 
 | } | 
 |  | 
 | void QuotaManagerImpl::NotifyWriteFailed(const blink::StorageKey& storage_key) { | 
 |   auto [time_of_last_stats, total_space, available_space] = | 
 |       cached_disk_stats_for_storage_pressure_; | 
 |   auto age_of_disk_stats = base::TimeTicks::Now() - time_of_last_stats; | 
 |  | 
 |   // Avoid polling for free disk space if disk stats have been recently | 
 |   // queried. | 
 |   if (age_of_disk_stats < kStoragePressureCheckDiskStatsInterval) { | 
 |     MaybeRunStoragePressureCallback(storage_key, total_space, available_space); | 
 |     return; | 
 |   } | 
 |  | 
 |   GetStorageCapacity( | 
 |       base::BindOnce(&QuotaManagerImpl::MaybeRunStoragePressureCallback, | 
 |                      weak_factory_.GetWeakPtr(), storage_key)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::StartEviction() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   if (eviction_disabled_) { | 
 |     return; | 
 |   } | 
 |   if (!temporary_storage_evictor_) { | 
 |     temporary_storage_evictor_ = | 
 |         std::make_unique<QuotaTemporaryStorageEvictor>(this, kEvictionInterval); | 
 |   } | 
 |   temporary_storage_evictor_->Start(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DeleteMediaLicenseDatabase() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   GetBucketsModifiedBetween( | 
 |       base::Time::Min(), base::Time::Max(), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBucketsForMediaLicenseDeletion, | 
 |                      weak_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetBucketsForMediaLicenseDeletion( | 
 |     const std::set<BucketLocator>& buckets) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   std::vector<base::FilePath> media_license_dir_paths; | 
 |  | 
 |   for (const BucketLocator& bucket : buckets) { | 
 |     if (bucket.storage_key.IsFirstPartyContext()) { | 
 |       media_license_dir_paths.push_back( | 
 |           CreateMediaLicenseBucketPath(profile_path_, bucket)); | 
 |     } | 
 |   } | 
 |  | 
 |   db_runner_->PostTask(FROM_HERE, | 
 |                        base::BindOnce( | 
 |                            [](std::vector<base::FilePath> file_paths) { | 
 |                              for (base::FilePath& path : file_paths) { | 
 |                                base::DeletePathRecursively(path); | 
 |                              } | 
 |                            }, | 
 |                            std::move(media_license_dir_paths))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DeleteBucketFromDatabase( | 
 |     const BucketLocator& bucket, | 
 |     bool commit_immediately, | 
 |     base::OnceCallback<void(QuotaErrorOr<mojom::BucketTableEntryPtr>)> | 
 |         callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |  | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](const BucketLocator& bucket, bool commit_immediately, | 
 |              QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             auto result = database->DeleteBucketData(bucket); | 
 |             if (commit_immediately && result.has_value()) { | 
 |               database->CommitNow(); | 
 |             } | 
 |  | 
 |             return result; | 
 |           }, | 
 |           bucket, commit_immediately), | 
 |       base::BindOnce(&QuotaManagerImpl::OnBucketDeleted, | 
 |                      weak_factory_.GetWeakPtr(), std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidEvictBucketData( | 
 |     BucketId evicted_bucket_id, | 
 |     base::RepeatingCallback<void(bool)> barrier, | 
 |     QuotaErrorOr<mojom::BucketTableEntryPtr> entry) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(io_thread_->BelongsToCurrentThread()); | 
 |  | 
 |   if (entry.has_value()) { | 
 |     DCHECK(entry.value()); | 
 |     base::Time now = QuotaDatabase::GetNow(); | 
 |     base::UmaHistogramCounts1M( | 
 |         QuotaManagerImpl::kEvictedBucketAccessedCountHistogram, | 
 |         entry.value()->use_count); | 
 |     base::UmaHistogramCounts1000( | 
 |         QuotaManagerImpl::kEvictedBucketDaysSinceAccessHistogram, | 
 |         (now - entry.value()->last_accessed).InDays()); | 
 |     barrier.Run(true); | 
 |   } else { | 
 |     // We only try to evict buckets that are not in use, so basically deletion | 
 |     // attempt for eviction should not fail.  Let's record the bucket if we get | 
 |     // an error and exclude it from future eviction if the error happens | 
 |     // consistently (> kThresholdOfErrorsToBeDenylisted). | 
 |     buckets_in_error_[evicted_bucket_id]++; | 
 |     barrier.Run(false); | 
 |   } | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DeleteBucketDataInternal( | 
 |     const BucketLocator& bucket, | 
 |     QuotaClientTypes quota_client_types, | 
 |     base::OnceCallback<void(QuotaErrorOr<mojom::BucketTableEntryPtr>)> | 
 |         callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kDatabaseDisabled)); | 
 |     return; | 
 |   } | 
 |   auto bucket_deleter = std::make_unique<BucketDataDeleter>( | 
 |       this, bucket, std::move(quota_client_types), /*commit_immediately=*/true, | 
 |       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, | 
 |     base::OnceCallback<void(QuotaErrorOr<mojom::BucketTableEntryPtr>)> callback, | 
 |     BucketDataDeleter* deleter, | 
 |     QuotaErrorOr<mojom::BucketTableEntryPtr> result) { | 
 |   DCHECK(callback); | 
 |   DCHECK(deleter); | 
 |   DCHECK(deleter->completed()); | 
 |  | 
 |   if (quota_manager) { | 
 |     quota_manager->bucket_data_deleters_.erase(deleter); | 
 |   } | 
 |  | 
 |   std::move(callback).Run(std::move(result)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidDeleteBucketForRecreation( | 
 |     const BucketInitParams& params, | 
 |     base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback, | 
 |     BucketInfo bucket_info, | 
 |     QuotaErrorOr<mojom::BucketTableEntryPtr> result) { | 
 |   if (result.has_value()) { | 
 |     UpdateOrCreateBucket(params, std::move(callback)); | 
 |   } else { | 
 |     std::move(callback).Run(base::unexpected(QuotaError::kDatabaseError)); | 
 |   } | 
 | } | 
 |  | 
 | void QuotaManagerImpl::MaybeRunStoragePressureCallback( | 
 |     const StorageKey& storage_key, | 
 |     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(crbug.com/40121667): Figure out what 0 total_space means | 
 |   // and how to handle the storage pressure callback in these cases. | 
 |   if (total_space == 0) { | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!storage_pressure_callback_) { | 
 |     // Quota will hold onto a storage pressure notification if no storage | 
 |     // pressure callback is set. | 
 |     storage_key_for_pending_storage_pressure_callback_ = std::move(storage_key); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (available_space < kStoragePressureThresholdRatio * total_space) { | 
 |     storage_pressure_callback_.Run(std::move(storage_key)); | 
 |   } | 
 | } | 
 |  | 
 | void QuotaManagerImpl::SimulateStoragePressure(const url::Origin& origin_url) { | 
 |   const StorageKey key = StorageKey::CreateFirstParty(origin_url); | 
 |   // In Incognito, since no data is stored on disk, storage pressure should be | 
 |   // ignored. | 
 |   DCHECK_EQ(is_incognito_, storage_pressure_callback_.is_null()); | 
 |  | 
 |   if (storage_pressure_callback_.is_null()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   storage_pressure_callback_.Run(key); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::IsSimulateStoragePressureAvailable( | 
 |     IsSimulateStoragePressureAvailableCallback callback) { | 
 |   // We assume this is only the case in incognito. If it changes, update this. | 
 |   DCHECK_EQ(is_incognito_, storage_pressure_callback_.is_null()); | 
 |  | 
 |   std::move(callback).Run(!storage_pressure_callback_.is_null()); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::SetStoragePressureCallback( | 
 |     base::RepeatingCallback<void(const StorageKey&)> | 
 |         storage_pressure_callback) { | 
 |   storage_pressure_callback_ = storage_pressure_callback; | 
 |   if (storage_key_for_pending_storage_pressure_callback_.has_value()) { | 
 |     storage_pressure_callback_.Run( | 
 |         std::move(storage_key_for_pending_storage_pressure_callback_.value())); | 
 |     storage_key_for_pending_storage_pressure_callback_ = std::nullopt; | 
 |   } | 
 | } | 
 |  | 
 | int QuotaManagerImpl::GetOverrideHandleId() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   return ++next_override_handle_id_; | 
 | } | 
 |  | 
 | void QuotaManagerImpl::OverrideQuotaForStorageKey( | 
 |     int handle_id, | 
 |     const StorageKey& storage_key, | 
 |     std::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 | 
 |     // QuotaOverride in the case that one does not exist for storage key. | 
 |     devtools_overrides_[storage_key].active_override_session_ids.insert( | 
 |         handle_id); | 
 |     devtools_overrides_[storage_key].quota_size = quota_size.value(); | 
 |   } else { | 
 |     devtools_overrides_.erase(storage_key); | 
 |   } | 
 | } | 
 |  | 
 | void QuotaManagerImpl::WithdrawOverridesForHandle(int handle_id) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   std::vector<StorageKey> storage_keys_to_clear; | 
 |   for (auto& devtools_override : devtools_overrides_) { | 
 |     auto& quota_override = devtools_override.second; | 
 |     auto& storage_key = devtools_override.first; | 
 |  | 
 |     quota_override.active_override_session_ids.erase(handle_id); | 
 |  | 
 |     if (!quota_override.active_override_session_ids.size()) { | 
 |       storage_keys_to_clear.push_back(storage_key); | 
 |     } | 
 |   } | 
 |  | 
 |   for (auto& storage_key : storage_keys_to_clear) { | 
 |     devtools_overrides_.erase(storage_key); | 
 |   } | 
 | } | 
 |  | 
 | std::optional<int64_t> QuotaManagerImpl::GetQuotaOverrideForStorageKey( | 
 |     const StorageKey& storage_key) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   if (!base::Contains(devtools_overrides_, storage_key)) { | 
 |     return std::nullopt; | 
 |   } | 
 |   return devtools_overrides_[storage_key].quota_size; | 
 | } | 
 |  | 
 | void QuotaManagerImpl::CorruptDatabaseForTesting( | 
 |     base::OnceCallback<void(const base::FilePath&)> corrupter, | 
 |     base::OnceCallback<void(QuotaError)> callback) { | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](base::OnceCallback<void(const base::FilePath&)> corrupter, | 
 |              QuotaDatabase* database) { | 
 |             return database->CorruptForTesting(  // IN-TEST | 
 |                 std::move(corrupter)); | 
 |           }, | 
 |           std::move(corrupter)), | 
 |       std::move(callback)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::ReportHistogram() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(!is_incognito_); | 
 |  | 
 |   GetGlobalUsage( | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetGlobalUsageForHistogram, | 
 |                      weak_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetGlobalUsageForHistogram(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)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetStorageCapacityForHistogram( | 
 |     int64_t usage, | 
 |     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", | 
 |                              static_cast<int>((usage * 100) / total_space)); | 
 |     UMA_HISTOGRAM_MBYTES("Quota.AvailableDiskSpace2", available_space); | 
 |     UMA_HISTOGRAM_PERCENTAGE( | 
 |         "Quota.PercentDiskAvailable2", | 
 |         std::min(100, static_cast<int>((available_space * 100 / total_space)))); | 
 |   } | 
 |  | 
 |   // We DumpBucketTable last to ensure the trackers caches are loaded. | 
 |   DumpBucketTable( | 
 |       base::BindOnce(&QuotaManagerImpl::DidDumpBucketTableForHistogram, | 
 |                      weak_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidDumpBucketTableForHistogram( | 
 |     BucketTableEntries entries) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   base::UmaHistogramCounts100000("Quota.TotalBucketCount", entries.size()); | 
 |  | 
 |   std::map<StorageKey, int64_t> usage_map = | 
 |       GetUsageTracker()->GetCachedStorageKeysUsage(); | 
 |   base::Time now = QuotaDatabase::GetNow(); | 
 |   for (const auto& info : entries) { | 
 |     std::optional<StorageKey> storage_key = | 
 |         StorageKey::Deserialize(info->storage_key); | 
 |     if (!storage_key.has_value()) { | 
 |       continue; | 
 |     } | 
 |     auto it = usage_map.find(*storage_key); | 
 |     if (it == usage_map.end() || it->second == 0) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     base::TimeDelta age = | 
 |         now - std::max(info->last_accessed, info->last_modified); | 
 |     base::UmaHistogramCounts1000("Quota.AgeOfOriginInDays", age.InDays()); | 
 |  | 
 |     int64_t kilobytes = std::max(it->second / int64_t{1024}, int64_t{1}); | 
 |     base::Histogram::FactoryGet("Quota.AgeOfDataInDays", 1, 1000, 50, | 
 |                                 base::HistogramBase::kUmaTargetedHistogramFlag) | 
 |         ->AddCount(age.InDays(), base::saturated_cast<int>(kilobytes)); | 
 |   } | 
 | } | 
 |  | 
 | 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) { | 
 |       exceptions.insert(p.first); | 
 |     } | 
 |   } | 
 |  | 
 |   return exceptions; | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetEvictionBuckets( | 
 |     GetBucketsCallback callback, | 
 |     const std::set<BucketLocator>& buckets) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   // Filter out buckets that were accessed while getting eviction buckets. | 
 |   auto bucket_wasnt_accessed = | 
 |       [this](const BucketLocator& to_be_evicted_bucket) { | 
 |         return !std::count_if( | 
 |             access_notified_buckets_.begin(), access_notified_buckets_.end(), | 
 |             [&to_be_evicted_bucket](const BucketLocator& accessed_bucket) { | 
 |               return to_be_evicted_bucket.IsEquivalentTo(accessed_bucket); | 
 |             }); | 
 |       }; | 
 |  | 
 |   std::set<BucketLocator> bucket_copies; | 
 |   std::copy_if(buckets.begin(), buckets.end(), | 
 |                std::inserter(bucket_copies, bucket_copies.end()), | 
 |                bucket_wasnt_accessed); | 
 |   std::move(callback).Run(bucket_copies); | 
 |   access_notified_buckets_.clear(); | 
 |   is_getting_eviction_bucket_ = false; | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetEvictionBuckets(int64_t target_usage, | 
 |                                           GetBucketsCallback 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; | 
 |  | 
 |   // The usage map should have been cached recently due to | 
 |   // `GetEvictionRoundInfo()`. | 
 |   std::map<BucketLocator, int64_t> usage_map = | 
 |       GetUsageTracker()->GetCachedBucketsUsage(); | 
 |  | 
 |   GetBucketsForEvictionFromDatabase( | 
 |       target_usage, std::move(usage_map), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetEvictionBuckets, | 
 |                      weak_factory_.GetWeakPtr(), std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::EvictExpiredBuckets(StatusCallback done) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(done); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(done).Run(blink::mojom::QuotaStatusCode::kUnknown); | 
 |     return; | 
 |   } | 
 |  | 
 |   auto buckets_deleter = std::make_unique<BucketSetDataDeleter>( | 
 |       this, base::BindOnce(&QuotaManagerImpl::DidDeleteBuckets, | 
 |                            weak_factory_.GetWeakPtr(), std::move(done))); | 
 |   auto* buckets_deleter_ptr = buckets_deleter.get(); | 
 |   bucket_set_data_deleters_[buckets_deleter_ptr] = std::move(buckets_deleter); | 
 |  | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](SpecialStoragePolicy* policy, QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->GetExpiredBuckets(policy); | 
 |           }, | 
 |           base::RetainedRef(special_storage_policy_)), | 
 |       buckets_deleter_ptr->GetBucketDeletionCallback()); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::EvictBucketData( | 
 |     const std::set<BucketLocator>& buckets, | 
 |     base::OnceCallback<void(int)> on_eviction_done) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(io_thread_->BelongsToCurrentThread()); | 
 |  | 
 |   auto barrier = base::BarrierCallback<bool>( | 
 |       buckets.size(), base::BindOnce( | 
 |                           [](base::OnceCallback<void(int)> on_eviction_done, | 
 |                              std::vector<bool> results) { | 
 |                             const int evicted_count = std::count( | 
 |                                 results.begin(), results.end(), true); | 
 |                             std::move(on_eviction_done).Run(evicted_count); | 
 |                           }, | 
 |                           std::move(on_eviction_done))); | 
 |  | 
 |   for (const auto& bucket : buckets) { | 
 |     DeleteBucketDataInternal( | 
 |         bucket, AllQuotaClientTypes(), | 
 |         base::BindOnce(&QuotaManagerImpl::DidEvictBucketData, | 
 |                        weak_factory_.GetWeakPtr(), bucket.id, barrier)); | 
 |   } | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetEvictionRoundInfo( | 
 |     EvictionRoundInfoCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(io_thread_->BelongsToCurrentThread()); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   DCHECK(!eviction_helper_); | 
 |   eviction_helper_ = std::make_unique<EvictionRoundInfoHelper>( | 
 |       this, std::move(callback), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetEvictionRoundInfo, | 
 |                      weak_factory_.GetWeakPtr())); | 
 |   eviction_helper_->Run(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetEvictionRoundInfo() { | 
 |   DCHECK(eviction_helper_); | 
 |   eviction_helper_.reset(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetBucketsForEvictionFromDatabase( | 
 |     int64_t target_usage, | 
 |     std::map<BucketLocator, int64_t> usage_map, | 
 |     GetBucketsCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |   EnsureDatabaseOpened(); | 
 |  | 
 |   if (db_disabled_) { | 
 |     std::move(callback).Run({}); | 
 |     return; | 
 |   } | 
 |  | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](int64_t target_usage, std::map<BucketLocator, int64_t> usage_map, | 
 |              const std::set<BucketId>& bucket_exceptions, | 
 |              SpecialStoragePolicy* policy, QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->GetBucketsForEviction(target_usage, usage_map, | 
 |                                                    bucket_exceptions, policy); | 
 |           }, | 
 |           target_usage, std::move(usage_map), GetEvictionBucketExceptions(), | 
 |           base::RetainedRef(special_storage_policy_)), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBucketsForEvictionFromDatabase, | 
 |                      weak_factory_.GetWeakPtr(), std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetBucketsForEvictionFromDatabase( | 
 |     GetBucketsCallback callback, | 
 |     QuotaErrorOr<std::set<BucketLocator>> result) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   if (result.has_value()) { | 
 |     std::move(callback).Run(result.value()); | 
 |   } else { | 
 |     std::move(callback).Run({}); | 
 |   } | 
 | } | 
 |  | 
 | 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_); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!settings_callbacks_.Add(std::move(callback))) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // We invoke our clients GetQuotaSettingsFunc on the | 
 |   // UI thread and plumb the resulting value back to this thread. | 
 |   get_settings_task_runner_->PostTask( | 
 |       FROM_HERE, | 
 |       base::BindOnce( | 
 |           get_settings_function_, | 
 |           base::BindPostTask(base::SingleThreadTaskRunner::GetCurrentDefault(), | 
 |                              base::BindOnce(&QuotaManagerImpl::DidGetSettings, | 
 |                                             weak_factory_.GetWeakPtr())))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetSettings(std::optional<QuotaSettings> settings) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   if (!settings) { | 
 |     settings = settings_; | 
 |     settings->refresh_interval = base::Minutes(1); | 
 |   } | 
 |   SetQuotaSettings(*settings); | 
 |   settings_callbacks_.Run(*settings); | 
 |   UMA_HISTOGRAM_MBYTES("Quota.GlobalTemporaryPoolSize", settings->pool_size); | 
 |   LOG_IF(WARNING, settings->pool_size == 0) | 
 |       << "No storage quota provided in QuotaSettings."; | 
 | } | 
 |  | 
 | void QuotaManagerImpl::GetStorageCapacity(StorageCapacityCallback callback) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   if (!storage_capacity_callbacks_.Add(std::move(callback))) { | 
 |     return; | 
 |   } | 
 |   if (is_incognito_) { | 
 |     GetQuotaSettings( | 
 |         base::BindOnce(&QuotaManagerImpl::ContinueIncognitoGetStorageCapacity, | 
 |                        weak_factory_.GetWeakPtr())); | 
 |     return; | 
 |   } | 
 |   db_runner_->PostTaskAndReplyWithResult( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&QuotaManagerImpl::CallGetVolumeInfo, get_volume_info_fn_, | 
 |                      profile_path_), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetStorageCapacity, | 
 |                      weak_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::ContinueIncognitoGetStorageCapacity( | 
 |     const QuotaSettings& settings) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   auto* temporary_usage_tracker = GetUsageTracker(); | 
 |   int64_t temporary_usage = temporary_usage_tracker == nullptr | 
 |                                 ? 0 | 
 |                                 : temporary_usage_tracker->GetCachedUsage(); | 
 |   DCHECK_GE(temporary_usage, -1); | 
 |  | 
 |   int64_t available_space = | 
 |       std::max(int64_t{0}, settings.pool_size - temporary_usage); | 
 |   DidGetStorageCapacity(QuotaAvailability(settings.pool_size, available_space)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetStorageCapacity( | 
 |     const QuotaAvailability& quota_usage) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   int64_t total_space = quota_usage.total; | 
 |   DCHECK_GE(total_space, 0); | 
 |  | 
 |   int64_t available_space = quota_usage.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); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidRecoverOrRazeForReBootstrap(bool success) { | 
 |   if (success) { | 
 |     MaybeBootstrapDatabase(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Deleting the database failed. Disable the database and hope we'll recover | 
 |   // after Chrome restarts instead. | 
 |   db_disabled_ = true; | 
 |   ReportDatabaseDisabledReason(DatabaseDisabledReason::kRazeFailed); | 
 |   is_bootstrapping_database_ = false; | 
 |   RunDatabaseCallbacks(); | 
 |   // No reason to restart eviction here. Without a working database there is | 
 |   // nothing to evict. | 
 | } | 
 |  | 
 | void QuotaManagerImpl::NotifyUpdatedBucket( | 
 |     const QuotaErrorOr<BucketInfo>& result) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   if (!result.has_value()) { | 
 |     return; | 
 |   } | 
 |   for (auto& observer : observers_) { | 
 |     observer->OnCreateOrUpdateBucket(result.value()); | 
 |   } | 
 | } | 
 |  | 
 | void QuotaManagerImpl::OnBucketDeleted( | 
 |     base::OnceCallback<void(QuotaErrorOr<mojom::BucketTableEntryPtr>)> callback, | 
 |     QuotaErrorOr<mojom::BucketTableEntryPtr> result) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   if (result.has_value()) { | 
 |     const mojom::BucketTableEntryPtr& entry = result.value(); | 
 |     auto storage_key = blink::StorageKey::Deserialize(entry->storage_key); | 
 |     if (storage_key) { | 
 |       storage::BucketLocator bucket_locator(BucketId(entry->bucket_id), | 
 |                                             storage_key.value(), | 
 |                                             entry->name == kDefaultBucketName); | 
 |       for (auto& observer : observers_) { | 
 |         observer->OnDeleteBucket(bucket_locator); | 
 |       } | 
 |     } | 
 |   } | 
 |   std::move(callback).Run(std::move(result)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetQuotaSettingsForBucketCreation( | 
 |     const BucketInitParams& bucket_params, | 
 |     base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback, | 
 |     const QuotaSettings& settings) { | 
 |   const int64_t quota = | 
 |       GetQuotaForStorageKey(bucket_params.storage_key, settings); | 
 |   int64_t max_buckets = (quota == kNoLimit) ? 0 : (quota / kTypicalBucketUsage); | 
 |   DCHECK_EQ(max_buckets == 0, IsStorageUnlimited(bucket_params.storage_key)); | 
 |  | 
 |   PostTaskAndReplyWithResultForDBThread( | 
 |       base::BindOnce( | 
 |           [](const BucketInitParams& params, int max_buckets, | 
 |              QuotaDatabase* database) { | 
 |             DCHECK(database); | 
 |             return database->UpdateOrCreateBucket(params, max_buckets); | 
 |           }, | 
 |           bucket_params, max_buckets), | 
 |       base::BindOnce(&QuotaManagerImpl::DidGetBucketCheckExpiration, | 
 |                      weak_factory_.GetWeakPtr(), bucket_params, | 
 |                      std::move(callback))); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetBucket( | 
 |     bool notify_update_bucket, | 
 |     base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback, | 
 |     QuotaErrorOr<BucketInfo> result) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   if (notify_update_bucket) { | 
 |     NotifyUpdatedBucket(result); | 
 |   } | 
 |  | 
 |   std::move(callback).Run(std::move(result)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetBucketCheckExpiration( | 
 |     const BucketInitParams& params, | 
 |     base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback, | 
 |     QuotaErrorOr<BucketInfo> result) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   if (result.has_value() && !result->expiration.is_null() && | 
 |       result->expiration <= QuotaDatabase::GetNow()) { | 
 |     DeleteBucketDataInternal( | 
 |         result->ToBucketLocator(), AllQuotaClientTypes(), | 
 |         base::BindOnce(&QuotaManagerImpl::DidDeleteBucketForRecreation, | 
 |                        weak_factory_.GetWeakPtr(), params, std::move(callback), | 
 |                        result.value())); | 
 |     return; | 
 |   } | 
 |  | 
 |   NotifyUpdatedBucket(result); | 
 |   std::move(callback).Run(std::move(result)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetBucketForDeletion( | 
 |     StatusCallback callback, | 
 |     QuotaErrorOr<BucketInfo> result) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   if (!result.has_value()) { | 
 |     // Return QuotaStatusCode::kOk if bucket not found. No work needed. | 
 |     std::move(callback).Run(result.error() == QuotaError::kNotFound | 
 |                                 ? blink::mojom::QuotaStatusCode::kOk | 
 |                                 : blink::mojom::QuotaStatusCode::kUnknown); | 
 |     return; | 
 |   } | 
 |  | 
 |   auto result_callback = base::BindOnce( | 
 |       [](StatusCallback callback, | 
 |          QuotaErrorOr<mojom::BucketTableEntryPtr> result) { | 
 |         std::move(callback).Run(result.has_value() | 
 |                                     ? blink::mojom::QuotaStatusCode::kOk | 
 |                                     : blink::mojom::QuotaStatusCode::kUnknown); | 
 |       }, | 
 |       std::move(callback)); | 
 |   DeleteBucketDataInternal(result->ToBucketLocator(), AllQuotaClientTypes(), | 
 |                            std::move(result_callback)); | 
 |   return; | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetBucketForUsageAndQuota( | 
 |     UsageAndQuotaCallback callback, | 
 |     QuotaErrorOr<BucketInfo> result) { | 
 |   if (!result.has_value()) { | 
 |     std::move(callback).Run(blink::mojom::QuotaStatusCode::kUnknown, 0, 0); | 
 |     return; | 
 |   } | 
 |  | 
 |   UsageAndQuotaInfoGatherer* helper = new UsageAndQuotaInfoGatherer( | 
 |       this, result.value(), is_incognito_, | 
 |       base::BindOnce(&DidGetUsageAndQuotaStripOverride, | 
 |                      base::BindOnce(&DidGetUsageAndQuotaStripBreakdown, | 
 |                                     std::move(callback)))); | 
 |   helper->Start(); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetStorageKeys( | 
 |     GetStorageKeysCallback callback, | 
 |     QuotaErrorOr<std::set<StorageKey>> result) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   std::move(callback).Run(result.value_or(std::set<StorageKey>())); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetBuckets( | 
 |     base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback, | 
 |     QuotaErrorOr<std::set<BucketInfo>> result) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   std::move(callback).Run(std::move(result)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetBucketsCheckExpiration( | 
 |     base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback, | 
 |     QuotaErrorOr<std::set<BucketInfo>> result) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   if (!result.has_value()) { | 
 |     std::move(callback).Run(std::move(result)); | 
 |     return; | 
 |   } | 
 |  | 
 |   std::set<BucketInfo> kept_buckets; | 
 |   std::set<BucketInfo> buckets_to_delete; | 
 |   for (const BucketInfo& bucket : result.value()) { | 
 |     if (!bucket.expiration.is_null() && | 
 |         bucket.expiration <= QuotaDatabase::GetNow()) { | 
 |       buckets_to_delete.insert(bucket); | 
 |     } else { | 
 |       kept_buckets.insert(bucket); | 
 |     } | 
 |   } | 
 |  | 
 |   if (buckets_to_delete.empty()) { | 
 |     std::move(callback).Run(kept_buckets); | 
 |     return; | 
 |   } | 
 |  | 
 |   base::RepeatingClosure barrier = | 
 |       base::BarrierClosure(buckets_to_delete.size(), | 
 |                            base::BindOnce(std::move(callback), kept_buckets)); | 
 |   for (const BucketInfo& bucket : buckets_to_delete) { | 
 |     DeleteBucketDataInternal( | 
 |         bucket.ToBucketLocator(), AllQuotaClientTypes(), | 
 |         base::IgnoreArgs<QuotaErrorOr<mojom::BucketTableEntryPtr>>(barrier)); | 
 |   } | 
 | } | 
 |  | 
 | void QuotaManagerImpl::DidGetModifiedBetween( | 
 |     GetBucketsCallback callback, | 
 |     QuotaErrorOr<std::set<BucketLocator>> result) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(callback); | 
 |  | 
 |   std::move(callback).Run(result.value_or(std::set<BucketLocator>())); | 
 | } | 
 |  | 
 | template <typename ValueType> | 
 | void QuotaManagerImpl::PostTaskAndReplyWithResultForDBThread( | 
 |     base::OnceCallback<QuotaErrorOr<ValueType>(QuotaDatabase*)> task, | 
 |     base::OnceCallback<void(QuotaErrorOr<ValueType>)> reply, | 
 |     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; | 
 |   } | 
 |  | 
 |   db_runner_->PostTaskAndReplyWithResult( | 
 |       from_here, | 
 |       base::BindOnce(std::move(task), base::Unretained(database_.get())), | 
 |       std::move(reply)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::PostTaskAndReplyWithResultForDBThread( | 
 |     base::OnceCallback<QuotaError(QuotaDatabase*)> task, | 
 |     base::OnceCallback<void(QuotaError)> reply, | 
 |     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; | 
 |   } | 
 |  | 
 |   db_runner_->PostTaskAndReplyWithResult( | 
 |       from_here, | 
 |       base::BindOnce(std::move(task), base::Unretained(database_.get())), | 
 |       std::move(reply)); | 
 | } | 
 |  | 
 | // static | 
 | QuotaAvailability QuotaManagerImpl::CallGetVolumeInfo( | 
 |     GetVolumeInfoFn get_volume_info_fn, | 
 |     const base::FilePath& path) { | 
 |   if (!base::CreateDirectory(path)) { | 
 |     LOG(WARNING) << "Create directory failed for path" << path.value(); | 
 |     return QuotaAvailability(0, 0); | 
 |   } | 
 |  | 
 |   const QuotaAvailability quotaAvailability = get_volume_info_fn(path); | 
 |   const auto total = quotaAvailability.total; | 
 |   const auto available = quotaAvailability.available; | 
 |  | 
 |   if (total < 0 || available < 0) { | 
 |     LOG(WARNING) << "Unable to get volume info: " << path.value(); | 
 |     return QuotaAvailability(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) { | 
 |     UMA_HISTOGRAM_PERCENTAGE( | 
 |         "Quota.PercentDiskAvailable", | 
 |         std::min(100, static_cast<int>((available * 100) / total))); | 
 |   } | 
 |   return QuotaAvailability(total, available); | 
 | } | 
 |  | 
 | // static | 
 | QuotaAvailability QuotaManagerImpl::GetVolumeInfo(const base::FilePath& path) { | 
 |   return QuotaAvailability( | 
 |       base::SysInfo::AmountOfTotalDiskSpace(path).value_or(-1), | 
 |       base::SysInfo::AmountOfFreeDiskSpace(path).value_or(-1)); | 
 | } | 
 |  | 
 | void QuotaManagerImpl::AddObserver( | 
 |     mojo::PendingRemote<storage::mojom::QuotaManagerObserver> observer) { | 
 |   // `MojoQuotaManagerObserver` is self owned and deletes itself when its remote | 
 |   // is disconnected. | 
 |   observers_.Add(std::move(observer)); | 
 | } | 
 | }  // namespace storage |