// Copyright 2014 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_proxy.h"

#include <stdint.h>

#include <memory>
#include <string>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/time/time.h"
#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "components/services/storage/public/cpp/constants.h"
#include "components/services/storage/public/mojom/quota_client.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "storage/browser/quota/quota_client_type.h"
#include "storage/browser/quota/quota_manager_impl.h"
#include "storage/browser/quota/quota_override_handle.h"
#include "storage/browser/quota/storage_directory_util.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"

using ::blink::StorageKey;

namespace storage {

// This object signals the given `WaitableEvent` when it goes out of scope.
class ScopedWaitableEvent {
 public:
  explicit ScopedWaitableEvent(base::WaitableEvent& event) : event_(&event) {}
  ScopedWaitableEvent(ScopedWaitableEvent&& other) {
    event_ = std::exchange(other.event_, nullptr);
  }

  ~ScopedWaitableEvent() {
    if (event_) {
      event_->Signal();
    }
  }

  ScopedWaitableEvent(const ScopedWaitableEvent& other) = delete;
  ScopedWaitableEvent& operator=(const ScopedWaitableEvent& other) = delete;

 private:
  raw_ptr<base::WaitableEvent> event_;
};

QuotaManagerProxy::QuotaManagerProxy(
    QuotaManagerImpl* quota_manager_impl,
    scoped_refptr<base::SequencedTaskRunner> quota_manager_impl_task_runner,
    const base::FilePath& profile_path)
    : quota_manager_impl_(quota_manager_impl),
      quota_manager_impl_task_runner_(
          std::move(quota_manager_impl_task_runner)),
      profile_path_(profile_path) {
  CHECK(quota_manager_impl_task_runner_.get(), base::NotFatalUntil::M148);

  DETACH_FROM_SEQUENCE(quota_manager_impl_sequence_checker_);
}

base::FilePath QuotaManagerProxy::GetBucketPath(const BucketLocator& bucket) {
  return CreateBucketPath(profile_path_, bucket);
}

base::FilePath QuotaManagerProxy::GetClientBucketPath(
    const BucketLocator& bucket,
    QuotaClientType client_type) {
  return CreateClientBucketPath(profile_path_, bucket, client_type);
}

void QuotaManagerProxy::RegisterClient(
    mojo::PendingRemote<mojom::QuotaClient> client,
    QuotaClientType client_type) {
  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&QuotaManagerProxy::RegisterClient, this,
                                  std::move(client), client_type));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  if (quota_manager_impl_) {
    quota_manager_impl_->RegisterClient(std::move(client), client_type);
  }
}

void QuotaManagerProxy::BindInternalsHandler(
    mojo::PendingReceiver<mojom::QuotaInternalsHandler> receiver) {
  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&QuotaManagerProxy::BindInternalsHandler,
                                  this, std::move(receiver)));
    return;
  }
  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  if (quota_manager_impl_) {
    quota_manager_impl_->BindInternalsHandler(std::move(receiver));
  }
}

void QuotaManagerProxy::UpdateOrCreateBucket(
    const BucketInitParams& bucket_params,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::UpdateOrCreateBucket, this,
                       bucket_params, std::move(callback_task_runner),
                       std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(base::unexpected(QuotaError::kUnknownError));
    return;
  }

  quota_manager_impl_->UpdateOrCreateBucket(bucket_params, std::move(respond));
}

QuotaErrorOr<BucketInfo> QuotaManagerProxy::GetOrCreateBucketSync(
    const BucketInitParams& params) {
  // Ensure that the task runner we want is free and can be blocked on.
  DCHECK(!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence());
  QuotaErrorOr<BucketInfo> bucket = base::unexpected(QuotaError::kUnknownError);
  base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                             base::WaitableEvent::InitialState::NOT_SIGNALED);
  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                base::BlockingType::WILL_BLOCK);
  // Asynchronously call UpdateOrCreateBucket and block until it completes.
  quota_manager_impl_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(
          [](const scoped_refptr<QuotaManagerProxy>& self,
             const BucketInitParams& params, ScopedWaitableEvent waiter,
             QuotaErrorOr<BucketInfo>* sync_bucket) {
            DCHECK_CALLED_ON_VALID_SEQUENCE(
                self->quota_manager_impl_sequence_checker_);
            // If the database is still bootstrapping, return rather than
            // risking deadlock.
            if (!self->quota_manager_impl_ ||
                self->quota_manager_impl_->is_bootstrapping_database_) {
              return;
            }
            // Otherwise, return the bucket value and resolve the waiter.
            self->quota_manager_impl_->UpdateOrCreateBucket(
                params, base::BindOnce(
                            [](ScopedWaitableEvent waiter,
                               QuotaErrorOr<BucketInfo>* sync_bucket,
                               QuotaErrorOr<BucketInfo> result_bucket) {
                              *sync_bucket = std::move(result_bucket);
                            },
                            std::move(waiter), sync_bucket));
          },
          base::WrapRefCounted(this), params, ScopedWaitableEvent(waiter),
          &bucket));
  waiter.Wait();
  return bucket;
}

