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