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       &current_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(&copy);
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, &params->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), &params->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       &current_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