void QuotaManagerProxy::CreateBucketForTesting(
    const StorageKey& storage_key,
    const std::string& bucket_name,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::CreateBucketForTesting, this,
                       storage_key, bucket_name,
                       std::move(callback_task_runner), std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(base::unexpected(QuotaError::kUnknownError));
    return;
  }

  quota_manager_impl_->CreateBucketForTesting(  // IN-TEST
      storage_key, bucket_name, std::move(respond));
}

void QuotaManagerProxy::GetBucketByNameUnsafe(
    const StorageKey& storage_key,
    const std::string& bucket_name,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::GetBucketByNameUnsafe, this,
                       storage_key, bucket_name,
                       std::move(callback_task_runner), std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(base::unexpected(QuotaError::kUnknownError));
    return;
  }

  quota_manager_impl_->GetBucketByNameUnsafe(  // IN-TEST
      storage_key, bucket_name, std::move(respond));
}

void QuotaManagerProxy::GetBucketsForStorageKey(
    const blink::StorageKey& storage_key,
    bool delete_expired,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::GetBucketsForStorageKey, this,
                       storage_key, delete_expired,
                       std::move(callback_task_runner), std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(base::unexpected(QuotaError::kUnknownError));
    return;
  }

  quota_manager_impl_->GetBucketsForStorageKey(storage_key, std::move(respond),
                                               delete_expired);
}

void QuotaManagerProxy::GetBucketById(
    const BucketId& bucket_id,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::GetBucketById, this, bucket_id,
                       std::move(callback_task_runner), std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(base::unexpected(QuotaError::kUnknownError));
    return;
  }

  quota_manager_impl_->GetBucketById(bucket_id, std::move(respond));
}

void QuotaManagerProxy::DeleteBucket(
    const StorageKey& storage_key,
    const std::string& bucket_name,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(blink::mojom::QuotaStatusCode)> callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::DeleteBucket, this, storage_key,
                       bucket_name, std::move(callback_task_runner),
                       std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(blink::mojom::QuotaStatusCode::kUnknown);
    return;
  }

  quota_manager_impl_->FindAndDeleteBucketData(storage_key, bucket_name,
                                               std::move(respond));
}

void QuotaManagerProxy::UpdateBucketExpiration(
    BucketId bucket,
    const base::Time& expiration,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::UpdateBucketExpiration, this, bucket,
                       expiration, std::move(callback_task_runner),
                       std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(base::unexpected(QuotaError::kUnknownError));
    return;
  }

  quota_manager_impl_->UpdateBucketExpiration(bucket, expiration,
                                              std::move(respond));
}

void QuotaManagerProxy::UpdateBucketPersistence(
    BucketId bucket,
    bool persistent,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::UpdateBucketPersistence, this,
                       bucket, persistent, std::move(callback_task_runner),
                       std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(base::unexpected(QuotaError::kUnknownError));
    return;
  }

  quota_manager_impl_->UpdateBucketPersistence(bucket, persistent,
                                               std::move(respond));
}

void QuotaManagerProxy::NotifyBucketAccessed(const BucketLocator& bucket,
                                             base::Time access_time) {
  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&QuotaManagerProxy::NotifyBucketAccessed,
                                  this, bucket, access_time));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  if (quota_manager_impl_) {
    quota_manager_impl_->NotifyBucketAccessed(bucket, access_time);
  }
}

void QuotaManagerProxy::NotifyBucketModified(
    QuotaClientType client_id,
    const BucketLocator& bucket,
    std::optional<int64_t> delta,
    base::Time modification_time,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceClosure callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::NotifyBucketModified, this,
                       client_id, bucket, delta, modification_time,
                       std::move(callback_task_runner), std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  auto manager_callback =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));

  if (!quota_manager_impl_) {
    std::move(manager_callback).Run();
    return;
  }

  quota_manager_impl_->NotifyBucketModified(
      client_id, bucket, delta, modification_time, std::move(manager_callback));
}

void QuotaManagerProxy::OnClientWriteFailed(const StorageKey& storage_key) {
  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&QuotaManagerProxy::OnClientWriteFailed, this,
                                  storage_key));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  if (quota_manager_impl_) {
    quota_manager_impl_->OnClientWriteFailed(storage_key);
  }
}

void QuotaManagerProxy::SetUsageCacheEnabled(QuotaClientType client_id,
                                             const StorageKey& storage_key,
                                             bool enabled) {
  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&QuotaManagerProxy::SetUsageCacheEnabled,
                                  this, client_id, storage_key, enabled));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  if (quota_manager_impl_) {
    quota_manager_impl_->SetUsageCacheEnabled(client_id, storage_key, enabled);
  }
}

