Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_database.cc
blob5334541c20a72723aa70fa51694f2cabfdd1efe6
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.
5 #include "content/browser/indexed_db/indexed_db_database.h"
7 #include <math.h>
8 #include <limits>
9 #include <set>
11 #include "base/auto_reset.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "content/browser/indexed_db/indexed_db_blob_info.h"
20 #include "content/browser/indexed_db/indexed_db_class_factory.h"
21 #include "content/browser/indexed_db/indexed_db_connection.h"
22 #include "content/browser/indexed_db/indexed_db_context_impl.h"
23 #include "content/browser/indexed_db/indexed_db_cursor.h"
24 #include "content/browser/indexed_db/indexed_db_factory.h"
25 #include "content/browser/indexed_db/indexed_db_index_writer.h"
26 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
27 #include "content/browser/indexed_db/indexed_db_return_value.h"
28 #include "content/browser/indexed_db/indexed_db_tracing.h"
29 #include "content/browser/indexed_db/indexed_db_transaction.h"
30 #include "content/browser/indexed_db/indexed_db_value.h"
31 #include "content/common/indexed_db/indexed_db_constants.h"
32 #include "content/common/indexed_db/indexed_db_key_path.h"
33 #include "content/common/indexed_db/indexed_db_key_range.h"
34 #include "storage/browser/blob/blob_data_handle.h"
35 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h"
36 #include "third_party/leveldatabase/env_chromium.h"
38 using base::ASCIIToUTF16;
39 using base::Int64ToString16;
40 using blink::WebIDBKeyTypeNumber;
42 namespace content {
44 namespace {
46 // Used for WebCore.IndexedDB.Schema.ObjectStore.KeyPathType and
47 // WebCore.IndexedDB.Schema.Index.KeyPathType histograms. Do not
48 // modify (delete, re-order, renumber) these values other than
49 // the _MAX value.
50 enum HistogramIDBKeyPathType {
51 KEY_PATH_TYPE_NONE = 0,
52 KEY_PATH_TYPE_STRING = 1,
53 KEY_PATH_TYPE_ARRAY = 2,
54 KEY_PATH_TYPE_MAX = 3, // Keep as last/max entry, for histogram range.
57 HistogramIDBKeyPathType HistogramKeyPathType(const IndexedDBKeyPath& key_path) {
58 switch (key_path.type()) {
59 case blink::WebIDBKeyPathTypeNull:
60 return KEY_PATH_TYPE_NONE;
61 case blink::WebIDBKeyPathTypeString:
62 return KEY_PATH_TYPE_STRING;
63 case blink::WebIDBKeyPathTypeArray:
64 return KEY_PATH_TYPE_ARRAY;
66 NOTREACHED();
67 return KEY_PATH_TYPE_NONE;
70 } // namespace
72 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
73 // in-progress connection.
74 class IndexedDBDatabase::PendingUpgradeCall {
75 public:
76 PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks,
77 scoped_ptr<IndexedDBConnection> connection,
78 int64 transaction_id,
79 int64 version)
80 : callbacks_(callbacks),
81 connection_(connection.Pass()),
82 version_(version),
83 transaction_id_(transaction_id) {}
84 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
85 // Takes ownership of the connection object.
86 scoped_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT {
87 return connection_.Pass();
89 int64 version() const { return version_; }
90 int64 transaction_id() const { return transaction_id_; }
92 private:
93 scoped_refptr<IndexedDBCallbacks> callbacks_;
94 scoped_ptr<IndexedDBConnection> connection_;
95 int64 version_;
96 const int64 transaction_id_;
99 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
100 // owned elsewhere, but we need to cancel the success call if that connection
101 // closes before it is sent.
102 class IndexedDBDatabase::PendingSuccessCall {
103 public:
104 PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks,
105 IndexedDBConnection* connection,
106 int64 version)
107 : callbacks_(callbacks), connection_(connection), version_(version) {}
108 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
109 IndexedDBConnection* connection() const { return connection_; }
110 int64 version() const { return version_; }
112 private:
113 scoped_refptr<IndexedDBCallbacks> callbacks_;
114 IndexedDBConnection* connection_;
115 int64 version_;
118 class IndexedDBDatabase::PendingDeleteCall {
119 public:
120 explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
121 : callbacks_(callbacks) {}
122 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
124 private:
125 scoped_refptr<IndexedDBCallbacks> callbacks_;
128 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
129 const base::string16& name,
130 IndexedDBBackingStore* backing_store,
131 IndexedDBFactory* factory,
132 const Identifier& unique_identifier,
133 leveldb::Status* s) {
134 scoped_refptr<IndexedDBDatabase> database =
135 IndexedDBClassFactory::Get()->CreateIndexedDBDatabase(
136 name, backing_store, factory, unique_identifier);
137 *s = database->OpenInternal();
138 if (s->ok())
139 return database;
140 else
141 return NULL;
144 namespace {
145 const base::string16::value_type kNoStringVersion[] = {0};
148 IndexedDBDatabase::IndexedDBDatabase(const base::string16& name,
149 IndexedDBBackingStore* backing_store,
150 IndexedDBFactory* factory,
151 const Identifier& unique_identifier)
152 : backing_store_(backing_store),
153 metadata_(name,
154 kInvalidId,
155 kNoStringVersion,
156 IndexedDBDatabaseMetadata::NO_INT_VERSION,
157 kInvalidId),
158 identifier_(unique_identifier),
159 factory_(factory) {
160 DCHECK(factory != NULL);
163 void IndexedDBDatabase::AddObjectStore(
164 const IndexedDBObjectStoreMetadata& object_store,
165 int64 new_max_object_store_id) {
166 DCHECK(metadata_.object_stores.find(object_store.id) ==
167 metadata_.object_stores.end());
168 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
169 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
170 metadata_.max_object_store_id = new_max_object_store_id;
172 metadata_.object_stores[object_store.id] = object_store;
175 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) {
176 DCHECK(metadata_.object_stores.find(object_store_id) !=
177 metadata_.object_stores.end());
178 metadata_.object_stores.erase(object_store_id);
181 void IndexedDBDatabase::AddIndex(int64 object_store_id,
182 const IndexedDBIndexMetadata& index,
183 int64 new_max_index_id) {
184 DCHECK(metadata_.object_stores.find(object_store_id) !=
185 metadata_.object_stores.end());
186 IndexedDBObjectStoreMetadata object_store =
187 metadata_.object_stores[object_store_id];
189 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
190 object_store.indexes[index.id] = index;
191 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
192 DCHECK_LT(object_store.max_index_id, new_max_index_id);
193 object_store.max_index_id = new_max_index_id;
195 metadata_.object_stores[object_store_id] = object_store;
198 void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
199 DCHECK(metadata_.object_stores.find(object_store_id) !=
200 metadata_.object_stores.end());
201 IndexedDBObjectStoreMetadata object_store =
202 metadata_.object_stores[object_store_id];
204 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
205 object_store.indexes.erase(index_id);
206 metadata_.object_stores[object_store_id] = object_store;
209 leveldb::Status IndexedDBDatabase::OpenInternal() {
210 bool success = false;
211 leveldb::Status s = backing_store_->GetIDBDatabaseMetaData(
212 metadata_.name, &metadata_, &success);
213 DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
214 << " id = " << metadata_.id;
215 if (!s.ok())
216 return s;
217 if (success)
218 return backing_store_->GetObjectStores(metadata_.id,
219 &metadata_.object_stores);
221 return backing_store_->CreateIDBDatabaseMetaData(
222 metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id);
225 IndexedDBDatabase::~IndexedDBDatabase() {
226 DCHECK(transactions_.empty());
227 DCHECK(pending_open_calls_.empty());
228 DCHECK(pending_delete_calls_.empty());
231 size_t IndexedDBDatabase::GetMaxMessageSizeInBytes() const {
232 return kMaxIDBMessageSizeInBytes;
235 scoped_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection(
236 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
237 int child_process_id) {
238 scoped_ptr<IndexedDBConnection> connection(
239 new IndexedDBConnection(this, database_callbacks));
240 connections_.insert(connection.get());
241 backing_store_->GrantChildProcessPermissions(child_process_id);
242 return connection.Pass();
245 IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
246 int64 transaction_id) const {
247 TransactionMap::const_iterator trans_iterator =
248 transactions_.find(transaction_id);
249 if (trans_iterator == transactions_.end())
250 return NULL;
251 return trans_iterator->second;
254 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const {
255 if (!ContainsKey(metadata_.object_stores, object_store_id)) {
256 DLOG(ERROR) << "Invalid object_store_id";
257 return false;
259 return true;
262 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id,
263 int64 index_id) const {
264 if (!ValidateObjectStoreId(object_store_id))
265 return false;
266 const IndexedDBObjectStoreMetadata& object_store_metadata =
267 metadata_.object_stores.find(object_store_id)->second;
268 if (!ContainsKey(object_store_metadata.indexes, index_id)) {
269 DLOG(ERROR) << "Invalid index_id";
270 return false;
272 return true;
275 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
276 int64 object_store_id,
277 int64 index_id) const {
278 if (!ValidateObjectStoreId(object_store_id))
279 return false;
280 const IndexedDBObjectStoreMetadata& object_store_metadata =
281 metadata_.object_stores.find(object_store_id)->second;
282 if (index_id != IndexedDBIndexMetadata::kInvalidId &&
283 !ContainsKey(object_store_metadata.indexes, index_id)) {
284 DLOG(ERROR) << "Invalid index_id";
285 return false;
287 return true;
290 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
291 int64 object_store_id,
292 int64 index_id) const {
293 if (!ValidateObjectStoreId(object_store_id))
294 return false;
295 const IndexedDBObjectStoreMetadata& object_store_metadata =
296 metadata_.object_stores.find(object_store_id)->second;
297 if (ContainsKey(object_store_metadata.indexes, index_id)) {
298 DLOG(ERROR) << "Invalid index_id";
299 return false;
301 return true;
304 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
305 int64 object_store_id,
306 const base::string16& name,
307 const IndexedDBKeyPath& key_path,
308 bool auto_increment) {
309 IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id);
310 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
311 if (!transaction)
312 return;
313 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
315 if (ContainsKey(metadata_.object_stores, object_store_id)) {
316 DLOG(ERROR) << "Invalid object_store_id";
317 return;
320 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.ObjectStore.KeyPathType",
321 HistogramKeyPathType(key_path), KEY_PATH_TYPE_MAX);
322 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.ObjectStore.AutoIncrement",
323 auto_increment);
325 // Store creation is done synchronously, as it may be followed by
326 // index creation (also sync) since preemptive OpenCursor/SetIndexKeys
327 // may follow.
328 IndexedDBObjectStoreMetadata object_store_metadata(
329 name,
330 object_store_id,
331 key_path,
332 auto_increment,
333 IndexedDBDatabase::kMinimumIndexId);
335 leveldb::Status s =
336 backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(),
337 transaction->database()->id(),
338 object_store_metadata.id,
339 object_store_metadata.name,
340 object_store_metadata.key_path,
341 object_store_metadata.auto_increment);
342 if (!s.ok()) {
343 IndexedDBDatabaseError error(
344 blink::WebIDBDatabaseExceptionUnknownError,
345 ASCIIToUTF16("Internal error creating object store '") +
346 object_store_metadata.name + ASCIIToUTF16("'."));
347 transaction->Abort(error);
348 if (s.IsCorruption())
349 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
350 error);
351 return;
354 AddObjectStore(object_store_metadata, object_store_id);
355 transaction->ScheduleAbortTask(
356 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
357 this,
358 object_store_id));
361 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
362 int64 object_store_id) {
363 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id);
364 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
365 if (!transaction)
366 return;
367 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
369 if (!ValidateObjectStoreId(object_store_id))
370 return;
372 transaction->ScheduleTask(
373 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation,
374 this,
375 object_store_id));
378 void IndexedDBDatabase::CreateIndex(int64 transaction_id,
379 int64 object_store_id,
380 int64 index_id,
381 const base::string16& name,
382 const IndexedDBKeyPath& key_path,
383 bool unique,
384 bool multi_entry) {
385 IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id);
386 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
387 if (!transaction)
388 return;
389 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
391 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
392 return;
394 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.Index.KeyPathType",
395 HistogramKeyPathType(key_path), KEY_PATH_TYPE_MAX);
396 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.Unique", unique);
397 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.MultiEntry",
398 multi_entry);
400 // Index creation is done synchronously since preemptive
401 // OpenCursor/SetIndexKeys may follow.
402 const IndexedDBIndexMetadata index_metadata(
403 name, index_id, key_path, unique, multi_entry);
405 if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
406 transaction->database()->id(),
407 object_store_id,
408 index_metadata.id,
409 index_metadata.name,
410 index_metadata.key_path,
411 index_metadata.unique,
412 index_metadata.multi_entry).ok()) {
413 base::string16 error_string =
414 ASCIIToUTF16("Internal error creating index '") +
415 index_metadata.name + ASCIIToUTF16("'.");
416 transaction->Abort(IndexedDBDatabaseError(
417 blink::WebIDBDatabaseExceptionUnknownError, error_string));
418 return;
421 AddIndex(object_store_id, index_metadata, index_id);
422 transaction->ScheduleAbortTask(
423 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
424 this,
425 object_store_id,
426 index_id));
429 void IndexedDBDatabase::CreateIndexAbortOperation(
430 int64 object_store_id,
431 int64 index_id,
432 IndexedDBTransaction* transaction) {
433 DCHECK(!transaction);
434 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
435 RemoveIndex(object_store_id, index_id);
438 void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
439 int64 object_store_id,
440 int64 index_id) {
441 IDB_TRACE1("IndexedDBDatabase::DeleteIndex", "txn.id", transaction_id);
442 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
443 if (!transaction)
444 return;
445 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
447 if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
448 return;
450 transaction->ScheduleTask(
451 base::Bind(&IndexedDBDatabase::DeleteIndexOperation,
452 this,
453 object_store_id,
454 index_id));
457 void IndexedDBDatabase::DeleteIndexOperation(
458 int64 object_store_id,
459 int64 index_id,
460 IndexedDBTransaction* transaction) {
461 IDB_TRACE1(
462 "IndexedDBDatabase::DeleteIndexOperation", "txn.id", transaction->id());
464 const IndexedDBIndexMetadata index_metadata =
465 metadata_.object_stores[object_store_id].indexes[index_id];
467 leveldb::Status s =
468 backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
469 transaction->database()->id(),
470 object_store_id,
471 index_id);
472 if (!s.ok()) {
473 base::string16 error_string =
474 ASCIIToUTF16("Internal error deleting index '") +
475 index_metadata.name + ASCIIToUTF16("'.");
476 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
477 error_string);
478 transaction->Abort(error);
479 if (s.IsCorruption())
480 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
481 error);
482 return;
485 RemoveIndex(object_store_id, index_id);
486 transaction->ScheduleAbortTask(
487 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
488 this,
489 object_store_id,
490 index_metadata));
493 void IndexedDBDatabase::DeleteIndexAbortOperation(
494 int64 object_store_id,
495 const IndexedDBIndexMetadata& index_metadata,
496 IndexedDBTransaction* transaction) {
497 DCHECK(!transaction);
498 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
499 AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId);
502 void IndexedDBDatabase::Commit(int64 transaction_id) {
503 // The frontend suggests that we commit, but we may have previously initiated
504 // an abort, and so have disposed of the transaction. on_abort has already
505 // been dispatched to the frontend, so it will find out about that
506 // asynchronously.
507 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
508 if (transaction) {
509 scoped_refptr<IndexedDBFactory> factory = factory_;
510 leveldb::Status s = transaction->Commit();
511 if (s.IsCorruption()) {
512 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
513 "Internal error committing transaction.");
514 factory->HandleBackingStoreCorruption(identifier_.first, error);
519 void IndexedDBDatabase::Abort(int64 transaction_id) {
520 // If the transaction is unknown, then it has already been aborted by the
521 // backend before this call so it is safe to ignore it.
522 IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id);
523 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
524 if (transaction)
525 transaction->Abort();
528 void IndexedDBDatabase::Abort(int64 transaction_id,
529 const IndexedDBDatabaseError& error) {
530 IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id);
531 // If the transaction is unknown, then it has already been aborted by the
532 // backend before this call so it is safe to ignore it.
533 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
534 if (transaction)
535 transaction->Abort(error);
538 void IndexedDBDatabase::GetAll(int64 transaction_id,
539 int64 object_store_id,
540 int64 index_id,
541 scoped_ptr<IndexedDBKeyRange> key_range,
542 bool key_only,
543 int64 max_count,
544 scoped_refptr<IndexedDBCallbacks> callbacks) {
545 IDB_TRACE1("IndexedDBDatabase::GetAll", "txn.id", transaction_id);
546 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
547 if (!transaction)
548 return;
550 if (!ValidateObjectStoreId(object_store_id))
551 return;
553 transaction->ScheduleTask(base::Bind(
554 &IndexedDBDatabase::GetAllOperation, this, object_store_id, index_id,
555 Passed(&key_range),
556 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
557 max_count, callbacks));
560 void IndexedDBDatabase::Get(int64 transaction_id,
561 int64 object_store_id,
562 int64 index_id,
563 scoped_ptr<IndexedDBKeyRange> key_range,
564 bool key_only,
565 scoped_refptr<IndexedDBCallbacks> callbacks) {
566 IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id);
567 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
568 if (!transaction)
569 return;
571 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
572 return;
574 transaction->ScheduleTask(base::Bind(
575 &IndexedDBDatabase::GetOperation,
576 this,
577 object_store_id,
578 index_id,
579 Passed(&key_range),
580 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
581 callbacks));
584 void IndexedDBDatabase::GetOperation(
585 int64 object_store_id,
586 int64 index_id,
587 scoped_ptr<IndexedDBKeyRange> key_range,
588 indexed_db::CursorType cursor_type,
589 scoped_refptr<IndexedDBCallbacks> callbacks,
590 IndexedDBTransaction* transaction) {
591 IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction->id());
593 DCHECK(metadata_.object_stores.find(object_store_id) !=
594 metadata_.object_stores.end());
595 const IndexedDBObjectStoreMetadata& object_store_metadata =
596 metadata_.object_stores[object_store_id];
598 const IndexedDBKey* key;
600 leveldb::Status s;
601 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
602 if (key_range->IsOnlyKey()) {
603 key = &key_range->lower();
604 } else {
605 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
606 DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY);
607 // ObjectStore Retrieval Operation
608 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
609 transaction->BackingStoreTransaction(),
610 id(),
611 object_store_id,
612 *key_range,
613 blink::WebIDBCursorDirectionNext,
614 &s);
615 } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
616 // Index Value Retrieval Operation
617 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
618 transaction->BackingStoreTransaction(),
619 id(),
620 object_store_id,
621 index_id,
622 *key_range,
623 blink::WebIDBCursorDirectionNext,
624 &s);
625 } else {
626 // Index Referenced Value Retrieval Operation
627 backing_store_cursor = backing_store_->OpenIndexCursor(
628 transaction->BackingStoreTransaction(),
629 id(),
630 object_store_id,
631 index_id,
632 *key_range,
633 blink::WebIDBCursorDirectionNext,
634 &s);
637 if (!s.ok()) {
638 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
639 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
640 "Internal error deleting data in range");
641 if (s.IsCorruption()) {
642 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
643 error);
647 if (!backing_store_cursor) {
648 callbacks->OnSuccess();
649 return;
652 key = &backing_store_cursor->key();
655 scoped_ptr<IndexedDBKey> primary_key;
656 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
657 // Object Store Retrieval Operation
658 IndexedDBReturnValue value;
659 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
660 id(),
661 object_store_id,
662 *key,
663 &value);
664 if (!s.ok()) {
665 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
666 "Internal error in GetRecord.");
667 callbacks->OnError(error);
669 if (s.IsCorruption())
670 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
671 error);
672 return;
675 if (value.empty()) {
676 callbacks->OnSuccess();
677 return;
680 if (object_store_metadata.auto_increment &&
681 !object_store_metadata.key_path.IsNull()) {
682 value.primary_key = *key;
683 value.key_path = object_store_metadata.key_path;
686 callbacks->OnSuccess(&value);
687 return;
690 // From here we are dealing only with indexes.
691 s = backing_store_->GetPrimaryKeyViaIndex(
692 transaction->BackingStoreTransaction(),
693 id(),
694 object_store_id,
695 index_id,
696 *key,
697 &primary_key);
698 if (!s.ok()) {
699 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
700 "Internal error in GetPrimaryKeyViaIndex.");
701 callbacks->OnError(error);
702 if (s.IsCorruption())
703 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
704 error);
705 return;
707 if (!primary_key) {
708 callbacks->OnSuccess();
709 return;
711 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
712 // Index Value Retrieval Operation
713 callbacks->OnSuccess(*primary_key);
714 return;
717 // Index Referenced Value Retrieval Operation
718 IndexedDBReturnValue value;
719 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
720 id(),
721 object_store_id,
722 *primary_key,
723 &value);
724 if (!s.ok()) {
725 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
726 "Internal error in GetRecord.");
727 callbacks->OnError(error);
728 if (s.IsCorruption())
729 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
730 error);
731 return;
734 if (value.empty()) {
735 callbacks->OnSuccess();
736 return;
738 if (object_store_metadata.auto_increment &&
739 !object_store_metadata.key_path.IsNull()) {
740 value.primary_key = *primary_key;
741 value.key_path = object_store_metadata.key_path;
743 callbacks->OnSuccess(&value);
746 void IndexedDBDatabase::GetAllOperation(
747 int64 object_store_id,
748 int64 index_id,
749 scoped_ptr<IndexedDBKeyRange> key_range,
750 indexed_db::CursorType cursor_type,
751 int64 max_count,
752 scoped_refptr<IndexedDBCallbacks> callbacks,
753 IndexedDBTransaction* transaction) {
754 IDB_TRACE1("IndexedDBDatabase::GetAllOperation", "txn.id", transaction->id());
756 DCHECK_GT(max_count, 0);
758 DCHECK(metadata_.object_stores.find(object_store_id) !=
759 metadata_.object_stores.end());
760 const IndexedDBObjectStoreMetadata& object_store_metadata =
761 metadata_.object_stores[object_store_id];
763 leveldb::Status s;
765 scoped_ptr<IndexedDBBackingStore::Cursor> cursor;
767 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
768 // Retrieving keys
769 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
770 // Object Store: Key Retrieval Operation
771 cursor = backing_store_->OpenObjectStoreKeyCursor(
772 transaction->BackingStoreTransaction(), id(), object_store_id,
773 *key_range, blink::WebIDBCursorDirectionNext, &s);
774 } else {
775 // Index Value: (Primary Key) Retrieval Operation
776 cursor = backing_store_->OpenIndexKeyCursor(
777 transaction->BackingStoreTransaction(), id(), object_store_id,
778 index_id, *key_range, blink::WebIDBCursorDirectionNext, &s);
780 } else {
781 // Retrieving values
782 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
783 // Object Store: Value Retrieval Operation
784 cursor = backing_store_->OpenObjectStoreCursor(
785 transaction->BackingStoreTransaction(), id(), object_store_id,
786 *key_range, blink::WebIDBCursorDirectionNext, &s);
787 } else {
788 // Object Store: Referenced Value Retrieval Operation
789 cursor = backing_store_->OpenIndexCursor(
790 transaction->BackingStoreTransaction(), id(), object_store_id,
791 index_id, *key_range, blink::WebIDBCursorDirectionNext, &s);
795 if (!s.ok()) {
796 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
797 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
798 "Internal error in GetAllOperation");
799 callbacks->OnError(error);
800 if (s.IsCorruption()) {
801 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
802 error);
804 return;
807 std::vector<IndexedDBKey> found_keys;
808 std::vector<IndexedDBReturnValue> found_values;
809 if (!cursor) {
810 // Doesn't matter if key or value array here - will be empty array when it
811 // hits JavaScript.
812 callbacks->OnSuccessArray(&found_values, object_store_metadata.key_path);
813 return;
816 bool did_first_seek = false;
817 bool generated_key = object_store_metadata.auto_increment &&
818 !object_store_metadata.key_path.IsNull();
820 size_t response_size = kMaxIDBMessageOverhead;
821 int64 num_found_items = 0;
822 while (num_found_items++ < max_count) {
823 bool cursor_valid;
824 if (did_first_seek) {
825 cursor_valid = cursor->Continue(&s);
826 } else {
827 cursor_valid = cursor->FirstSeek(&s);
828 did_first_seek = true;
830 if (!s.ok()) {
831 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
832 "Internal error in GetAllOperation.");
833 callbacks->OnError(error);
834 if (s.IsCorruption())
835 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
836 error);
837 return;
840 if (!cursor_valid)
841 break;
843 IndexedDBReturnValue return_value;
844 IndexedDBKey return_key;
846 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
847 return_key = cursor->primary_key();
848 } else {
849 // Retrieving values
850 return_value.swap(*cursor->value());
851 if (!return_value.empty() && generated_key) {
852 return_value.primary_key = cursor->primary_key();
853 return_value.key_path = object_store_metadata.key_path;
857 if (cursor_type == indexed_db::CURSOR_KEY_ONLY)
858 response_size += return_key.size_estimate();
859 else
860 response_size += return_value.SizeEstimate();
861 if (response_size > GetMaxMessageSizeInBytes()) {
862 callbacks->OnError(
863 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
864 "Maximum IPC message size exceeded."));
865 return;
868 if (cursor_type == indexed_db::CURSOR_KEY_ONLY)
869 found_keys.push_back(return_key);
870 else
871 found_values.push_back(return_value);
874 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
875 // IndexedDBKey already supports an array of values so we can leverage this
876 // to return an array of keys - no need to create our own array of keys.
877 callbacks->OnSuccess(IndexedDBKey(found_keys));
878 } else {
879 callbacks->OnSuccessArray(&found_values, object_store_metadata.key_path);
883 static scoped_ptr<IndexedDBKey> GenerateKey(
884 IndexedDBBackingStore* backing_store,
885 IndexedDBTransaction* transaction,
886 int64 database_id,
887 int64 object_store_id) {
888 const int64 max_generator_value =
889 9007199254740992LL; // Maximum integer storable as ECMAScript number.
890 int64 current_number;
891 leveldb::Status s = backing_store->GetKeyGeneratorCurrentNumber(
892 transaction->BackingStoreTransaction(),
893 database_id,
894 object_store_id,
895 &current_number);
896 if (!s.ok()) {
897 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
898 return make_scoped_ptr(new IndexedDBKey());
900 if (current_number < 0 || current_number > max_generator_value)
901 return make_scoped_ptr(new IndexedDBKey());
903 return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber));
906 static leveldb::Status UpdateKeyGenerator(IndexedDBBackingStore* backing_store,
907 IndexedDBTransaction* transaction,
908 int64 database_id,
909 int64 object_store_id,
910 const IndexedDBKey& key,
911 bool check_current) {
912 DCHECK_EQ(WebIDBKeyTypeNumber, key.type());
913 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
914 transaction->BackingStoreTransaction(),
915 database_id,
916 object_store_id,
917 static_cast<int64>(floor(key.number())) + 1,
918 check_current);
921 struct IndexedDBDatabase::PutOperationParams {
922 PutOperationParams() {}
923 int64 object_store_id;
924 IndexedDBValue value;
925 ScopedVector<storage::BlobDataHandle> handles;
926 scoped_ptr<IndexedDBKey> key;
927 blink::WebIDBPutMode put_mode;
928 scoped_refptr<IndexedDBCallbacks> callbacks;
929 std::vector<IndexKeys> index_keys;
931 private:
932 DISALLOW_COPY_AND_ASSIGN(PutOperationParams);
935 void IndexedDBDatabase::Put(int64 transaction_id,
936 int64 object_store_id,
937 IndexedDBValue* value,
938 ScopedVector<storage::BlobDataHandle>* handles,
939 scoped_ptr<IndexedDBKey> key,
940 blink::WebIDBPutMode put_mode,
941 scoped_refptr<IndexedDBCallbacks> callbacks,
942 const std::vector<IndexKeys>& index_keys) {
943 IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id);
944 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
945 if (!transaction)
946 return;
947 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
949 if (!ValidateObjectStoreId(object_store_id))
950 return;
952 DCHECK(key);
953 DCHECK(value);
954 scoped_ptr<PutOperationParams> params(new PutOperationParams());
955 params->object_store_id = object_store_id;
956 params->value.swap(*value);
957 params->handles.swap(*handles);
958 params->key = key.Pass();
959 params->put_mode = put_mode;
960 params->callbacks = callbacks;
961 params->index_keys = index_keys;
962 transaction->ScheduleTask(base::Bind(
963 &IndexedDBDatabase::PutOperation, this, base::Passed(&params)));
966 void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
967 IndexedDBTransaction* transaction) {
968 IDB_TRACE1("IndexedDBDatabase::PutOperation", "txn.id", transaction->id());
969 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
970 bool key_was_generated = false;
972 DCHECK(metadata_.object_stores.find(params->object_store_id) !=
973 metadata_.object_stores.end());
974 const IndexedDBObjectStoreMetadata& object_store =
975 metadata_.object_stores[params->object_store_id];
976 DCHECK(object_store.auto_increment || params->key->IsValid());
978 scoped_ptr<IndexedDBKey> key;
979 if (params->put_mode != blink::WebIDBPutModeCursorUpdate &&
980 object_store.auto_increment && !params->key->IsValid()) {
981 scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
982 backing_store_.get(), transaction, id(), params->object_store_id);
983 key_was_generated = true;
984 if (!auto_inc_key->IsValid()) {
985 params->callbacks->OnError(
986 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
987 "Maximum key generator value reached."));
988 return;
990 key = auto_inc_key.Pass();
991 } else {
992 key = params->key.Pass();
995 DCHECK(key->IsValid());
997 IndexedDBBackingStore::RecordIdentifier record_identifier;
998 if (params->put_mode == blink::WebIDBPutModeAddOnly) {
999 bool found = false;
1000 leveldb::Status s = backing_store_->KeyExistsInObjectStore(
1001 transaction->BackingStoreTransaction(),
1002 id(),
1003 params->object_store_id,
1004 *key,
1005 &record_identifier,
1006 &found);
1007 if (!s.ok()) {
1008 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1009 "Internal error checking key existence.");
1010 params->callbacks->OnError(error);
1011 if (s.IsCorruption())
1012 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1013 error);
1014 return;
1016 if (found) {
1017 params->callbacks->OnError(
1018 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
1019 "Key already exists in the object store."));
1020 return;
1024 ScopedVector<IndexWriter> index_writers;
1025 base::string16 error_message;
1026 bool obeys_constraints = false;
1027 bool backing_store_success = MakeIndexWriters(transaction,
1028 backing_store_.get(),
1029 id(),
1030 object_store,
1031 *key,
1032 key_was_generated,
1033 params->index_keys,
1034 &index_writers,
1035 &error_message,
1036 &obeys_constraints);
1037 if (!backing_store_success) {
1038 params->callbacks->OnError(IndexedDBDatabaseError(
1039 blink::WebIDBDatabaseExceptionUnknownError,
1040 "Internal error: backing store error updating index keys."));
1041 return;
1043 if (!obeys_constraints) {
1044 params->callbacks->OnError(IndexedDBDatabaseError(
1045 blink::WebIDBDatabaseExceptionConstraintError, error_message));
1046 return;
1049 // Before this point, don't do any mutation. After this point, rollback the
1050 // transaction in case of error.
1051 leveldb::Status s =
1052 backing_store_->PutRecord(transaction->BackingStoreTransaction(),
1053 id(),
1054 params->object_store_id,
1055 *key,
1056 &params->value,
1057 &params->handles,
1058 &record_identifier);
1059 if (!s.ok()) {
1060 IndexedDBDatabaseError error(
1061 blink::WebIDBDatabaseExceptionUnknownError,
1062 "Internal error: backing store error performing put/add.");
1063 params->callbacks->OnError(error);
1064 if (s.IsCorruption())
1065 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1066 error);
1067 return;
1070 IDB_TRACE1("IndexedDBDatabase::PutOperation.UpdateIndexes", "txn.id",
1071 transaction->id());
1072 for (size_t i = 0; i < index_writers.size(); ++i) {
1073 IndexWriter* index_writer = index_writers[i];
1074 index_writer->WriteIndexKeys(record_identifier, backing_store_.get(),
1075 transaction->BackingStoreTransaction(), id(),
1076 params->object_store_id);
1080 if (object_store.auto_increment &&
1081 params->put_mode != blink::WebIDBPutModeCursorUpdate &&
1082 key->type() == WebIDBKeyTypeNumber) {
1083 IDB_TRACE1("IndexedDBDatabase::PutOperation.AutoIncrement", "txn.id",
1084 transaction->id());
1085 leveldb::Status s = UpdateKeyGenerator(backing_store_.get(),
1086 transaction,
1087 id(),
1088 params->object_store_id,
1089 *key,
1090 !key_was_generated);
1091 if (!s.ok()) {
1092 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1093 "Internal error updating key generator.");
1094 params->callbacks->OnError(error);
1095 if (s.IsCorruption())
1096 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1097 error);
1098 return;
1102 IDB_TRACE1("IndexedDBDatabase::PutOperation.Callbacks", "txn.id",
1103 transaction->id());
1104 params->callbacks->OnSuccess(*key);
1108 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
1109 int64 object_store_id,
1110 scoped_ptr<IndexedDBKey> primary_key,
1111 const std::vector<IndexKeys>& index_keys) {
1112 IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id);
1113 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1114 if (!transaction)
1115 return;
1116 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
1118 // TODO(alecflett): This method could be asynchronous, but we need to
1119 // evaluate if it's worth the extra complexity.
1120 IndexedDBBackingStore::RecordIdentifier record_identifier;
1121 bool found = false;
1122 leveldb::Status s = backing_store_->KeyExistsInObjectStore(
1123 transaction->BackingStoreTransaction(),
1124 metadata_.id,
1125 object_store_id,
1126 *primary_key,
1127 &record_identifier,
1128 &found);
1129 if (!s.ok()) {
1130 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1131 "Internal error setting index keys.");
1132 transaction->Abort(error);
1133 if (s.IsCorruption())
1134 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1135 error);
1136 return;
1138 if (!found) {
1139 transaction->Abort(IndexedDBDatabaseError(
1140 blink::WebIDBDatabaseExceptionUnknownError,
1141 "Internal error setting index keys for object store."));
1142 return;
1145 ScopedVector<IndexWriter> index_writers;
1146 base::string16 error_message;
1147 bool obeys_constraints = false;
1148 DCHECK(metadata_.object_stores.find(object_store_id) !=
1149 metadata_.object_stores.end());
1150 const IndexedDBObjectStoreMetadata& object_store_metadata =
1151 metadata_.object_stores[object_store_id];
1152 bool backing_store_success = MakeIndexWriters(transaction,
1153 backing_store_.get(),
1154 id(),
1155 object_store_metadata,
1156 *primary_key,
1157 false,
1158 index_keys,
1159 &index_writers,
1160 &error_message,
1161 &obeys_constraints);
1162 if (!backing_store_success) {
1163 transaction->Abort(IndexedDBDatabaseError(
1164 blink::WebIDBDatabaseExceptionUnknownError,
1165 "Internal error: backing store error updating index keys."));
1166 return;
1168 if (!obeys_constraints) {
1169 transaction->Abort(IndexedDBDatabaseError(
1170 blink::WebIDBDatabaseExceptionConstraintError, error_message));
1171 return;
1174 for (size_t i = 0; i < index_writers.size(); ++i) {
1175 IndexWriter* index_writer = index_writers[i];
1176 index_writer->WriteIndexKeys(record_identifier,
1177 backing_store_.get(),
1178 transaction->BackingStoreTransaction(),
1179 id(),
1180 object_store_id);
1184 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
1185 int64,
1186 const std::vector<int64>& index_ids) {
1187 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1188 if (!transaction)
1189 return;
1190 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
1192 transaction->ScheduleTask(
1193 blink::WebIDBTaskTypePreemptive,
1194 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation,
1195 this,
1196 index_ids.size()));
1199 void IndexedDBDatabase::SetIndexesReadyOperation(
1200 size_t index_count,
1201 IndexedDBTransaction* transaction) {
1202 for (size_t i = 0; i < index_count; ++i)
1203 transaction->DidCompletePreemptiveEvent();
1206 struct IndexedDBDatabase::OpenCursorOperationParams {
1207 OpenCursorOperationParams() {}
1208 int64 object_store_id;
1209 int64 index_id;
1210 scoped_ptr<IndexedDBKeyRange> key_range;
1211 blink::WebIDBCursorDirection direction;
1212 indexed_db::CursorType cursor_type;
1213 blink::WebIDBTaskType task_type;
1214 scoped_refptr<IndexedDBCallbacks> callbacks;
1216 private:
1217 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams);
1220 void IndexedDBDatabase::OpenCursor(
1221 int64 transaction_id,
1222 int64 object_store_id,
1223 int64 index_id,
1224 scoped_ptr<IndexedDBKeyRange> key_range,
1225 blink::WebIDBCursorDirection direction,
1226 bool key_only,
1227 blink::WebIDBTaskType task_type,
1228 scoped_refptr<IndexedDBCallbacks> callbacks) {
1229 IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id);
1230 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1231 if (!transaction)
1232 return;
1234 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1235 return;
1237 scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams());
1238 params->object_store_id = object_store_id;
1239 params->index_id = index_id;
1240 params->key_range = key_range.Pass();
1241 params->direction = direction;
1242 params->cursor_type =
1243 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE;
1244 params->task_type = task_type;
1245 params->callbacks = callbacks;
1246 transaction->ScheduleTask(base::Bind(
1247 &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(&params)));
1250 void IndexedDBDatabase::OpenCursorOperation(
1251 scoped_ptr<OpenCursorOperationParams> params,
1252 IndexedDBTransaction* transaction) {
1253 IDB_TRACE1(
1254 "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction->id());
1256 // The frontend has begun indexing, so this pauses the transaction
1257 // until the indexing is complete. This can't happen any earlier
1258 // because we don't want to switch to early mode in case multiple
1259 // indexes are being created in a row, with Put()'s in between.
1260 if (params->task_type == blink::WebIDBTaskTypePreemptive)
1261 transaction->AddPreemptiveEvent();
1263 leveldb::Status s;
1264 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1265 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
1266 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1267 DCHECK_EQ(params->task_type, blink::WebIDBTaskTypeNormal);
1268 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1269 transaction->BackingStoreTransaction(),
1270 id(),
1271 params->object_store_id,
1272 *params->key_range,
1273 params->direction,
1274 &s);
1275 } else {
1276 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1277 transaction->BackingStoreTransaction(),
1278 id(),
1279 params->object_store_id,
1280 *params->key_range,
1281 params->direction,
1282 &s);
1284 } else {
1285 DCHECK_EQ(params->task_type, blink::WebIDBTaskTypeNormal);
1286 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1287 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1288 transaction->BackingStoreTransaction(),
1289 id(),
1290 params->object_store_id,
1291 params->index_id,
1292 *params->key_range,
1293 params->direction,
1294 &s);
1295 } else {
1296 backing_store_cursor = backing_store_->OpenIndexCursor(
1297 transaction->BackingStoreTransaction(),
1298 id(),
1299 params->object_store_id,
1300 params->index_id,
1301 *params->key_range,
1302 params->direction,
1303 &s);
1307 if (!s.ok()) {
1308 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
1309 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1310 "Internal error opening cursor operation");
1311 if (s.IsCorruption()) {
1312 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1313 error);
1317 if (!backing_store_cursor) {
1318 // Why is Success being called?
1319 params->callbacks->OnSuccess(nullptr);
1320 return;
1323 scoped_refptr<IndexedDBCursor> cursor =
1324 new IndexedDBCursor(backing_store_cursor.Pass(),
1325 params->cursor_type,
1326 params->task_type,
1327 transaction);
1328 params->callbacks->OnSuccess(
1329 cursor, cursor->key(), cursor->primary_key(), cursor->Value());
1332 void IndexedDBDatabase::Count(int64 transaction_id,
1333 int64 object_store_id,
1334 int64 index_id,
1335 scoped_ptr<IndexedDBKeyRange> key_range,
1336 scoped_refptr<IndexedDBCallbacks> callbacks) {
1337 IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id);
1338 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1339 if (!transaction)
1340 return;
1342 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1343 return;
1345 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation,
1346 this,
1347 object_store_id,
1348 index_id,
1349 base::Passed(&key_range),
1350 callbacks));
1353 void IndexedDBDatabase::CountOperation(
1354 int64 object_store_id,
1355 int64 index_id,
1356 scoped_ptr<IndexedDBKeyRange> key_range,
1357 scoped_refptr<IndexedDBCallbacks> callbacks,
1358 IndexedDBTransaction* transaction) {
1359 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction->id());
1360 uint32 count = 0;
1361 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1363 leveldb::Status s;
1364 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
1365 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1366 transaction->BackingStoreTransaction(),
1367 id(),
1368 object_store_id,
1369 *key_range,
1370 blink::WebIDBCursorDirectionNext,
1371 &s);
1372 } else {
1373 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1374 transaction->BackingStoreTransaction(),
1375 id(),
1376 object_store_id,
1377 index_id,
1378 *key_range,
1379 blink::WebIDBCursorDirectionNext,
1380 &s);
1382 if (!s.ok()) {
1383 DLOG(ERROR) << "Unable perform count operation: " << s.ToString();
1384 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1385 "Internal error performing count operation");
1386 if (s.IsCorruption()) {
1387 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1388 error);
1391 if (!backing_store_cursor) {
1392 callbacks->OnSuccess(count);
1393 return;
1396 do {
1397 ++count;
1398 } while (backing_store_cursor->Continue(&s));
1400 // TODO(cmumford): Check for database corruption.
1402 callbacks->OnSuccess(count);
1405 void IndexedDBDatabase::DeleteRange(
1406 int64 transaction_id,
1407 int64 object_store_id,
1408 scoped_ptr<IndexedDBKeyRange> key_range,
1409 scoped_refptr<IndexedDBCallbacks> callbacks) {
1410 IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id);
1411 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1412 if (!transaction)
1413 return;
1414 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
1416 if (!ValidateObjectStoreId(object_store_id))
1417 return;
1419 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation,
1420 this,
1421 object_store_id,
1422 base::Passed(&key_range),
1423 callbacks));
1426 void IndexedDBDatabase::DeleteRangeOperation(
1427 int64 object_store_id,
1428 scoped_ptr<IndexedDBKeyRange> key_range,
1429 scoped_refptr<IndexedDBCallbacks> callbacks,
1430 IndexedDBTransaction* transaction) {
1431 IDB_TRACE1(
1432 "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction->id());
1433 leveldb::Status s =
1434 backing_store_->DeleteRange(transaction->BackingStoreTransaction(),
1435 id(),
1436 object_store_id,
1437 *key_range);
1438 if (!s.ok()) {
1439 base::string16 error_string =
1440 ASCIIToUTF16("Internal error deleting data in range");
1441 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1442 error_string);
1443 transaction->Abort(error);
1444 if (s.IsCorruption()) {
1445 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1446 error);
1448 return;
1450 callbacks->OnSuccess();
1453 void IndexedDBDatabase::Clear(int64 transaction_id,
1454 int64 object_store_id,
1455 scoped_refptr<IndexedDBCallbacks> callbacks) {
1456 IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id);
1457 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1458 if (!transaction)
1459 return;
1460 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
1462 if (!ValidateObjectStoreId(object_store_id))
1463 return;
1465 transaction->ScheduleTask(base::Bind(
1466 &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks));
1469 void IndexedDBDatabase::ClearOperation(
1470 int64 object_store_id,
1471 scoped_refptr<IndexedDBCallbacks> callbacks,
1472 IndexedDBTransaction* transaction) {
1473 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction->id());
1474 leveldb::Status s = backing_store_->ClearObjectStore(
1475 transaction->BackingStoreTransaction(), id(), object_store_id);
1476 if (!s.ok()) {
1477 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1478 "Internal error clearing object store");
1479 callbacks->OnError(error);
1480 if (s.IsCorruption()) {
1481 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1482 error);
1484 return;
1486 callbacks->OnSuccess();
1489 void IndexedDBDatabase::DeleteObjectStoreOperation(
1490 int64 object_store_id,
1491 IndexedDBTransaction* transaction) {
1492 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation",
1493 "txn.id",
1494 transaction->id());
1496 const IndexedDBObjectStoreMetadata object_store_metadata =
1497 metadata_.object_stores[object_store_id];
1498 leveldb::Status s =
1499 backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
1500 transaction->database()->id(),
1501 object_store_id);
1502 if (!s.ok()) {
1503 base::string16 error_string =
1504 ASCIIToUTF16("Internal error deleting object store '") +
1505 object_store_metadata.name + ASCIIToUTF16("'.");
1506 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1507 error_string);
1508 transaction->Abort(error);
1509 if (s.IsCorruption())
1510 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1511 error);
1512 return;
1515 RemoveObjectStore(object_store_id);
1516 transaction->ScheduleAbortTask(
1517 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
1518 this,
1519 object_store_metadata));
1522 void IndexedDBDatabase::VersionChangeOperation(
1523 int64 version,
1524 scoped_refptr<IndexedDBCallbacks> callbacks,
1525 scoped_ptr<IndexedDBConnection> connection,
1526 IndexedDBTransaction* transaction) {
1527 IDB_TRACE1(
1528 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id());
1529 int64 old_version = metadata_.int_version;
1530 DCHECK_GT(version, old_version);
1532 if (!backing_store_->UpdateIDBDatabaseIntVersion(
1533 transaction->BackingStoreTransaction(), id(), version)) {
1534 IndexedDBDatabaseError error(
1535 blink::WebIDBDatabaseExceptionUnknownError,
1536 ASCIIToUTF16(
1537 "Internal error writing data to stable storage when "
1538 "updating version."));
1539 callbacks->OnError(error);
1540 transaction->Abort(error);
1541 return;
1544 transaction->ScheduleAbortTask(
1545 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
1546 this,
1547 metadata_.version,
1548 metadata_.int_version));
1549 metadata_.int_version = version;
1550 metadata_.version = kNoStringVersion;
1552 DCHECK(!pending_second_half_open_);
1553 pending_second_half_open_.reset(
1554 new PendingSuccessCall(callbacks, connection.get(), version));
1555 callbacks->OnUpgradeNeeded(old_version, connection.Pass(), metadata());
1558 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
1559 bool committed) {
1560 IDB_TRACE1("IndexedDBTransaction::TransactionFinished", "txn.id", id());
1561 DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1562 DCHECK_EQ(transactions_[transaction->id()], transaction);
1563 transactions_.erase(transaction->id());
1565 if (transaction->mode() == blink::WebIDBTransactionModeVersionChange) {
1566 if (pending_second_half_open_) {
1567 if (committed) {
1568 DCHECK_EQ(pending_second_half_open_->version(), metadata_.int_version);
1569 DCHECK(metadata_.id != kInvalidId);
1571 // Connection was already minted for OnUpgradeNeeded callback.
1572 scoped_ptr<IndexedDBConnection> connection;
1573 pending_second_half_open_->callbacks()->OnSuccess(connection.Pass(),
1574 this->metadata());
1575 } else {
1576 pending_second_half_open_->callbacks()->OnError(
1577 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1578 "Version change transaction was aborted in "
1579 "upgradeneeded event handler."));
1581 pending_second_half_open_.reset();
1584 // Connection queue is now unblocked.
1585 ProcessPendingCalls();
1589 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status& status) {
1590 if (status.IsCorruption()) {
1591 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1592 "Error committing transaction");
1593 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), error);
1594 } else {
1595 factory_->HandleBackingStoreFailure(backing_store_->origin_url());
1599 size_t IndexedDBDatabase::ConnectionCount() const {
1600 // This does not include pending open calls, as those should not block version
1601 // changes and deletes.
1602 return connections_.size();
1605 size_t IndexedDBDatabase::PendingOpenCount() const {
1606 return pending_open_calls_.size();
1609 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1610 return pending_run_version_change_transaction_call_ ? 1 : 0;
1613 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1614 return pending_second_half_open_ ? 1 : 0;
1617 size_t IndexedDBDatabase::PendingDeleteCount() const {
1618 return pending_delete_calls_.size();
1621 void IndexedDBDatabase::ProcessPendingCalls() {
1622 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1623 DCHECK(pending_run_version_change_transaction_call_->version() >
1624 metadata_.int_version);
1625 scoped_ptr<PendingUpgradeCall> pending_call =
1626 pending_run_version_change_transaction_call_.Pass();
1627 RunVersionChangeTransactionFinal(pending_call->callbacks(),
1628 pending_call->ReleaseConnection(),
1629 pending_call->transaction_id(),
1630 pending_call->version());
1631 DCHECK_EQ(1u, ConnectionCount());
1632 // Fall through would be a no-op, since transaction must complete
1633 // asynchronously.
1634 DCHECK(IsDeleteDatabaseBlocked());
1635 DCHECK(IsOpenConnectionBlocked());
1636 return;
1639 if (!IsDeleteDatabaseBlocked()) {
1640 PendingDeleteCallList pending_delete_calls;
1641 pending_delete_calls_.swap(pending_delete_calls);
1642 while (!pending_delete_calls.empty()) {
1643 // Only the first delete call will delete the database, but each must fire
1644 // callbacks.
1645 scoped_ptr<PendingDeleteCall> pending_delete_call(
1646 pending_delete_calls.front());
1647 pending_delete_calls.pop_front();
1648 DeleteDatabaseFinal(pending_delete_call->callbacks());
1650 // delete_database_final should never re-queue calls.
1651 DCHECK(pending_delete_calls_.empty());
1652 // Fall through when complete, as pending opens may be unblocked.
1655 if (!IsOpenConnectionBlocked()) {
1656 PendingOpenCallList pending_open_calls;
1657 pending_open_calls_.swap(pending_open_calls);
1658 while (!pending_open_calls.empty()) {
1659 OpenConnection(pending_open_calls.front());
1660 pending_open_calls.pop_front();
1665 void IndexedDBDatabase::CreateTransaction(
1666 int64 transaction_id,
1667 IndexedDBConnection* connection,
1668 const std::vector<int64>& object_store_ids,
1669 blink::WebIDBTransactionMode mode) {
1670 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id);
1671 DCHECK(connections_.count(connection));
1672 DCHECK(transactions_.find(transaction_id) == transactions_.end());
1673 if (transactions_.find(transaction_id) != transactions_.end())
1674 return;
1676 // The transaction will add itself to this database's coordinator, which
1677 // manages the lifetime of the object.
1678 TransactionCreated(IndexedDBClassFactory::Get()->CreateIndexedDBTransaction(
1679 transaction_id, connection->callbacks(),
1680 std::set<int64>(object_store_ids.begin(), object_store_ids.end()), mode,
1681 this, new IndexedDBBackingStore::Transaction(backing_store_.get())));
1684 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
1685 transactions_[transaction->id()] = transaction;
1688 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1689 return !pending_delete_calls_.empty() ||
1690 transaction_coordinator_.IsRunningVersionChangeTransaction() ||
1691 pending_run_version_change_transaction_call_;
1694 void IndexedDBDatabase::OpenConnection(
1695 const IndexedDBPendingConnection& connection) {
1696 DCHECK(backing_store_.get());
1698 // TODO(jsbell): Should have a priority queue so that higher version
1699 // requests are processed first. http://crbug.com/225850
1700 if (IsOpenConnectionBlocked()) {
1701 // The backing store only detects data loss when it is first opened. The
1702 // presence of existing connections means we didn't even check for data loss
1703 // so there'd better not be any.
1704 DCHECK_NE(blink::WebIDBDataLossTotal, connection.callbacks->data_loss());
1705 pending_open_calls_.push_back(connection);
1706 return;
1709 if (metadata_.id == kInvalidId) {
1710 // The database was deleted then immediately re-opened; OpenInternal()
1711 // recreates it in the backing store.
1712 if (OpenInternal().ok()) {
1713 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
1714 metadata_.int_version);
1715 } else {
1716 base::string16 message;
1717 if (connection.version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1718 message = ASCIIToUTF16(
1719 "Internal error opening database with no version specified.");
1720 } else {
1721 message =
1722 ASCIIToUTF16("Internal error opening database with version ") +
1723 Int64ToString16(connection.version);
1725 connection.callbacks->OnError(IndexedDBDatabaseError(
1726 blink::WebIDBDatabaseExceptionUnknownError, message));
1727 return;
1731 // We infer that the database didn't exist from its lack of either type of
1732 // version.
1733 bool is_new_database =
1734 metadata_.version == kNoStringVersion &&
1735 metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
1737 if (connection.version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
1738 // For unit tests only - skip upgrade steps. Calling from script with
1739 // DEFAULT_INT_VERSION throws exception.
1740 // TODO(jsbell): DCHECK that not in unit tests.
1741 DCHECK(is_new_database);
1742 connection.callbacks->OnSuccess(
1743 CreateConnection(connection.database_callbacks,
1744 connection.child_process_id),
1745 this->metadata());
1746 return;
1749 // We may need to change the version.
1750 int64 local_version = connection.version;
1751 if (local_version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1752 if (!is_new_database) {
1753 connection.callbacks->OnSuccess(
1754 CreateConnection(connection.database_callbacks,
1755 connection.child_process_id),
1756 this->metadata());
1757 return;
1759 // Spec says: If no version is specified and no database exists, set
1760 // database version to 1.
1761 local_version = 1;
1764 if (local_version > metadata_.int_version) {
1765 RunVersionChangeTransaction(connection.callbacks,
1766 CreateConnection(connection.database_callbacks,
1767 connection.child_process_id),
1768 connection.transaction_id,
1769 local_version);
1770 return;
1772 if (local_version < metadata_.int_version) {
1773 connection.callbacks->OnError(IndexedDBDatabaseError(
1774 blink::WebIDBDatabaseExceptionVersionError,
1775 ASCIIToUTF16("The requested version (") +
1776 Int64ToString16(local_version) +
1777 ASCIIToUTF16(") is less than the existing version (") +
1778 Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
1779 return;
1781 DCHECK_EQ(local_version, metadata_.int_version);
1782 connection.callbacks->OnSuccess(
1783 CreateConnection(connection.database_callbacks,
1784 connection.child_process_id),
1785 this->metadata());
1788 void IndexedDBDatabase::RunVersionChangeTransaction(
1789 scoped_refptr<IndexedDBCallbacks> callbacks,
1790 scoped_ptr<IndexedDBConnection> connection,
1791 int64 transaction_id,
1792 int64 requested_version) {
1793 DCHECK(callbacks.get());
1794 DCHECK(connections_.count(connection.get()));
1795 if (ConnectionCount() > 1) {
1796 DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss());
1797 // Front end ensures the event is not fired at connections that have
1798 // close_pending set.
1799 for (const auto* iter : connections_) {
1800 if (iter != connection.get()) {
1801 iter->callbacks()->OnVersionChange(metadata_.int_version,
1802 requested_version);
1805 // OnBlocked will be fired at the request when one of the other
1806 // connections acks that the OnVersionChange was ignored.
1808 DCHECK(!pending_run_version_change_transaction_call_);
1809 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall(
1810 callbacks, connection.Pass(), transaction_id, requested_version));
1811 return;
1813 RunVersionChangeTransactionFinal(
1814 callbacks, connection.Pass(), transaction_id, requested_version);
1817 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1818 scoped_refptr<IndexedDBCallbacks> callbacks,
1819 scoped_ptr<IndexedDBConnection> connection,
1820 int64 transaction_id,
1821 int64 requested_version) {
1823 std::vector<int64> object_store_ids;
1824 CreateTransaction(transaction_id,
1825 connection.get(),
1826 object_store_ids,
1827 blink::WebIDBTransactionModeVersionChange);
1829 transactions_[transaction_id]->ScheduleTask(
1830 base::Bind(&IndexedDBDatabase::VersionChangeOperation,
1831 this,
1832 requested_version,
1833 callbacks,
1834 base::Passed(&connection)));
1835 DCHECK(!pending_second_half_open_);
1838 void IndexedDBDatabase::DeleteDatabase(
1839 scoped_refptr<IndexedDBCallbacks> callbacks) {
1841 if (IsDeleteDatabaseBlocked()) {
1842 for (const auto* connection : connections_) {
1843 // Front end ensures the event is not fired at connections that have
1844 // close_pending set.
1845 connection->callbacks()->OnVersionChange(
1846 metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION);
1848 // OnBlocked will be fired at the request when one of the other
1849 // connections acks that the OnVersionChange was ignored.
1851 pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
1852 return;
1854 DeleteDatabaseFinal(callbacks);
1857 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1858 return !!ConnectionCount();
1861 void IndexedDBDatabase::DeleteDatabaseFinal(
1862 scoped_refptr<IndexedDBCallbacks> callbacks) {
1863 DCHECK(!IsDeleteDatabaseBlocked());
1864 DCHECK(backing_store_.get());
1865 leveldb::Status s = backing_store_->DeleteDatabase(metadata_.name);
1866 if (!s.ok()) {
1867 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1868 "Internal error deleting database.");
1869 callbacks->OnError(error);
1870 if (s.IsCorruption()) {
1871 GURL origin_url = backing_store_->origin_url();
1872 backing_store_ = NULL;
1873 factory_->HandleBackingStoreCorruption(origin_url, error);
1875 return;
1877 int64 old_version = metadata_.int_version;
1878 metadata_.version = kNoStringVersion;
1879 metadata_.id = kInvalidId;
1880 metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1881 metadata_.object_stores.clear();
1882 callbacks->OnSuccess(old_version);
1883 factory_->DatabaseDeleted(identifier_);
1886 void IndexedDBDatabase::ForceClose() {
1887 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1888 scoped_refptr<IndexedDBDatabase> protect(this);
1889 ConnectionSet::const_iterator it = connections_.begin();
1890 while (it != connections_.end()) {
1891 IndexedDBConnection* connection = *it++;
1892 connection->ForceClose();
1894 DCHECK(connections_.empty());
1897 void IndexedDBDatabase::VersionChangeIgnored() {
1898 if (pending_run_version_change_transaction_call_)
1899 pending_run_version_change_transaction_call_->callbacks()->OnBlocked(
1900 metadata_.int_version);
1902 for (const auto& pending_delete_call : pending_delete_calls_)
1903 pending_delete_call->callbacks()->OnBlocked(metadata_.int_version);
1907 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
1908 DCHECK(connections_.count(connection));
1909 DCHECK(connection->IsConnected());
1910 DCHECK(connection->database() == this);
1912 IDB_TRACE("IndexedDBDatabase::Close");
1914 connections_.erase(connection);
1916 // Abort outstanding transactions from the closing connection. This
1917 // can not happen if the close is requested by the connection itself
1918 // as the front-end defers the close until all transactions are
1919 // complete, but can occur on process termination or forced close.
1921 TransactionMap transactions(transactions_);
1922 for (const auto& it : transactions) {
1923 if (it.second->connection() == connection->callbacks())
1924 it.second->Abort(
1925 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1926 "Connection is closing."));
1930 if (pending_second_half_open_ &&
1931 pending_second_half_open_->connection() == connection) {
1932 pending_second_half_open_->callbacks()->OnError(
1933 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1934 "The connection was closed."));
1935 pending_second_half_open_.reset();
1938 ProcessPendingCalls();
1940 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1941 if (!ConnectionCount() && !pending_open_calls_.size() &&
1942 !pending_delete_calls_.size()) {
1943 DCHECK(transactions_.empty());
1945 const GURL origin_url = backing_store_->origin_url();
1946 backing_store_ = NULL;
1948 factory_->ReleaseDatabase(identifier_, forced);
1952 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1953 int64 object_store_id,
1954 IndexedDBTransaction* transaction) {
1955 DCHECK(!transaction);
1956 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1957 RemoveObjectStore(object_store_id);
1960 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1961 const IndexedDBObjectStoreMetadata& object_store_metadata,
1962 IndexedDBTransaction* transaction) {
1963 DCHECK(!transaction);
1964 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
1965 AddObjectStore(object_store_metadata,
1966 IndexedDBObjectStoreMetadata::kInvalidId);
1969 void IndexedDBDatabase::VersionChangeAbortOperation(
1970 const base::string16& previous_version,
1971 int64 previous_int_version,
1972 IndexedDBTransaction* transaction) {
1973 DCHECK(!transaction);
1974 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
1975 metadata_.version = previous_version;
1976 metadata_.int_version = previous_int_version;
1979 } // namespace content