cc: Use worker context for one-copy tile initialization.
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_database.cc
blob4b50a9e32905eb93185ada1fbac965ed52d504f2
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_connection.h"
21 #include "content/browser/indexed_db/indexed_db_context_impl.h"
22 #include "content/browser/indexed_db/indexed_db_cursor.h"
23 #include "content/browser/indexed_db/indexed_db_factory.h"
24 #include "content/browser/indexed_db/indexed_db_index_writer.h"
25 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
26 #include "content/browser/indexed_db/indexed_db_return_value.h"
27 #include "content/browser/indexed_db/indexed_db_tracing.h"
28 #include "content/browser/indexed_db/indexed_db_transaction.h"
29 #include "content/browser/indexed_db/indexed_db_value.h"
30 #include "content/common/indexed_db/indexed_db_constants.h"
31 #include "content/common/indexed_db/indexed_db_key_path.h"
32 #include "content/common/indexed_db/indexed_db_key_range.h"
33 #include "storage/browser/blob/blob_data_handle.h"
34 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h"
35 #include "third_party/leveldatabase/env_chromium.h"
37 using base::ASCIIToUTF16;
38 using base::Int64ToString16;
39 using blink::WebIDBKeyTypeNumber;
41 namespace content {
43 namespace {
45 // Used for WebCore.IndexedDB.Schema.ObjectStore.KeyPathType and
46 // WebCore.IndexedDB.Schema.Index.KeyPathType histograms. Do not
47 // modify (delete, re-order, renumber) these values other than
48 // the _MAX value.
49 enum HistogramIDBKeyPathType {
50 KEY_PATH_TYPE_NONE = 0,
51 KEY_PATH_TYPE_STRING = 1,
52 KEY_PATH_TYPE_ARRAY = 2,
53 KEY_PATH_TYPE_MAX = 3, // Keep as last/max entry, for histogram range.
56 HistogramIDBKeyPathType HistogramKeyPathType(const IndexedDBKeyPath& key_path) {
57 switch (key_path.type()) {
58 case blink::WebIDBKeyPathTypeNull:
59 return KEY_PATH_TYPE_NONE;
60 case blink::WebIDBKeyPathTypeString:
61 return KEY_PATH_TYPE_STRING;
62 case blink::WebIDBKeyPathTypeArray:
63 return KEY_PATH_TYPE_ARRAY;
65 NOTREACHED();
66 return KEY_PATH_TYPE_NONE;
69 } // namespace
71 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
72 // in-progress connection.
73 class IndexedDBDatabase::PendingUpgradeCall {
74 public:
75 PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks,
76 scoped_ptr<IndexedDBConnection> connection,
77 int64 transaction_id,
78 int64 version)
79 : callbacks_(callbacks),
80 connection_(connection.Pass()),
81 version_(version),
82 transaction_id_(transaction_id) {}
83 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
84 // Takes ownership of the connection object.
85 scoped_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT {
86 return connection_.Pass();
88 int64 version() const { return version_; }
89 int64 transaction_id() const { return transaction_id_; }
91 private:
92 scoped_refptr<IndexedDBCallbacks> callbacks_;
93 scoped_ptr<IndexedDBConnection> connection_;
94 int64 version_;
95 const int64 transaction_id_;
98 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
99 // owned elsewhere, but we need to cancel the success call if that connection
100 // closes before it is sent.
101 class IndexedDBDatabase::PendingSuccessCall {
102 public:
103 PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks,
104 IndexedDBConnection* connection,
105 int64 version)
106 : callbacks_(callbacks), connection_(connection), version_(version) {}
107 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
108 IndexedDBConnection* connection() const { return connection_; }
109 int64 version() const { return version_; }
111 private:
112 scoped_refptr<IndexedDBCallbacks> callbacks_;
113 IndexedDBConnection* connection_;
114 int64 version_;
117 class IndexedDBDatabase::PendingDeleteCall {
118 public:
119 explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
120 : callbacks_(callbacks) {}
121 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
123 private:
124 scoped_refptr<IndexedDBCallbacks> callbacks_;
127 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
128 const base::string16& name,
129 IndexedDBBackingStore* backing_store,
130 IndexedDBFactory* factory,
131 const Identifier& unique_identifier,
132 leveldb::Status* s) {
133 scoped_refptr<IndexedDBDatabase> database =
134 new IndexedDBDatabase(name, backing_store, factory, unique_identifier);
135 *s = database->OpenInternal();
136 if (s->ok())
137 return database;
138 else
139 return NULL;
142 namespace {
143 const base::string16::value_type kNoStringVersion[] = {0};
146 IndexedDBDatabase::IndexedDBDatabase(const base::string16& name,
147 IndexedDBBackingStore* backing_store,
148 IndexedDBFactory* factory,
149 const Identifier& unique_identifier)
150 : backing_store_(backing_store),
151 metadata_(name,
152 kInvalidId,
153 kNoStringVersion,
154 IndexedDBDatabaseMetadata::NO_INT_VERSION,
155 kInvalidId),
156 identifier_(unique_identifier),
157 factory_(factory) {
158 DCHECK(factory != NULL);
161 void IndexedDBDatabase::AddObjectStore(
162 const IndexedDBObjectStoreMetadata& object_store,
163 int64 new_max_object_store_id) {
164 DCHECK(metadata_.object_stores.find(object_store.id) ==
165 metadata_.object_stores.end());
166 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
167 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
168 metadata_.max_object_store_id = new_max_object_store_id;
170 metadata_.object_stores[object_store.id] = object_store;
173 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) {
174 DCHECK(metadata_.object_stores.find(object_store_id) !=
175 metadata_.object_stores.end());
176 metadata_.object_stores.erase(object_store_id);
179 void IndexedDBDatabase::AddIndex(int64 object_store_id,
180 const IndexedDBIndexMetadata& index,
181 int64 new_max_index_id) {
182 DCHECK(metadata_.object_stores.find(object_store_id) !=
183 metadata_.object_stores.end());
184 IndexedDBObjectStoreMetadata object_store =
185 metadata_.object_stores[object_store_id];
187 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
188 object_store.indexes[index.id] = index;
189 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
190 DCHECK_LT(object_store.max_index_id, new_max_index_id);
191 object_store.max_index_id = new_max_index_id;
193 metadata_.object_stores[object_store_id] = object_store;
196 void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
197 DCHECK(metadata_.object_stores.find(object_store_id) !=
198 metadata_.object_stores.end());
199 IndexedDBObjectStoreMetadata object_store =
200 metadata_.object_stores[object_store_id];
202 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
203 object_store.indexes.erase(index_id);
204 metadata_.object_stores[object_store_id] = object_store;
207 leveldb::Status IndexedDBDatabase::OpenInternal() {
208 bool success = false;
209 leveldb::Status s = backing_store_->GetIDBDatabaseMetaData(
210 metadata_.name, &metadata_, &success);
211 DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
212 << " id = " << metadata_.id;
213 if (!s.ok())
214 return s;
215 if (success)
216 return backing_store_->GetObjectStores(metadata_.id,
217 &metadata_.object_stores);
219 return backing_store_->CreateIDBDatabaseMetaData(
220 metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id);
223 IndexedDBDatabase::~IndexedDBDatabase() {
224 DCHECK(transactions_.empty());
225 DCHECK(pending_open_calls_.empty());
226 DCHECK(pending_delete_calls_.empty());
229 scoped_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection(
230 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
231 int child_process_id) {
232 scoped_ptr<IndexedDBConnection> connection(
233 new IndexedDBConnection(this, database_callbacks));
234 connections_.insert(connection.get());
235 backing_store_->GrantChildProcessPermissions(child_process_id);
236 return connection.Pass();
239 IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
240 int64 transaction_id) const {
241 TransactionMap::const_iterator trans_iterator =
242 transactions_.find(transaction_id);
243 if (trans_iterator == transactions_.end())
244 return NULL;
245 return trans_iterator->second;
248 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const {
249 if (!ContainsKey(metadata_.object_stores, object_store_id)) {
250 DLOG(ERROR) << "Invalid object_store_id";
251 return false;
253 return true;
256 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id,
257 int64 index_id) const {
258 if (!ValidateObjectStoreId(object_store_id))
259 return false;
260 const IndexedDBObjectStoreMetadata& object_store_metadata =
261 metadata_.object_stores.find(object_store_id)->second;
262 if (!ContainsKey(object_store_metadata.indexes, index_id)) {
263 DLOG(ERROR) << "Invalid index_id";
264 return false;
266 return true;
269 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
270 int64 object_store_id,
271 int64 index_id) const {
272 if (!ValidateObjectStoreId(object_store_id))
273 return false;
274 const IndexedDBObjectStoreMetadata& object_store_metadata =
275 metadata_.object_stores.find(object_store_id)->second;
276 if (index_id != IndexedDBIndexMetadata::kInvalidId &&
277 !ContainsKey(object_store_metadata.indexes, index_id)) {
278 DLOG(ERROR) << "Invalid index_id";
279 return false;
281 return true;
284 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
285 int64 object_store_id,
286 int64 index_id) const {
287 if (!ValidateObjectStoreId(object_store_id))
288 return false;
289 const IndexedDBObjectStoreMetadata& object_store_metadata =
290 metadata_.object_stores.find(object_store_id)->second;
291 if (ContainsKey(object_store_metadata.indexes, index_id)) {
292 DLOG(ERROR) << "Invalid index_id";
293 return false;
295 return true;
298 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
299 int64 object_store_id,
300 const base::string16& name,
301 const IndexedDBKeyPath& key_path,
302 bool auto_increment) {
303 IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id);
304 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
305 if (!transaction)
306 return;
307 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
309 if (ContainsKey(metadata_.object_stores, object_store_id)) {
310 DLOG(ERROR) << "Invalid object_store_id";
311 return;
314 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.ObjectStore.KeyPathType",
315 HistogramKeyPathType(key_path), KEY_PATH_TYPE_MAX);
316 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.ObjectStore.AutoIncrement",
317 auto_increment);
319 // Store creation is done synchronously, as it may be followed by
320 // index creation (also sync) since preemptive OpenCursor/SetIndexKeys
321 // may follow.
322 IndexedDBObjectStoreMetadata object_store_metadata(
323 name,
324 object_store_id,
325 key_path,
326 auto_increment,
327 IndexedDBDatabase::kMinimumIndexId);
329 leveldb::Status s =
330 backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(),
331 transaction->database()->id(),
332 object_store_metadata.id,
333 object_store_metadata.name,
334 object_store_metadata.key_path,
335 object_store_metadata.auto_increment);
336 if (!s.ok()) {
337 IndexedDBDatabaseError error(
338 blink::WebIDBDatabaseExceptionUnknownError,
339 ASCIIToUTF16("Internal error creating object store '") +
340 object_store_metadata.name + ASCIIToUTF16("'."));
341 transaction->Abort(error);
342 if (s.IsCorruption())
343 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
344 error);
345 return;
348 AddObjectStore(object_store_metadata, object_store_id);
349 transaction->ScheduleAbortTask(
350 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
351 this,
352 object_store_id));
355 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
356 int64 object_store_id) {
357 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id);
358 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
359 if (!transaction)
360 return;
361 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
363 if (!ValidateObjectStoreId(object_store_id))
364 return;
366 transaction->ScheduleTask(
367 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation,
368 this,
369 object_store_id));
372 void IndexedDBDatabase::CreateIndex(int64 transaction_id,
373 int64 object_store_id,
374 int64 index_id,
375 const base::string16& name,
376 const IndexedDBKeyPath& key_path,
377 bool unique,
378 bool multi_entry) {
379 IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id);
380 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
381 if (!transaction)
382 return;
383 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
385 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
386 return;
388 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.Index.KeyPathType",
389 HistogramKeyPathType(key_path), KEY_PATH_TYPE_MAX);
390 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.Unique", unique);
391 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.MultiEntry",
392 multi_entry);
394 // Index creation is done synchronously since preemptive
395 // OpenCursor/SetIndexKeys may follow.
396 const IndexedDBIndexMetadata index_metadata(
397 name, index_id, key_path, unique, multi_entry);
399 if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
400 transaction->database()->id(),
401 object_store_id,
402 index_metadata.id,
403 index_metadata.name,
404 index_metadata.key_path,
405 index_metadata.unique,
406 index_metadata.multi_entry).ok()) {
407 base::string16 error_string =
408 ASCIIToUTF16("Internal error creating index '") +
409 index_metadata.name + ASCIIToUTF16("'.");
410 transaction->Abort(IndexedDBDatabaseError(
411 blink::WebIDBDatabaseExceptionUnknownError, error_string));
412 return;
415 AddIndex(object_store_id, index_metadata, index_id);
416 transaction->ScheduleAbortTask(
417 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
418 this,
419 object_store_id,
420 index_id));
423 void IndexedDBDatabase::CreateIndexAbortOperation(
424 int64 object_store_id,
425 int64 index_id,
426 IndexedDBTransaction* transaction) {
427 DCHECK(!transaction);
428 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
429 RemoveIndex(object_store_id, index_id);
432 void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
433 int64 object_store_id,
434 int64 index_id) {
435 IDB_TRACE1("IndexedDBDatabase::DeleteIndex", "txn.id", transaction_id);
436 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
437 if (!transaction)
438 return;
439 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
441 if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
442 return;
444 transaction->ScheduleTask(
445 base::Bind(&IndexedDBDatabase::DeleteIndexOperation,
446 this,
447 object_store_id,
448 index_id));
451 void IndexedDBDatabase::DeleteIndexOperation(
452 int64 object_store_id,
453 int64 index_id,
454 IndexedDBTransaction* transaction) {
455 IDB_TRACE1(
456 "IndexedDBDatabase::DeleteIndexOperation", "txn.id", transaction->id());
458 const IndexedDBIndexMetadata index_metadata =
459 metadata_.object_stores[object_store_id].indexes[index_id];
461 leveldb::Status s =
462 backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
463 transaction->database()->id(),
464 object_store_id,
465 index_id);
466 if (!s.ok()) {
467 base::string16 error_string =
468 ASCIIToUTF16("Internal error deleting index '") +
469 index_metadata.name + ASCIIToUTF16("'.");
470 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
471 error_string);
472 transaction->Abort(error);
473 if (s.IsCorruption())
474 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
475 error);
476 return;
479 RemoveIndex(object_store_id, index_id);
480 transaction->ScheduleAbortTask(
481 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
482 this,
483 object_store_id,
484 index_metadata));
487 void IndexedDBDatabase::DeleteIndexAbortOperation(
488 int64 object_store_id,
489 const IndexedDBIndexMetadata& index_metadata,
490 IndexedDBTransaction* transaction) {
491 DCHECK(!transaction);
492 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
493 AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId);
496 void IndexedDBDatabase::Commit(int64 transaction_id) {
497 // The frontend suggests that we commit, but we may have previously initiated
498 // an abort, and so have disposed of the transaction. on_abort has already
499 // been dispatched to the frontend, so it will find out about that
500 // asynchronously.
501 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
502 if (transaction) {
503 scoped_refptr<IndexedDBFactory> factory = factory_;
504 leveldb::Status s = transaction->Commit();
505 if (s.IsCorruption()) {
506 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
507 "Internal error committing transaction.");
508 factory->HandleBackingStoreCorruption(identifier_.first, error);
513 void IndexedDBDatabase::Abort(int64 transaction_id) {
514 // If the transaction is unknown, then it has already been aborted by the
515 // backend before this call so it is safe to ignore it.
516 IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id);
517 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
518 if (transaction)
519 transaction->Abort();
522 void IndexedDBDatabase::Abort(int64 transaction_id,
523 const IndexedDBDatabaseError& error) {
524 IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id);
525 // If the transaction is unknown, then it has already been aborted by the
526 // backend before this call so it is safe to ignore it.
527 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
528 if (transaction)
529 transaction->Abort(error);
532 void IndexedDBDatabase::GetAll(int64 transaction_id,
533 int64 object_store_id,
534 int64 index_id,
535 scoped_ptr<IndexedDBKeyRange> key_range,
536 bool key_only,
537 int64 max_count,
538 scoped_refptr<IndexedDBCallbacks> callbacks) {
539 IDB_TRACE1("IndexedDBDatabase::GetAll", "txn.id", transaction_id);
540 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
541 if (!transaction)
542 return;
544 if (!ValidateObjectStoreId(object_store_id))
545 return;
547 transaction->ScheduleTask(base::Bind(
548 &IndexedDBDatabase::GetAllOperation, this, object_store_id, index_id,
549 Passed(&key_range),
550 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
551 max_count, callbacks));
554 void IndexedDBDatabase::Get(int64 transaction_id,
555 int64 object_store_id,
556 int64 index_id,
557 scoped_ptr<IndexedDBKeyRange> key_range,
558 bool key_only,
559 scoped_refptr<IndexedDBCallbacks> callbacks) {
560 IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id);
561 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
562 if (!transaction)
563 return;
565 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
566 return;
568 transaction->ScheduleTask(base::Bind(
569 &IndexedDBDatabase::GetOperation,
570 this,
571 object_store_id,
572 index_id,
573 Passed(&key_range),
574 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
575 callbacks));
578 void IndexedDBDatabase::GetOperation(
579 int64 object_store_id,
580 int64 index_id,
581 scoped_ptr<IndexedDBKeyRange> key_range,
582 indexed_db::CursorType cursor_type,
583 scoped_refptr<IndexedDBCallbacks> callbacks,
584 IndexedDBTransaction* transaction) {
585 IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction->id());
587 DCHECK(metadata_.object_stores.find(object_store_id) !=
588 metadata_.object_stores.end());
589 const IndexedDBObjectStoreMetadata& object_store_metadata =
590 metadata_.object_stores[object_store_id];
592 const IndexedDBKey* key;
594 leveldb::Status s;
595 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
596 if (key_range->IsOnlyKey()) {
597 key = &key_range->lower();
598 } else {
599 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
600 DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY);
601 // ObjectStore Retrieval Operation
602 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
603 transaction->BackingStoreTransaction(),
604 id(),
605 object_store_id,
606 *key_range,
607 blink::WebIDBCursorDirectionNext,
608 &s);
609 } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
610 // Index Value Retrieval Operation
611 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
612 transaction->BackingStoreTransaction(),
613 id(),
614 object_store_id,
615 index_id,
616 *key_range,
617 blink::WebIDBCursorDirectionNext,
618 &s);
619 } else {
620 // Index Referenced Value Retrieval Operation
621 backing_store_cursor = backing_store_->OpenIndexCursor(
622 transaction->BackingStoreTransaction(),
623 id(),
624 object_store_id,
625 index_id,
626 *key_range,
627 blink::WebIDBCursorDirectionNext,
628 &s);
631 if (!s.ok()) {
632 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
633 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
634 "Internal error deleting data in range");
635 if (s.IsCorruption()) {
636 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
637 error);
641 if (!backing_store_cursor) {
642 callbacks->OnSuccess();
643 return;
646 key = &backing_store_cursor->key();
649 scoped_ptr<IndexedDBKey> primary_key;
650 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
651 // Object Store Retrieval Operation
652 IndexedDBReturnValue value;
653 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
654 id(),
655 object_store_id,
656 *key,
657 &value);
658 if (!s.ok()) {
659 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
660 "Internal error in GetRecord.");
661 callbacks->OnError(error);
663 if (s.IsCorruption())
664 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
665 error);
666 return;
669 if (value.empty()) {
670 callbacks->OnSuccess();
671 return;
674 if (object_store_metadata.auto_increment &&
675 !object_store_metadata.key_path.IsNull()) {
676 value.primary_key = *key;
677 value.key_path = object_store_metadata.key_path;
680 callbacks->OnSuccess(&value);
681 return;
684 // From here we are dealing only with indexes.
685 s = backing_store_->GetPrimaryKeyViaIndex(
686 transaction->BackingStoreTransaction(),
687 id(),
688 object_store_id,
689 index_id,
690 *key,
691 &primary_key);
692 if (!s.ok()) {
693 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
694 "Internal error in GetPrimaryKeyViaIndex.");
695 callbacks->OnError(error);
696 if (s.IsCorruption())
697 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
698 error);
699 return;
701 if (!primary_key) {
702 callbacks->OnSuccess();
703 return;
705 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
706 // Index Value Retrieval Operation
707 callbacks->OnSuccess(*primary_key);
708 return;
711 // Index Referenced Value Retrieval Operation
712 IndexedDBReturnValue value;
713 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
714 id(),
715 object_store_id,
716 *primary_key,
717 &value);
718 if (!s.ok()) {
719 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
720 "Internal error in GetRecord.");
721 callbacks->OnError(error);
722 if (s.IsCorruption())
723 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
724 error);
725 return;
728 if (value.empty()) {
729 callbacks->OnSuccess();
730 return;
732 if (object_store_metadata.auto_increment &&
733 !object_store_metadata.key_path.IsNull()) {
734 value.primary_key = *primary_key;
735 value.key_path = object_store_metadata.key_path;
737 callbacks->OnSuccess(&value);
740 void IndexedDBDatabase::GetAllOperation(
741 int64 object_store_id,
742 int64 index_id,
743 scoped_ptr<IndexedDBKeyRange> key_range,
744 indexed_db::CursorType cursor_type,
745 int64 max_count,
746 scoped_refptr<IndexedDBCallbacks> callbacks,
747 IndexedDBTransaction* transaction) {
748 IDB_TRACE1("IndexedDBDatabase::GetAllOperation", "txn.id", transaction->id());
750 DCHECK_GT(max_count, 0);
752 DCHECK(metadata_.object_stores.find(object_store_id) !=
753 metadata_.object_stores.end());
754 const IndexedDBObjectStoreMetadata& object_store_metadata =
755 metadata_.object_stores[object_store_id];
757 leveldb::Status s;
759 scoped_ptr<IndexedDBBackingStore::Cursor> cursor;
761 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
762 // Retrieving keys
763 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
764 // Object Store: Key Retrieval Operation
765 cursor = backing_store_->OpenObjectStoreKeyCursor(
766 transaction->BackingStoreTransaction(), id(), object_store_id,
767 *key_range, blink::WebIDBCursorDirectionNext, &s);
768 } else {
769 // Index Value: (Primary Key) Retrieval Operation
770 cursor = backing_store_->OpenIndexKeyCursor(
771 transaction->BackingStoreTransaction(), id(), object_store_id,
772 index_id, *key_range, blink::WebIDBCursorDirectionNext, &s);
774 } else {
775 // Retrieving values
776 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
777 // Object Store: Value Retrieval Operation
778 cursor = backing_store_->OpenObjectStoreCursor(
779 transaction->BackingStoreTransaction(), id(), object_store_id,
780 *key_range, blink::WebIDBCursorDirectionNext, &s);
781 } else {
782 // Object Store: Referenced Value Retrieval Operation
783 cursor = backing_store_->OpenIndexCursor(
784 transaction->BackingStoreTransaction(), id(), object_store_id,
785 index_id, *key_range, blink::WebIDBCursorDirectionNext, &s);
789 if (!s.ok()) {
790 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
791 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
792 "Internal error in GetAllOperation");
793 callbacks->OnError(error);
794 if (s.IsCorruption()) {
795 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
796 error);
798 return;
801 std::vector<IndexedDBKey> found_keys;
802 std::vector<IndexedDBReturnValue> found_values;
803 if (!cursor) {
804 // Doesn't matter if key or value array here - will be empty array when it
805 // hits JavaScript.
806 callbacks->OnSuccessArray(&found_values, object_store_metadata.key_path);
807 return;
810 bool did_first_seek = false;
811 bool generated_key = object_store_metadata.auto_increment &&
812 !object_store_metadata.key_path.IsNull();
814 size_t response_size = kMaxIDBMessageOverhead;
815 int64 num_found_items = 0;
816 while (num_found_items++ < max_count) {
817 bool cursor_valid;
818 if (did_first_seek) {
819 cursor_valid = cursor->Continue(&s);
820 } else {
821 cursor_valid = cursor->FirstSeek(&s);
822 did_first_seek = true;
824 if (!s.ok()) {
825 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
826 "Internal error in GetAllOperation.");
827 callbacks->OnError(error);
828 if (s.IsCorruption())
829 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
830 error);
831 return;
834 if (!cursor_valid)
835 break;
837 IndexedDBReturnValue return_value;
838 IndexedDBKey return_key;
840 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
841 return_key = cursor->primary_key();
842 } else {
843 // Retrieving values
844 return_value.swap(*cursor->value());
845 if (!return_value.empty() && generated_key) {
846 return_value.primary_key = cursor->primary_key();
847 return_value.key_path = object_store_metadata.key_path;
851 if (cursor_type == indexed_db::CURSOR_KEY_ONLY)
852 response_size += return_key.size_estimate();
853 else
854 response_size += return_value.SizeEstimate();
855 if (response_size > IPC::Channel::kMaximumMessageSize) {
856 // TODO(cmumford): Reach this limit more gracefully (crbug.com/478949)
857 break;
860 if (cursor_type == indexed_db::CURSOR_KEY_ONLY)
861 found_keys.push_back(return_key);
862 else
863 found_values.push_back(return_value);
866 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
867 // IndexedDBKey already supports an array of values so we can leverage this
868 // to return an array of keys - no need to create our own array of keys.
869 callbacks->OnSuccess(IndexedDBKey(found_keys));
870 } else {
871 callbacks->OnSuccessArray(&found_values, object_store_metadata.key_path);
875 static scoped_ptr<IndexedDBKey> GenerateKey(
876 IndexedDBBackingStore* backing_store,
877 IndexedDBTransaction* transaction,
878 int64 database_id,
879 int64 object_store_id) {
880 const int64 max_generator_value =
881 9007199254740992LL; // Maximum integer storable as ECMAScript number.
882 int64 current_number;
883 leveldb::Status s = backing_store->GetKeyGeneratorCurrentNumber(
884 transaction->BackingStoreTransaction(),
885 database_id,
886 object_store_id,
887 &current_number);
888 if (!s.ok()) {
889 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
890 return make_scoped_ptr(new IndexedDBKey());
892 if (current_number < 0 || current_number > max_generator_value)
893 return make_scoped_ptr(new IndexedDBKey());
895 return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber));
898 static leveldb::Status UpdateKeyGenerator(IndexedDBBackingStore* backing_store,
899 IndexedDBTransaction* transaction,
900 int64 database_id,
901 int64 object_store_id,
902 const IndexedDBKey& key,
903 bool check_current) {
904 DCHECK_EQ(WebIDBKeyTypeNumber, key.type());
905 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
906 transaction->BackingStoreTransaction(),
907 database_id,
908 object_store_id,
909 static_cast<int64>(floor(key.number())) + 1,
910 check_current);
913 struct IndexedDBDatabase::PutOperationParams {
914 PutOperationParams() {}
915 int64 object_store_id;
916 IndexedDBValue value;
917 ScopedVector<storage::BlobDataHandle> handles;
918 scoped_ptr<IndexedDBKey> key;
919 blink::WebIDBPutMode put_mode;
920 scoped_refptr<IndexedDBCallbacks> callbacks;
921 std::vector<IndexKeys> index_keys;
923 private:
924 DISALLOW_COPY_AND_ASSIGN(PutOperationParams);
927 void IndexedDBDatabase::Put(int64 transaction_id,
928 int64 object_store_id,
929 IndexedDBValue* value,
930 ScopedVector<storage::BlobDataHandle>* handles,
931 scoped_ptr<IndexedDBKey> key,
932 blink::WebIDBPutMode put_mode,
933 scoped_refptr<IndexedDBCallbacks> callbacks,
934 const std::vector<IndexKeys>& index_keys) {
935 IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id);
936 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
937 if (!transaction)
938 return;
939 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
941 if (!ValidateObjectStoreId(object_store_id))
942 return;
944 DCHECK(key);
945 DCHECK(value);
946 scoped_ptr<PutOperationParams> params(new PutOperationParams());
947 params->object_store_id = object_store_id;
948 params->value.swap(*value);
949 params->handles.swap(*handles);
950 params->key = key.Pass();
951 params->put_mode = put_mode;
952 params->callbacks = callbacks;
953 params->index_keys = index_keys;
954 transaction->ScheduleTask(base::Bind(
955 &IndexedDBDatabase::PutOperation, this, base::Passed(&params)));
958 void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
959 IndexedDBTransaction* transaction) {
960 IDB_TRACE1("IndexedDBDatabase::PutOperation", "txn.id", transaction->id());
961 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
962 bool key_was_generated = false;
964 DCHECK(metadata_.object_stores.find(params->object_store_id) !=
965 metadata_.object_stores.end());
966 const IndexedDBObjectStoreMetadata& object_store =
967 metadata_.object_stores[params->object_store_id];
968 DCHECK(object_store.auto_increment || params->key->IsValid());
970 scoped_ptr<IndexedDBKey> key;
971 if (params->put_mode != blink::WebIDBPutModeCursorUpdate &&
972 object_store.auto_increment && !params->key->IsValid()) {
973 scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
974 backing_store_.get(), transaction, id(), params->object_store_id);
975 key_was_generated = true;
976 if (!auto_inc_key->IsValid()) {
977 params->callbacks->OnError(
978 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
979 "Maximum key generator value reached."));
980 return;
982 key = auto_inc_key.Pass();
983 } else {
984 key = params->key.Pass();
987 DCHECK(key->IsValid());
989 IndexedDBBackingStore::RecordIdentifier record_identifier;
990 if (params->put_mode == blink::WebIDBPutModeAddOnly) {
991 bool found = false;
992 leveldb::Status s = backing_store_->KeyExistsInObjectStore(
993 transaction->BackingStoreTransaction(),
994 id(),
995 params->object_store_id,
996 *key,
997 &record_identifier,
998 &found);
999 if (!s.ok()) {
1000 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1001 "Internal error checking key existence.");
1002 params->callbacks->OnError(error);
1003 if (s.IsCorruption())
1004 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1005 error);
1006 return;
1008 if (found) {
1009 params->callbacks->OnError(
1010 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
1011 "Key already exists in the object store."));
1012 return;
1016 ScopedVector<IndexWriter> index_writers;
1017 base::string16 error_message;
1018 bool obeys_constraints = false;
1019 bool backing_store_success = MakeIndexWriters(transaction,
1020 backing_store_.get(),
1021 id(),
1022 object_store,
1023 *key,
1024 key_was_generated,
1025 params->index_keys,
1026 &index_writers,
1027 &error_message,
1028 &obeys_constraints);
1029 if (!backing_store_success) {
1030 params->callbacks->OnError(IndexedDBDatabaseError(
1031 blink::WebIDBDatabaseExceptionUnknownError,
1032 "Internal error: backing store error updating index keys."));
1033 return;
1035 if (!obeys_constraints) {
1036 params->callbacks->OnError(IndexedDBDatabaseError(
1037 blink::WebIDBDatabaseExceptionConstraintError, error_message));
1038 return;
1041 // Before this point, don't do any mutation. After this point, rollback the
1042 // transaction in case of error.
1043 leveldb::Status s =
1044 backing_store_->PutRecord(transaction->BackingStoreTransaction(),
1045 id(),
1046 params->object_store_id,
1047 *key,
1048 &params->value,
1049 &params->handles,
1050 &record_identifier);
1051 if (!s.ok()) {
1052 IndexedDBDatabaseError error(
1053 blink::WebIDBDatabaseExceptionUnknownError,
1054 "Internal error: backing store error performing put/add.");
1055 params->callbacks->OnError(error);
1056 if (s.IsCorruption())
1057 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1058 error);
1059 return;
1062 for (size_t i = 0; i < index_writers.size(); ++i) {
1063 IndexWriter* index_writer = index_writers[i];
1064 index_writer->WriteIndexKeys(record_identifier,
1065 backing_store_.get(),
1066 transaction->BackingStoreTransaction(),
1067 id(),
1068 params->object_store_id);
1071 if (object_store.auto_increment &&
1072 params->put_mode != blink::WebIDBPutModeCursorUpdate &&
1073 key->type() == WebIDBKeyTypeNumber) {
1074 leveldb::Status s = UpdateKeyGenerator(backing_store_.get(),
1075 transaction,
1076 id(),
1077 params->object_store_id,
1078 *key,
1079 !key_was_generated);
1080 if (!s.ok()) {
1081 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1082 "Internal error updating key generator.");
1083 params->callbacks->OnError(error);
1084 if (s.IsCorruption())
1085 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1086 error);
1087 return;
1091 params->callbacks->OnSuccess(*key);
1094 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
1095 int64 object_store_id,
1096 scoped_ptr<IndexedDBKey> primary_key,
1097 const std::vector<IndexKeys>& index_keys) {
1098 IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id);
1099 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1100 if (!transaction)
1101 return;
1102 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
1104 // TODO(alecflett): This method could be asynchronous, but we need to
1105 // evaluate if it's worth the extra complexity.
1106 IndexedDBBackingStore::RecordIdentifier record_identifier;
1107 bool found = false;
1108 leveldb::Status s = backing_store_->KeyExistsInObjectStore(
1109 transaction->BackingStoreTransaction(),
1110 metadata_.id,
1111 object_store_id,
1112 *primary_key,
1113 &record_identifier,
1114 &found);
1115 if (!s.ok()) {
1116 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1117 "Internal error setting index keys.");
1118 transaction->Abort(error);
1119 if (s.IsCorruption())
1120 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1121 error);
1122 return;
1124 if (!found) {
1125 transaction->Abort(IndexedDBDatabaseError(
1126 blink::WebIDBDatabaseExceptionUnknownError,
1127 "Internal error setting index keys for object store."));
1128 return;
1131 ScopedVector<IndexWriter> index_writers;
1132 base::string16 error_message;
1133 bool obeys_constraints = false;
1134 DCHECK(metadata_.object_stores.find(object_store_id) !=
1135 metadata_.object_stores.end());
1136 const IndexedDBObjectStoreMetadata& object_store_metadata =
1137 metadata_.object_stores[object_store_id];
1138 bool backing_store_success = MakeIndexWriters(transaction,
1139 backing_store_.get(),
1140 id(),
1141 object_store_metadata,
1142 *primary_key,
1143 false,
1144 index_keys,
1145 &index_writers,
1146 &error_message,
1147 &obeys_constraints);
1148 if (!backing_store_success) {
1149 transaction->Abort(IndexedDBDatabaseError(
1150 blink::WebIDBDatabaseExceptionUnknownError,
1151 "Internal error: backing store error updating index keys."));
1152 return;
1154 if (!obeys_constraints) {
1155 transaction->Abort(IndexedDBDatabaseError(
1156 blink::WebIDBDatabaseExceptionConstraintError, error_message));
1157 return;
1160 for (size_t i = 0; i < index_writers.size(); ++i) {
1161 IndexWriter* index_writer = index_writers[i];
1162 index_writer->WriteIndexKeys(record_identifier,
1163 backing_store_.get(),
1164 transaction->BackingStoreTransaction(),
1165 id(),
1166 object_store_id);
1170 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
1171 int64,
1172 const std::vector<int64>& index_ids) {
1173 IDB_TRACE1("IndexedDBDatabase::SetIndexesReady", "txn.id", transaction_id);
1174 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1175 if (!transaction)
1176 return;
1177 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
1179 transaction->ScheduleTask(
1180 blink::WebIDBTaskTypePreemptive,
1181 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation,
1182 this,
1183 index_ids.size()));
1186 void IndexedDBDatabase::SetIndexesReadyOperation(
1187 size_t index_count,
1188 IndexedDBTransaction* transaction) {
1189 IDB_TRACE1("IndexedDBDatabase::SetIndexesReadyOperation",
1190 "txn.id",
1191 transaction->id());
1192 for (size_t i = 0; i < index_count; ++i)
1193 transaction->DidCompletePreemptiveEvent();
1196 struct IndexedDBDatabase::OpenCursorOperationParams {
1197 OpenCursorOperationParams() {}
1198 int64 object_store_id;
1199 int64 index_id;
1200 scoped_ptr<IndexedDBKeyRange> key_range;
1201 blink::WebIDBCursorDirection direction;
1202 indexed_db::CursorType cursor_type;
1203 blink::WebIDBTaskType task_type;
1204 scoped_refptr<IndexedDBCallbacks> callbacks;
1206 private:
1207 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams);
1210 void IndexedDBDatabase::OpenCursor(
1211 int64 transaction_id,
1212 int64 object_store_id,
1213 int64 index_id,
1214 scoped_ptr<IndexedDBKeyRange> key_range,
1215 blink::WebIDBCursorDirection direction,
1216 bool key_only,
1217 blink::WebIDBTaskType task_type,
1218 scoped_refptr<IndexedDBCallbacks> callbacks) {
1219 IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id);
1220 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1221 if (!transaction)
1222 return;
1224 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1225 return;
1227 scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams());
1228 params->object_store_id = object_store_id;
1229 params->index_id = index_id;
1230 params->key_range = key_range.Pass();
1231 params->direction = direction;
1232 params->cursor_type =
1233 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE;
1234 params->task_type = task_type;
1235 params->callbacks = callbacks;
1236 transaction->ScheduleTask(base::Bind(
1237 &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(&params)));
1240 void IndexedDBDatabase::OpenCursorOperation(
1241 scoped_ptr<OpenCursorOperationParams> params,
1242 IndexedDBTransaction* transaction) {
1243 IDB_TRACE1(
1244 "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction->id());
1246 // The frontend has begun indexing, so this pauses the transaction
1247 // until the indexing is complete. This can't happen any earlier
1248 // because we don't want to switch to early mode in case multiple
1249 // indexes are being created in a row, with Put()'s in between.
1250 if (params->task_type == blink::WebIDBTaskTypePreemptive)
1251 transaction->AddPreemptiveEvent();
1253 leveldb::Status s;
1254 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1255 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
1256 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1257 DCHECK_EQ(params->task_type, blink::WebIDBTaskTypeNormal);
1258 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1259 transaction->BackingStoreTransaction(),
1260 id(),
1261 params->object_store_id,
1262 *params->key_range,
1263 params->direction,
1264 &s);
1265 } else {
1266 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1267 transaction->BackingStoreTransaction(),
1268 id(),
1269 params->object_store_id,
1270 *params->key_range,
1271 params->direction,
1272 &s);
1274 } else {
1275 DCHECK_EQ(params->task_type, blink::WebIDBTaskTypeNormal);
1276 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1277 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1278 transaction->BackingStoreTransaction(),
1279 id(),
1280 params->object_store_id,
1281 params->index_id,
1282 *params->key_range,
1283 params->direction,
1284 &s);
1285 } else {
1286 backing_store_cursor = backing_store_->OpenIndexCursor(
1287 transaction->BackingStoreTransaction(),
1288 id(),
1289 params->object_store_id,
1290 params->index_id,
1291 *params->key_range,
1292 params->direction,
1293 &s);
1297 if (!s.ok()) {
1298 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
1299 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1300 "Internal error opening cursor operation");
1301 if (s.IsCorruption()) {
1302 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1303 error);
1307 if (!backing_store_cursor) {
1308 // Why is Success being called?
1309 params->callbacks->OnSuccess(nullptr);
1310 return;
1313 scoped_refptr<IndexedDBCursor> cursor =
1314 new IndexedDBCursor(backing_store_cursor.Pass(),
1315 params->cursor_type,
1316 params->task_type,
1317 transaction);
1318 params->callbacks->OnSuccess(
1319 cursor, cursor->key(), cursor->primary_key(), cursor->Value());
1322 void IndexedDBDatabase::Count(int64 transaction_id,
1323 int64 object_store_id,
1324 int64 index_id,
1325 scoped_ptr<IndexedDBKeyRange> key_range,
1326 scoped_refptr<IndexedDBCallbacks> callbacks) {
1327 IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id);
1328 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1329 if (!transaction)
1330 return;
1332 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1333 return;
1335 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation,
1336 this,
1337 object_store_id,
1338 index_id,
1339 base::Passed(&key_range),
1340 callbacks));
1343 void IndexedDBDatabase::CountOperation(
1344 int64 object_store_id,
1345 int64 index_id,
1346 scoped_ptr<IndexedDBKeyRange> key_range,
1347 scoped_refptr<IndexedDBCallbacks> callbacks,
1348 IndexedDBTransaction* transaction) {
1349 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction->id());
1350 uint32 count = 0;
1351 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1353 leveldb::Status s;
1354 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
1355 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1356 transaction->BackingStoreTransaction(),
1357 id(),
1358 object_store_id,
1359 *key_range,
1360 blink::WebIDBCursorDirectionNext,
1361 &s);
1362 } else {
1363 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1364 transaction->BackingStoreTransaction(),
1365 id(),
1366 object_store_id,
1367 index_id,
1368 *key_range,
1369 blink::WebIDBCursorDirectionNext,
1370 &s);
1372 if (!s.ok()) {
1373 DLOG(ERROR) << "Unable perform count operation: " << s.ToString();
1374 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1375 "Internal error performing count operation");
1376 if (s.IsCorruption()) {
1377 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1378 error);
1381 if (!backing_store_cursor) {
1382 callbacks->OnSuccess(count);
1383 return;
1386 do {
1387 ++count;
1388 } while (backing_store_cursor->Continue(&s));
1390 // TODO(cmumford): Check for database corruption.
1392 callbacks->OnSuccess(count);
1395 void IndexedDBDatabase::DeleteRange(
1396 int64 transaction_id,
1397 int64 object_store_id,
1398 scoped_ptr<IndexedDBKeyRange> key_range,
1399 scoped_refptr<IndexedDBCallbacks> callbacks) {
1400 IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id);
1401 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1402 if (!transaction)
1403 return;
1404 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
1406 if (!ValidateObjectStoreId(object_store_id))
1407 return;
1409 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation,
1410 this,
1411 object_store_id,
1412 base::Passed(&key_range),
1413 callbacks));
1416 void IndexedDBDatabase::DeleteRangeOperation(
1417 int64 object_store_id,
1418 scoped_ptr<IndexedDBKeyRange> key_range,
1419 scoped_refptr<IndexedDBCallbacks> callbacks,
1420 IndexedDBTransaction* transaction) {
1421 IDB_TRACE1(
1422 "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction->id());
1423 leveldb::Status s =
1424 backing_store_->DeleteRange(transaction->BackingStoreTransaction(),
1425 id(),
1426 object_store_id,
1427 *key_range);
1428 if (!s.ok()) {
1429 base::string16 error_string =
1430 ASCIIToUTF16("Internal error deleting data in range");
1431 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1432 error_string);
1433 transaction->Abort(error);
1434 if (s.IsCorruption()) {
1435 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1436 error);
1438 return;
1440 callbacks->OnSuccess();
1443 void IndexedDBDatabase::Clear(int64 transaction_id,
1444 int64 object_store_id,
1445 scoped_refptr<IndexedDBCallbacks> callbacks) {
1446 IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id);
1447 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1448 if (!transaction)
1449 return;
1450 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
1452 if (!ValidateObjectStoreId(object_store_id))
1453 return;
1455 transaction->ScheduleTask(base::Bind(
1456 &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks));
1459 void IndexedDBDatabase::ClearOperation(
1460 int64 object_store_id,
1461 scoped_refptr<IndexedDBCallbacks> callbacks,
1462 IndexedDBTransaction* transaction) {
1463 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction->id());
1464 leveldb::Status s = backing_store_->ClearObjectStore(
1465 transaction->BackingStoreTransaction(), id(), object_store_id);
1466 if (!s.ok()) {
1467 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1468 "Internal error clearing object store");
1469 callbacks->OnError(error);
1470 if (s.IsCorruption()) {
1471 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1472 error);
1474 return;
1476 callbacks->OnSuccess();
1479 void IndexedDBDatabase::DeleteObjectStoreOperation(
1480 int64 object_store_id,
1481 IndexedDBTransaction* transaction) {
1482 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation",
1483 "txn.id",
1484 transaction->id());
1486 const IndexedDBObjectStoreMetadata object_store_metadata =
1487 metadata_.object_stores[object_store_id];
1488 leveldb::Status s =
1489 backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
1490 transaction->database()->id(),
1491 object_store_id);
1492 if (!s.ok()) {
1493 base::string16 error_string =
1494 ASCIIToUTF16("Internal error deleting object store '") +
1495 object_store_metadata.name + ASCIIToUTF16("'.");
1496 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1497 error_string);
1498 transaction->Abort(error);
1499 if (s.IsCorruption())
1500 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1501 error);
1502 return;
1505 RemoveObjectStore(object_store_id);
1506 transaction->ScheduleAbortTask(
1507 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
1508 this,
1509 object_store_metadata));
1512 void IndexedDBDatabase::VersionChangeOperation(
1513 int64 version,
1514 scoped_refptr<IndexedDBCallbacks> callbacks,
1515 scoped_ptr<IndexedDBConnection> connection,
1516 IndexedDBTransaction* transaction) {
1517 IDB_TRACE1(
1518 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id());
1519 int64 old_version = metadata_.int_version;
1520 DCHECK_GT(version, old_version);
1522 if (!backing_store_->UpdateIDBDatabaseIntVersion(
1523 transaction->BackingStoreTransaction(), id(), version)) {
1524 IndexedDBDatabaseError error(
1525 blink::WebIDBDatabaseExceptionUnknownError,
1526 ASCIIToUTF16(
1527 "Internal error writing data to stable storage when "
1528 "updating version."));
1529 callbacks->OnError(error);
1530 transaction->Abort(error);
1531 return;
1534 transaction->ScheduleAbortTask(
1535 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
1536 this,
1537 metadata_.version,
1538 metadata_.int_version));
1539 metadata_.int_version = version;
1540 metadata_.version = kNoStringVersion;
1542 DCHECK(!pending_second_half_open_);
1543 pending_second_half_open_.reset(
1544 new PendingSuccessCall(callbacks, connection.get(), version));
1545 callbacks->OnUpgradeNeeded(old_version, connection.Pass(), metadata());
1548 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
1549 bool committed) {
1550 DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1551 DCHECK_EQ(transactions_[transaction->id()], transaction);
1552 transactions_.erase(transaction->id());
1554 if (transaction->mode() == blink::WebIDBTransactionModeVersionChange) {
1555 if (pending_second_half_open_) {
1556 if (committed) {
1557 DCHECK_EQ(pending_second_half_open_->version(), metadata_.int_version);
1558 DCHECK(metadata_.id != kInvalidId);
1560 // Connection was already minted for OnUpgradeNeeded callback.
1561 scoped_ptr<IndexedDBConnection> connection;
1562 pending_second_half_open_->callbacks()->OnSuccess(connection.Pass(),
1563 this->metadata());
1564 } else {
1565 pending_second_half_open_->callbacks()->OnError(
1566 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1567 "Version change transaction was aborted in "
1568 "upgradeneeded event handler."));
1570 pending_second_half_open_.reset();
1573 // Connection queue is now unblocked.
1574 ProcessPendingCalls();
1578 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status& status) {
1579 if (status.IsCorruption()) {
1580 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1581 "Error committing transaction");
1582 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), error);
1583 } else {
1584 factory_->HandleBackingStoreFailure(backing_store_->origin_url());
1588 size_t IndexedDBDatabase::ConnectionCount() const {
1589 // This does not include pending open calls, as those should not block version
1590 // changes and deletes.
1591 return connections_.size();
1594 size_t IndexedDBDatabase::PendingOpenCount() const {
1595 return pending_open_calls_.size();
1598 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1599 return pending_run_version_change_transaction_call_ ? 1 : 0;
1602 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1603 return pending_second_half_open_ ? 1 : 0;
1606 size_t IndexedDBDatabase::PendingDeleteCount() const {
1607 return pending_delete_calls_.size();
1610 void IndexedDBDatabase::ProcessPendingCalls() {
1611 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1612 DCHECK(pending_run_version_change_transaction_call_->version() >
1613 metadata_.int_version);
1614 scoped_ptr<PendingUpgradeCall> pending_call =
1615 pending_run_version_change_transaction_call_.Pass();
1616 RunVersionChangeTransactionFinal(pending_call->callbacks(),
1617 pending_call->ReleaseConnection(),
1618 pending_call->transaction_id(),
1619 pending_call->version());
1620 DCHECK_EQ(1u, ConnectionCount());
1621 // Fall through would be a no-op, since transaction must complete
1622 // asynchronously.
1623 DCHECK(IsDeleteDatabaseBlocked());
1624 DCHECK(IsOpenConnectionBlocked());
1625 return;
1628 if (!IsDeleteDatabaseBlocked()) {
1629 PendingDeleteCallList pending_delete_calls;
1630 pending_delete_calls_.swap(pending_delete_calls);
1631 while (!pending_delete_calls.empty()) {
1632 // Only the first delete call will delete the database, but each must fire
1633 // callbacks.
1634 scoped_ptr<PendingDeleteCall> pending_delete_call(
1635 pending_delete_calls.front());
1636 pending_delete_calls.pop_front();
1637 DeleteDatabaseFinal(pending_delete_call->callbacks());
1639 // delete_database_final should never re-queue calls.
1640 DCHECK(pending_delete_calls_.empty());
1641 // Fall through when complete, as pending opens may be unblocked.
1644 if (!IsOpenConnectionBlocked()) {
1645 PendingOpenCallList pending_open_calls;
1646 pending_open_calls_.swap(pending_open_calls);
1647 while (!pending_open_calls.empty()) {
1648 OpenConnection(pending_open_calls.front());
1649 pending_open_calls.pop_front();
1654 void IndexedDBDatabase::CreateTransaction(
1655 int64 transaction_id,
1656 IndexedDBConnection* connection,
1657 const std::vector<int64>& object_store_ids,
1658 blink::WebIDBTransactionMode mode) {
1659 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id);
1660 DCHECK(connections_.count(connection));
1661 DCHECK(transactions_.find(transaction_id) == transactions_.end());
1662 if (transactions_.find(transaction_id) != transactions_.end())
1663 return;
1665 // The transaction will add itself to this database's coordinator, which
1666 // manages the lifetime of the object.
1667 TransactionCreated(new IndexedDBTransaction(
1668 transaction_id,
1669 connection->callbacks(),
1670 std::set<int64>(object_store_ids.begin(), object_store_ids.end()),
1671 mode,
1672 this,
1673 new IndexedDBBackingStore::Transaction(backing_store_.get())));
1676 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
1677 transactions_[transaction->id()] = transaction;
1680 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1681 return !pending_delete_calls_.empty() ||
1682 transaction_coordinator_.IsRunningVersionChangeTransaction() ||
1683 pending_run_version_change_transaction_call_;
1686 void IndexedDBDatabase::OpenConnection(
1687 const IndexedDBPendingConnection& connection) {
1688 DCHECK(backing_store_.get());
1690 // TODO(jsbell): Should have a priority queue so that higher version
1691 // requests are processed first. http://crbug.com/225850
1692 if (IsOpenConnectionBlocked()) {
1693 // The backing store only detects data loss when it is first opened. The
1694 // presence of existing connections means we didn't even check for data loss
1695 // so there'd better not be any.
1696 DCHECK_NE(blink::WebIDBDataLossTotal, connection.callbacks->data_loss());
1697 pending_open_calls_.push_back(connection);
1698 return;
1701 if (metadata_.id == kInvalidId) {
1702 // The database was deleted then immediately re-opened; OpenInternal()
1703 // recreates it in the backing store.
1704 if (OpenInternal().ok()) {
1705 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
1706 metadata_.int_version);
1707 } else {
1708 base::string16 message;
1709 if (connection.version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1710 message = ASCIIToUTF16(
1711 "Internal error opening database with no version specified.");
1712 } else {
1713 message =
1714 ASCIIToUTF16("Internal error opening database with version ") +
1715 Int64ToString16(connection.version);
1717 connection.callbacks->OnError(IndexedDBDatabaseError(
1718 blink::WebIDBDatabaseExceptionUnknownError, message));
1719 return;
1723 // We infer that the database didn't exist from its lack of either type of
1724 // version.
1725 bool is_new_database =
1726 metadata_.version == kNoStringVersion &&
1727 metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
1729 if (connection.version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
1730 // For unit tests only - skip upgrade steps. Calling from script with
1731 // DEFAULT_INT_VERSION throws exception.
1732 // TODO(jsbell): DCHECK that not in unit tests.
1733 DCHECK(is_new_database);
1734 connection.callbacks->OnSuccess(
1735 CreateConnection(connection.database_callbacks,
1736 connection.child_process_id),
1737 this->metadata());
1738 return;
1741 // We may need to change the version.
1742 int64 local_version = connection.version;
1743 if (local_version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1744 if (!is_new_database) {
1745 connection.callbacks->OnSuccess(
1746 CreateConnection(connection.database_callbacks,
1747 connection.child_process_id),
1748 this->metadata());
1749 return;
1751 // Spec says: If no version is specified and no database exists, set
1752 // database version to 1.
1753 local_version = 1;
1756 if (local_version > metadata_.int_version) {
1757 RunVersionChangeTransaction(connection.callbacks,
1758 CreateConnection(connection.database_callbacks,
1759 connection.child_process_id),
1760 connection.transaction_id,
1761 local_version);
1762 return;
1764 if (local_version < metadata_.int_version) {
1765 connection.callbacks->OnError(IndexedDBDatabaseError(
1766 blink::WebIDBDatabaseExceptionVersionError,
1767 ASCIIToUTF16("The requested version (") +
1768 Int64ToString16(local_version) +
1769 ASCIIToUTF16(") is less than the existing version (") +
1770 Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
1771 return;
1773 DCHECK_EQ(local_version, metadata_.int_version);
1774 connection.callbacks->OnSuccess(
1775 CreateConnection(connection.database_callbacks,
1776 connection.child_process_id),
1777 this->metadata());
1780 void IndexedDBDatabase::RunVersionChangeTransaction(
1781 scoped_refptr<IndexedDBCallbacks> callbacks,
1782 scoped_ptr<IndexedDBConnection> connection,
1783 int64 transaction_id,
1784 int64 requested_version) {
1785 DCHECK(callbacks.get());
1786 DCHECK(connections_.count(connection.get()));
1787 if (ConnectionCount() > 1) {
1788 DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss());
1789 // Front end ensures the event is not fired at connections that have
1790 // close_pending set.
1791 for (const auto* iter : connections_) {
1792 if (iter != connection.get()) {
1793 iter->callbacks()->OnVersionChange(metadata_.int_version,
1794 requested_version);
1797 // OnBlocked will be fired at the request when one of the other
1798 // connections acks that the OnVersionChange was ignored.
1800 DCHECK(!pending_run_version_change_transaction_call_);
1801 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall(
1802 callbacks, connection.Pass(), transaction_id, requested_version));
1803 return;
1805 RunVersionChangeTransactionFinal(
1806 callbacks, connection.Pass(), transaction_id, requested_version);
1809 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1810 scoped_refptr<IndexedDBCallbacks> callbacks,
1811 scoped_ptr<IndexedDBConnection> connection,
1812 int64 transaction_id,
1813 int64 requested_version) {
1815 std::vector<int64> object_store_ids;
1816 CreateTransaction(transaction_id,
1817 connection.get(),
1818 object_store_ids,
1819 blink::WebIDBTransactionModeVersionChange);
1821 transactions_[transaction_id]->ScheduleTask(
1822 base::Bind(&IndexedDBDatabase::VersionChangeOperation,
1823 this,
1824 requested_version,
1825 callbacks,
1826 base::Passed(&connection)));
1827 DCHECK(!pending_second_half_open_);
1830 void IndexedDBDatabase::DeleteDatabase(
1831 scoped_refptr<IndexedDBCallbacks> callbacks) {
1833 if (IsDeleteDatabaseBlocked()) {
1834 for (const auto* connection : connections_) {
1835 // Front end ensures the event is not fired at connections that have
1836 // close_pending set.
1837 connection->callbacks()->OnVersionChange(
1838 metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION);
1840 // OnBlocked will be fired at the request when one of the other
1841 // connections acks that the OnVersionChange was ignored.
1843 pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
1844 return;
1846 DeleteDatabaseFinal(callbacks);
1849 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1850 return !!ConnectionCount();
1853 void IndexedDBDatabase::DeleteDatabaseFinal(
1854 scoped_refptr<IndexedDBCallbacks> callbacks) {
1855 DCHECK(!IsDeleteDatabaseBlocked());
1856 DCHECK(backing_store_.get());
1857 leveldb::Status s = backing_store_->DeleteDatabase(metadata_.name);
1858 if (!s.ok()) {
1859 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1860 "Internal error deleting database.");
1861 callbacks->OnError(error);
1862 if (s.IsCorruption()) {
1863 GURL origin_url = backing_store_->origin_url();
1864 backing_store_ = NULL;
1865 factory_->HandleBackingStoreCorruption(origin_url, error);
1867 return;
1869 int64 old_version = metadata_.int_version;
1870 metadata_.version = kNoStringVersion;
1871 metadata_.id = kInvalidId;
1872 metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1873 metadata_.object_stores.clear();
1874 callbacks->OnSuccess(old_version);
1875 factory_->DatabaseDeleted(identifier_);
1878 void IndexedDBDatabase::ForceClose() {
1879 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1880 scoped_refptr<IndexedDBDatabase> protect(this);
1881 ConnectionSet::const_iterator it = connections_.begin();
1882 while (it != connections_.end()) {
1883 IndexedDBConnection* connection = *it++;
1884 connection->ForceClose();
1886 DCHECK(connections_.empty());
1889 void IndexedDBDatabase::VersionChangeIgnored() {
1890 if (pending_run_version_change_transaction_call_)
1891 pending_run_version_change_transaction_call_->callbacks()->OnBlocked(
1892 metadata_.int_version);
1894 for (const auto& pending_delete_call : pending_delete_calls_)
1895 pending_delete_call->callbacks()->OnBlocked(metadata_.int_version);
1899 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
1900 DCHECK(connections_.count(connection));
1901 DCHECK(connection->IsConnected());
1902 DCHECK(connection->database() == this);
1904 IDB_TRACE("IndexedDBDatabase::Close");
1906 connections_.erase(connection);
1908 // Abort outstanding transactions from the closing connection. This
1909 // can not happen if the close is requested by the connection itself
1910 // as the front-end defers the close until all transactions are
1911 // complete, but can occur on process termination or forced close.
1913 TransactionMap transactions(transactions_);
1914 for (const auto& it : transactions) {
1915 if (it.second->connection() == connection->callbacks())
1916 it.second->Abort(
1917 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1918 "Connection is closing."));
1922 if (pending_second_half_open_ &&
1923 pending_second_half_open_->connection() == connection) {
1924 pending_second_half_open_->callbacks()->OnError(
1925 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1926 "The connection was closed."));
1927 pending_second_half_open_.reset();
1930 ProcessPendingCalls();
1932 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1933 if (!ConnectionCount() && !pending_open_calls_.size() &&
1934 !pending_delete_calls_.size()) {
1935 DCHECK(transactions_.empty());
1937 const GURL origin_url = backing_store_->origin_url();
1938 backing_store_ = NULL;
1940 factory_->ReleaseDatabase(identifier_, forced);
1944 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1945 int64 object_store_id,
1946 IndexedDBTransaction* transaction) {
1947 DCHECK(!transaction);
1948 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1949 RemoveObjectStore(object_store_id);
1952 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1953 const IndexedDBObjectStoreMetadata& object_store_metadata,
1954 IndexedDBTransaction* transaction) {
1955 DCHECK(!transaction);
1956 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
1957 AddObjectStore(object_store_metadata,
1958 IndexedDBObjectStoreMetadata::kInvalidId);
1961 void IndexedDBDatabase::VersionChangeAbortOperation(
1962 const base::string16& previous_version,
1963 int64 previous_int_version,
1964 IndexedDBTransaction* transaction) {
1965 DCHECK(!transaction);
1966 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
1967 metadata_.version = previous_version;
1968 metadata_.int_version = previous_int_version;
1971 } // namespace content