blob: 52047647c9848744847e9f87241522ae8ef79940 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2013 The Chromium Authors
[email protected]72a4183d2013-05-31 18:33:102// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Evan Stade1a8d9d42024-09-10 19:37:195#include "content/browser/indexed_db/instance/database.h"
dcheng531cca92016-04-09 03:03:236
Chase Phillips93a43cd2019-03-22 17:15:527#include <math.h>
Evan Stade87073092024-01-12 03:53:238
Evan Staded9529ea52025-04-11 17:02:509#include <algorithm>
Numfor Mbiziwo-Tiapo7baae9f2020-08-13 20:39:3610#include <cstddef>
Evan Staded9529ea52025-04-11 17:02:5011#include <cstdint>
12#include <memory>
13#include <optional>
14#include <string>
15#include <tuple>
Chase Phillipsc07c4f392018-11-12 22:05:5516#include <utility>
Evan Staded9529ea52025-04-11 17:02:5017#include <vector>
[email protected]72a4183d2013-05-31 18:33:1018
Evan Staded9529ea52025-04-11 17:02:5019#include "base/check.h"
20#include "base/check_op.h"
Lei Zhangde197672021-04-29 08:11:2421#include "base/containers/contains.h"
Mingyu Lei2753e432023-01-13 13:48:4222#include "base/containers/flat_set.h"
Evan Stadeb77419382025-03-17 09:24:2323#include "base/debug/dump_without_crashing.h"
Avi Drissmanadac21992023-01-11 23:46:3924#include "base/functional/bind.h"
Evan Staded9529ea52025-04-11 17:02:5025#include "base/functional/callback_forward.h"
Mingyu Lei4b683372023-01-25 07:44:5126#include "base/functional/callback_helpers.h"
[email protected]72a4183d2013-05-31 18:33:1027#include "base/logging.h"
Daniel Murphy7797b8c2019-03-07 00:08:4428#include "base/memory/weak_ptr.h"
Evan Staded9529ea52025-04-11 17:02:5029#include "base/notreached.h"
palakjdbb27252016-06-09 19:01:2430#include "base/numerics/safe_conversions.h"
Abhishek Shanthkumar88ce4902025-06-26 16:00:0031#include "base/strings/stringprintf.h"
Evan Stadedc38e9712023-07-14 02:04:5432#include "base/strings/utf_string_conversions.h"
Etienne Pierre-dorayfc7952f02025-06-06 00:04:3333#include "base/trace_event/trace_event.h"
Evan Stade5808daf2025-05-21 21:41:3334#include "base/types/expected_macros.h"
Evan Staded9529ea52025-04-11 17:02:5035#include "base/unguessable_token.h"
Mingyu Lei2753e432023-01-13 13:48:4236#include "components/services/storage/indexed_db/locks/partitioned_lock_id.h"
Nathan Memmottaecdf3452022-10-10 17:53:3737#include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h"
Evan Staded9529ea52025-04-11 17:02:5038#include "components/services/storage/privileged/mojom/indexed_db_client_state_checker.mojom.h"
Evan Staded9529ea52025-04-11 17:02:5039#include "components/services/storage/privileged/mojom/indexed_db_internals_types.mojom.h"
Marijn Kruisselbrinka6d9daf2020-01-31 02:13:0340#include "content/browser/indexed_db/indexed_db_external_object.h"
[email protected]120875f2014-03-19 23:08:5241#include "content/browser/indexed_db/indexed_db_value.h"
Evan Staded9529ea52025-04-11 17:02:5042#include "content/browser/indexed_db/instance/backing_store.h"
Evan Stade1a8d9d42024-09-10 19:37:1943#include "content/browser/indexed_db/instance/bucket_context.h"
44#include "content/browser/indexed_db/instance/bucket_context_handle.h"
45#include "content/browser/indexed_db/instance/callback_helpers.h"
46#include "content/browser/indexed_db/instance/connection.h"
47#include "content/browser/indexed_db/instance/cursor.h"
48#include "content/browser/indexed_db/instance/database_callbacks.h"
49#include "content/browser/indexed_db/instance/factory_client.h"
50#include "content/browser/indexed_db/instance/index_writer.h"
Evan Stade1a8d9d42024-09-10 19:37:1951#include "content/browser/indexed_db/instance/pending_connection.h"
52#include "content/browser/indexed_db/instance/transaction.h"
Evan Staded9529ea52025-04-11 17:02:5053#include "content/browser/indexed_db/status.h"
Tom Sepez36c46c02025-07-11 21:43:5654#include "ipc/constants.mojom.h"
Evan Staded9529ea52025-04-11 17:02:5055#include "mojo/public/cpp/bindings/associated_remote.h"
56#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
57#include "mojo/public/cpp/bindings/pending_associated_remote.h"
58#include "mojo/public/cpp/bindings/remote.h"
Chase Phillips850b96f72018-08-24 19:44:1659#include "third_party/blink/public/common/indexeddb/indexeddb_key_path.h"
Chase Phillips33d161d62018-08-28 19:44:1260#include "third_party/blink/public/common/indexeddb/indexeddb_key_range.h"
61#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
Ari Chivukula30ca2ae2021-06-10 19:22:3562#include "third_party/blink/public/common/storage_key/storage_key.h"
Henrique Ferreiroda0a55c2019-11-12 14:06:0463#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
[email protected]41db229742014-05-31 22:17:5164#include "third_party/leveldatabase/env_chromium.h"
[email protected]72a4183d2013-05-31 18:33:1065
Chase Phillips33d161d62018-08-28 19:44:1266using blink::IndexedDBDatabaseMetadata;
Chase Phillips68ecf5512018-08-16 01:59:1367using blink::IndexedDBIndexKeys;
Chase Phillips33d161d62018-08-28 19:44:1268using blink::IndexedDBIndexMetadata;
Chase Phillips68ecf5512018-08-16 01:59:1369using blink::IndexedDBKey;
Chase Phillips850b96f72018-08-24 19:44:1670using blink::IndexedDBKeyPath;
Chase Phillips33d161d62018-08-28 19:44:1271using blink::IndexedDBKeyRange;
72using blink::IndexedDBObjectStoreMetadata;
[email protected]72a4183d2013-05-31 18:33:1073
Evan Stadecbb1e002024-09-13 20:06:5774namespace content::indexed_db {
jsbella5dd3ef2015-04-23 00:24:1975namespace {
76
Abhishek Shanthkumar88ce4902025-06-26 16:00:0077// `backing_store_db` can be null only if `mode` is VersionChange.
78std::vector<PartitionedLockManager::PartitionedLockRequest>
79BuildLockRequestsForLevelDb(const std::u16string& database_name,
80 const BackingStore::Database* backing_store_db,
81 blink::mojom::IDBTransactionMode mode,
82 const std::set<int64_t>& scope) {
83 // NB: LevelDB lock IDs are potentially persisted to disk - see
84 // `LevelDBPartitionedLock`.
85 const constexpr int kDatabaseLockPartition = 0;
86 PartitionedLockId database_lock_id{kDatabaseLockPartition,
87 base::UTF16ToUTF8(database_name)};
88 if (mode == blink::mojom::IDBTransactionMode::VersionChange) {
89 return {{std::move(database_lock_id),
90 PartitionedLockManager::LockType::kExclusive}};
91 }
92 CHECK(backing_store_db);
93 std::vector<PartitionedLockManager::PartitionedLockRequest> lock_requests;
94 lock_requests.reserve(1 + scope.size());
95 lock_requests.emplace_back(std::move(database_lock_id),
96 PartitionedLockManager::LockType::kShared);
97 const constexpr int kObjectStoreLockPartition = 1;
98 const auto object_store_lock_type =
99 mode == blink::mojom::IDBTransactionMode::ReadOnly
100 ? PartitionedLockManager::LockType::kShared
101 : PartitionedLockManager::LockType::kExclusive;
102 for (int64_t object_store_id : scope) {
103 lock_requests.emplace_back(
104 PartitionedLockId{
105 kObjectStoreLockPartition,
106 backing_store_db->GetObjectStoreLockIdKey(object_store_id)},
107 object_store_lock_type);
108 }
109 return lock_requests;
110}
111
112std::vector<PartitionedLockManager::PartitionedLockRequest>
113BuildLockRequestsForSqlite(uint32_t database_id,
114 blink::mojom::IDBTransactionMode mode,
115 const std::set<int64_t>& scope) {
116 // TODO(crbug.com/427608926): Refactor `PartitionedLockId` to not need `key`
117 // to be a string.
118 const constexpr int kMetadataLockPartition = 0;
119 PartitionedLockId metadata_lock_id{kMetadataLockPartition,
120 base::StringPrintf("%u", database_id)};
121 if (mode == blink::mojom::IDBTransactionMode::VersionChange) {
122 return {{std::move(metadata_lock_id),
123 PartitionedLockManager::LockType::kExclusive}};
124 }
125 std::vector<PartitionedLockManager::PartitionedLockRequest> lock_requests{
126 {std::move(metadata_lock_id), PartitionedLockManager::LockType::kShared}};
127 if (mode == blink::mojom::IDBTransactionMode::ReadWrite) {
128 const constexpr int kWriteOperationsLockPartition = 1;
129 lock_requests.emplace_back(
130 PartitionedLockId{kWriteOperationsLockPartition,
131 base::StringPrintf("%u", database_id)},
132 PartitionedLockManager::LockType::kExclusive);
133 }
134 lock_requests.reserve(lock_requests.size() + scope.size());
135 const constexpr int kObjectStoreLockPartition = 2;
136 const auto object_store_lock_type =
137 mode == blink::mojom::IDBTransactionMode::ReadOnly
138 ? PartitionedLockManager::LockType::kShared
139 : PartitionedLockManager::LockType::kExclusive;
140 for (int64_t object_store_id : scope) {
141 lock_requests.emplace_back(
142 PartitionedLockId{
143 kObjectStoreLockPartition,
144 base::StringPrintf("%u|%lld", database_id, object_store_id)},
145 object_store_lock_type);
146 }
147 return lock_requests;
148}
Evan Stadee24c0072025-06-18 18:29:17149
Evan Stade295d0ab2025-06-02 15:27:54150// Values returned to the IDB client may contain a primary key value generated
151// by IDB. This is optional and only done when using a key generator. This key
152// value cannot (at least easily) be amended to the object being written to the
153// database, so they are kept separately, and sent back with the original data
154// so that the render process can amend the returned object.
155blink::mojom::IDBReturnValuePtr ConvertValueToReturnValue(
Evan Stade822bdb42025-08-07 18:37:41156 Transaction& transaction,
Evan Stade295d0ab2025-06-02 15:27:54157 IndexedDBValue value,
158 blink::IndexedDBKey primary_key,
159 blink::IndexedDBKeyPath key_path) {
160 auto mojo_value = blink::mojom::IDBReturnValue::New();
Evan Stade295d0ab2025-06-02 15:27:54161 if (primary_key.IsValid()) {
162 mojo_value->primary_key = std::move(primary_key);
163 mojo_value->key_path = std::move(key_path);
164 }
Evan Stade2bc92e82025-06-04 19:07:36165 mojo_value->value = transaction.BuildMojoValue(std::move(value));
Evan Stade295d0ab2025-06-02 15:27:54166 return mojo_value;
167}
168
Steve Becker4848af32024-10-29 22:57:19169// Returns an `IDBReturnValuePtr` created from the cursor's current position.
170blink::mojom::IDBReturnValuePtr ExtractReturnValueFromCursorValue(
Evan Stade822bdb42025-08-07 18:37:41171 Transaction& transaction,
Steve Becker4848af32024-10-29 22:57:19172 const IndexedDBObjectStoreMetadata& object_store_metadata,
Evan Staded9529ea52025-04-11 17:02:50173 BackingStore::Cursor& cursor) {
Evan Stade295d0ab2025-06-02 15:27:54174 IndexedDBValue value(std::move(cursor.GetValue()));
Steve Becker4848af32024-10-29 22:57:19175
Evan Stade295d0ab2025-06-02 15:27:54176 const bool is_generated_key = !value.empty() &&
177 object_store_metadata.auto_increment &&
178 !object_store_metadata.key_path.IsNull();
179 blink::IndexedDBKey primary_key;
180 blink::IndexedDBKeyPath key_path;
181
Steve Becker4848af32024-10-29 22:57:19182 if (is_generated_key) {
Evan Stade295d0ab2025-06-02 15:27:54183 primary_key = cursor.GetPrimaryKey().Clone();
184 key_path = object_store_metadata.key_path;
Adrienne Walker7c30dd62020-07-27 11:00:06185 }
Steve Becker4848af32024-10-29 22:57:19186
Evan Stade2bc92e82025-06-04 19:07:36187 return ConvertValueToReturnValue(transaction, std::move(value),
Evan Stade295d0ab2025-06-02 15:27:54188 std::move(primary_key), std::move(key_path));
Adrienne Walker7c30dd62020-07-27 11:00:06189}
190
Evan Stadedc38e9712023-07-14 02:04:54191blink::mojom::IDBErrorPtr CreateIDBErrorPtr(blink::mojom::IDBException code,
192 const std::string& message,
Evan Stadecbb1e002024-09-13 20:06:57193 Transaction* transaction) {
Chase Phillips2da038752019-05-22 23:32:36194 transaction->IncrementNumErrorsSent();
Evan Stadedc38e9712023-07-14 02:04:54195 return blink::mojom::IDBError::New(code, base::UTF8ToUTF16(message));
Chase Phillips2da038752019-05-22 23:32:36196}
197
jsbella5dd3ef2015-04-23 00:24:19198} // namespace
199
Evan Stadecbb1e002024-09-13 20:06:57200Database::OpenCursorOperationParams::OpenCursorOperationParams() = default;
201Database::OpenCursorOperationParams::~OpenCursorOperationParams() = default;
Daniel Murphy0240b782019-08-14 21:15:09202
Abhishek Shanthkumar88ce4902025-06-26 16:00:00203Database::Database(uint32_t id_for_locks,
204 const std::u16string& name,
205 BucketContext& bucket_context)
206 : id_for_locks_(id_for_locks),
Evan Stadee24c0072025-06-18 18:29:17207 name_(name),
Evan Stadea35e2722023-09-05 22:53:42208 bucket_context_(bucket_context),
Evan Stadea35e2722023-09-05 22:53:42209 connection_coordinator_(this, bucket_context) {}
[email protected]72a4183d2013-05-31 18:33:10210
Evan Stadecbb1e002024-09-13 20:06:57211Database::~Database() = default;
Daniel Murphy4c0f9c12019-05-23 01:14:35212
Evan Stadecbb1e002024-09-13 20:06:57213BackingStore* Database::backing_store() {
Evan Stadea35e2722023-09-05 22:53:42214 return bucket_context_->backing_store();
215}
216
Evan Stadecbb1e002024-09-13 20:06:57217PartitionedLockManager& Database::lock_manager() {
Evan Stadea35e2722023-09-05 22:53:42218 return bucket_context_->lock_manager();
219}
220
Evan Stade2fbd4532025-04-30 10:19:22221int64_t Database::version() const {
222 return backing_store_db_ ? metadata().version
223 : blink::IndexedDBDatabaseMetadata::NO_VERSION;
224}
225
226bool Database::IsInitialized() const {
227 return backing_store_db_ != nullptr;
228}
229
Evan Stadee2a68162025-07-08 17:22:54230StatusOr<int64_t> Database::DeleteDatabase(std::vector<PartitionedLock> locks,
231 base::OnceClosure on_complete) {
232 if (!backing_store_db_) {
233 return blink::IndexedDBDatabaseMetadata::DEFAULT_VERSION;
234 }
235
236 const int64_t old_version = version();
237 Status s = backing_store_db_->DeleteDatabase(std::move(locks),
238 std::move(on_complete));
239 backing_store_db_.reset();
240 if (!s.ok()) {
241 return base::unexpected(s);
242 }
243 return old_version;
244}
245
Abhishek Shanthkumar88ce4902025-06-26 16:00:00246std::vector<PartitionedLockManager::PartitionedLockRequest>
247Database::BuildLockRequestsForTransaction(
248 blink::mojom::IDBTransactionMode mode,
249 const std::set<int64_t>& scope) const {
250 return bucket_context_->ShouldUseSqlite()
251 ? BuildLockRequestsForSqlite(id_for_locks_, mode, scope)
252 : BuildLockRequestsForLevelDb(name_, backing_store_db_.get(), mode,
253 scope);
254}
255
Evan Stade8c6ddfd2025-01-06 17:41:10256bool Database::OnlyHasOneClient() const {
257 if (connections_.empty()) {
258 return true;
259 }
260
261 const base::UnguessableToken& token = (*connections_.begin())->client_token();
262 return std::all_of(connections_.begin(), connections_.end(),
263 [&token](Connection* connection) {
264 return connection->client_token() == token;
265 });
266}
267
Evan Stadecbb1e002024-09-13 20:06:57268void Database::RequireBlockingTransactionClientsToBeActive(
269 Transaction* current_transaction,
Mingyu Lei5cff96b2023-01-13 17:09:51270 std::vector<PartitionedLockManager::PartitionedLockRequest>&
271 lock_requests) {
Evan Stade8c6ddfd2025-01-06 17:41:10272 if (OnlyHasOneClient()) {
273 return;
274 }
275
Mingyu Lei5cff96b2023-01-13 17:09:51276 std::vector<PartitionedLockId> blocked_lock_ids =
Evan Stade22f9c2642024-02-04 00:35:28277 lock_manager().GetUnacquirableLocks(lock_requests);
Mingyu Lei5cff96b2023-01-13 17:09:51278
279 if (blocked_lock_ids.empty()) {
280 return;
281 }
282
Evan Stadecbb1e002024-09-13 20:06:57283 for (Connection* connection : connections_) {
Abhishek Shanthkumar0d9abbe32024-06-07 01:28:00284 if (connection->client_token() ==
285 current_transaction->connection()->client_token()) {
Evan Stade2db7aa72024-02-01 17:07:27286 continue;
287 }
288
Evan Stade87073092024-01-12 03:53:23289 // If any of the connection's transactions is holding one of the blocked
290 // lock IDs, require that client to be active.
Patrick Monetted32aa3d2024-11-13 21:12:31291 if (connection->IsHoldingLocks(blocked_lock_ids)) {
Mingyu Lei4b683372023-01-25 07:44:51292 connection->DisallowInactiveClient(
293 storage::mojom::DisallowInactiveClientReason::
Evan Stade2db7aa72024-02-01 17:07:27294 kTransactionIsAcquiringLocks,
Mingyu Lei27682952023-05-04 08:13:21295 base::DoNothing());
Mingyu Lei5cff96b2023-01-13 17:09:51296 }
297 }
298}
299
Evan Stadecbb1e002024-09-13 20:06:57300void Database::RegisterAndScheduleTransaction(Transaction* transaction) {
301 TRACE_EVENT1("IndexedDB", "Database::RegisterAndScheduleTransaction",
Mingyu Lei2753e432023-01-13 13:48:42302 "txn.id", transaction->id());
Evan Stade87073092024-01-12 03:53:23303 // Locks for version change transactions are covered by `ConnectionRequest`.
304 DCHECK_NE(transaction->mode(),
305 blink::mojom::IDBTransactionMode::VersionChange);
Mingyu Lei2753e432023-01-13 13:48:42306 std::vector<PartitionedLockManager::PartitionedLockRequest> lock_requests =
Abhishek Shanthkumar88ce4902025-06-26 16:00:00307 BuildLockRequestsForTransaction(transaction->mode(),
308 transaction->scope());
Mingyu Lei5cff96b2023-01-13 17:09:51309
Mingyu Leiac9bde42023-06-22 09:56:24310 RequireBlockingTransactionClientsToBeActive(transaction, lock_requests);
Mingyu Lei5cff96b2023-01-13 17:09:51311
Evan Stade22f9c2642024-02-04 00:35:28312 lock_manager().AcquireLocks(
Evan Staded9055a72024-08-22 23:11:27313 std::move(lock_requests), *transaction->mutable_locks_receiver(),
Evan Stadecbb1e002024-09-13 20:06:57314 base::BindOnce(&Transaction::Start, transaction->AsWeakPtr()),
315 base::BindRepeating(&Connection::HasHigherPriorityThan,
Evan Stade8e75d402024-09-04 00:42:43316 transaction->mutable_locks_receiver()));
Daniel Murphy48b6b3a2019-08-07 20:38:25317}
318
Evan Stade4c8bfd62025-05-08 15:52:36319Status Database::RunTasks() {
Daniel Murphy7c750432019-08-20 01:39:47320 // First execute any pending tasks in the connection coordinator.
Evan Stadecbb1e002024-09-13 20:06:57321 ConnectionCoordinator::ExecuteTaskResult task_state;
Mike Wasserman0d5da522024-09-27 07:47:03322 Status status;
Daniel Murphy7c750432019-08-20 01:39:47323 do {
324 std::tie(task_state, status) =
325 connection_coordinator_.ExecuteTask(!connections_.empty());
Evan Stadecbb1e002024-09-13 20:06:57326 } while (task_state == ConnectionCoordinator::ExecuteTaskResult::kMoreTasks);
Daniel Murphy7c750432019-08-20 01:39:47327
Evan Stadecbb1e002024-09-13 20:06:57328 if (task_state == ConnectionCoordinator::ExecuteTaskResult::kError) {
Evan Stade4c8bfd62025-05-08 15:52:36329 CHECK(!status.ok());
330 return status;
Evan Stade1a8d9d42024-09-10 19:37:19331 }
Daniel Murphy7c750432019-08-20 01:39:47332
333 bool transactions_removed = true;
334
335 // Finally, execute transactions that have tasks & remove those that are
336 // complete.
337 while (transactions_removed) {
338 transactions_removed = false;
Evan Stadecbb1e002024-09-13 20:06:57339 Transaction* finished_upgrade_transaction = nullptr;
Daniel Murphy7c750432019-08-20 01:39:47340 bool upgrade_transaction_commmitted = false;
Evan Stadecbb1e002024-09-13 20:06:57341 for (Connection* connection : connections_) {
Daniel Murphy7c750432019-08-20 01:39:47342 std::vector<int64_t> txns_to_remove;
343 for (const auto& id_txn_pair : connection->transactions()) {
Evan Stadecbb1e002024-09-13 20:06:57344 Transaction* txn = id_txn_pair.second.get();
Daniel Murphy7c750432019-08-20 01:39:47345 // Determine if the transaction's task queue should be processed.
346 switch (txn->state()) {
Evan Stadecbb1e002024-09-13 20:06:57347 case Transaction::FINISHED:
Daniel Murphy7c750432019-08-20 01:39:47348 if (txn->mode() ==
349 blink::mojom::IDBTransactionMode::VersionChange) {
350 finished_upgrade_transaction = txn;
351 upgrade_transaction_commmitted = !txn->aborted();
352 }
353 txns_to_remove.push_back(id_txn_pair.first);
354 continue;
Evan Stadecbb1e002024-09-13 20:06:57355 case Transaction::CREATED:
Daniel Murphy7c750432019-08-20 01:39:47356 continue;
Evan Stadecbb1e002024-09-13 20:06:57357 case Transaction::STARTED:
358 case Transaction::COMMITTING:
Daniel Murphy7c750432019-08-20 01:39:47359 break;
360 }
361
362 // Process the queue for transactions that are STARTED or COMMITTING.
363 // Add transactions that can be removed to a queue.
Evan Stade630ca5c42025-08-14 15:10:27364 StatusOr<Transaction::RunTasksResult> task_result = txn->RunTasks();
365 if (!task_result.has_value()) {
366 return task_result.error();
367 }
368
369 switch (task_result.value()) {
Evan Stadecbb1e002024-09-13 20:06:57370 case Transaction::RunTasksResult::kCommitted:
371 case Transaction::RunTasksResult::kAborted:
Daniel Murphy7c750432019-08-20 01:39:47372 if (txn->mode() ==
373 blink::mojom::IDBTransactionMode::VersionChange) {
374 DCHECK(!finished_upgrade_transaction);
375 finished_upgrade_transaction = txn;
376 upgrade_transaction_commmitted = !txn->aborted();
377 }
378 txns_to_remove.push_back(txn->id());
379 break;
Evan Stadecbb1e002024-09-13 20:06:57380 case Transaction::RunTasksResult::kNotFinished:
Daniel Murphy7c750432019-08-20 01:39:47381 continue;
382 }
383 }
384 // Do the removals.
385 for (int64_t id : txns_to_remove) {
386 connection->RemoveTransaction(id);
387 transactions_removed = true;
388 }
389 if (finished_upgrade_transaction) {
390 connection_coordinator_.OnUpgradeTransactionFinished(
391 upgrade_transaction_commmitted);
392 }
393 }
394 }
Evan Stade4c8bfd62025-05-08 15:52:36395 return Status::OK();
Daniel Murphy7c750432019-08-20 01:39:47396}
397
Mingyu Lei87210bc2025-04-17 09:35:50398Status Database::ForceCloseAndRunTasks(const std::string& message) {
Evan Stade6b4d5a62025-05-29 20:53:53399 if (!bucket_context_->ShouldUseSqlite()) {
400 DCHECK(!force_closing_);
401 } else if (force_closing_) {
402 // Re-entrancy can validly occur if there's an error in the code below,
403 // e.g. in `CloseAndReportForceClose`.
404 return Status::OK();
405 }
406
Daniel Murphy7c750432019-08-20 01:39:47407 force_closing_ = true;
Evan Stadecbb1e002024-09-13 20:06:57408 for (Connection* connection : connections_) {
Mingyu Lei87210bc2025-04-17 09:35:50409 connection->CloseAndReportForceClose(message);
Daniel Murphy7c750432019-08-20 01:39:47410 }
411 connections_.clear();
Evan Stadee2eb7ee2025-05-21 21:41:46412 IDB_RETURN_IF_ERROR(connection_coordinator_.PruneTasksForForceClose(message));
Daniel Murphy7c750432019-08-20 01:39:47413 connection_coordinator_.OnNoConnections();
414
415 // Execute any pending tasks in the connection coordinator.
Evan Stadecbb1e002024-09-13 20:06:57416 ConnectionCoordinator::ExecuteTaskResult task_state;
Evan Stadee2eb7ee2025-05-21 21:41:46417 Status status;
Daniel Murphy7c750432019-08-20 01:39:47418 do {
419 std::tie(task_state, status) = connection_coordinator_.ExecuteTask(false);
420 DCHECK(task_state !=
Evan Stadecbb1e002024-09-13 20:06:57421 ConnectionCoordinator::ExecuteTaskResult::kPendingAsyncWork)
Daniel Murphy7c750432019-08-20 01:39:47422 << "There are no more connections, so all tasks should be able to "
423 "complete synchronously.";
Evan Stadecbb1e002024-09-13 20:06:57424 } while (task_state != ConnectionCoordinator::ExecuteTaskResult::kDone &&
425 task_state != ConnectionCoordinator::ExecuteTaskResult::kError);
Daniel Murphy7c750432019-08-20 01:39:47426 DCHECK(connections_.empty());
Evan Stade68334f92024-02-08 17:09:27427 bucket_context_->QueueRunTasks();
Daniel Murphy7c750432019-08-20 01:39:47428 return status;
Daniel Murphy48b6b3a2019-08-07 20:38:25429}
430
Evan Stadecbb1e002024-09-13 20:06:57431void Database::ScheduleOpenConnection(
432 std::unique_ptr<PendingConnection> connection) {
Evan Stade9f5e401c2024-01-04 23:58:35433 connection_coordinator_.ScheduleOpenConnection(std::move(connection));
Daniel Murphy48b6b3a2019-08-07 20:38:25434}
435
Evan Stadecbb1e002024-09-13 20:06:57436void Database::ScheduleDeleteDatabase(
437 std::unique_ptr<FactoryClient> factory_client,
Daniel Murphy48b6b3a2019-08-07 20:38:25438 base::OnceClosure on_deletion_complete) {
Daniel Murphyede91592019-08-12 19:50:22439 connection_coordinator_.ScheduleDeleteDatabase(
Evan Stadea35e2722023-09-05 22:53:42440 std::move(factory_client), std::move(on_deletion_complete));
Daniel Murphy48b6b3a2019-08-07 20:38:25441}
442
Evan Stadecbb1e002024-09-13 20:06:57443Status Database::VersionChangeOperation(int64_t version,
444 Transaction* transaction) {
445 TRACE_EVENT1("IndexedDB", "Database::VersionChangeOperation", "txn.id",
446 transaction->id());
Evan Stade2fbd4532025-04-30 10:19:22447 int64_t old_version = metadata().version;
Daniel Murphy48b6b3a2019-08-07 20:38:25448 DCHECK_GT(version, old_version);
449
Evan Stadee2eb7ee2025-05-21 21:41:46450 IDB_RETURN_IF_ERROR(
451 transaction->BackingStoreTransaction()->SetDatabaseVersion(version));
Daniel Murphy48b6b3a2019-08-07 20:38:25452
Guido Urdaneta16511382023-10-25 17:40:04453 connection_coordinator_.BindVersionChangeTransactionReceiver();
Daniel Murphyede91592019-08-12 19:50:22454 connection_coordinator_.OnUpgradeTransactionStarted(old_version);
Daniel Murphy48b6b3a2019-08-07 20:38:25455 return Status::OK();
456}
457
Evan Stadecbb1e002024-09-13 20:06:57458Status Database::GetOperation(int64_t object_store_id,
459 int64_t index_id,
Evan Stadeca999b12025-05-09 19:09:11460 IndexedDBKeyRange key_range,
Evan Stadecbb1e002024-09-13 20:06:57461 CursorType cursor_type,
462 blink::mojom::IDBDatabase::GetCallback callback,
463 Transaction* transaction) {
464 TRACE_EVENT1("IndexedDB", "Database::GetOperation", "txn.id",
Pei Zhangc420ad72022-02-12 06:11:55465 transaction->id());
[email protected]65880a82013-08-16 21:30:08466
Daniel Murphy0240b782019-08-14 21:15:09467 if (!IsObjectStoreIdAndMaybeIndexIdInMetadata(object_store_id, index_id)) {
Daniel Murphy0240b782019-08-14 21:15:09468 std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
Evan Stadedc38e9712023-07-14 02:04:54469 CreateIDBErrorPtr(blink::mojom::IDBException::kUnknownError,
470 "Bad request", transaction)));
Mike Wasserman0d5da522024-09-27 07:47:03471 return Status::InvalidArgument("Invalid object_store_id and/or index_id.");
Daniel Murphy0240b782019-08-14 21:15:09472 }
473
[email protected]65880a82013-08-16 21:30:08474 const IndexedDBObjectStoreMetadata& object_store_metadata =
Evan Stadecaa7d182025-04-30 17:42:12475 GetObjectStoreMetadata(object_store_id);
[email protected]72a4183d2013-05-31 18:33:10476
Evan Stadeca999b12025-05-09 19:09:11477 IndexedDBKey key;
478 if (key_range.IsOnlyKey()) {
479 key = std::move(key_range).TakeOnlyKey();
[email protected]72a4183d2013-05-31 18:33:10480 } else {
Evan Stadea496adc2025-05-21 21:42:43481 StatusOr<std::unique_ptr<BackingStore::Cursor>> backing_store_cursor;
[email protected]65880a82013-08-16 21:30:08482 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
[email protected]72a4183d2013-05-31 18:33:10483 // ObjectStore Retrieval Operation
Evan Stadecbb1e002024-09-13 20:06:57484 if (cursor_type == CursorType::kKeyOnly) {
Evan Stadee07c68a62025-04-11 15:21:56485 backing_store_cursor =
Evan Staded9529ea52025-04-11 17:02:50486 transaction->BackingStoreTransaction()->OpenObjectStoreKeyCursor(
Evan Stadeca999b12025-05-09 19:09:11487 object_store_id, key_range,
Evan Stade2563f3e2025-05-08 07:46:03488 blink::mojom::IDBCursorDirection::Next);
jsbell1e5f0bc2016-08-30 03:20:44489 } else {
Evan Staded9529ea52025-04-11 17:02:50490 backing_store_cursor =
491 transaction->BackingStoreTransaction()->OpenObjectStoreCursor(
Evan Stadeca999b12025-05-09 19:09:11492 object_store_id, key_range,
Evan Stade2563f3e2025-05-08 07:46:03493 blink::mojom::IDBCursorDirection::Next);
jsbell1e5f0bc2016-08-30 03:20:44494 }
Evan Stadecbb1e002024-09-13 20:06:57495 } else if (cursor_type == CursorType::kKeyOnly) {
[email protected]72a4183d2013-05-31 18:33:10496 // Index Value Retrieval Operation
Evan Staded9529ea52025-04-11 17:02:50497 backing_store_cursor =
498 transaction->BackingStoreTransaction()->OpenIndexKeyCursor(
Evan Stadeca999b12025-05-09 19:09:11499 object_store_id, index_id, key_range,
Evan Stade2563f3e2025-05-08 07:46:03500 blink::mojom::IDBCursorDirection::Next);
[email protected]72a4183d2013-05-31 18:33:10501 } else {
502 // Index Referenced Value Retrieval Operation
Evan Staded9529ea52025-04-11 17:02:50503 backing_store_cursor =
504 transaction->BackingStoreTransaction()->OpenIndexCursor(
Evan Stadeca999b12025-05-09 19:09:11505 object_store_id, index_id, key_range,
Evan Stade2563f3e2025-05-08 07:46:03506 blink::mojom::IDBCursorDirection::Next);
[email protected]b05831e2014-04-16 18:32:19507 }
508
Evan Stade2563f3e2025-05-08 07:46:03509 if (!backing_store_cursor.has_value()) {
Chase Phillipsc7467772019-06-04 01:02:53510 std::move(callback).Run(
Evan Stadedc38e9712023-07-14 02:04:54511 blink::mojom::IDBDatabaseGetResult::NewErrorResult(CreateIDBErrorPtr(
512 blink::mojom::IDBException::kUnknownError,
513 "Corruption detected, unable to continue", transaction)));
Evan Stade2563f3e2025-05-08 07:46:03514 return backing_store_cursor.error();
Chase Phillipsc7467772019-06-04 01:02:53515 }
[email protected]72a4183d2013-05-31 18:33:10516
Evan Stade2563f3e2025-05-08 07:46:03517 if (!*backing_store_cursor) {
dmurph50ab051b32016-11-29 22:13:30518 // This means we've run out of data.
Chase Phillipsc7467772019-06-04 01:02:53519 std::move(callback).Run(
520 blink::mojom::IDBDatabaseGetResult::NewEmpty(true));
Evan Stade2563f3e2025-05-08 07:46:03521 return Status::OK();
[email protected]72a4183d2013-05-31 18:33:10522 }
523
Evan Stadeca999b12025-05-09 19:09:11524 key = std::move(**backing_store_cursor).TakeKey();
[email protected]72a4183d2013-05-31 18:33:10525 }
526
[email protected]65880a82013-08-16 21:30:08527 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
[email protected]72a4183d2013-05-31 18:33:10528 // Object Store Retrieval Operation
Evan Stade295d0ab2025-06-02 15:27:54529 ASSIGN_OR_RETURN(
530 IndexedDBValue value,
531 transaction->BackingStoreTransaction()->GetRecord(object_store_id, key),
532 [&callback, transaction](const Status& status) {
533 std::move(callback).Run(
534 blink::mojom::IDBDatabaseGetResult::NewErrorResult(
535 CreateIDBErrorPtr(blink::mojom::IDBException::kUnknownError,
536 "Unknown error", transaction)));
537 return status;
538 });
[email protected]72a4183d2013-05-31 18:33:10539
540 if (value.empty()) {
Chase Phillipsc7467772019-06-04 01:02:53541 std::move(callback).Run(
542 blink::mojom::IDBDatabaseGetResult::NewEmpty(true));
Evan Stade295d0ab2025-06-02 15:27:54543 return Status::OK();
[email protected]72a4183d2013-05-31 18:33:10544 }
545
Evan Stadecbb1e002024-09-13 20:06:57546 if (cursor_type == CursorType::kKeyOnly) {
Chase Phillipsc7467772019-06-04 01:02:53547 std::move(callback).Run(
Evan Stadeca999b12025-05-09 19:09:11548 blink::mojom::IDBDatabaseGetResult::NewKey(std::move(key)));
Evan Stade295d0ab2025-06-02 15:27:54549 return Status::OK();
jsbell1e5f0bc2016-08-30 03:20:44550 }
551
Evan Stade295d0ab2025-06-02 15:27:54552 blink::IndexedDBKey primary_key;
553 blink::IndexedDBKeyPath key_path;
554
[email protected]65880a82013-08-16 21:30:08555 if (object_store_metadata.auto_increment &&
556 !object_store_metadata.key_path.IsNull()) {
Evan Stade295d0ab2025-06-02 15:27:54557 primary_key = std::move(key);
558 key_path = object_store_metadata.key_path;
[email protected]72a4183d2013-05-31 18:33:10559 }
560
Evan Stade822bdb42025-08-07 18:37:41561 blink::mojom::IDBReturnValuePtr mojo_value =
562 ConvertValueToReturnValue(*transaction, std::move(value),
563 std::move(primary_key), std::move(key_path));
Chase Phillipsc7467772019-06-04 01:02:53564 std::move(callback).Run(
565 blink::mojom::IDBDatabaseGetResult::NewValue(std::move(mojo_value)));
Evan Stade295d0ab2025-06-02 15:27:54566 return Status::OK();
[email protected]72a4183d2013-05-31 18:33:10567 }
568
569 // From here we are dealing only with indexes.
Evan Stade8df643b2025-05-29 22:38:53570 ASSIGN_OR_RETURN(
571 IndexedDBKey primary_key,
Abhishek Shanthkumare6f4cf52025-06-17 20:40:45572 transaction->BackingStoreTransaction()->GetFirstPrimaryKeyForIndexKey(
Evan Stade8df643b2025-05-29 22:38:53573 object_store_id, index_id, key),
574 [&callback, transaction](const Status& status) {
575 std::move(callback).Run(
576 blink::mojom::IDBDatabaseGetResult::NewErrorResult(
577 CreateIDBErrorPtr(blink::mojom::IDBException::kUnknownError,
578 "Unknown error", transaction)));
579 return status;
580 });
dmurph9d00e05d2016-12-01 23:00:34581
Evan Stade8df643b2025-05-29 22:38:53582 if (!primary_key.IsValid()) {
Chase Phillipsc7467772019-06-04 01:02:53583 std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewEmpty(true));
Evan Stade8df643b2025-05-29 22:38:53584 return Status::OK();
[email protected]72a4183d2013-05-31 18:33:10585 }
Evan Stadecbb1e002024-09-13 20:06:57586 if (cursor_type == CursorType::kKeyOnly) {
[email protected]72a4183d2013-05-31 18:33:10587 // Index Value Retrieval Operation
Chase Phillipsc7467772019-06-04 01:02:53588 std::move(callback).Run(
Evan Stade8df643b2025-05-29 22:38:53589 blink::mojom::IDBDatabaseGetResult::NewKey(std::move(primary_key)));
590 return Status::OK();
[email protected]72a4183d2013-05-31 18:33:10591 }
592
593 // Index Referenced Value Retrieval Operation
Evan Stade295d0ab2025-06-02 15:27:54594 ASSIGN_OR_RETURN(
595 IndexedDBValue value,
596 transaction->BackingStoreTransaction()->GetRecord(object_store_id,
597 primary_key),
598 [&callback, transaction](const Status& status) {
599 std::move(callback).Run(
600 blink::mojom::IDBDatabaseGetResult::NewErrorResult(
601 CreateIDBErrorPtr(blink::mojom::IDBException::kUnknownError,
602 "Unknown error", transaction)));
603 return status;
604 });
[email protected]72a4183d2013-05-31 18:33:10605
606 if (value.empty()) {
Chase Phillipsc7467772019-06-04 01:02:53607 std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewEmpty(true));
Evan Stade295d0ab2025-06-02 15:27:54608 return Status::OK();
[email protected]72a4183d2013-05-31 18:33:10609 }
Chase Phillipsc7467772019-06-04 01:02:53610
Evan Stade295d0ab2025-06-02 15:27:54611 blink::IndexedDBKey primary_key_return;
612 blink::IndexedDBKeyPath key_path_return;
613
614 if (object_store_metadata.auto_increment &&
615 !object_store_metadata.key_path.IsNull()) {
616 primary_key_return = std::move(primary_key);
617 key_path_return = object_store_metadata.key_path;
618 }
619
620 blink::mojom::IDBReturnValuePtr mojo_value = ConvertValueToReturnValue(
Evan Stade822bdb42025-08-07 18:37:41621 *transaction, std::move(value), std::move(primary_key_return),
622 std::move(key_path_return));
Chase Phillipsc7467772019-06-04 01:02:53623 std::move(callback).Run(
624 blink::mojom::IDBDatabaseGetResult::NewValue(std::move(mojo_value)));
Evan Stade295d0ab2025-06-02 15:27:54625 return Status::OK();
[email protected]72a4183d2013-05-31 18:33:10626}
627
Evan Stadecbb1e002024-09-13 20:06:57628Transaction::Operation Database::CreateGetAllOperation(
Evan Stade4202b1f2024-06-12 16:10:54629 int64_t object_store_id,
630 int64_t index_id,
Evan Stadeca999b12025-05-09 19:09:11631 blink::IndexedDBKeyRange key_range,
Steve Becker4848af32024-10-29 22:57:19632 blink::mojom::IDBGetAllResultType result_type,
Evan Stade4202b1f2024-06-12 16:10:54633 int64_t max_count,
Steve Becker4848af32024-10-29 22:57:19634 blink::mojom::IDBCursorDirection direction,
Evan Stade4202b1f2024-06-12 16:10:54635 blink::mojom::IDBDatabase::GetAllCallback callback,
Evan Stadecbb1e002024-09-13 20:06:57636 Transaction* transaction) {
637 return BindWeakOperation(&Database::GetAllOperation, AsWeakPtr(),
Evan Stade4202b1f2024-06-12 16:10:54638 object_store_id, index_id, std::move(key_range),
Steve Becker4848af32024-10-29 22:57:19639 result_type, max_count, direction,
Evan Stade4202b1f2024-06-12 16:10:54640 std::make_unique<GetAllResultSinkWrapper>(
641 transaction->AsWeakPtr(), std::move(callback)));
642}
643
Chase Phillips7027bd52018-08-30 17:31:55644static_assert(sizeof(size_t) >= sizeof(int32_t),
645 "Size of size_t is less than size of int32");
646static_assert(blink::mojom::kIDBMaxMessageOverhead <= INT32_MAX,
647 "kIDBMaxMessageOverhead is more than INT32_MAX");
648
Evan Stadecbb1e002024-09-13 20:06:57649Database::GetAllResultSinkWrapper::GetAllResultSinkWrapper(
650 base::WeakPtr<Transaction> transaction,
Evan Stade4202b1f2024-06-12 16:10:54651 blink::mojom::IDBDatabase::GetAllCallback callback)
652 : transaction_(transaction), callback_(std::move(callback)) {}
653
Evan Stadecbb1e002024-09-13 20:06:57654Database::GetAllResultSinkWrapper::~GetAllResultSinkWrapper() {
Evan Stade4202b1f2024-06-12 16:10:54655 if (!callback_) {
656 return;
657 }
658
659 if (transaction_) {
660 transaction_->IncrementNumErrorsSent();
Evan Stadecbb1e002024-09-13 20:06:57661 // If we're reaching this line because the Connection has been
Evan Stade278881c2024-06-14 05:33:04662 // disconnected from its remote, then `result_sink_` won't have been
663 // successfully associated, and invoking any methods on it will CHECK.
664 // See crbug.com/346955148.
665 // TODO(crbug.com/347047640): remove this workaround when 347047640 is
666 // fixed.
667 if (!transaction_->connection()->is_shutting_down()) {
Evan Stadecbb1e002024-09-13 20:06:57668 DatabaseError error(blink::mojom::IDBException::kIgnorableAbortError,
669 "Backend aborted error");
Evan Stade278881c2024-06-14 05:33:04670 Get()->OnError(
671 blink::mojom::IDBError::New(error.code(), error.message()));
672 }
673 } else {
674 // Make sure `callback_` is invoked because the Mojo client is waiting for a
675 // response.
676 Get();
Evan Stade4202b1f2024-06-12 16:10:54677 }
Evan Stade4202b1f2024-06-12 16:10:54678}
679
680mojo::AssociatedRemote<blink::mojom::IDBDatabaseGetAllResultSink>&
Evan Stadecbb1e002024-09-13 20:06:57681Database::GetAllResultSinkWrapper::Get() {
Evan Stade4202b1f2024-06-12 16:10:54682 if (!result_sink_) {
Steve Becker4848af32024-10-29 22:57:19683 mojo::PendingAssociatedReceiver<blink::mojom::IDBDatabaseGetAllResultSink>
684 pending_receiver;
685 if (use_dedicated_receiver_for_testing_) {
686 pending_receiver = result_sink_.BindNewEndpointAndPassDedicatedReceiver();
687 } else {
688 pending_receiver = result_sink_.BindNewEndpointAndPassReceiver();
689 }
690 std::move(callback_).Run(std::move(pending_receiver));
Evan Stade4202b1f2024-06-12 16:10:54691 }
692 return result_sink_;
693}
694
Evan Stadecbb1e002024-09-13 20:06:57695Status Database::GetAllOperation(
avib7348942015-12-25 20:57:10696 int64_t object_store_id,
697 int64_t index_id,
Evan Stadeca999b12025-05-09 19:09:11698 IndexedDBKeyRange key_range,
Steve Becker4848af32024-10-29 22:57:19699 blink::mojom::IDBGetAllResultType result_type,
avib7348942015-12-25 20:57:10700 int64_t max_count,
Steve Becker4848af32024-10-29 22:57:19701 blink::mojom::IDBCursorDirection direction,
Evan Stade4202b1f2024-06-12 16:10:54702 std::unique_ptr<GetAllResultSinkWrapper> result_sink,
Evan Stadecbb1e002024-09-13 20:06:57703 Transaction* transaction) {
704 TRACE_EVENT1("IndexedDB", "Database::GetAllOperation", "txn.id",
Pei Zhangc420ad72022-02-12 06:11:55705 transaction->id());
cmumfordb86405eb2015-05-14 14:50:39706
Joshua Bell60124c12020-01-28 23:10:37707 if (!IsObjectStoreIdAndMaybeIndexIdInMetadata(object_store_id, index_id)) {
Evan Stade4202b1f2024-06-12 16:10:54708 result_sink->Get()->OnError(CreateIDBErrorPtr(
Evan Stadedc38e9712023-07-14 02:04:54709 blink::mojom::IDBException::kUnknownError, "Bad request", transaction));
Mike Wasserman0d5da522024-09-27 07:47:03710 return Status::InvalidArgument("Invalid object_store_id.");
Daniel Murphy0240b782019-08-14 21:15:09711 }
712
cmumfordca3da5232015-05-15 23:09:34713 DCHECK_GT(max_count, 0);
cmumfordb86405eb2015-05-14 14:50:39714
cmumfordb86405eb2015-05-14 14:50:39715 const IndexedDBObjectStoreMetadata& object_store_metadata =
Evan Stadecaa7d182025-04-30 17:42:12716 GetObjectStoreMetadata(object_store_id);
cmumfordb86405eb2015-05-14 14:50:39717
Evan Stadea496adc2025-05-21 21:42:43718 StatusOr<std::unique_ptr<BackingStore::Cursor>> cursor;
cmumford75696ff62015-05-19 22:21:57719
Steve Becker4848af32024-10-29 22:57:19720 if (result_type == blink::mojom::IDBGetAllResultType::Keys) {
cmumford5065ab72015-06-03 00:51:49721 // Retrieving keys
722 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
723 // Object Store: Key Retrieval Operation
Evan Staded9529ea52025-04-11 17:02:50724 cursor = transaction->BackingStoreTransaction()->OpenObjectStoreKeyCursor(
Evan Stadeca999b12025-05-09 19:09:11725 object_store_id, key_range, direction);
cmumford5065ab72015-06-03 00:51:49726 } else {
727 // Index Value: (Primary Key) Retrieval Operation
Evan Staded9529ea52025-04-11 17:02:50728 cursor = transaction->BackingStoreTransaction()->OpenIndexKeyCursor(
Evan Stadeca999b12025-05-09 19:09:11729 object_store_id, index_id, key_range, direction);
cmumford5065ab72015-06-03 00:51:49730 }
cmumford75696ff62015-05-19 22:21:57731 } else {
cmumford5065ab72015-06-03 00:51:49732 // Retrieving values
733 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
734 // Object Store: Value Retrieval Operation
Evan Staded9529ea52025-04-11 17:02:50735 cursor = transaction->BackingStoreTransaction()->OpenObjectStoreCursor(
Evan Stadeca999b12025-05-09 19:09:11736 object_store_id, key_range, direction);
cmumford5065ab72015-06-03 00:51:49737 } else {
738 // Object Store: Referenced Value Retrieval Operation
Evan Staded9529ea52025-04-11 17:02:50739 cursor = transaction->BackingStoreTransaction()->OpenIndexCursor(
Evan Stadeca999b12025-05-09 19:09:11740 object_store_id, index_id, key_range, direction);
cmumford5065ab72015-06-03 00:51:49741 }
cmumford75696ff62015-05-19 22:21:57742 }
cmumfordb86405eb2015-05-14 14:50:39743
Evan Stade2563f3e2025-05-08 07:46:03744 if (!cursor.has_value()) {
745 DLOG(ERROR) << "Unable to open cursor operation: "
746 << cursor.error().ToString();
Evan Stade4202b1f2024-06-12 16:10:54747 result_sink->Get()->OnError(CreateIDBErrorPtr(
Evan Stadedc38e9712023-07-14 02:04:54748 blink::mojom::IDBException::kUnknownError,
749 "Corruption detected, unable to continue", transaction));
Evan Stade2563f3e2025-05-08 07:46:03750 return cursor.error();
cmumfordb86405eb2015-05-14 14:50:39751 }
752
Steve Becker4848af32024-10-29 22:57:19753 std::vector<blink::mojom::IDBRecordPtr> found_records;
Evan Stade4202b1f2024-06-12 16:10:54754
Steve Becker4848af32024-10-29 22:57:19755 auto send_records = [&](bool done) {
756 result_sink->Get()->ReceiveResults(std::move(found_records), done);
757 found_records.clear();
Evan Stade4202b1f2024-06-12 16:10:54758 };
759
Steve Becker4848af32024-10-29 22:57:19760 // No records found.
Evan Stade2563f3e2025-05-08 07:46:03761 if (!*cursor) {
Steve Becker4848af32024-10-29 22:57:19762 send_records(/*done=*/true);
Evan Stade2563f3e2025-05-08 07:46:03763 return Status::OK();
cmumfordb86405eb2015-05-14 14:50:39764 }
765
766 bool did_first_seek = false;
cmumfordb86405eb2015-05-14 14:50:39767
Adrienne Walker7c30dd62020-07-27 11:00:06768 // Max idbvalue size before blob wrapping is 64k, so make an assumption
769 // that max key/value size is 128kb tops, to fit under 128mb mojo limit.
770 // This value is just a heuristic and is an attempt to make sure that
771 // GetAll fits under the message limit size.
772 static_assert(
773 blink::mojom::kIDBMaxMessageSize >
774 blink::mojom::kIDBGetAllChunkSize * blink::mojom::kIDBWrapThreshold,
775 "Chunk heuristic too large");
776
777 const size_t max_values_before_sending = blink::mojom::kIDBGetAllChunkSize;
avib7348942015-12-25 20:57:10778 int64_t num_found_items = 0;
cmumford38073632015-05-29 18:40:09779 while (num_found_items++ < max_count) {
Evan Stade5dd95ec22025-06-10 23:47:26780 StatusOr<bool> cursor_valid = true;
cmumfordb86405eb2015-05-14 14:50:39781 if (did_first_seek) {
Evan Stade5dd95ec22025-06-10 23:47:26782 cursor_valid = (*cursor)->Continue();
cmumfordb86405eb2015-05-14 14:50:39783 } else {
Steve Becker4848af32024-10-29 22:57:19784 // Cursor creation performs the first seek, returning a nullptr cursor
785 // when invalid.
cmumfordb86405eb2015-05-14 14:50:39786 did_first_seek = true;
787 }
Evan Stade5dd95ec22025-06-10 23:47:26788 if (!cursor_valid.has_value()) {
789 result_sink->Get()->OnError(
790 CreateIDBErrorPtr(blink::mojom::IDBException::kUnknownError,
791 "Seek failure, unable to continue", transaction));
792 return cursor_valid.error();
793 }
cmumfordb86405eb2015-05-14 14:50:39794
Evan Stade5dd95ec22025-06-10 23:47:26795 if (!cursor_valid.value()) {
cmumfordb86405eb2015-05-14 14:50:39796 break;
Evan Stade4202b1f2024-06-12 16:10:54797 }
cmumfordb86405eb2015-05-14 14:50:39798
Steve Becker4848af32024-10-29 22:57:19799 blink::mojom::IDBRecordPtr return_record;
cmumfordb86405eb2015-05-14 14:50:39800
Steve Becker4848af32024-10-29 22:57:19801 if (result_type == blink::mojom::IDBGetAllResultType::Keys) {
Evan Stadeca999b12025-05-09 19:09:11802 return_record =
803 blink::mojom::IDBRecord::New((*cursor)->GetPrimaryKey().Clone(),
804 /*value=*/nullptr,
805 /*index_key=*/std::nullopt);
Steve Becker4848af32024-10-29 22:57:19806 } else if (result_type == blink::mojom::IDBGetAllResultType::Values) {
807 blink::mojom::IDBReturnValuePtr return_value =
Evan Stade822bdb42025-08-07 18:37:41808 ExtractReturnValueFromCursorValue(*transaction, object_store_metadata,
809 **cursor);
Steve Becker4848af32024-10-29 22:57:19810 return_record = blink::mojom::IDBRecord::New(
811 /*primary_key=*/std::nullopt, std::move(return_value),
812 /*index_key=*/std::nullopt);
813 } else if (result_type == blink::mojom::IDBGetAllResultType::Records) {
814 // Construct the record, which includes the primary key, value and index
815 // key.
816 blink::mojom::IDBReturnValuePtr return_value =
Evan Stade822bdb42025-08-07 18:37:41817 ExtractReturnValueFromCursorValue(*transaction, object_store_metadata,
818 **cursor);
Steve Becker4848af32024-10-29 22:57:19819 std::optional<IndexedDBKey> index_key;
820 if (index_id != IndexedDBIndexMetadata::kInvalidId) {
821 // The index key only exists for `IDBIndex::getAllRecords()`.
Evan Stadeca999b12025-05-09 19:09:11822 index_key = (*cursor)->GetKey().Clone();
cmumford38073632015-05-29 18:40:09823 }
Evan Stadeca999b12025-05-09 19:09:11824 return_record = blink::mojom::IDBRecord::New(
825 (*cursor)->GetPrimaryKey().Clone(), std::move(return_value),
826 std::move(index_key));
Evan Stade6265dcd2024-02-27 20:50:04827 } else {
Steve Becker4848af32024-10-29 22:57:19828 NOTREACHED();
Evan Stade6265dcd2024-02-27 20:50:04829 }
Adrienne Walker7c30dd62020-07-27 11:00:06830
Evan Stadeca999b12025-05-09 19:09:11831 found_records.emplace_back(std::move(return_record));
Steve Becker4848af32024-10-29 22:57:19832
833 // Periodically stream records if we have too many.
834 if (found_records.size() >= max_values_before_sending) {
835 send_records(/*done=*/false);
Adrienne Walker7c30dd62020-07-27 11:00:06836 }
cmumford38073632015-05-29 18:40:09837 }
Steve Becker4848af32024-10-29 22:57:19838 send_records(/*done=*/true);
Evan Stade2563f3e2025-05-08 07:46:03839 return Status::OK();
cmumfordb86405eb2015-05-14 14:50:39840}
841
Evan Stadecbb1e002024-09-13 20:06:57842Status Database::OpenCursorOperation(
dcheng531cca92016-04-09 03:03:23843 std::unique_ptr<OpenCursorOperationParams> params,
Ari Chivukula65a987e2022-04-26 19:04:12844 const storage::BucketLocator& bucket_locator,
Evan Stadecbb1e002024-09-13 20:06:57845 Transaction* transaction) {
846 TRACE_EVENT1("IndexedDB", "Database::OpenCursorOperation", "txn.id",
Pei Zhangc420ad72022-02-12 06:11:55847 transaction->id());
[email protected]72a4183d2013-05-31 18:33:10848
Daniel Murphy0240b782019-08-14 21:15:09849 if (!IsObjectStoreIdAndMaybeIndexIdInMetadata(params->object_store_id,
850 params->index_id)) {
Mike Wasserman0d5da522024-09-27 07:47:03851 return Status::InvalidArgument("Invalid object_store_id and/or index_id.");
Daniel Murphy0240b782019-08-14 21:15:09852 }
853
[email protected]72a4183d2013-05-31 18:33:10854 // The frontend has begun indexing, so this pauses the transaction
855 // until the indexing is complete. This can't happen any earlier
856 // because we don't want to switch to early mode in case multiple
857 // indexes are being created in a row, with Put()'s in between.
Evan Stade4202b1f2024-06-12 16:10:54858 if (params->task_type == blink::mojom::IDBTaskType::Preemptive) {
[email protected]72a4183d2013-05-31 18:33:10859 transaction->AddPreemptiveEvent();
Evan Stade4202b1f2024-06-12 16:10:54860 }
[email protected]72a4183d2013-05-31 18:33:10861
Evan Stadea496adc2025-05-21 21:42:43862 StatusOr<std::unique_ptr<BackingStore::Cursor>> backing_store_cursor;
[email protected]65880a82013-08-16 21:30:08863 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
Evan Stadecbb1e002024-09-13 20:06:57864 if (params->cursor_type == CursorType::kKeyOnly) {
Chase Phillipsb2851f322018-11-16 00:27:48865 DCHECK_EQ(params->task_type, blink::mojom::IDBTaskType::Normal);
Evan Staded9529ea52025-04-11 17:02:50866 backing_store_cursor =
867 transaction->BackingStoreTransaction()->OpenObjectStoreKeyCursor(
Evan Stadeca999b12025-05-09 19:09:11868 params->object_store_id, params->key_range, params->direction);
[email protected]fe4aa9dc2013-08-27 03:28:57869 } else {
Evan Staded9529ea52025-04-11 17:02:50870 backing_store_cursor =
871 transaction->BackingStoreTransaction()->OpenObjectStoreCursor(
Evan Stadeca999b12025-05-09 19:09:11872 params->object_store_id, params->key_range, params->direction);
[email protected]fe4aa9dc2013-08-27 03:28:57873 }
[email protected]72a4183d2013-05-31 18:33:10874 } else {
Chase Phillipsb2851f322018-11-16 00:27:48875 DCHECK_EQ(params->task_type, blink::mojom::IDBTaskType::Normal);
Evan Stadecbb1e002024-09-13 20:06:57876 if (params->cursor_type == CursorType::kKeyOnly) {
Evan Staded9529ea52025-04-11 17:02:50877 backing_store_cursor =
878 transaction->BackingStoreTransaction()->OpenIndexKeyCursor(
Evan Stadeca999b12025-05-09 19:09:11879 params->object_store_id, params->index_id, params->key_range,
Evan Stade2563f3e2025-05-08 07:46:03880 params->direction);
[email protected]72a4183d2013-05-31 18:33:10881 } else {
Evan Staded9529ea52025-04-11 17:02:50882 backing_store_cursor =
883 transaction->BackingStoreTransaction()->OpenIndexCursor(
Evan Stadeca999b12025-05-09 19:09:11884 params->object_store_id, params->index_id, params->key_range,
Evan Stade2563f3e2025-05-08 07:46:03885 params->direction);
[email protected]b05831e2014-04-16 18:32:19886 }
887 }
888
Evan Stade2563f3e2025-05-08 07:46:03889 if (!backing_store_cursor.has_value()) {
890 DLOG(ERROR) << "Unable to open cursor operation: "
891 << backing_store_cursor.error().ToString();
892 return backing_store_cursor.error();
[email protected]72a4183d2013-05-31 18:33:10893 }
894
Evan Stade2563f3e2025-05-08 07:46:03895 if (!*backing_store_cursor) {
dmurph50ab051b32016-11-29 22:13:30896 // Occurs when we've reached the end of cursor's data.
Chase Phillips46d3cdb2019-07-29 17:50:14897 std::move(params->callback)
898 .Run(blink::mojom::IDBDatabaseOpenCursorResult::NewEmpty(true));
Evan Stade2563f3e2025-05-08 07:46:03899 return Status::OK();
[email protected]72a4183d2013-05-31 18:33:10900 }
901
Evan Stadeb568dbdc2023-08-10 05:21:24902 mojo::PendingAssociatedRemote<blink::mojom::IDBCursor> pending_remote;
Evan Stadecbb1e002024-09-13 20:06:57903 Cursor* cursor = Cursor::CreateAndBind(
Evan Stade2563f3e2025-05-08 07:46:03904 std::move(*backing_store_cursor), params->cursor_type, params->task_type,
Evan Stade6584a492023-09-25 21:02:30905 transaction->AsWeakPtr(), pending_remote);
Evan Stadeb568dbdc2023-08-10 05:21:24906 transaction->RegisterOpenCursor(cursor);
Chase Phillips46d3cdb2019-07-29 17:50:14907
Chase Phillips46d3cdb2019-07-29 17:50:14908 blink::mojom::IDBValuePtr mojo_value;
Evan Stadeb568dbdc2023-08-10 05:21:24909 if (cursor->Value()) {
Evan Stade822bdb42025-08-07 18:37:41910 mojo_value = transaction->BuildMojoValue(std::move(*cursor->Value()));
Marijn Kruisselbrink145f7592020-02-18 08:49:31911 }
Chase Phillips46d3cdb2019-07-29 17:50:14912
Chase Phillips46d3cdb2019-07-29 17:50:14913 std::move(params->callback)
914 .Run(blink::mojom::IDBDatabaseOpenCursorResult::NewValue(
915 blink::mojom::IDBDatabaseOpenCursorValue::New(
Evan Stadeca999b12025-05-09 19:09:11916 std::move(pending_remote), cursor->key().Clone(),
917 cursor->primary_key().Clone(), std::move(mojo_value))));
Evan Stade2563f3e2025-05-08 07:46:03918 return Status::OK();
[email protected]72a4183d2013-05-31 18:33:10919}
920
Evan Stadecbb1e002024-09-13 20:06:57921Status Database::CountOperation(
avib7348942015-12-25 20:57:10922 int64_t object_store_id,
923 int64_t index_id,
Evan Stadeca999b12025-05-09 19:09:11924 IndexedDBKeyRange key_range,
Evan Stadeb7ecb0c2023-07-05 19:22:58925 blink::mojom::IDBDatabase::CountCallback callback,
Evan Stadecbb1e002024-09-13 20:06:57926 Transaction* transaction) {
927 TRACE_EVENT1("IndexedDB", "Database::CountOperation", "txn.id",
Pei Zhangc420ad72022-02-12 06:11:55928 transaction->id());
Daniel Murphy0240b782019-08-14 21:15:09929
Evan Stadeb7ecb0c2023-07-05 19:22:58930 if (!IsObjectStoreIdAndMaybeIndexIdInMetadata(object_store_id, index_id)) {
Mike Wasserman0d5da522024-09-27 07:47:03931 return Status::InvalidArgument("Invalid object_store_id and/or index_id.");
Evan Stadeb7ecb0c2023-07-05 19:22:58932 }
Daniel Murphy0240b782019-08-14 21:15:09933
Abhishek Shanthkumar9b509ca2025-05-29 15:41:04934 uint32_t count = -1;
[email protected]65880a82013-08-16 21:30:08935 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
Abhishek Shanthkumar9b509ca2025-05-29 15:41:04936 ASSIGN_OR_RETURN(
937 count, transaction->BackingStoreTransaction()->GetObjectStoreKeyCount(
938 object_store_id, std::move(key_range)));
939
[email protected]72a4183d2013-05-31 18:33:10940 } else {
Abhishek Shanthkumar9b509ca2025-05-29 15:41:04941 ASSIGN_OR_RETURN(count,
942 transaction->BackingStoreTransaction()->GetIndexKeyCount(
943 object_store_id, index_id, std::move(key_range)));
Evan Stade2563f3e2025-05-08 07:46:03944 }
Evan Stadeb7ecb0c2023-07-05 19:22:58945 std::move(callback).Run(/*success=*/true, count);
Evan Stade2563f3e2025-05-08 07:46:03946 return Status::OK();
[email protected]72a4183d2013-05-31 18:33:10947}
948
Evan Stadecbb1e002024-09-13 20:06:57949Status Database::DeleteRangeOperation(
avib7348942015-12-25 20:57:10950 int64_t object_store_id,
Evan Stadeca999b12025-05-09 19:09:11951 IndexedDBKeyRange key_range,
Evan Stadead03d762023-06-01 16:37:58952 blink::mojom::IDBDatabase::DeleteRangeCallback success_callback,
Evan Stadecbb1e002024-09-13 20:06:57953 Transaction* transaction) {
954 TRACE_EVENT1("IndexedDB", "Database::DeleteRangeOperation", "txn.id",
Pei Zhangc420ad72022-02-12 06:11:55955 transaction->id());
Daniel Murphy0240b782019-08-14 21:15:09956
Evan Stadead03d762023-06-01 16:37:58957 Status s;
958 if (IsObjectStoreIdInMetadata(object_store_id)) {
Evan Stade2fbd4532025-04-30 10:19:22959 s = transaction->BackingStoreTransaction()->DeleteRange(object_store_id,
Evan Stadeca999b12025-05-09 19:09:11960 key_range);
Evan Stadead03d762023-06-01 16:37:58961 } else {
Mike Wasserman0d5da522024-09-27 07:47:03962 s = Status::InvalidArgument("Invalid object_store_id.");
Evan Stadead03d762023-06-01 16:37:58963 }
964 if (s.ok()) {
Evan Stadecaa7d182025-04-30 17:42:12965 const IndexedDBObjectStoreMetadata& object_store_metadata =
966 GetObjectStoreMetadata(object_store_id);
Evan Stadea35e2722023-09-05 22:53:42967 bucket_context_->delegate().on_content_changed.Run(
Evan Stadecaa7d182025-04-30 17:42:12968 metadata().name, object_store_metadata.name);
Evan Stadead03d762023-06-01 16:37:58969 }
970 std::move(success_callback).Run(s.ok());
dmurph50ab051b32016-11-29 22:13:30971 return s;
[email protected]72a4183d2013-05-31 18:33:10972}
973
Evan Stadecbb1e002024-09-13 20:06:57974Status Database::GetKeyGeneratorCurrentNumberOperation(
Harley Li20add692019-02-15 22:54:12975 int64_t object_store_id,
Evan Stadedc38e9712023-07-14 02:04:54976 blink::mojom::IDBDatabase::GetKeyGeneratorCurrentNumberCallback callback,
Evan Stadecbb1e002024-09-13 20:06:57977 Transaction* transaction) {
Daniel Murphy0240b782019-08-14 21:15:09978 if (!IsObjectStoreIdInMetadata(object_store_id)) {
Evan Stadedc38e9712023-07-14 02:04:54979 std::move(callback).Run(
980 -1, CreateIDBErrorPtr(blink::mojom::IDBException::kDataError,
981 "Object store id not valid.", transaction));
Mike Wasserman0d5da522024-09-27 07:47:03982 return Status::InvalidArgument("Invalid object_store_id.");
Daniel Murphy0240b782019-08-14 21:15:09983 }
984
Evan Stade027d5d52025-05-21 23:04:32985 ASSIGN_OR_RETURN(
986 int64_t current_number,
Evan Stadece09c4c2025-04-18 17:23:27987 transaction->BackingStoreTransaction()->GetKeyGeneratorCurrentNumber(
Evan Stade027d5d52025-05-21 23:04:32988 object_store_id),
989 [&callback, transaction](const Status& status) {
990 std::move(callback).Run(
991 -1, CreateIDBErrorPtr(
992 blink::mojom::IDBException::kDataError,
993 "Failed to get the current number of key generator.",
994 transaction));
995 return status;
996 });
997
Evan Stadedc38e9712023-07-14 02:04:54998 std::move(callback).Run(current_number, nullptr);
Evan Stade027d5d52025-05-21 23:04:32999 return Status::OK();
Harley Li20add692019-02-15 22:54:121000}
1001
Evan Stadecbb1e002024-09-13 20:06:571002Status Database::ClearOperation(
avib7348942015-12-25 20:57:101003 int64_t object_store_id,
Evan Stadec5b90f02023-05-23 20:25:071004 blink::mojom::IDBDatabase::ClearCallback success_callback,
Evan Stadecbb1e002024-09-13 20:06:571005 Transaction* transaction) {
1006 TRACE_EVENT1("IndexedDB", "Database::ClearOperation", "txn.id",
Pei Zhangc420ad72022-02-12 06:11:551007 transaction->id());
Mike Wasserman0d5da522024-09-27 07:47:031008 Status s = Status::InvalidArgument("Invalid object_store_id.");
Evan Stadec5b90f02023-05-23 20:25:071009 if (IsObjectStoreIdInMetadata(object_store_id)) {
Evan Stadece09c4c2025-04-18 17:23:271010 s = transaction->BackingStoreTransaction()->ClearObjectStore(
Evan Stade2fbd4532025-04-30 10:19:221011 object_store_id);
Evan Stadec5b90f02023-05-23 20:25:071012 }
1013 if (s.ok()) {
Evan Stadecaa7d182025-04-30 17:42:121014 const IndexedDBObjectStoreMetadata& object_store_metadata =
1015 GetObjectStoreMetadata(object_store_id);
Evan Stadea35e2722023-09-05 22:53:421016 bucket_context_->delegate().on_content_changed.Run(
Evan Stadecaa7d182025-04-30 17:42:121017 name_, object_store_metadata.name);
Evan Stadec5b90f02023-05-23 20:25:071018 }
1019 std::move(success_callback).Run(s.ok());
dmurph50ab051b32016-11-29 22:13:301020 return s;
[email protected]72a4183d2013-05-31 18:33:101021}
1022
Evan Stadecbb1e002024-09-13 20:06:571023bool Database::IsObjectStoreIdInMetadata(int64_t object_store_id) const {
Evan Stade2fbd4532025-04-30 10:19:221024 if (!base::Contains(metadata().object_stores, object_store_id)) {
Daniel Murphy0240b782019-08-14 21:15:091025 DLOG(ERROR) << "Invalid object_store_id";
1026 return false;
1027 }
1028 return true;
1029}
1030
Evan Stadecbb1e002024-09-13 20:06:571031bool Database::IsObjectStoreIdAndMaybeIndexIdInMetadata(
Daniel Murphy0240b782019-08-14 21:15:091032 int64_t object_store_id,
1033 int64_t index_id) const {
Evan Stade4202b1f2024-06-12 16:10:541034 if (!IsObjectStoreIdInMetadata(object_store_id)) {
Daniel Murphy0240b782019-08-14 21:15:091035 return false;
Evan Stade4202b1f2024-06-12 16:10:541036 }
Daniel Murphy0240b782019-08-14 21:15:091037 const IndexedDBObjectStoreMetadata& object_store_metadata =
Evan Stadecaa7d182025-04-30 17:42:121038 GetObjectStoreMetadata(object_store_id);
Daniel Murphy0240b782019-08-14 21:15:091039 if (index_id != IndexedDBIndexMetadata::kInvalidId &&
1040 !base::Contains(object_store_metadata.indexes, index_id)) {
1041 DLOG(ERROR) << "Invalid index_id";
1042 return false;
1043 }
1044 return true;
1045}
1046
Evan Stadecbb1e002024-09-13 20:06:571047storage::mojom::IdbDatabaseMetadataPtr Database::GetIdbInternalsMetadata()
1048 const {
Brad Triebwasserfe45f3f2024-06-03 19:20:291049 storage::mojom::IdbDatabaseMetadataPtr info =
1050 storage::mojom::IdbDatabaseMetadata::New();
1051 info->name = name();
1052 info->connection_count = ConnectionCount();
1053 info->active_open_delete = ActiveOpenDeleteCount();
1054 info->pending_open_delete = PendingOpenDeleteCount();
Evan Stadecbb1e002024-09-13 20:06:571055 for (const Connection* connection : connections()) {
Brad Triebwasserfe45f3f2024-06-03 19:20:291056 for (const auto& [_, transaction] : connection->transactions()) {
1057 info->transactions.push_back(transaction->GetIdbInternalsMetadata());
1058 }
1059 }
1060 return info;
1061}
1062
Evan Stadecbb1e002024-09-13 20:06:571063void Database::NotifyOfIdbInternalsRelevantChange() {
Brad Triebwasser88b84af2024-06-04 19:36:551064 // This metadata is included in the context metadata, so call up the chain.
1065 bucket_context_->NotifyOfIdbInternalsRelevantChange();
1066}
1067
Daniel Murphy48b6b3a2019-08-07 20:38:251068// kIDBMaxMessageSize is defined based on the original
Tom Sepez36c46c02025-07-11 21:43:561069// IPC::mojom::kChannelMaximumMessageSize value. We use kIDBMaxMessageSize to
Evan Stade4202b1f2024-06-12 16:10:541070// limit the size of arguments we pass into our Mojo calls. We want to ensure
1071// this value is always no bigger than the current kMaximumMessageSize value
1072// which also ensures it is always no bigger than the current Mojo message
1073// size limit.
Daniel Murphy48b6b3a2019-08-07 20:38:251074static_assert(
Tom Sepez36c46c02025-07-11 21:43:561075 blink::mojom::kIDBMaxMessageSize <= IPC::mojom::kChannelMaximumMessageSize,
1076 "kIDBMaxMessageSize is bigger than IPC::mojom::kChannelMaximumMessageSize");
[email protected]12c337722014-05-22 19:42:421077
Evan Stadecbb1e002024-09-13 20:06:571078void Database::CallUpgradeTransactionStartedForTesting(int64_t old_version) {
Daniel Murphyede91592019-08-12 19:50:221079 connection_coordinator_.OnUpgradeTransactionStarted(old_version);
[email protected]72a4183d2013-05-31 18:33:101080}
1081
Evan Stadecbb1e002024-09-13 20:06:571082Status Database::OpenInternal() {
Evan Stade2fbd4532025-04-30 10:19:221083 auto result = backing_store()->CreateOrOpenDatabase(name_);
1084 if (result.has_value()) {
1085 backing_store_db_ = std::move(result.value());
1086 return Status::OK();
Evan Stade4202b1f2024-06-12 16:10:541087 }
Evan Stade2fbd4532025-04-30 10:19:221088 return result.error();
[email protected]72a4183d2013-05-31 18:33:101089}
1090
Evan Stadecbb1e002024-09-13 20:06:571091std::unique_ptr<Connection> Database::CreateConnection(
1092 std::unique_ptr<DatabaseCallbacks> database_callbacks,
Evan Stade9f5e401c2024-01-04 23:58:351093 mojo::Remote<storage::mojom::IndexedDBClientStateChecker>
Evan Stade2db7aa72024-02-01 17:07:271094 client_state_checker,
Evan Stade8e75d402024-09-04 00:42:431095 base::UnguessableToken client_token,
1096 int scheduling_priority) {
Evan Stadecbb1e002024-09-13 20:06:571097 auto connection = std::make_unique<Connection>(
Evan Stade6a314fe2023-09-25 21:24:121098 *bucket_context_, weak_factory_.GetWeakPtr(),
Evan Stadecbb1e002024-09-13 20:06:571099 base::BindRepeating(&Database::VersionChangeIgnored,
Evan Stade6a314fe2023-09-25 21:24:121100 weak_factory_.GetWeakPtr()),
Evan Stadecbb1e002024-09-13 20:06:571101 base::BindOnce(&Database::ConnectionClosed, weak_factory_.GetWeakPtr()),
Evan Stade2db7aa72024-02-01 17:07:271102 std::move(database_callbacks), std::move(client_state_checker),
Evan Stade8e75d402024-09-04 00:42:431103 client_token, scheduling_priority);
Daniel Murphy7c750432019-08-20 01:39:471104 connections_.insert(connection.get());
Evan Stade9dc36392024-10-29 13:10:391105 bucket_context_->OnConnectionPriorityUpdated();
Daniel Murphy48b6b3a2019-08-07 20:38:251106 return connection;
[email protected]72a4183d2013-05-31 18:33:101107}
1108
Evan Stadecbb1e002024-09-13 20:06:571109void Database::VersionChangeIgnored() {
Daniel Murphyede91592019-08-12 19:50:221110 connection_coordinator_.OnVersionChangeIgnored();
Daniel Murphy48b6b3a2019-08-07 20:38:251111}
1112
Evan Stadecbb1e002024-09-13 20:06:571113bool Database::HasNoConnections() const {
Daniel Murphy7c750432019-08-20 01:39:471114 return force_closing_ || connections().empty();
Daniel Murphy48b6b3a2019-08-07 20:38:251115}
1116
Evan Stadecbb1e002024-09-13 20:06:571117void Database::SendVersionChangeToAllConnections(int64_t old_version,
1118 int64_t new_version) {
Evan Stade4202b1f2024-06-12 16:10:541119 if (force_closing_) {
Daniel Murphy48b6b3a2019-08-07 20:38:251120 return;
Evan Stade4202b1f2024-06-12 16:10:541121 }
Mingyu Lei85c2caf2022-12-16 09:46:391122 for (auto* connection : connections()) {
Evan Stadecbb1e002024-09-13 20:06:571123 // Before invoking this method, the `ConnectionCoordinator` had
Evan Stade4202b1f2024-06-12 16:10:541124 // set the request state to `kPendingNoConnections`. Now the request will
1125 // be blocked until all the existing connections to this database is
1126 // closed. There are three possible ways for the connection to be closed:
1127 // 1. If the client is already pending close, then the `VersionChange`
1128 // event will be ignored and the open request will be deemed blocked until
1129 // the pending close completes.
Mingyu Lei85c2caf2022-12-16 09:46:391130 // 2. If the client is active, the `VersionChange` event will be enqueued
Evan Stade4202b1f2024-06-12 16:10:541131 // and the registered event listener will be fired asynchronously. The
1132 // event listener should be responsible for actively closing the IndexedDB
Mingyu Lei85c2caf2022-12-16 09:46:391133 // connection. The document won't be eligible for BFCache before the
1134 // connection is closed if it receives the `versionchange` event.
1135 // 3. While the above two cases rely on the `VersionChange` event to be
Evan Stade4202b1f2024-06-12 16:10:541136 // delivered to the renderer process, the third case happens purely from
1137 // the IndexedDB/browser context. If the client is inactive, the
1138 // `VersionChange` event will not be delivered, instead, a mojo call is
1139 // sent to the browser process to disallow the activation of the inactive
1140 // client, which will close the connection as part of the destruction. No
1141 // matter which path it follows, the `SendVersionChangeToAllConnections`
Mingyu Lei85c2caf2022-12-16 09:46:391142 // method is executed asynchronously.
Mingyu Leiac9bde42023-06-22 09:56:241143 connection->DisallowInactiveClient(
Evan Stade2db7aa72024-02-01 17:07:271144 storage::mojom::DisallowInactiveClientReason::kVersionChangeEvent,
Mingyu Leiac9bde42023-06-22 09:56:241145 base::BindOnce(
Evan Stadecbb1e002024-09-13 20:06:571146 [](base::WeakPtr<Connection> connection, int64_t old_version,
1147 int64_t new_version, bool was_client_active) {
Mingyu Leiac9bde42023-06-22 09:56:241148 if (connection && connection->IsConnected() &&
1149 was_client_active) {
1150 connection->callbacks()->OnVersionChange(old_version,
1151 new_version);
1152 }
1153 },
1154 connection->GetWeakPtr(), old_version, new_version));
Mingyu Lei85c2caf2022-12-16 09:46:391155 }
Daniel Murphy48b6b3a2019-08-07 20:38:251156}
1157
Evan Stadecbb1e002024-09-13 20:06:571158void Database::ConnectionClosed(Connection* connection) {
1159 TRACE_EVENT0("IndexedDB", "Database::ConnectionClosed");
Daniel Murphy7c750432019-08-20 01:39:471160 // Ignore connection closes during force close to prevent re-entry.
Evan Stade4202b1f2024-06-12 16:10:541161 if (force_closing_) {
Daniel Murphy7c750432019-08-20 01:39:471162 return;
Evan Stade4202b1f2024-06-12 16:10:541163 }
Daniel Murphy7c750432019-08-20 01:39:471164 connections_.erase(connection);
Evan Stade9dc36392024-10-29 13:10:391165 bucket_context_->OnConnectionPriorityUpdated();
Daniel Murphyede91592019-08-12 19:50:221166 connection_coordinator_.OnConnectionClosed(connection);
Evan Stade4202b1f2024-06-12 16:10:541167 if (connections_.empty()) {
Daniel Murphy7c750432019-08-20 01:39:471168 connection_coordinator_.OnNoConnections();
Evan Stade4202b1f2024-06-12 16:10:541169 }
1170 if (CanBeDestroyed()) {
Evan Stade0c0e6d652023-11-09 23:28:031171 bucket_context_->QueueRunTasks();
Evan Stade4202b1f2024-06-12 16:10:541172 }
Daniel Murphy7c750432019-08-20 01:39:471173}
1174
Evan Stadecbb1e002024-09-13 20:06:571175bool Database::CanBeDestroyed() {
Daniel Murphy7c750432019-08-20 01:39:471176 return !connection_coordinator_.HasTasks() && connections_.empty();
[email protected]75a854a2014-07-07 15:57:361177}
[email protected]72a4183d2013-05-31 18:33:101178
Evan Stadecaa7d182025-04-30 17:42:121179const IndexedDBObjectStoreMetadata& Database::GetObjectStoreMetadata(
1180 int64_t object_store_id) const {
1181 auto object_store_it = metadata().object_stores.find(object_store_id);
1182 DCHECK(object_store_it != metadata().object_stores.end());
1183 return object_store_it->second;
1184}
1185
Evan Stadecbb1e002024-09-13 20:06:571186} // namespace content::indexed_db