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