1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/indexed_db/indexed_db_database.h"
6
7 #include <math.h>
8 #include <algorithm>
9 #include <limits>
10 #include <set>
11 #include <utility>
12
13 #include "base/auto_reset.h"
14 #include "base/bind.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "base/memory/scoped_refptr.h"
18 #include "base/memory/weak_ptr.h"
19 #include "base/metrics/histogram_functions.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/numerics/safe_conversions.h"
22 #include "base/stl_util.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "components/services/storage/indexed_db/scopes/leveldb_scope.h"
26 #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h"
27 #include "components/services/storage/indexed_db/scopes/scope_lock.h"
28 #include "components/services/storage/indexed_db/scopes/scopes_lock_manager.h"
29 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
30 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h"
31 #include "content/browser/indexed_db/cursor_impl.h"
32 #include "content/browser/indexed_db/indexed_db_callback_helpers.h"
33 #include "content/browser/indexed_db/indexed_db_callbacks.h"
34 #include "content/browser/indexed_db/indexed_db_class_factory.h"
35 #include "content/browser/indexed_db/indexed_db_connection.h"
36 #include "content/browser/indexed_db/indexed_db_context_impl.h"
37 #include "content/browser/indexed_db/indexed_db_cursor.h"
38 #include "content/browser/indexed_db/indexed_db_external_object.h"
39 #include "content/browser/indexed_db/indexed_db_factory.h"
40 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
41 #include "content/browser/indexed_db/indexed_db_index_writer.h"
42 #include "content/browser/indexed_db/indexed_db_metadata_coding.h"
43 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
44 #include "content/browser/indexed_db/indexed_db_return_value.h"
45 #include "content/browser/indexed_db/indexed_db_tracing.h"
46 #include "content/browser/indexed_db/indexed_db_transaction.h"
47 #include "content/browser/indexed_db/indexed_db_value.h"
48 #include "content/browser/indexed_db/transaction_impl.h"
49 #include "ipc/ipc_channel.h"
50 #include "storage/browser/blob/blob_data_handle.h"
51 #include "third_party/blink/public/common/indexeddb/indexeddb_key_path.h"
52 #include "third_party/blink/public/common/indexeddb/indexeddb_key_range.h"
53 #include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
54 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
55 #include "third_party/leveldatabase/env_chromium.h"
56 #include "url/origin.h"
57
58 using blink::IndexedDBDatabaseMetadata;
59 using blink::IndexedDBIndexKeys;
60 using blink::IndexedDBIndexMetadata;
61 using blink::IndexedDBKey;
62 using blink::IndexedDBKeyPath;
63 using blink::IndexedDBKeyRange;
64 using blink::IndexedDBObjectStoreMetadata;
65 using leveldb::Status;
66
67 namespace content {
68 namespace {
69
CreateError(blink::mojom::IDBException code,const char * message,IndexedDBTransaction * transaction)70 IndexedDBDatabaseError CreateError(blink::mojom::IDBException code,
71 const char* message,
72 IndexedDBTransaction* transaction) {
73 transaction->IncrementNumErrorsSent();
74 return IndexedDBDatabaseError(code, message);
75 }
76
CreateError(blink::mojom::IDBException code,const base::string16 & message,IndexedDBTransaction * transaction)77 IndexedDBDatabaseError CreateError(blink::mojom::IDBException code,
78 const base::string16& message,
79 IndexedDBTransaction* transaction) {
80 transaction->IncrementNumErrorsSent();
81 return IndexedDBDatabaseError(code, message);
82 }
83
GenerateKey(IndexedDBBackingStore * backing_store,IndexedDBTransaction * transaction,int64_t database_id,int64_t object_store_id)84 std::unique_ptr<IndexedDBKey> GenerateKey(IndexedDBBackingStore* backing_store,
85 IndexedDBTransaction* transaction,
86 int64_t database_id,
87 int64_t object_store_id) {
88 // Maximum integer uniquely representable as ECMAScript number.
89 const int64_t max_generator_value = 9007199254740992LL;
90 int64_t current_number;
91 Status s = backing_store->GetKeyGeneratorCurrentNumber(
92 transaction->BackingStoreTransaction(), database_id, object_store_id,
93 ¤t_number);
94 if (!s.ok()) {
95 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
96 return std::make_unique<IndexedDBKey>();
97 }
98 if (current_number < 0 || current_number > max_generator_value)
99 return std::make_unique<IndexedDBKey>();
100
101 return std::make_unique<IndexedDBKey>(current_number,
102 blink::mojom::IDBKeyType::Number);
103 }
104
105 // Called at the end of a "put" operation. The key is a number that was either
106 // generated by the generator which now needs to be incremented (so
107 // |check_current| is false) or was user-supplied so we only conditionally use
108 // (and |check_current| is true).
UpdateKeyGenerator(IndexedDBBackingStore * backing_store,IndexedDBTransaction * transaction,int64_t database_id,int64_t object_store_id,const IndexedDBKey & key,bool check_current)109 Status UpdateKeyGenerator(IndexedDBBackingStore* backing_store,
110 IndexedDBTransaction* transaction,
111 int64_t database_id,
112 int64_t object_store_id,
113 const IndexedDBKey& key,
114 bool check_current) {
115 DCHECK_EQ(blink::mojom::IDBKeyType::Number, key.type());
116 // Maximum integer uniquely representable as ECMAScript number.
117 const double max_generator_value = 9007199254740992.0;
118 int64_t value = base::saturated_cast<int64_t>(
119 floor(std::min(key.number(), max_generator_value)));
120 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
121 transaction->BackingStoreTransaction(), database_id, object_store_id,
122 value + 1, check_current);
123 }
124
125 } // namespace
126
127 IndexedDBDatabase::PutOperationParams::PutOperationParams() = default;
128 IndexedDBDatabase::PutOperationParams::~PutOperationParams() = default;
129
130 IndexedDBDatabase::OpenCursorOperationParams::OpenCursorOperationParams() =
131 default;
132 IndexedDBDatabase::OpenCursorOperationParams::~OpenCursorOperationParams() =
133 default;
134
IndexedDBDatabase(const base::string16 & name,IndexedDBBackingStore * backing_store,IndexedDBFactory * factory,IndexedDBClassFactory * class_factory,TasksAvailableCallback tasks_available_callback,std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,const Identifier & unique_identifier,ScopesLockManager * transaction_lock_manager)135 IndexedDBDatabase::IndexedDBDatabase(
136 const base::string16& name,
137 IndexedDBBackingStore* backing_store,
138 IndexedDBFactory* factory,
139 IndexedDBClassFactory* class_factory,
140 TasksAvailableCallback tasks_available_callback,
141 std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,
142 const Identifier& unique_identifier,
143 ScopesLockManager* transaction_lock_manager)
144 : backing_store_(backing_store),
145 metadata_(name,
146 kInvalidId,
147 IndexedDBDatabaseMetadata::NO_VERSION,
148 kInvalidId),
149 identifier_(unique_identifier),
150 factory_(factory),
151 class_factory_(class_factory),
152 metadata_coding_(std::move(metadata_coding)),
153 lock_manager_(transaction_lock_manager),
154 tasks_available_callback_(tasks_available_callback),
155 connection_coordinator_(this, tasks_available_callback) {
156 DCHECK(factory != nullptr);
157 }
158
159 IndexedDBDatabase::~IndexedDBDatabase() = default;
160
RegisterAndScheduleTransaction(IndexedDBTransaction * transaction)161 void IndexedDBDatabase::RegisterAndScheduleTransaction(
162 IndexedDBTransaction* transaction) {
163 IDB_TRACE1("IndexedDBDatabase::RegisterAndScheduleTransaction", "txn.id",
164 transaction->id());
165 std::vector<ScopesLockManager::ScopeLockRequest> lock_requests;
166 lock_requests.reserve(1 + transaction->scope().size());
167 lock_requests.emplace_back(
168 kDatabaseRangeLockLevel, GetDatabaseLockRange(id()),
169 transaction->mode() == blink::mojom::IDBTransactionMode::VersionChange
170 ? ScopesLockManager::LockType::kExclusive
171 : ScopesLockManager::LockType::kShared);
172 ScopesLockManager::LockType lock_type =
173 transaction->mode() == blink::mojom::IDBTransactionMode::ReadOnly
174 ? ScopesLockManager::LockType::kShared
175 : ScopesLockManager::LockType::kExclusive;
176 for (int64_t object_store : transaction->scope()) {
177 lock_requests.emplace_back(kObjectStoreRangeLockLevel,
178 GetObjectStoreLockRange(id(), object_store),
179 lock_type);
180 }
181 lock_manager_->AcquireLocks(
182 std::move(lock_requests),
183 transaction->mutable_locks_receiver()->weak_factory.GetWeakPtr(),
184 base::BindOnce(&IndexedDBTransaction::Start, transaction->AsWeakPtr()));
185 }
186
187 std::tuple<IndexedDBDatabase::RunTasksResult, leveldb::Status>
RunTasks()188 IndexedDBDatabase::RunTasks() {
189 // First execute any pending tasks in the connection coordinator.
190 IndexedDBConnectionCoordinator::ExecuteTaskResult task_state;
191 leveldb::Status status;
192 do {
193 std::tie(task_state, status) =
194 connection_coordinator_.ExecuteTask(!connections_.empty());
195 } while (task_state ==
196 IndexedDBConnectionCoordinator::ExecuteTaskResult::kMoreTasks);
197
198 if (task_state == IndexedDBConnectionCoordinator::ExecuteTaskResult::kError)
199 return std::make_tuple(RunTasksResult::kError, status);
200
201 bool transactions_removed = true;
202
203 // Finally, execute transactions that have tasks & remove those that are
204 // complete.
205 while (transactions_removed) {
206 transactions_removed = false;
207 IndexedDBTransaction* finished_upgrade_transaction = nullptr;
208 bool upgrade_transaction_commmitted = false;
209 for (IndexedDBConnection* connection : connections_) {
210 std::vector<int64_t> txns_to_remove;
211 for (const auto& id_txn_pair : connection->transactions()) {
212 IndexedDBTransaction* txn = id_txn_pair.second.get();
213 // Determine if the transaction's task queue should be processed.
214 switch (txn->state()) {
215 case IndexedDBTransaction::FINISHED:
216 if (txn->mode() ==
217 blink::mojom::IDBTransactionMode::VersionChange) {
218 finished_upgrade_transaction = txn;
219 upgrade_transaction_commmitted = !txn->aborted();
220 }
221 txns_to_remove.push_back(id_txn_pair.first);
222 continue;
223 case IndexedDBTransaction::CREATED:
224 continue;
225 case IndexedDBTransaction::STARTED:
226 case IndexedDBTransaction::COMMITTING:
227 break;
228 }
229
230 // Process the queue for transactions that are STARTED or COMMITTING.
231 // Add transactions that can be removed to a queue.
232 IndexedDBTransaction::RunTasksResult task_result;
233 leveldb::Status transaction_status;
234 std::tie(task_result, transaction_status) = txn->RunTasks();
235 switch (task_result) {
236 case IndexedDBTransaction::RunTasksResult::kError:
237 return std::make_tuple(RunTasksResult::kError, transaction_status);
238 case IndexedDBTransaction::RunTasksResult::kCommitted:
239 case IndexedDBTransaction::RunTasksResult::kAborted:
240 if (txn->mode() ==
241 blink::mojom::IDBTransactionMode::VersionChange) {
242 DCHECK(!finished_upgrade_transaction);
243 finished_upgrade_transaction = txn;
244 upgrade_transaction_commmitted = !txn->aborted();
245 }
246 txns_to_remove.push_back(txn->id());
247 break;
248 case IndexedDBTransaction::RunTasksResult::kNotFinished:
249 continue;
250 }
251 }
252 // Do the removals.
253 for (int64_t id : txns_to_remove) {
254 connection->RemoveTransaction(id);
255 transactions_removed = true;
256 }
257 if (finished_upgrade_transaction) {
258 connection_coordinator_.OnUpgradeTransactionFinished(
259 upgrade_transaction_commmitted);
260 }
261 }
262 }
263 if (CanBeDestroyed())
264 return std::make_tuple(RunTasksResult::kCanBeDestroyed, leveldb::Status::OK());
265 return std::make_tuple(RunTasksResult::kDone, leveldb::Status::OK());
266 }
267
ForceCloseAndRunTasks()268 leveldb::Status IndexedDBDatabase::ForceCloseAndRunTasks() {
269 leveldb::Status status;
270 DCHECK(!force_closing_);
271 force_closing_ = true;
272 for (IndexedDBConnection* connection : connections_) {
273 leveldb::Status last_error = connection->CloseAndReportForceClose();
274 if (UNLIKELY(!last_error.ok())) {
275 base::UmaHistogramEnumeration(
276 "WebCore.IndexedDB.ErrorDuringForceCloseAborts",
277 leveldb_env::GetLevelDBStatusUMAValue(last_error),
278 leveldb_env::LEVELDB_STATUS_MAX);
279 }
280 }
281 connections_.clear();
282 leveldb::Status abort_status =
283 connection_coordinator_.PruneTasksForForceClose();
284 if (UNLIKELY(!abort_status.ok()))
285 return abort_status;
286 connection_coordinator_.OnNoConnections();
287
288 // Execute any pending tasks in the connection coordinator.
289 IndexedDBConnectionCoordinator::ExecuteTaskResult task_state;
290 do {
291 std::tie(task_state, status) = connection_coordinator_.ExecuteTask(false);
292 DCHECK(task_state !=
293 IndexedDBConnectionCoordinator::ExecuteTaskResult::kPendingAsyncWork)
294 << "There are no more connections, so all tasks should be able to "
295 "complete synchronously.";
296 } while (
297 task_state != IndexedDBConnectionCoordinator::ExecuteTaskResult::kDone &&
298 task_state != IndexedDBConnectionCoordinator::ExecuteTaskResult::kError);
299 DCHECK(connections_.empty());
300 force_closing_ = false;
301 if (CanBeDestroyed())
302 tasks_available_callback_.Run();
303 return status;
304 }
305
Commit(IndexedDBTransaction * transaction)306 void IndexedDBDatabase::Commit(IndexedDBTransaction* transaction) {
307 // The frontend suggests that we commit, but we may have previously initiated
308 // an abort, and so have disposed of the transaction. on_abort has already
309 // been dispatched to the frontend, so it will find out about that
310 // asynchronously.
311 if (transaction) {
312 transaction->SetCommitFlag();
313 }
314 }
315
TransactionCreated()316 void IndexedDBDatabase::TransactionCreated() {
317 UMA_HISTOGRAM_COUNTS_1000(
318 "WebCore.IndexedDB.Database.OutstandingTransactionCount",
319 transaction_count_);
320 ++transaction_count_;
321 }
322
TransactionFinished(blink::mojom::IDBTransactionMode mode,bool committed)323 void IndexedDBDatabase::TransactionFinished(
324 blink::mojom::IDBTransactionMode mode,
325 bool committed) {
326 --transaction_count_;
327 DCHECK_GE(transaction_count_, 0);
328
329 // TODO(dmurph): To help remove this integration with IndexedDBDatabase, make
330 // a 'committed' listener closure on all transactions. Then the request can
331 // just listen for that.
332
333 // This may be an unrelated transaction finishing while waiting for
334 // connections to close, or the actual upgrade transaction from an active
335 // request. Notify the active request if it's the latter.
336 if (mode == blink::mojom::IDBTransactionMode::VersionChange) {
337 connection_coordinator_.OnUpgradeTransactionFinished(committed);
338 }
339 }
340
AddPendingObserver(IndexedDBTransaction * transaction,int32_t observer_id,const IndexedDBObserver::Options & options)341 void IndexedDBDatabase::AddPendingObserver(
342 IndexedDBTransaction* transaction,
343 int32_t observer_id,
344 const IndexedDBObserver::Options& options) {
345 DCHECK(transaction);
346 transaction->AddPendingObserver(observer_id, options);
347 }
348
FilterObservation(IndexedDBTransaction * transaction,int64_t object_store_id,blink::mojom::IDBOperationType type,const IndexedDBKeyRange & key_range,const IndexedDBValue * value)349 void IndexedDBDatabase::FilterObservation(IndexedDBTransaction* transaction,
350 int64_t object_store_id,
351 blink::mojom::IDBOperationType type,
352 const IndexedDBKeyRange& key_range,
353 const IndexedDBValue* value) {
354 for (auto* connection : connections()) {
355 bool recorded = false;
356 for (const auto& observer : connection->active_observers()) {
357 if (!observer->IsRecordingType(type) ||
358 !observer->IsRecordingObjectStore(object_store_id))
359 continue;
360 if (!recorded) {
361 auto observation = blink::mojom::IDBObservation::New();
362 observation->object_store_id = object_store_id;
363 observation->type = type;
364 if (type != blink::mojom::IDBOperationType::Clear)
365 observation->key_range = key_range;
366 transaction->AddObservation(connection->id(), std::move(observation));
367 recorded = true;
368 }
369 blink::mojom::IDBObserverChangesPtr& changes =
370 *transaction->GetPendingChangesForConnection(connection->id());
371
372 changes->observation_index_map[observer->id()].push_back(
373 changes->observations.size() - 1);
374 if (value && observer->values() && !changes->observations.back()->value) {
375 // TODO(dmurph): Avoid any and all IndexedDBValue copies. Perhaps defer
376 // this until the end of the transaction, where we can safely erase the
377 // indexeddb value. crbug.com/682363
378 IndexedDBValue copy = *value;
379 changes->observations.back()->value =
380 IndexedDBValue::ConvertAndEraseValue(©);
381 }
382 }
383 }
384 }
385
SendObservations(std::map<int32_t,blink::mojom::IDBObserverChangesPtr> changes_map)386 void IndexedDBDatabase::SendObservations(
387 std::map<int32_t, blink::mojom::IDBObserverChangesPtr> changes_map) {
388 for (auto* conn : connections()) {
389 auto it = changes_map.find(conn->id());
390 if (it != changes_map.end())
391 conn->callbacks()->OnDatabaseChange(std::move(it->second));
392 }
393 }
394
ScheduleOpenConnection(IndexedDBOriginStateHandle origin_state_handle,std::unique_ptr<IndexedDBPendingConnection> connection)395 void IndexedDBDatabase::ScheduleOpenConnection(
396 IndexedDBOriginStateHandle origin_state_handle,
397 std::unique_ptr<IndexedDBPendingConnection> connection) {
398 connection_coordinator_.ScheduleOpenConnection(std::move(origin_state_handle),
399 std::move(connection));
400 }
401
ScheduleDeleteDatabase(IndexedDBOriginStateHandle origin_state_handle,scoped_refptr<IndexedDBCallbacks> callbacks,base::OnceClosure on_deletion_complete)402 void IndexedDBDatabase::ScheduleDeleteDatabase(
403 IndexedDBOriginStateHandle origin_state_handle,
404 scoped_refptr<IndexedDBCallbacks> callbacks,
405 base::OnceClosure on_deletion_complete) {
406 connection_coordinator_.ScheduleDeleteDatabase(
407 std::move(origin_state_handle), std::move(callbacks),
408 std::move(on_deletion_complete));
409 }
410
AddObjectStoreToMetadata(IndexedDBObjectStoreMetadata object_store,int64_t new_max_object_store_id)411 void IndexedDBDatabase::AddObjectStoreToMetadata(
412 IndexedDBObjectStoreMetadata object_store,
413 int64_t new_max_object_store_id) {
414 DCHECK(metadata_.object_stores.find(object_store.id) ==
415 metadata_.object_stores.end());
416 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
417 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
418 metadata_.max_object_store_id = new_max_object_store_id;
419 }
420 metadata_.object_stores[object_store.id] = std::move(object_store);
421 }
422
RemoveObjectStoreFromMetadata(int64_t object_store_id)423 IndexedDBObjectStoreMetadata IndexedDBDatabase::RemoveObjectStoreFromMetadata(
424 int64_t object_store_id) {
425 auto it = metadata_.object_stores.find(object_store_id);
426 CHECK(it != metadata_.object_stores.end());
427 IndexedDBObjectStoreMetadata metadata = std::move(it->second);
428 metadata_.object_stores.erase(it);
429 return metadata;
430 }
431
AddIndexToMetadata(int64_t object_store_id,IndexedDBIndexMetadata index,int64_t new_max_index_id)432 void IndexedDBDatabase::AddIndexToMetadata(int64_t object_store_id,
433 IndexedDBIndexMetadata index,
434 int64_t new_max_index_id) {
435 DCHECK(metadata_.object_stores.find(object_store_id) !=
436 metadata_.object_stores.end());
437 IndexedDBObjectStoreMetadata& object_store =
438 metadata_.object_stores[object_store_id];
439
440 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
441 object_store.indexes[index.id] = std::move(index);
442 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
443 DCHECK_LT(object_store.max_index_id, new_max_index_id);
444 object_store.max_index_id = new_max_index_id;
445 }
446 }
447
RemoveIndexFromMetadata(int64_t object_store_id,int64_t index_id)448 IndexedDBIndexMetadata IndexedDBDatabase::RemoveIndexFromMetadata(
449 int64_t object_store_id,
450 int64_t index_id) {
451 DCHECK(metadata_.object_stores.find(object_store_id) !=
452 metadata_.object_stores.end());
453 IndexedDBObjectStoreMetadata& object_store =
454 metadata_.object_stores[object_store_id];
455
456 auto it = object_store.indexes.find(index_id);
457 CHECK(it != object_store.indexes.end());
458 IndexedDBIndexMetadata metadata = std::move(it->second);
459 object_store.indexes.erase(it);
460 return metadata;
461 }
462
CreateObjectStoreOperation(int64_t object_store_id,const base::string16 & name,const IndexedDBKeyPath & key_path,bool auto_increment,IndexedDBTransaction * transaction)463 leveldb::Status IndexedDBDatabase::CreateObjectStoreOperation(
464 int64_t object_store_id,
465 const base::string16& name,
466 const IndexedDBKeyPath& key_path,
467 bool auto_increment,
468 IndexedDBTransaction* transaction) {
469 DCHECK(transaction);
470 IDB_TRACE1("IndexedDBDatabase::CreateObjectStoreOperation", "txn.id",
471 transaction->id());
472 DCHECK_EQ(transaction->mode(),
473 blink::mojom::IDBTransactionMode::VersionChange);
474
475 if (base::Contains(metadata_.object_stores, object_store_id))
476 return leveldb::Status::InvalidArgument("Invalid object_store_id");
477
478 IndexedDBObjectStoreMetadata object_store_metadata;
479 Status s = metadata_coding_->CreateObjectStore(
480 transaction->BackingStoreTransaction()->transaction(),
481 transaction->database()->id(), object_store_id, name, key_path,
482 auto_increment, &object_store_metadata);
483
484 if (!s.ok())
485 return s;
486
487 AddObjectStoreToMetadata(std::move(object_store_metadata), object_store_id);
488
489 transaction->ScheduleAbortTask(
490 base::BindOnce(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
491 AsWeakPtr(), object_store_id));
492 return Status::OK();
493 }
494
CreateObjectStoreAbortOperation(int64_t object_store_id)495 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
496 int64_t object_store_id) {
497 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
498 RemoveObjectStoreFromMetadata(object_store_id);
499 }
500
DeleteObjectStoreOperation(int64_t object_store_id,IndexedDBTransaction * transaction)501 Status IndexedDBDatabase::DeleteObjectStoreOperation(
502 int64_t object_store_id,
503 IndexedDBTransaction* transaction) {
504 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation", "txn.id",
505 transaction->id());
506 DCHECK_EQ(transaction->mode(),
507 blink::mojom::IDBTransactionMode::VersionChange);
508
509 if (!IsObjectStoreIdInMetadata(object_store_id))
510 return leveldb::Status::InvalidArgument("Invalid object_store_id.");
511
512 IndexedDBObjectStoreMetadata object_store_metadata =
513 RemoveObjectStoreFromMetadata(object_store_id);
514
515 // First remove metadata.
516 Status s = metadata_coding_->DeleteObjectStore(
517 transaction->BackingStoreTransaction()->transaction(),
518 transaction->database()->id(), object_store_metadata);
519
520 if (!s.ok()) {
521 AddObjectStoreToMetadata(std::move(object_store_metadata),
522 IndexedDBObjectStoreMetadata::kInvalidId);
523 return s;
524 }
525
526 // Then remove object store contents.
527 s = backing_store_->ClearObjectStore(transaction->BackingStoreTransaction(),
528 transaction->database()->id(),
529 object_store_id);
530
531 if (!s.ok()) {
532 AddObjectStoreToMetadata(std::move(object_store_metadata),
533 IndexedDBObjectStoreMetadata::kInvalidId);
534 return s;
535 }
536 transaction->ScheduleAbortTask(
537 base::BindOnce(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
538 AsWeakPtr(), std::move(object_store_metadata)));
539 return s;
540 }
541
DeleteObjectStoreAbortOperation(IndexedDBObjectStoreMetadata object_store_metadata)542 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
543 IndexedDBObjectStoreMetadata object_store_metadata) {
544 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
545 AddObjectStoreToMetadata(std::move(object_store_metadata),
546 IndexedDBObjectStoreMetadata::kInvalidId);
547 }
548
RenameObjectStoreOperation(int64_t object_store_id,const base::string16 & new_name,IndexedDBTransaction * transaction)549 leveldb::Status IndexedDBDatabase::RenameObjectStoreOperation(
550 int64_t object_store_id,
551 const base::string16& new_name,
552 IndexedDBTransaction* transaction) {
553 DCHECK(transaction);
554 IDB_TRACE1("IndexedDBDatabase::RenameObjectStore", "txn.id",
555 transaction->id());
556 DCHECK_EQ(transaction->mode(),
557 blink::mojom::IDBTransactionMode::VersionChange);
558
559 if (!IsObjectStoreIdInMetadata(object_store_id))
560 return leveldb::Status::InvalidArgument("Invalid object_store_id.");
561
562 // Store renaming is done synchronously, as it may be followed by
563 // index creation (also sync) since preemptive OpenCursor/SetIndexKeys
564 // may follow.
565 IndexedDBObjectStoreMetadata& object_store_metadata =
566 metadata_.object_stores[object_store_id];
567
568 base::string16 old_name;
569
570 Status s = metadata_coding_->RenameObjectStore(
571 transaction->BackingStoreTransaction()->transaction(),
572 transaction->database()->id(), new_name, &old_name,
573 &object_store_metadata);
574
575 if (!s.ok())
576 return s;
577 DCHECK_EQ(object_store_metadata.name, new_name);
578
579 transaction->ScheduleAbortTask(
580 base::BindOnce(&IndexedDBDatabase::RenameObjectStoreAbortOperation,
581 AsWeakPtr(), object_store_id, std::move(old_name)));
582 return leveldb::Status::OK();
583 }
584
RenameObjectStoreAbortOperation(int64_t object_store_id,base::string16 old_name)585 void IndexedDBDatabase::RenameObjectStoreAbortOperation(
586 int64_t object_store_id,
587 base::string16 old_name) {
588 IDB_TRACE("IndexedDBDatabase::RenameObjectStoreAbortOperation");
589
590 DCHECK(metadata_.object_stores.find(object_store_id) !=
591 metadata_.object_stores.end());
592 metadata_.object_stores[object_store_id].name = std::move(old_name);
593 }
594
VersionChangeOperation(int64_t version,scoped_refptr<IndexedDBCallbacks> callbacks,IndexedDBTransaction * transaction)595 Status IndexedDBDatabase::VersionChangeOperation(
596 int64_t version,
597 scoped_refptr<IndexedDBCallbacks> callbacks,
598 IndexedDBTransaction* transaction) {
599 IDB_TRACE1("IndexedDBDatabase::VersionChangeOperation", "txn.id",
600 transaction->id());
601 int64_t old_version = metadata_.version;
602 DCHECK_GT(version, old_version);
603
604 leveldb::Status s = metadata_coding_->SetDatabaseVersion(
605 transaction->BackingStoreTransaction()->transaction(), id(), version,
606 &metadata_);
607 if (!s.ok())
608 return s;
609
610 transaction->ScheduleAbortTask(
611 base::BindOnce(&IndexedDBDatabase::VersionChangeAbortOperation,
612 AsWeakPtr(), old_version));
613
614 connection_coordinator_.CreateAndBindUpgradeTransaction();
615 connection_coordinator_.OnUpgradeTransactionStarted(old_version);
616 return Status::OK();
617 }
618
VersionChangeAbortOperation(int64_t previous_version)619 void IndexedDBDatabase::VersionChangeAbortOperation(int64_t previous_version) {
620 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
621 metadata_.version = previous_version;
622 }
623
CreateIndexOperation(int64_t object_store_id,int64_t index_id,const base::string16 & name,const IndexedDBKeyPath & key_path,bool unique,bool multi_entry,IndexedDBTransaction * transaction)624 leveldb::Status IndexedDBDatabase::CreateIndexOperation(
625 int64_t object_store_id,
626 int64_t index_id,
627 const base::string16& name,
628 const IndexedDBKeyPath& key_path,
629 bool unique,
630 bool multi_entry,
631 IndexedDBTransaction* transaction) {
632 DCHECK(transaction);
633 IDB_TRACE1("IndexedDBDatabase::CreateIndexOperation", "txn.id",
634 transaction->id());
635 DCHECK_EQ(transaction->mode(),
636 blink::mojom::IDBTransactionMode::VersionChange);
637
638 if (!IsObjectStoreIdInMetadataAndIndexNotInMetadata(object_store_id,
639 index_id)) {
640 return leveldb::Status::InvalidArgument(
641 "Invalid object_store_id and/or index_id.");
642 }
643
644 IndexedDBIndexMetadata index_metadata;
645 Status s = metadata_coding_->CreateIndex(
646 transaction->BackingStoreTransaction()->transaction(),
647 transaction->database()->id(), object_store_id, index_id, name, key_path,
648 unique, multi_entry, &index_metadata);
649
650 if (!s.ok())
651 return s;
652
653 AddIndexToMetadata(object_store_id, std::move(index_metadata), index_id);
654 transaction->ScheduleAbortTask(
655 base::BindOnce(&IndexedDBDatabase::CreateIndexAbortOperation, AsWeakPtr(),
656 object_store_id, index_id));
657 return s;
658 }
659
CreateIndexAbortOperation(int64_t object_store_id,int64_t index_id)660 void IndexedDBDatabase::CreateIndexAbortOperation(int64_t object_store_id,
661 int64_t index_id) {
662 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
663 RemoveIndexFromMetadata(object_store_id, index_id);
664 }
665
DeleteIndexOperation(int64_t object_store_id,int64_t index_id,IndexedDBTransaction * transaction)666 Status IndexedDBDatabase::DeleteIndexOperation(
667 int64_t object_store_id,
668 int64_t index_id,
669 IndexedDBTransaction* transaction) {
670 IDB_TRACE1("IndexedDBDatabase::DeleteIndexOperation", "txn.id",
671 transaction->id());
672 DCHECK_EQ(transaction->mode(),
673 blink::mojom::IDBTransactionMode::VersionChange);
674
675 if (!IsObjectStoreIdAndIndexIdInMetadata(object_store_id, index_id)) {
676 return leveldb::Status::InvalidArgument(
677 "Invalid object_store_id and/or index_id.");
678 }
679
680 IndexedDBIndexMetadata index_metadata =
681 RemoveIndexFromMetadata(object_store_id, index_id);
682
683 Status s = metadata_coding_->DeleteIndex(
684 transaction->BackingStoreTransaction()->transaction(),
685 transaction->database()->id(), object_store_id, index_metadata);
686
687 if (!s.ok())
688 return s;
689
690 s = backing_store_->ClearIndex(transaction->BackingStoreTransaction(),
691 transaction->database()->id(), object_store_id,
692 index_id);
693 if (!s.ok()) {
694 AddIndexToMetadata(object_store_id, std::move(index_metadata),
695 IndexedDBIndexMetadata::kInvalidId);
696 return s;
697 }
698
699 transaction->ScheduleAbortTask(
700 base::BindOnce(&IndexedDBDatabase::DeleteIndexAbortOperation, AsWeakPtr(),
701 object_store_id, std::move(index_metadata)));
702 return s;
703 }
704
DeleteIndexAbortOperation(int64_t object_store_id,IndexedDBIndexMetadata index_metadata)705 void IndexedDBDatabase::DeleteIndexAbortOperation(
706 int64_t object_store_id,
707 IndexedDBIndexMetadata index_metadata) {
708 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
709 AddIndexToMetadata(object_store_id, std::move(index_metadata),
710 IndexedDBIndexMetadata::kInvalidId);
711 }
712
RenameIndexOperation(int64_t object_store_id,int64_t index_id,const base::string16 & new_name,IndexedDBTransaction * transaction)713 leveldb::Status IndexedDBDatabase::RenameIndexOperation(
714 int64_t object_store_id,
715 int64_t index_id,
716 const base::string16& new_name,
717 IndexedDBTransaction* transaction) {
718 DCHECK(transaction);
719 IDB_TRACE1("IndexedDBDatabase::RenameIndex", "txn.id", transaction->id());
720 DCHECK_EQ(transaction->mode(),
721 blink::mojom::IDBTransactionMode::VersionChange);
722
723 if (!IsObjectStoreIdAndIndexIdInMetadata(object_store_id, index_id)) {
724 return leveldb::Status::InvalidArgument(
725 "Invalid object_store_id and/or index_id.");
726 }
727
728 IndexedDBIndexMetadata& index_metadata =
729 metadata_.object_stores[object_store_id].indexes[index_id];
730
731 base::string16 old_name;
732 Status s = metadata_coding_->RenameIndex(
733 transaction->BackingStoreTransaction()->transaction(),
734 transaction->database()->id(), object_store_id, new_name, &old_name,
735 &index_metadata);
736 if (!s.ok())
737 return s;
738
739 DCHECK_EQ(index_metadata.name, new_name);
740 transaction->ScheduleAbortTask(
741 base::BindOnce(&IndexedDBDatabase::RenameIndexAbortOperation, AsWeakPtr(),
742 object_store_id, index_id, std::move(old_name)));
743 return leveldb::Status::OK();
744 }
745
RenameIndexAbortOperation(int64_t object_store_id,int64_t index_id,base::string16 old_name)746 void IndexedDBDatabase::RenameIndexAbortOperation(int64_t object_store_id,
747 int64_t index_id,
748 base::string16 old_name) {
749 IDB_TRACE("IndexedDBDatabase::RenameIndexAbortOperation");
750
751 DCHECK(metadata_.object_stores.find(object_store_id) !=
752 metadata_.object_stores.end());
753 IndexedDBObjectStoreMetadata& object_store =
754 metadata_.object_stores[object_store_id];
755
756 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
757 object_store.indexes[index_id].name = std::move(old_name);
758 }
759
GetOperation(base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,int64_t object_store_id,int64_t index_id,std::unique_ptr<IndexedDBKeyRange> key_range,indexed_db::CursorType cursor_type,blink::mojom::IDBDatabase::GetCallback callback,IndexedDBTransaction * transaction)760 Status IndexedDBDatabase::GetOperation(
761 base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
762 int64_t object_store_id,
763 int64_t index_id,
764 std::unique_ptr<IndexedDBKeyRange> key_range,
765 indexed_db::CursorType cursor_type,
766 blink::mojom::IDBDatabase::GetCallback callback,
767 IndexedDBTransaction* transaction) {
768 IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction->id());
769
770 if (!IsObjectStoreIdAndMaybeIndexIdInMetadata(object_store_id, index_id)) {
771 IndexedDBDatabaseError error = CreateError(
772 blink::mojom::IDBException::kUnknownError, "Bad request", transaction);
773 std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
774 blink::mojom::IDBError::New(error.code(), error.message())));
775 return leveldb::Status::InvalidArgument(
776 "Invalid object_store_id and/or index_id.");
777 }
778
779 DCHECK(metadata_.object_stores.find(object_store_id) !=
780 metadata_.object_stores.end());
781 const IndexedDBObjectStoreMetadata& object_store_metadata =
782 metadata_.object_stores[object_store_id];
783
784 const IndexedDBKey* key;
785
786 Status s = Status::OK();
787 if (!dispatcher_host) {
788 IndexedDBDatabaseError error =
789 CreateError(blink::mojom::IDBException::kUnknownError, "Unknown error",
790 transaction);
791 std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
792 blink::mojom::IDBError::New(error.code(), error.message())));
793 return s;
794 }
795
796 std::unique_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
797 if (key_range->IsOnlyKey()) {
798 key = &key_range->lower();
799 } else {
800 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
801 // ObjectStore Retrieval Operation
802 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
803 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
804 transaction->BackingStoreTransaction(), id(), object_store_id,
805 *key_range, blink::mojom::IDBCursorDirection::Next, &s);
806 } else {
807 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
808 transaction->BackingStoreTransaction(), id(), object_store_id,
809 *key_range, blink::mojom::IDBCursorDirection::Next, &s);
810 }
811 } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
812 // Index Value Retrieval Operation
813 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
814 transaction->BackingStoreTransaction(), id(), object_store_id,
815 index_id, *key_range, blink::mojom::IDBCursorDirection::Next, &s);
816 } else {
817 // Index Referenced Value Retrieval Operation
818 backing_store_cursor = backing_store_->OpenIndexCursor(
819 transaction->BackingStoreTransaction(), id(), object_store_id,
820 index_id, *key_range, blink::mojom::IDBCursorDirection::Next, &s);
821 }
822
823 if (!s.ok()) {
824 IndexedDBDatabaseError error =
825 CreateError(blink::mojom::IDBException::kUnknownError,
826 "Corruption detected, unable to continue", transaction);
827 std::move(callback).Run(
828 blink::mojom::IDBDatabaseGetResult::NewErrorResult(
829 blink::mojom::IDBError::New(error.code(), error.message())));
830 return s;
831 }
832
833 if (!backing_store_cursor) {
834 // This means we've run out of data.
835 std::move(callback).Run(
836 blink::mojom::IDBDatabaseGetResult::NewEmpty(true));
837 return s;
838 }
839
840 key = &backing_store_cursor->key();
841 }
842
843 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
844 // Object Store Retrieval Operation
845 IndexedDBReturnValue value;
846 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(), id(),
847 object_store_id, *key, &value);
848 if (!s.ok()) {
849 IndexedDBDatabaseError error =
850 CreateError(blink::mojom::IDBException::kUnknownError,
851 "Unknown error", transaction);
852 std::move(callback).Run(
853 blink::mojom::IDBDatabaseGetResult::NewErrorResult(
854 blink::mojom::IDBError::New(error.code(), error.message())));
855 return s;
856 }
857
858 if (value.empty()) {
859 std::move(callback).Run(
860 blink::mojom::IDBDatabaseGetResult::NewEmpty(true));
861 return s;
862 }
863
864 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
865 std::move(callback).Run(
866 blink::mojom::IDBDatabaseGetResult::NewKey(std::move(*key)));
867 return s;
868 }
869
870 if (object_store_metadata.auto_increment &&
871 !object_store_metadata.key_path.IsNull()) {
872 value.primary_key = *key;
873 value.key_path = object_store_metadata.key_path;
874 }
875
876 blink::mojom::IDBReturnValuePtr mojo_value =
877 IndexedDBReturnValue::ConvertReturnValue(&value);
878 dispatcher_host->CreateAllExternalObjects(
879 origin(), value.external_objects, &mojo_value->value->external_objects);
880 std::move(callback).Run(
881 blink::mojom::IDBDatabaseGetResult::NewValue(std::move(mojo_value)));
882 return s;
883 }
884
885 // From here we are dealing only with indexes.
886 std::unique_ptr<IndexedDBKey> primary_key;
887 s = backing_store_->GetPrimaryKeyViaIndex(
888 transaction->BackingStoreTransaction(), id(), object_store_id, index_id,
889 *key, &primary_key);
890 if (!s.ok()) {
891 IndexedDBDatabaseError error =
892 CreateError(blink::mojom::IDBException::kUnknownError, "Unknown error",
893 transaction);
894 std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
895 blink::mojom::IDBError::New(error.code(), error.message())));
896 return s;
897 }
898
899 if (!primary_key) {
900 std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewEmpty(true));
901 return s;
902 }
903 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
904 // Index Value Retrieval Operation
905 std::move(callback).Run(
906 blink::mojom::IDBDatabaseGetResult::NewKey(std::move(*primary_key)));
907 return s;
908 }
909
910 // Index Referenced Value Retrieval Operation
911 IndexedDBReturnValue value;
912 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(), id(),
913 object_store_id, *primary_key, &value);
914 if (!s.ok()) {
915 IndexedDBDatabaseError error =
916 CreateError(blink::mojom::IDBException::kUnknownError, "Unknown error",
917 transaction);
918 std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
919 blink::mojom::IDBError::New(error.code(), error.message())));
920 return s;
921 }
922
923 if (value.empty()) {
924 std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewEmpty(true));
925 return s;
926 }
927 if (object_store_metadata.auto_increment &&
928 !object_store_metadata.key_path.IsNull()) {
929 value.primary_key = *primary_key;
930 value.key_path = object_store_metadata.key_path;
931 }
932
933 blink::mojom::IDBReturnValuePtr mojo_value =
934 IndexedDBReturnValue::ConvertReturnValue(&value);
935 dispatcher_host->CreateAllExternalObjects(
936 origin(), value.external_objects, &mojo_value->value->external_objects);
937 std::move(callback).Run(
938 blink::mojom::IDBDatabaseGetResult::NewValue(std::move(mojo_value)));
939 return s;
940 }
941
942 static_assert(sizeof(size_t) >= sizeof(int32_t),
943 "Size of size_t is less than size of int32");
944 static_assert(blink::mojom::kIDBMaxMessageOverhead <= INT32_MAX,
945 "kIDBMaxMessageOverhead is more than INT32_MAX");
946
GetAllOperation(base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,int64_t object_store_id,int64_t index_id,std::unique_ptr<IndexedDBKeyRange> key_range,indexed_db::CursorType cursor_type,int64_t max_count,blink::mojom::IDBDatabase::GetAllCallback callback,IndexedDBTransaction * transaction)947 Status IndexedDBDatabase::GetAllOperation(
948 base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
949 int64_t object_store_id,
950 int64_t index_id,
951 std::unique_ptr<IndexedDBKeyRange> key_range,
952 indexed_db::CursorType cursor_type,
953 int64_t max_count,
954 blink::mojom::IDBDatabase::GetAllCallback callback,
955 IndexedDBTransaction* transaction) {
956 IDB_TRACE1("IndexedDBDatabase::GetAllOperation", "txn.id", transaction->id());
957
958 if (!IsObjectStoreIdAndMaybeIndexIdInMetadata(object_store_id, index_id)) {
959 IndexedDBDatabaseError error = CreateError(
960 blink::mojom::IDBException::kUnknownError, "Bad request", transaction);
961 std::move(callback).Run(
962 blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
963 blink::mojom::IDBError::New(error.code(), error.message())));
964 return leveldb::Status::InvalidArgument("Invalid object_store_id.");
965 }
966
967 DCHECK_GT(max_count, 0);
968
969 DCHECK(metadata_.object_stores.find(object_store_id) !=
970 metadata_.object_stores.end());
971 const IndexedDBObjectStoreMetadata& object_store_metadata =
972 metadata_.object_stores[object_store_id];
973
974 Status s = Status::OK();
975 if (!dispatcher_host) {
976 IndexedDBDatabaseError error =
977 CreateError(blink::mojom::IDBException::kUnknownError, "Unknown error",
978 transaction);
979 std::move(callback).Run(
980 blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
981 blink::mojom::IDBError::New(error.code(), error.message())));
982 return s;
983 }
984
985 std::unique_ptr<IndexedDBBackingStore::Cursor> cursor;
986
987 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
988 // Retrieving keys
989 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
990 // Object Store: Key Retrieval Operation
991 cursor = backing_store_->OpenObjectStoreKeyCursor(
992 transaction->BackingStoreTransaction(), id(), object_store_id,
993 *key_range, blink::mojom::IDBCursorDirection::Next, &s);
994 } else {
995 // Index Value: (Primary Key) Retrieval Operation
996 cursor = backing_store_->OpenIndexKeyCursor(
997 transaction->BackingStoreTransaction(), id(), object_store_id,
998 index_id, *key_range, blink::mojom::IDBCursorDirection::Next, &s);
999 }
1000 } else {
1001 // Retrieving values
1002 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
1003 // Object Store: Value Retrieval Operation
1004 cursor = backing_store_->OpenObjectStoreCursor(
1005 transaction->BackingStoreTransaction(), id(), object_store_id,
1006 *key_range, blink::mojom::IDBCursorDirection::Next, &s);
1007 } else {
1008 // Object Store: Referenced Value Retrieval Operation
1009 cursor = backing_store_->OpenIndexCursor(
1010 transaction->BackingStoreTransaction(), id(), object_store_id,
1011 index_id, *key_range, blink::mojom::IDBCursorDirection::Next, &s);
1012 }
1013 }
1014
1015 if (!s.ok()) {
1016 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
1017 IndexedDBDatabaseError error =
1018 CreateError(blink::mojom::IDBException::kUnknownError,
1019 "Corruption detected, unable to continue", transaction);
1020 std::move(callback).Run(
1021 blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
1022 blink::mojom::IDBError::New(error.code(), error.message())));
1023 return s;
1024 }
1025
1026 std::vector<IndexedDBKey> found_keys;
1027 std::vector<IndexedDBReturnValue> found_values;
1028 if (!cursor) {
1029 // Doesn't matter if key or value array here - will be empty array when it
1030 // hits JavaScript.
1031 std::vector<blink::mojom::IDBReturnValuePtr> mojo_found_values;
1032 std::move(callback).Run(blink::mojom::IDBDatabaseGetAllResult::NewValues(
1033 std::move(mojo_found_values)));
1034 return s;
1035 }
1036
1037 bool did_first_seek = false;
1038 bool generated_key = object_store_metadata.auto_increment &&
1039 !object_store_metadata.key_path.IsNull();
1040
1041 size_t response_size = blink::mojom::kIDBMaxMessageOverhead;
1042 int64_t num_found_items = 0;
1043 while (num_found_items++ < max_count) {
1044 bool cursor_valid;
1045 if (did_first_seek) {
1046 cursor_valid = cursor->Continue(&s);
1047 } else {
1048 cursor_valid = cursor->FirstSeek(&s);
1049 did_first_seek = true;
1050 }
1051 if (!s.ok()) {
1052 IndexedDBDatabaseError error =
1053 CreateError(blink::mojom::IDBException::kUnknownError,
1054 "Seek failure, unable to continue", transaction);
1055 std::move(callback).Run(
1056 blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
1057 blink::mojom::IDBError::New(error.code(), error.message())));
1058 return s;
1059 }
1060
1061 if (!cursor_valid)
1062 break;
1063
1064 IndexedDBReturnValue return_value;
1065 IndexedDBKey return_key;
1066
1067 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1068 return_key = cursor->primary_key();
1069 } else {
1070 // Retrieving values
1071 return_value.swap(*cursor->value());
1072 if (!return_value.empty() && generated_key) {
1073 return_value.primary_key = cursor->primary_key();
1074 return_value.key_path = object_store_metadata.key_path;
1075 }
1076 }
1077
1078 if (cursor_type == indexed_db::CURSOR_KEY_ONLY)
1079 response_size += return_key.size_estimate();
1080 else
1081 response_size += return_value.SizeEstimate();
1082 if (response_size > GetUsableMessageSizeInBytes()) {
1083 IndexedDBDatabaseError error =
1084 CreateError(blink::mojom::IDBException::kUnknownError,
1085 "Maximum IPC message size exceeded.", transaction);
1086 std::move(callback).Run(
1087 blink::mojom::IDBDatabaseGetAllResult::NewErrorResult(
1088 blink::mojom::IDBError::New(error.code(), error.message())));
1089 return s;
1090 }
1091
1092 if (cursor_type == indexed_db::CURSOR_KEY_ONLY)
1093 found_keys.push_back(return_key);
1094 else
1095 found_values.push_back(return_value);
1096 }
1097
1098 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1099 // IndexedDBKey already supports an array of values so we can leverage this
1100 // to return an array of keys - no need to create our own array of keys.
1101 std::move(callback).Run(blink::mojom::IDBDatabaseGetAllResult::NewKey(
1102 IndexedDBKey(std::move(found_keys))));
1103 return s;
1104 }
1105
1106 std::vector<blink::mojom::IDBReturnValuePtr> mojo_values;
1107 mojo_values.reserve(found_values.size());
1108 for (size_t i = 0; i < found_values.size(); ++i) {
1109 mojo_values.push_back(
1110 IndexedDBReturnValue::ConvertReturnValue(&found_values[i]));
1111 dispatcher_host->CreateAllExternalObjects(
1112 origin(), found_values[i].external_objects,
1113 &mojo_values[i]->value->external_objects);
1114 }
1115 std::move(callback).Run(
1116 blink::mojom::IDBDatabaseGetAllResult::NewValues(std::move(mojo_values)));
1117 return s;
1118 }
1119
PutOperation(std::unique_ptr<PutOperationParams> params,IndexedDBTransaction * transaction)1120 Status IndexedDBDatabase::PutOperation(
1121 std::unique_ptr<PutOperationParams> params,
1122 IndexedDBTransaction* transaction) {
1123 IDB_TRACE2("IndexedDBDatabase::PutOperation", "txn.id", transaction->id(),
1124 "size", params->value.SizeEstimate());
1125 DCHECK_NE(transaction->mode(), blink::mojom::IDBTransactionMode::ReadOnly);
1126 bool key_was_generated = false;
1127 Status s = Status::OK();
1128 transaction->set_in_flight_memory(transaction->in_flight_memory() -
1129 params->value.SizeEstimate());
1130
1131 if (!IsObjectStoreIdInMetadata(params->object_store_id)) {
1132 IndexedDBDatabaseError error = CreateError(
1133 blink::mojom::IDBException::kUnknownError, "Bad request", transaction);
1134 std::move(params->callback)
1135 .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
1136 blink::mojom::IDBError::New(error.code(), error.message())));
1137 return leveldb::Status::InvalidArgument("Invalid object_store_id.");
1138 }
1139
1140 DCHECK(metadata_.object_stores.find(params->object_store_id) !=
1141 metadata_.object_stores.end());
1142 const IndexedDBObjectStoreMetadata& object_store =
1143 metadata_.object_stores[params->object_store_id];
1144 DCHECK(object_store.auto_increment || params->key->IsValid());
1145
1146 std::unique_ptr<IndexedDBKey> key;
1147 if (params->put_mode != blink::mojom::IDBPutMode::CursorUpdate &&
1148 object_store.auto_increment && !params->key->IsValid()) {
1149 std::unique_ptr<IndexedDBKey> auto_inc_key =
1150 GenerateKey(backing_store_, transaction, id(), params->object_store_id);
1151 key_was_generated = true;
1152 if (!auto_inc_key->IsValid()) {
1153 IndexedDBDatabaseError error =
1154 CreateError(blink::mojom::IDBException::kConstraintError,
1155 "Maximum key generator value reached.", transaction);
1156 std::move(params->callback)
1157 .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
1158 blink::mojom::IDBError::New(error.code(), error.message())));
1159 return s;
1160 }
1161 key = std::move(auto_inc_key);
1162 } else {
1163 key = std::move(params->key);
1164 }
1165
1166 DCHECK(key->IsValid());
1167
1168 IndexedDBBackingStore::RecordIdentifier record_identifier;
1169 if (params->put_mode == blink::mojom::IDBPutMode::AddOnly) {
1170 bool found = false;
1171 Status found_status = backing_store_->KeyExistsInObjectStore(
1172 transaction->BackingStoreTransaction(), id(), params->object_store_id,
1173 *key, &record_identifier, &found);
1174 if (!found_status.ok())
1175 return found_status;
1176 if (found) {
1177 IndexedDBDatabaseError error =
1178 CreateError(blink::mojom::IDBException::kConstraintError,
1179 "Key already exists in the object store.", transaction);
1180 std::move(params->callback)
1181 .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
1182 blink::mojom::IDBError::New(error.code(), error.message())));
1183 return found_status;
1184 }
1185 }
1186
1187 std::vector<std::unique_ptr<IndexWriter>> index_writers;
1188 base::string16 error_message;
1189 bool obeys_constraints = false;
1190 bool backing_store_success = MakeIndexWriters(
1191 transaction, backing_store_, id(), object_store, *key, key_was_generated,
1192 params->index_keys, &index_writers, &error_message, &obeys_constraints);
1193 if (!backing_store_success) {
1194 IndexedDBDatabaseError error =
1195 CreateError(blink::mojom::IDBException::kUnknownError,
1196 "Internal error: backing store error updating index keys.",
1197 transaction);
1198 std::move(params->callback)
1199 .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
1200 blink::mojom::IDBError::New(error.code(), error.message())));
1201 return s;
1202 }
1203 if (!obeys_constraints) {
1204 IndexedDBDatabaseError error =
1205 CreateError(blink::mojom::IDBException::kConstraintError, error_message,
1206 transaction);
1207 std::move(params->callback)
1208 .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
1209 blink::mojom::IDBError::New(error.code(), error.message())));
1210 return s;
1211 }
1212
1213 // Before this point, don't do any mutation. After this point, rollback the
1214 // transaction in case of error.
1215 s = backing_store_->PutRecord(transaction->BackingStoreTransaction(), id(),
1216 params->object_store_id, *key, ¶ms->value,
1217 &record_identifier);
1218 if (!s.ok())
1219 return s;
1220
1221 {
1222 IDB_TRACE1("IndexedDBDatabase::PutOperation.UpdateIndexes", "txn.id",
1223 transaction->id());
1224 for (const auto& writer : index_writers) {
1225 writer->WriteIndexKeys(record_identifier, backing_store_,
1226 transaction->BackingStoreTransaction(), id(),
1227 params->object_store_id);
1228 }
1229 }
1230
1231 if (object_store.auto_increment &&
1232 params->put_mode != blink::mojom::IDBPutMode::CursorUpdate &&
1233 key->type() == blink::mojom::IDBKeyType::Number) {
1234 IDB_TRACE1("IndexedDBDatabase::PutOperation.AutoIncrement", "txn.id",
1235 transaction->id());
1236 s = UpdateKeyGenerator(backing_store_, transaction, id(),
1237 params->object_store_id, *key, !key_was_generated);
1238 if (!s.ok())
1239 return s;
1240 }
1241 {
1242 IDB_TRACE1("IndexedDBDatabase::PutOperation.Callbacks", "txn.id",
1243 transaction->id());
1244 std::move(params->callback)
1245 .Run(blink::mojom::IDBTransactionPutResult::NewKey(*key));
1246 }
1247 FilterObservation(transaction, params->object_store_id,
1248 params->put_mode == blink::mojom::IDBPutMode::AddOnly
1249 ? blink::mojom::IDBOperationType::Add
1250 : blink::mojom::IDBOperationType::Put,
1251 IndexedDBKeyRange(*key), ¶ms->value);
1252 factory_->NotifyIndexedDBContentChanged(
1253 origin(), metadata_.name,
1254 metadata_.object_stores[params->object_store_id].name);
1255 return s;
1256 }
1257
SetIndexKeysOperation(int64_t object_store_id,std::unique_ptr<IndexedDBKey> primary_key,const std::vector<IndexedDBIndexKeys> & index_keys,IndexedDBTransaction * transaction)1258 Status IndexedDBDatabase::SetIndexKeysOperation(
1259 int64_t object_store_id,
1260 std::unique_ptr<IndexedDBKey> primary_key,
1261 const std::vector<IndexedDBIndexKeys>& index_keys,
1262 IndexedDBTransaction* transaction) {
1263 DCHECK(transaction);
1264 IDB_TRACE1("IndexedDBDatabase::SetIndexKeysOperation", "txn.id",
1265 transaction->id());
1266 DCHECK_EQ(transaction->mode(),
1267 blink::mojom::IDBTransactionMode::VersionChange);
1268
1269 IndexedDBBackingStore::RecordIdentifier record_identifier;
1270 bool found = false;
1271 Status s = backing_store_->KeyExistsInObjectStore(
1272 transaction->BackingStoreTransaction(), metadata_.id, object_store_id,
1273 *primary_key, &record_identifier, &found);
1274 if (!s.ok())
1275 return s;
1276 if (!found) {
1277 return transaction->Abort(IndexedDBDatabaseError(
1278 blink::mojom::IDBException::kUnknownError,
1279 "Internal error setting index keys for object store."));
1280 }
1281
1282 std::vector<std::unique_ptr<IndexWriter>> index_writers;
1283 base::string16 error_message;
1284 bool obeys_constraints = false;
1285 DCHECK(metadata_.object_stores.find(object_store_id) !=
1286 metadata_.object_stores.end());
1287 const IndexedDBObjectStoreMetadata& object_store_metadata =
1288 metadata_.object_stores[object_store_id];
1289 bool backing_store_success = MakeIndexWriters(
1290 transaction, backing_store_, id(), object_store_metadata, *primary_key,
1291 false, index_keys, &index_writers, &error_message, &obeys_constraints);
1292 if (!backing_store_success) {
1293 return transaction->Abort(IndexedDBDatabaseError(
1294 blink::mojom::IDBException::kUnknownError,
1295 "Internal error: backing store error updating index keys."));
1296 }
1297 if (!obeys_constraints) {
1298 return transaction->Abort(IndexedDBDatabaseError(
1299 blink::mojom::IDBException::kConstraintError, error_message));
1300 }
1301
1302 for (const auto& writer : index_writers) {
1303 s = writer->WriteIndexKeys(record_identifier, backing_store_,
1304 transaction->BackingStoreTransaction(), id(),
1305 object_store_id);
1306 if (!s.ok())
1307 return s;
1308 }
1309 return leveldb::Status::OK();
1310 }
1311
SetIndexesReadyOperation(size_t index_count,IndexedDBTransaction * transaction)1312 Status IndexedDBDatabase::SetIndexesReadyOperation(
1313 size_t index_count,
1314 IndexedDBTransaction* transaction) {
1315 // TODO(dmurph): This method should be refactored out for something more
1316 // reliable.
1317 for (size_t i = 0; i < index_count; ++i)
1318 transaction->DidCompletePreemptiveEvent();
1319 return Status::OK();
1320 }
1321
OpenCursorOperation(std::unique_ptr<OpenCursorOperationParams> params,const url::Origin & origin,base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,IndexedDBTransaction * transaction)1322 Status IndexedDBDatabase::OpenCursorOperation(
1323 std::unique_ptr<OpenCursorOperationParams> params,
1324 const url::Origin& origin,
1325 base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
1326 IndexedDBTransaction* transaction) {
1327 IDB_TRACE1("IndexedDBDatabase::OpenCursorOperation", "txn.id",
1328 transaction->id());
1329
1330 Status s;
1331 if (!dispatcher_host) {
1332 IndexedDBDatabaseError error =
1333 CreateError(blink::mojom::IDBException::kUnknownError,
1334 "Dispatcher not connected.", transaction);
1335 std::move(params->callback)
1336 .Run(blink::mojom::IDBDatabaseOpenCursorResult::NewErrorResult(
1337 blink::mojom::IDBError::New(error.code(), error.message())));
1338 return s;
1339 }
1340
1341 if (!IsObjectStoreIdAndMaybeIndexIdInMetadata(params->object_store_id,
1342 params->index_id)) {
1343 return leveldb::Status::InvalidArgument(
1344 "Invalid object_store_id and/or index_id.");
1345 }
1346
1347 // The frontend has begun indexing, so this pauses the transaction
1348 // until the indexing is complete. This can't happen any earlier
1349 // because we don't want to switch to early mode in case multiple
1350 // indexes are being created in a row, with Put()'s in between.
1351 if (params->task_type == blink::mojom::IDBTaskType::Preemptive)
1352 transaction->AddPreemptiveEvent();
1353
1354 std::unique_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1355 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
1356 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1357 DCHECK_EQ(params->task_type, blink::mojom::IDBTaskType::Normal);
1358 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1359 transaction->BackingStoreTransaction(), id(), params->object_store_id,
1360 *params->key_range, params->direction, &s);
1361 } else {
1362 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1363 transaction->BackingStoreTransaction(), id(), params->object_store_id,
1364 *params->key_range, params->direction, &s);
1365 }
1366 } else {
1367 DCHECK_EQ(params->task_type, blink::mojom::IDBTaskType::Normal);
1368 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1369 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1370 transaction->BackingStoreTransaction(), id(), params->object_store_id,
1371 params->index_id, *params->key_range, params->direction, &s);
1372 } else {
1373 backing_store_cursor = backing_store_->OpenIndexCursor(
1374 transaction->BackingStoreTransaction(), id(), params->object_store_id,
1375 params->index_id, *params->key_range, params->direction, &s);
1376 }
1377 }
1378
1379 if (!s.ok()) {
1380 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
1381 return s;
1382 }
1383
1384 if (!backing_store_cursor) {
1385 // Occurs when we've reached the end of cursor's data.
1386 std::move(params->callback)
1387 .Run(blink::mojom::IDBDatabaseOpenCursorResult::NewEmpty(true));
1388 return s;
1389 }
1390
1391 std::unique_ptr<IndexedDBCursor> cursor = std::make_unique<IndexedDBCursor>(
1392 std::move(backing_store_cursor), params->cursor_type, params->task_type,
1393 transaction->AsWeakPtr());
1394 IndexedDBCursor* cursor_ptr = cursor.get();
1395 transaction->RegisterOpenCursor(cursor_ptr);
1396
1397 blink::mojom::IDBValuePtr mojo_value;
1398 std::vector<IndexedDBExternalObject> external_objects;
1399 if (cursor_ptr->Value()) {
1400 mojo_value = IndexedDBValue::ConvertAndEraseValue(cursor_ptr->Value());
1401 external_objects.swap(cursor_ptr->Value()->external_objects);
1402 }
1403
1404 if (mojo_value) {
1405 dispatcher_host->CreateAllExternalObjects(origin, external_objects,
1406 &mojo_value->external_objects);
1407 }
1408
1409 std::move(params->callback)
1410 .Run(blink::mojom::IDBDatabaseOpenCursorResult::NewValue(
1411 blink::mojom::IDBDatabaseOpenCursorValue::New(
1412 dispatcher_host->CreateCursorBinding(origin, std::move(cursor)),
1413 cursor_ptr->key(), cursor_ptr->primary_key(),
1414 std::move(mojo_value))));
1415 return s;
1416 }
1417
CountOperation(int64_t object_store_id,int64_t index_id,std::unique_ptr<IndexedDBKeyRange> key_range,scoped_refptr<IndexedDBCallbacks> callbacks,IndexedDBTransaction * transaction)1418 Status IndexedDBDatabase::CountOperation(
1419 int64_t object_store_id,
1420 int64_t index_id,
1421 std::unique_ptr<IndexedDBKeyRange> key_range,
1422 scoped_refptr<IndexedDBCallbacks> callbacks,
1423 IndexedDBTransaction* transaction) {
1424 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction->id());
1425
1426 if (!IsObjectStoreIdAndMaybeIndexIdInMetadata(object_store_id, index_id))
1427 return leveldb::Status::InvalidArgument(
1428 "Invalid object_store_id and/or index_id.");
1429
1430 uint32_t count = 0;
1431 std::unique_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1432
1433 Status s = Status::OK();
1434 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
1435 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1436 transaction->BackingStoreTransaction(), id(), object_store_id,
1437 *key_range, blink::mojom::IDBCursorDirection::Next, &s);
1438 } else {
1439 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1440 transaction->BackingStoreTransaction(), id(), object_store_id, index_id,
1441 *key_range, blink::mojom::IDBCursorDirection::Next, &s);
1442 }
1443 if (!s.ok()) {
1444 DLOG(ERROR) << "Unable perform count operation: " << s.ToString();
1445 return s;
1446 }
1447 if (!backing_store_cursor) {
1448 callbacks->OnSuccess(count);
1449 return s;
1450 }
1451
1452 do {
1453 if (!s.ok())
1454 return s;
1455 ++count;
1456 } while (backing_store_cursor->Continue(&s));
1457
1458 callbacks->OnSuccess(count);
1459 return s;
1460 }
1461
DeleteRangeOperation(int64_t object_store_id,std::unique_ptr<IndexedDBKeyRange> key_range,scoped_refptr<IndexedDBCallbacks> callbacks,IndexedDBTransaction * transaction)1462 Status IndexedDBDatabase::DeleteRangeOperation(
1463 int64_t object_store_id,
1464 std::unique_ptr<IndexedDBKeyRange> key_range,
1465 scoped_refptr<IndexedDBCallbacks> callbacks,
1466 IndexedDBTransaction* transaction) {
1467 IDB_TRACE1("IndexedDBDatabase::DeleteRangeOperation", "txn.id",
1468 transaction->id());
1469
1470 if (!IsObjectStoreIdInMetadata(object_store_id))
1471 return leveldb::Status::InvalidArgument("Invalid object_store_id.");
1472
1473 Status s = backing_store_->DeleteRange(transaction->BackingStoreTransaction(),
1474 id(), object_store_id, *key_range);
1475 if (!s.ok())
1476 return s;
1477 callbacks->OnSuccess();
1478 FilterObservation(transaction, object_store_id,
1479 blink::mojom::IDBOperationType::Delete, *key_range,
1480 nullptr);
1481 factory_->NotifyIndexedDBContentChanged(
1482 origin(), metadata_.name, metadata_.object_stores[object_store_id].name);
1483 return s;
1484 }
1485
GetKeyGeneratorCurrentNumberOperation(int64_t object_store_id,scoped_refptr<IndexedDBCallbacks> callbacks,IndexedDBTransaction * transaction)1486 Status IndexedDBDatabase::GetKeyGeneratorCurrentNumberOperation(
1487 int64_t object_store_id,
1488 scoped_refptr<IndexedDBCallbacks> callbacks,
1489 IndexedDBTransaction* transaction) {
1490 if (!IsObjectStoreIdInMetadata(object_store_id)) {
1491 callbacks->OnError(CreateError(blink::mojom::IDBException::kDataError,
1492 "Object store id not valid.", transaction));
1493 return leveldb::Status::InvalidArgument("Invalid object_store_id.");
1494 }
1495
1496 int64_t current_number;
1497 Status s = backing_store_->GetKeyGeneratorCurrentNumber(
1498 transaction->BackingStoreTransaction(), id(), object_store_id,
1499 ¤t_number);
1500 if (!s.ok()) {
1501 callbacks->OnError(CreateError(
1502 blink::mojom::IDBException::kDataError,
1503 "Failed to get the current number of key generator.", transaction));
1504 return s;
1505 }
1506 callbacks->OnSuccess(current_number);
1507 return s;
1508 }
1509
ClearOperation(int64_t object_store_id,scoped_refptr<IndexedDBCallbacks> callbacks,IndexedDBTransaction * transaction)1510 Status IndexedDBDatabase::ClearOperation(
1511 int64_t object_store_id,
1512 scoped_refptr<IndexedDBCallbacks> callbacks,
1513 IndexedDBTransaction* transaction) {
1514 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction->id());
1515
1516 if (!IsObjectStoreIdInMetadata(object_store_id))
1517 return leveldb::Status::InvalidArgument("Invalid object_store_id.");
1518
1519 Status s = backing_store_->ClearObjectStore(
1520 transaction->BackingStoreTransaction(), id(), object_store_id);
1521 if (!s.ok())
1522 return s;
1523 callbacks->OnSuccess();
1524
1525 FilterObservation(transaction, object_store_id,
1526 blink::mojom::IDBOperationType::Clear, IndexedDBKeyRange(),
1527 nullptr);
1528 factory_->NotifyIndexedDBContentChanged(
1529 origin(), metadata_.name, metadata_.object_stores[object_store_id].name);
1530 return s;
1531 }
1532
IsObjectStoreIdInMetadata(int64_t object_store_id) const1533 bool IndexedDBDatabase::IsObjectStoreIdInMetadata(
1534 int64_t object_store_id) const {
1535 if (!base::Contains(metadata_.object_stores, object_store_id)) {
1536 DLOG(ERROR) << "Invalid object_store_id";
1537 return false;
1538 }
1539 return true;
1540 }
1541
IsObjectStoreIdAndIndexIdInMetadata(int64_t object_store_id,int64_t index_id) const1542 bool IndexedDBDatabase::IsObjectStoreIdAndIndexIdInMetadata(
1543 int64_t object_store_id,
1544 int64_t index_id) const {
1545 if (!IsObjectStoreIdInMetadata(object_store_id))
1546 return false;
1547 const IndexedDBObjectStoreMetadata& object_store_metadata =
1548 metadata_.object_stores.find(object_store_id)->second;
1549 if (!base::Contains(object_store_metadata.indexes, index_id)) {
1550 DLOG(ERROR) << "Invalid index_id";
1551 return false;
1552 }
1553 return true;
1554 }
1555
IsObjectStoreIdAndMaybeIndexIdInMetadata(int64_t object_store_id,int64_t index_id) const1556 bool IndexedDBDatabase::IsObjectStoreIdAndMaybeIndexIdInMetadata(
1557 int64_t object_store_id,
1558 int64_t index_id) const {
1559 if (!IsObjectStoreIdInMetadata(object_store_id))
1560 return false;
1561 const IndexedDBObjectStoreMetadata& object_store_metadata =
1562 metadata_.object_stores.find(object_store_id)->second;
1563 if (index_id != IndexedDBIndexMetadata::kInvalidId &&
1564 !base::Contains(object_store_metadata.indexes, index_id)) {
1565 DLOG(ERROR) << "Invalid index_id";
1566 return false;
1567 }
1568 return true;
1569 }
1570
IsObjectStoreIdInMetadataAndIndexNotInMetadata(int64_t object_store_id,int64_t index_id) const1571 bool IndexedDBDatabase::IsObjectStoreIdInMetadataAndIndexNotInMetadata(
1572 int64_t object_store_id,
1573 int64_t index_id) const {
1574 if (!IsObjectStoreIdInMetadata(object_store_id))
1575 return false;
1576 const IndexedDBObjectStoreMetadata& object_store_metadata =
1577 metadata_.object_stores.find(object_store_id)->second;
1578 if (base::Contains(object_store_metadata.indexes, index_id)) {
1579 DLOG(ERROR) << "Invalid index_id";
1580 return false;
1581 }
1582 return true;
1583 }
1584
1585 // kIDBMaxMessageSize is defined based on the original
1586 // IPC::Channel::kMaximumMessageSize value. We use kIDBMaxMessageSize to limit
1587 // the size of arguments we pass into our Mojo calls. We want to ensure this
1588 // value is always no bigger than the current kMaximumMessageSize value which
1589 // also ensures it is always no bigger than the current Mojo message size limit.
1590 static_assert(
1591 blink::mojom::kIDBMaxMessageSize <= IPC::Channel::kMaximumMessageSize,
1592 "kIDBMaxMessageSize is bigger than IPC::Channel::kMaximumMessageSize");
1593
GetUsableMessageSizeInBytes() const1594 size_t IndexedDBDatabase::GetUsableMessageSizeInBytes() const {
1595 return blink::mojom::kIDBMaxMessageSize -
1596 blink::mojom::kIDBMaxMessageOverhead;
1597 }
1598
CallUpgradeTransactionStartedForTesting(int64_t old_version)1599 void IndexedDBDatabase::CallUpgradeTransactionStartedForTesting(
1600 int64_t old_version) {
1601 connection_coordinator_.OnUpgradeTransactionStarted(old_version);
1602 }
1603
OpenInternal()1604 Status IndexedDBDatabase::OpenInternal() {
1605 bool found = false;
1606 Status s = metadata_coding_->ReadMetadataForDatabaseName(
1607 backing_store_->db(), backing_store_->origin_identifier(), metadata_.name,
1608 &metadata_, &found);
1609 DCHECK(found == (metadata_.id != kInvalidId))
1610 << "found = " << found << " id = " << metadata_.id;
1611 if (!s.ok() || found)
1612 return s;
1613
1614 return metadata_coding_->CreateDatabase(
1615 backing_store_->db(), backing_store_->origin_identifier(), metadata_.name,
1616 metadata_.version, &metadata_);
1617 }
1618
CreateConnection(IndexedDBOriginStateHandle origin_state_handle,scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks)1619 std::unique_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection(
1620 IndexedDBOriginStateHandle origin_state_handle,
1621 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks) {
1622 std::unique_ptr<IndexedDBConnection> connection =
1623 std::make_unique<IndexedDBConnection>(
1624 std::move(origin_state_handle), class_factory_,
1625 weak_factory_.GetWeakPtr(),
1626 base::BindRepeating(&IndexedDBDatabase::VersionChangeIgnored,
1627 weak_factory_.GetWeakPtr()),
1628 base::BindOnce(&IndexedDBDatabase::ConnectionClosed,
1629 weak_factory_.GetWeakPtr()),
1630 database_callbacks);
1631 connections_.insert(connection.get());
1632 return connection;
1633 }
1634
VersionChangeIgnored()1635 void IndexedDBDatabase::VersionChangeIgnored() {
1636 connection_coordinator_.OnVersionChangeIgnored();
1637 }
1638
HasNoConnections() const1639 bool IndexedDBDatabase::HasNoConnections() const {
1640 return force_closing_ || connections().empty();
1641 }
1642
SendVersionChangeToAllConnections(int64_t old_version,int64_t new_version)1643 void IndexedDBDatabase::SendVersionChangeToAllConnections(int64_t old_version,
1644 int64_t new_version) {
1645 if (force_closing_)
1646 return;
1647 for (const auto* connection : connections())
1648 connection->callbacks()->OnVersionChange(old_version, new_version);
1649 }
1650
ConnectionClosed(IndexedDBConnection * connection)1651 void IndexedDBDatabase::ConnectionClosed(IndexedDBConnection* connection) {
1652 IDB_TRACE("IndexedDBDatabase::ConnectionClosed");
1653 // Ignore connection closes during force close to prevent re-entry.
1654 if (force_closing_)
1655 return;
1656 connections_.erase(connection);
1657 connection_coordinator_.OnConnectionClosed(connection);
1658 if (connections_.empty())
1659 connection_coordinator_.OnNoConnections();
1660 if (CanBeDestroyed())
1661 tasks_available_callback_.Run();
1662 }
1663
CanBeDestroyed()1664 bool IndexedDBDatabase::CanBeDestroyed() {
1665 return !connection_coordinator_.HasTasks() && connections_.empty();
1666 }
1667
1668 } // namespace content
1669