1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
content / browser / indexed_db / instance / database.h [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 <string>
#include <tuple>
#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 IndexedDBKeyPath;
class IndexedDBKeyRange;
struct IndexedDBDatabaseMetadata;
struct IndexedDBIndexMetadata;
struct IndexedDBObjectStoreMetadata;
} // namespace blink
namespace content::indexed_db {
class BucketContext;
class Connection;
class DatabaseCallbacks;
class Transaction;
struct IndexedDBValue;
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:
// Identifier is pair of (bucket_locator, database name).
using Identifier = std::pair<storage::BucketLocator, std::u16string>;
// Used to report irrecoverable backend errors. The second argument can be
// null.
using ErrorCallback = base::RepeatingCallback<void(Status, const char*)>;
static const int64_t kInvalidId = 0;
static const int64_t kMinimumIndexId = 30;
Database(const std::u16string& name,
BucketContext& bucket_context,
const Identifier& unique_identifier);
Database(const Database&) = delete;
Database& operator=(const Database&) = delete;
virtual ~Database();
const Identifier& identifier() const { return identifier_; }
BackingStore* backing_store();
PartitionedLockManager& lock_manager();
int64_t id() const { return metadata_.id; }
const std::u16string& name() const { return metadata_.name; }
const storage::BucketLocator& bucket_locator() const {
return identifier_.first;
}
const blink::IndexedDBDatabaseMetadata& metadata() const { return metadata_; }
const list_set<Connection*>& connections() const { return connections_; }
enum class RunTasksResult { kDone, kError, kCanBeDestroyed };
std::tuple<RunTasksResult, 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();
void ScheduleOpenConnection(std::unique_ptr<PendingConnection> connection);
void ScheduleDeleteDatabase(std::unique_ptr<FactoryClient> factory_client,
base::OnceClosure on_deletion_complete);
void AddObjectStoreToMetadata(blink::IndexedDBObjectStoreMetadata metadata,
int64_t new_max_object_store_id);
blink::IndexedDBObjectStoreMetadata RemoveObjectStoreFromMetadata(
int64_t object_store_id);
void AddIndexToMetadata(int64_t object_store_id,
blink::IndexedDBIndexMetadata metadata,
int64_t new_max_index_id);
blink::IndexedDBIndexMetadata RemoveIndexFromMetadata(int64_t object_store_id,
int64_t index_id);
// The following methods all schedule a task on the transaction & modify the
// database:
// 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();
}
// The following methods are all of the ones actually scheduled asynchronously
// within transctions:
Status CreateObjectStoreOperation(int64_t object_store_id,
const std::u16string& name,
const blink::IndexedDBKeyPath& key_path,
bool auto_increment,
Transaction* transaction);
void CreateObjectStoreAbortOperation(int64_t object_store_id);
Status DeleteObjectStoreOperation(int64_t object_store_id,
Transaction* transaction);
void DeleteObjectStoreAbortOperation(
blink::IndexedDBObjectStoreMetadata object_store_metadata);
Status RenameObjectStoreOperation(int64_t object_store_id,
const std::u16string& new_name,
Transaction* transaction);
void RenameObjectStoreAbortOperation(int64_t object_store_id,
std::u16string old_name);
Status VersionChangeOperation(int64_t version, Transaction* transaction);
void VersionChangeAbortOperation(int64_t previous_version);
Status CreateIndexOperation(int64_t object_store_id,
int64_t index_id,
const std::u16string& name,
const blink::IndexedDBKeyPath& key_path,
bool unique,
bool multi_entry,
Transaction* transaction);
void CreateIndexAbortOperation(int64_t object_store_id, int64_t index_id);
Status DeleteIndexOperation(int64_t object_store_id,
int64_t index_id,
Transaction* transaction);
void DeleteIndexAbortOperation(int64_t object_store_id,
blink::IndexedDBIndexMetadata index_metadata);
Status RenameIndexOperation(int64_t object_store_id,
int64_t index_id,
const std::u16string& new_name,
Transaction* transaction);
void RenameIndexAbortOperation(int64_t object_store_id,
int64_t index_id,
std::u16string old_name);
Status GetOperation(int64_t object_store_id,
int64_t index_id,
std::unique_ptr<blink::IndexedDBKeyRange> key_range,
indexed_db::CursorType cursor_type,
blink::mojom::IDBDatabase::GetCallback callback,
Transaction* transaction);
struct CONTENT_EXPORT PutOperationParams {
PutOperationParams();
PutOperationParams(const PutOperationParams&) = delete;
PutOperationParams& operator=(const PutOperationParams&) = delete;
~PutOperationParams();
int64_t object_store_id;
IndexedDBValue value;
std::unique_ptr<blink::IndexedDBKey> key;
blink::mojom::IDBPutMode put_mode;
blink::mojom::IDBTransaction::PutCallback callback;
std::vector<blink::IndexedDBIndexKeys> index_keys;
};
Status PutOperation(std::unique_ptr<PutOperationParams> params,
Transaction* transaction);
Status SetIndexKeysOperation(
int64_t object_store_id,
std::unique_ptr<blink::IndexedDBKey> primary_key,
const std::vector<blink::IndexedDBIndexKeys>& index_keys,
Transaction* transaction);
Status SetIndexesReadyOperation(size_t index_count, Transaction* transaction);
struct OpenCursorOperationParams {
OpenCursorOperationParams();
OpenCursorOperationParams(const OpenCursorOperationParams&) = delete;
OpenCursorOperationParams& operator=(const OpenCursorOperationParams&) =
delete;
~OpenCursorOperationParams();
int64_t object_store_id;
int64_t index_id;
std::unique_ptr<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,
std::unique_ptr<blink::IndexedDBKeyRange> key_range,
blink::mojom::IDBDatabase::CountCallback callback,
Transaction* transaction);
Status DeleteRangeOperation(
int64_t object_store_id,
std::unique_ptr<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,
std::unique_ptr<blink::IndexedDBKeyRange> key_range,
indexed_db::CursorType cursor_type,
int64_t max_count,
blink::mojom::IDBDatabase::GetAllCallback callback,
Transaction* transaction);
bool IsObjectStoreIdInMetadata(int64_t object_store_id) const;
bool IsObjectStoreIdAndIndexIdInMetadata(int64_t object_store_id,
int64_t index_id) const;
bool IsObjectStoreIdAndMaybeIndexIdInMetadata(int64_t object_store_id,
int64_t index_id) const;
bool IsObjectStoreIdInMetadataAndIndexNotInMetadata(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) {
connections_.insert(connection);
}
protected:
friend class Transaction;
friend class ConnectionCoordinator;
friend class ConnectionCoordinator::ConnectionRequest;
friend class ConnectionCoordinator::OpenRequest;
friend class ConnectionCoordinator::DeleteRequest;
private:
FRIEND_TEST_ALL_PREFIXES(DatabaseTest, OpenDeleteClear);
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 GetAllResultSinkWrapper {
public:
GetAllResultSinkWrapper(base::WeakPtr<Transaction> transaction,
blink::mojom::IDBDatabase::GetAllCallback callback);
~GetAllResultSinkWrapper();
mojo::AssociatedRemote<blink::mojom::IDBDatabaseGetAllResultSink>& Get();
private:
base::WeakPtr<Transaction> transaction_;
blink::mojom::IDBDatabase::GetAllCallback callback_;
mojo::AssociatedRemote<blink::mojom::IDBDatabaseGetAllResultSink>
result_sink_;
};
Status GetAllOperation(int64_t object_store_id,
int64_t index_id,
std::unique_ptr<blink::IndexedDBKeyRange> key_range,
indexed_db::CursorType cursor_type,
int64_t max_count,
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);
bool CanBeDestroyed();
std::vector<PartitionedLockManager::PartitionedLockRequest>
BuildLockRequestsFromTransaction(Transaction* transaction) 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);
// `metadata_` may not be fully initialized, but its `name` will always be
// valid.
blink::IndexedDBDatabaseMetadata metadata_;
const Identifier identifier_;
// The object that owns `this`.
raw_ref<BucketContext> bucket_context_;
list_set<Connection*> connections_;
bool force_closing_ = false;
ConnectionCoordinator connection_coordinator_;
// `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_