void QuotaManagerProxy::GetUsageAndQuota(
    const StorageKey& storage_key,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    UsageAndQuotaCallback callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::GetUsageAndQuota, this, storage_key,
                       std::move(callback_task_runner), std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(blink::mojom::QuotaStatusCode::kErrorAbort, 0, 0);
    return;
  }

  quota_manager_impl_->GetUsageAndQuota(storage_key, std::move(respond));
}

void QuotaManagerProxy::GetBucketUsageAndReportedQuota(
    BucketId bucket,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    UsageAndQuotaCallback callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::GetBucketUsageAndReportedQuota, this,
                       bucket, std::move(callback_task_runner),
                       std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(blink::mojom::QuotaStatusCode::kErrorAbort, 0, 0);
    return;
  }

  quota_manager_impl_->GetBucketUsageAndReportedQuota(bucket,
                                                      std::move(respond));
}

void QuotaManagerProxy::GetBucketSpaceRemaining(
    const BucketLocator& bucket,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(QuotaErrorOr<int64_t>)> callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&QuotaManagerProxy::GetBucketSpaceRemaining,
                                  this, bucket, std::move(callback_task_runner),
                                  std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));

  if (!quota_manager_impl_) {
    std::move(respond).Run(base::unexpected(QuotaError::kUnknownError));
    return;
  }

  quota_manager_impl_->GetBucketSpaceRemaining(bucket, std::move(respond));
}

void QuotaManagerProxy::IsStorageUnlimited(
    const StorageKey& storage_key,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(bool)> callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&QuotaManagerProxy::IsStorageUnlimited, this,
                                  storage_key, std::move(callback_task_runner),
                                  std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  bool is_storage_unlimited =
      quota_manager_impl_ ? quota_manager_impl_->IsStorageUnlimited(storage_key)
                          : false;

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  std::move(respond).Run(is_storage_unlimited);
}

void QuotaManagerProxy::GetStorageKeyUsageWithBreakdown(
    const blink::StorageKey& storage_key,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    UsageWithBreakdownCallback callback) {
  CHECK(callback_task_runner);
  CHECK(callback);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::GetStorageKeyUsageWithBreakdown,
                       this, storage_key, std::move(callback_task_runner),
                       std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  if (!quota_manager_impl_) {
    std::move(respond).Run(0, nullptr);
    return;
  }

  quota_manager_impl_->GetStorageKeyUsageWithBreakdown(storage_key,
                                                       std::move(respond));
}

std::unique_ptr<QuotaOverrideHandle>
QuotaManagerProxy::GetQuotaOverrideHandle() {
  return std::make_unique<QuotaOverrideHandle>(this);
}

void QuotaManagerProxy::OverrideQuotaForStorageKey(
    int handle_id,
    const StorageKey& storage_key,
    std::optional<int64_t> quota_size,
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceClosure callback) {
  CHECK(callback_task_runner, base::NotFatalUntil::M148);
  CHECK(callback, base::NotFatalUntil::M148);

  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::OverrideQuotaForStorageKey, this,
                       handle_id, storage_key, quota_size,
                       std::move(callback_task_runner), std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  if (quota_manager_impl_) {
    quota_manager_impl_->OverrideQuotaForStorageKey(handle_id, storage_key,
                                                    quota_size);
  }

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  std::move(respond).Run();
}

void QuotaManagerProxy::WithdrawOverridesForHandle(int handle_id) {
  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::WithdrawOverridesForHandle, this,
                       handle_id));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  if (quota_manager_impl_) {
    quota_manager_impl_->WithdrawOverridesForHandle(handle_id);
  }
}

void QuotaManagerProxy::GetOverrideHandleId(
    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
    base::OnceCallback<void(int)> callback) {
  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&QuotaManagerProxy::GetOverrideHandleId, this,
                       std::move(callback_task_runner), std::move(callback)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  int handle_id =
      quota_manager_impl_ ? quota_manager_impl_->GetOverrideHandleId() : 0;

  auto respond =
      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
  std::move(respond).Run(handle_id);
}

QuotaManagerProxy::~QuotaManagerProxy() = default;

void QuotaManagerProxy::InvalidateQuotaManagerImpl(
    base::PassKey<QuotaManagerImpl>) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);

  CHECK(quota_manager_impl_, base::NotFatalUntil::M148)
      << __func__ << " called multiple times";
  quota_manager_impl_ = nullptr;
}

void QuotaManagerProxy::AddObserver(
    mojo::PendingRemote<storage::mojom::QuotaManagerObserver> observer) {
  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
    quota_manager_impl_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&QuotaManagerProxy::AddObserver, this,
                                  std::move(observer)));
    return;
  }

  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
  if (!quota_manager_impl_) {
    return;
  }

  quota_manager_impl_->AddObserver(std::move(observer));
}

}  // namespace storage
