blob: 24a94cedbf07e48120e2f4b7f5a7228ddd89b959 [file] [log] [blame]
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_BUDGETER_H_
#define CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_BUDGETER_H_
#include <memory>
#include <vector>
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "content/browser/private_aggregation/private_aggregation_budget_key.h"
#include "content/common/content_export.h"
template <class T>
class scoped_refptr;
namespace base {
class FilePath;
class SequencedTaskRunner;
} // namespace base
namespace content {
class PrivateAggregationBudgetStorage;
// UI thread class that provides an interface for querying and updating the
// budget used by each key, i.e. the sum of contributions, by interacting with
// the storage layer. This class is responsible for owning the storage class.
class CONTENT_EXPORT PrivateAggregationBudgeter {
public:
// Public for testing
enum class StorageStatus {
// The database is in the process of being initialized.
kInitializing,
// The database initialization did not succeed.
kInitializationFailed,
// The database successfully initialized and can be used.
kOpen,
};
// Maximum budget allowed to be claimed per-origin per-day per-API.
static constexpr int kMaxBudgetPerScope = 65536;
// To avoid unbounded memory growth, limit the number of pending consume
// budget calls during initialization.
static constexpr int kMaxPendingCalls = 1000;
// The total length of time that per-origin per-API budgets are enforced
// against. Note that there are 24 `PrivateAggregationBudgetKey::TimeWindow`s
// per `kBudgetScopeDuration`.
static constexpr base::TimeDelta kBudgetScopeDuration = base::Days(1);
static_assert(kBudgetScopeDuration %
PrivateAggregationBudgetKey::TimeWindow::kDuration ==
base::TimeDelta());
// `db_task_runner` should not be nullptr.
PrivateAggregationBudgeter(
scoped_refptr<base::SequencedTaskRunner> db_task_runner,
bool exclusively_run_in_memory,
const base::FilePath& path_to_db_dir);
PrivateAggregationBudgeter(const PrivateAggregationBudgeter& other) = delete;
PrivateAggregationBudgeter& operator=(
const PrivateAggregationBudgeter& other) = delete;
virtual ~PrivateAggregationBudgeter();
// Attempts to consume `budget` for `budget_key`. The callback
// `on_done` is then run with `true` if the attempt was successful and
// `false` otherwise.
//
// The attempt is rejected if it would cause an origin's daily per-API budget
// to exceed `kMaxBudgetPerScope` (for the 24-hour period ending at the *end*
// of `budget_key.time_window`, see `kBudgetScopeDuration` and
// `PrivateAggregationBudgetKey` for more detail). The attempt is also
// rejected if the requested `budget` is non-positive, if `budget_key.origin`
// is not potentially trustworthy or if the database is closed. If the
// database is initializing, this query is queued until the initialization is
// complete. Otherwise, the budget use is recorded and the attempt is
// successful. May clean up stale budget storage. Note that this call assumes
// that budget time windows are non-decreasing. In very rare cases, a network
// time update could allow budget to be used slightly early. Virtual for
// testing.
virtual void ConsumeBudget(int budget,
const PrivateAggregationBudgetKey& budget_key,
base::OnceCallback<void(bool)> on_done);
// TODO(crbug.com/1328439): Clear stale data periodically and on startup.
protected:
// Should only be used for testing/mocking to avoid creating the underlying
// storage.
PrivateAggregationBudgeter();
// Virtual for testing.
virtual void OnStorageDoneInitializing(
std::unique_ptr<PrivateAggregationBudgetStorage> storage);
StorageStatus storage_status_ = StorageStatus::kInitializing;
private:
void ConsumeBudgetImpl(int additional_budget,
const PrivateAggregationBudgetKey& budget_key,
base::OnceCallback<void(bool)> on_done);
void ProcessAllPendingCalls();
// While the storage initializes, queues calls to ConsumeBudget() in the
// order the calls are received. Should be empty after storage is initialized.
// The size is limited to `kMaxPendingCalls`.
std::vector<base::OnceClosure> pending_consume_budget_calls_;
// `nullptr` until initialization is complete or if initialization failed.
// Otherwise, owned by this class until destruction. Iff present,
// `storage_status_` should be `kOpen`.
std::unique_ptr<PrivateAggregationBudgetStorage> storage_;
// Holds a closure that will shut down the initializing storage until
// initialization is complete. After then, it is null.
base::OnceClosure shutdown_initializing_storage_;
base::WeakPtrFactory<PrivateAggregationBudgeter> weak_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_BUDGETER_H_