blob: ccb91f46ea3de2d4c9469f17f9b74a67d92dcc8c [file] [log] [blame]
// 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.
#ifndef CONTENT_BROWSER_INDEXED_DB_INSTANCE_DATABASE_H_
#define CONTENT_BROWSER_INDEXED_DB_INSTANCE_DATABASE_H_
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/queue.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h"
#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "content/browser/indexed_db/indexed_db_value.h"
#include "content/browser/indexed_db/instance/backing_store.h"
#include "content/browser/indexed_db/instance/connection_coordinator.h"
#include "content/browser/indexed_db/instance/factory_client.h"
#include "content/browser/indexed_db/instance/pending_connection.h"
#include "content/browser/indexed_db/list_set.h"
#include "content/common/content_export.h"
#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-forward.h"
namespace blink {
class IndexedDBKeyRange;
struct IndexedDBDatabaseMetadata;
} // namespace blink
namespace content::indexed_db {
class BucketContext;
class Connection;
class DatabaseCallbacks;
class Transaction;
enum class CursorType;
// This class maps to a single IDB database:
// https://www.w3.org/TR/IndexedDB/#database
//
// It is created and operated on a bucket thread.
class CONTENT_EXPORT Database {
public:
// Used to report irrecoverable backend errors. The second argument can be
// null.
using ErrorCallback = base::RepeatingCallback<void(Status, const char*)>;
Database(uint32_t id_for_locks,
const std::u16string& name,
BucketContext& bucket_context);
Database(const Database&) = delete;
Database& operator=(const Database&) = delete;
virtual ~Database();
BackingStore* backing_store();
BackingStore::Database* backing_store_db() { return backing_store_db_.get(); }
PartitionedLockManager& lock_manager();
const blink::IndexedDBDatabaseMetadata& metadata() const {
return backing_store_db_->GetMetadata();
}
const std::u16string& name() const { return name_; }
int64_t version() const;
bool IsInitialized() const;
// Called to permanently delete the database wrapped by `this`. Will call
// `on_complete` and release `locks` when done. This may be called more than
// once, in which case latter calls are a no-op, and `on_complete` will not be
// called. Returns an error, or the latest version of the deleted database
// if successful, or 0 if the database had already been deleted.
StatusOr<int64_t> DeleteDatabase(std::vector<PartitionedLock> locks,
base::OnceClosure on_complete);
// Builds the set of lock requests for the given transaction `mode` and
// `scope`. `scope` is used iff `mode` is not `VersionChange`.
std::vector<PartitionedLockManager::PartitionedLockRequest>
BuildLockRequestsForTransaction(blink::mojom::IDBTransactionMode mode,
const std::set<int64_t>& scope) const;
const list_set<Connection*>& connections() const { return connections_; }
Status RunTasks();
void RegisterAndScheduleTransaction(Transaction* transaction);
// The database object (this object) must be kept alive for the duration of
// this call. This means the caller should own an
// BucketContextHandle while calling this methods.
Status ForceCloseAndRunTasks(const std::string& message);
void ScheduleOpenConnection(std::unique_ptr<PendingConnection> connection);
void ScheduleDeleteDatabase(std::unique_ptr<FactoryClient> factory_client,
base::OnceClosure on_deletion_complete);
// Number of connections that have progressed passed initial open call.
size_t ConnectionCount() const { return connections_.size(); }
// Number of active open/delete calls (running or blocked on other
// connections).
size_t ActiveOpenDeleteCount() const {
return connection_coordinator_.ActiveOpenDeleteCount();
}
// Number of open/delete calls that are waiting their turn.
size_t PendingOpenDeleteCount() const {
return connection_coordinator_.PendingOpenDeleteCount();
}
Status VersionChangeOperation(int64_t version, Transaction* transaction);
Status GetOperation(int64_t object_store_id,
int64_t index_id,
blink::IndexedDBKeyRange key_range,
indexed_db::CursorType cursor_type,
blink::mojom::IDBDatabase::GetCallback callback,
Transaction* transaction);
struct OpenCursorOperationParams {
OpenCursorOperationParams();
OpenCursorOperationParams(const OpenCursorOperationParams&) = delete;
OpenCursorOperationParams& operator=(const OpenCursorOperationParams&) =
delete;
~OpenCursorOperationParams();
int64_t object_store_id;
int64_t index_id;
blink::IndexedDBKeyRange key_range;
blink::mojom::IDBCursorDirection direction;
indexed_db::CursorType cursor_type;
blink::mojom::IDBTaskType task_type;
blink::mojom::IDBDatabase::OpenCursorCallback callback;
};
Status OpenCursorOperation(std::unique_ptr<OpenCursorOperationParams> params,
const storage::BucketLocator& bucket_locator,
Transaction* transaction);
Status CountOperation(int64_t object_store_id,
int64_t index_id,
blink::IndexedDBKeyRange key_range,
blink::mojom::IDBDatabase::CountCallback callback,
Transaction* transaction);
Status DeleteRangeOperation(
int64_t object_store_id,
blink::IndexedDBKeyRange key_range,
blink::mojom::IDBDatabase::DeleteRangeCallback success_callback,
Transaction* transaction);
Status GetKeyGeneratorCurrentNumberOperation(
int64_t object_store_id,
blink::mojom::IDBDatabase::GetKeyGeneratorCurrentNumberCallback callback,
Transaction* transaction);
Status ClearOperation(int64_t object_store_id,
blink::mojom::IDBDatabase::ClearCallback callback,
Transaction* transaction);
// Use this factory function for GetAll instead of creating the operation
// directly.
base::OnceCallback<Status(Transaction*)> CreateGetAllOperation(
int64_t object_store_id,
int64_t index_id,
blink::IndexedDBKeyRange key_range,
blink::mojom::IDBGetAllResultType result_type,
int64_t max_count,
blink::mojom::IDBCursorDirection direction,
blink::mojom::IDBDatabase::GetAllCallback callback,
Transaction* transaction);
bool IsObjectStoreIdInMetadata(int64_t object_store_id) const;
bool IsObjectStoreIdAndMaybeIndexIdInMetadata(int64_t object_store_id,
int64_t index_id) const;
// Returns metadata relevant to idb-internals.
storage::mojom::IdbDatabaseMetadataPtr GetIdbInternalsMetadata() const;
// Called when the data used to populate the struct in
// `GetIdbInternalsMetadata` is changed in a significant way.
void NotifyOfIdbInternalsRelevantChange();
base::WeakPtr<Database> AsWeakPtr() { return weak_factory_.GetWeakPtr(); }
void AddConnectionForTesting(Connection* connection) {
if (connections_.empty()) {
OpenInternal();
}
connections_.insert(connection);
}
bool CanBeDestroyed();
protected:
friend class Transaction;
friend class ConnectionCoordinator::ConnectionRequest;
friend class ConnectionCoordinator::OpenRequest;
friend class ConnectionCoordinator::DeleteRequest;
private:
FRIEND_TEST_ALL_PREFIXES(DatabaseTest, OpenDeleteClear);
FRIEND_TEST_ALL_PREFIXES(DatabaseOperationTest,
ObjectStoreGetAllKeysWithInvalidObjectStoreId);
FRIEND_TEST_ALL_PREFIXES(DatabaseOperationTest,
IndexGetAllKeysWithInvalidIndexId);
friend class DatabaseOperationTest;
void CallUpgradeTransactionStartedForTesting(int64_t old_version);
class ConnectionRequest;
class OpenRequest;
class DeleteRequest;
Status OpenInternal();
// This class informs its result sink of an error if a `GetAllOperation` is
// deleted without being run. This functionality mimics that of
// AbortOnDestruct callbacks. `GetAll()` cannot easily be shoe-horned into the
// abort-on-destruct callback templating.
class CONTENT_EXPORT GetAllResultSinkWrapper {
public:
GetAllResultSinkWrapper(base::WeakPtr<Transaction> transaction,
blink::mojom::IDBDatabase::GetAllCallback callback);
~GetAllResultSinkWrapper();
mojo::AssociatedRemote<blink::mojom::IDBDatabaseGetAllResultSink>& Get();
// An override for unit tests to bind the associated receiver successfully
// without a pre-existing endpoint entanglement.
void UseDedicatedReceiverForTesting() {
use_dedicated_receiver_for_testing_ = true;
}
private:
base::WeakPtr<Transaction> transaction_;
blink::mojom::IDBDatabase::GetAllCallback callback_;
mojo::AssociatedRemote<blink::mojom::IDBDatabaseGetAllResultSink>
result_sink_;
bool use_dedicated_receiver_for_testing_ = false;
};
Status GetAllOperation(int64_t object_store_id,
int64_t index_id,
blink::IndexedDBKeyRange key_range,
blink::mojom::IDBGetAllResultType result_type,
int64_t max_count,
blink::mojom::IDBCursorDirection direction,
std::unique_ptr<GetAllResultSinkWrapper> result_sink,
Transaction* transaction);
// If there is no active request, grab a new one from the pending queue and
// start it. Afterwards, possibly release the database by calling
// MaybeReleaseDatabase().
void ProcessRequestQueueAndMaybeRelease();
// If there are no connections, pending requests, or an active request, then
// this function will call `destroy_me_`, which can destruct this object.
void MaybeReleaseDatabase();
std::unique_ptr<Connection> CreateConnection(
std::unique_ptr<DatabaseCallbacks> database_callbacks,
mojo::Remote<storage::mojom::IndexedDBClientStateChecker>
client_state_checker,
base::UnguessableToken client_token,
int scheduling_priority);
// Ack that one of the connections notified with a "versionchange" event did
// not promptly close. Therefore a "blocked" event should be fired at the
// pending connection.
void VersionChangeIgnored();
bool HasNoConnections() const;
void SendVersionChangeToAllConnections(int64_t old_version,
int64_t new_version);
// This can only be called when the given connection is closed and no longer
// has any transaction objects.
void ConnectionClosed(Connection* connection);
// In rare cases there are a very large number of queued
// requests/transactions, so calculations related to blocking or blocked
// clients can be expensive. See crbug.com/384476946. This method is used for
// shortcutting such operations when there's only a single client. Also
// returns true for zero clients.
bool OnlyHasOneClient() const;
// Find the transactions that block `current_transaction` from acquiring the
// locks, and ensure that the clients with blocking transactions are active.
void RequireBlockingTransactionClientsToBeActive(
Transaction* current_transaction,
std::vector<PartitionedLockManager::PartitionedLockRequest>&
lock_requests);
// Gets metadata for the given object store ID, asserting that the object
// store exists.
const blink::IndexedDBObjectStoreMetadata& GetObjectStoreMetadata(
int64_t object_store_id) const;
// This ID uniquely identifies this database within this process. It's not
// persisted anywhere. Only used when the backing store is SQLite.
uint32_t id_for_locks_;
std::u16string name_;
// The object that owns `this`.
raw_ref<BucketContext> bucket_context_;
list_set<Connection*> connections_;
// True once `ForceCloseAndRunTasks()` is called.
bool force_closing_ = false;
ConnectionCoordinator connection_coordinator_;
// Null until `OpenInternal()` is called successfully, as well as after the
// database has been deleted via `DeleteDatabase()`.
std::unique_ptr<BackingStore::Database> backing_store_db_;
// `weak_factory_` is used for all callback uses.
base::WeakPtrFactory<Database> weak_factory_{this};
};
} // namespace content::indexed_db
#endif // CONTENT_BROWSER_INDEXED_DB_INSTANCE_DATABASE_H_