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"
10 #include "base/auto_reset.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/scoped_vector.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "content/browser/indexed_db/indexed_db_blob_info.h"
18 #include "content/browser/indexed_db/indexed_db_connection.h"
19 #include "content/browser/indexed_db/indexed_db_context_impl.h"
20 #include "content/browser/indexed_db/indexed_db_cursor.h"
21 #include "content/browser/indexed_db/indexed_db_factory.h"
22 #include "content/browser/indexed_db/indexed_db_index_writer.h"
23 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
24 #include "content/browser/indexed_db/indexed_db_tracing.h"
25 #include "content/browser/indexed_db/indexed_db_transaction.h"
26 #include "content/browser/indexed_db/indexed_db_value.h"
27 #include "content/common/indexed_db/indexed_db_key_path.h"
28 #include "content/common/indexed_db/indexed_db_key_range.h"
29 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
30 #include "webkit/browser/blob/blob_data_handle.h"
32 using base::ASCIIToUTF16
;
33 using base::Int64ToString16
;
34 using blink::WebIDBKeyTypeNumber
;
38 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
39 // in-progress connection.
40 class IndexedDBDatabase::PendingUpgradeCall
{
42 PendingUpgradeCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
,
43 scoped_ptr
<IndexedDBConnection
> connection
,
46 : callbacks_(callbacks
),
47 connection_(connection
.Pass()),
49 transaction_id_(transaction_id
) {}
50 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
51 // Takes ownership of the connection object.
52 scoped_ptr
<IndexedDBConnection
> ReleaseConnection() WARN_UNUSED_RESULT
{
53 return connection_
.Pass();
55 int64
version() const { return version_
; }
56 int64
transaction_id() const { return transaction_id_
; }
59 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
60 scoped_ptr
<IndexedDBConnection
> connection_
;
62 const int64 transaction_id_
;
65 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
66 // owned elsewhere, but we need to cancel the success call if that connection
67 // closes before it is sent.
68 class IndexedDBDatabase::PendingSuccessCall
{
70 PendingSuccessCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
,
71 IndexedDBConnection
* connection
,
73 : callbacks_(callbacks
), connection_(connection
), version_(version
) {}
74 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
75 IndexedDBConnection
* connection() const { return connection_
; }
76 int64
version() const { return version_
; }
79 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
80 IndexedDBConnection
* connection_
;
84 class IndexedDBDatabase::PendingDeleteCall
{
86 explicit PendingDeleteCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
)
87 : callbacks_(callbacks
) {}
88 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
91 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
94 scoped_refptr
<IndexedDBDatabase
> IndexedDBDatabase::Create(
95 const base::string16
& name
,
96 IndexedDBBackingStore
* backing_store
,
97 IndexedDBFactory
* factory
,
98 const Identifier
& unique_identifier
,
100 scoped_refptr
<IndexedDBDatabase
> database
=
101 new IndexedDBDatabase(name
, backing_store
, factory
, unique_identifier
);
102 *s
= database
->OpenInternal();
110 const base::string16::value_type kNoStringVersion
[] = {0};
113 IndexedDBDatabase::IndexedDBDatabase(const base::string16
& name
,
114 IndexedDBBackingStore
* backing_store
,
115 IndexedDBFactory
* factory
,
116 const Identifier
& unique_identifier
)
117 : backing_store_(backing_store
),
121 IndexedDBDatabaseMetadata::NO_INT_VERSION
,
123 identifier_(unique_identifier
),
127 void IndexedDBDatabase::AddObjectStore(
128 const IndexedDBObjectStoreMetadata
& object_store
,
129 int64 new_max_object_store_id
) {
130 DCHECK(metadata_
.object_stores
.find(object_store
.id
) ==
131 metadata_
.object_stores
.end());
132 if (new_max_object_store_id
!= IndexedDBObjectStoreMetadata::kInvalidId
) {
133 DCHECK_LT(metadata_
.max_object_store_id
, new_max_object_store_id
);
134 metadata_
.max_object_store_id
= new_max_object_store_id
;
136 metadata_
.object_stores
[object_store
.id
] = object_store
;
139 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id
) {
140 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
141 metadata_
.object_stores
.end());
142 metadata_
.object_stores
.erase(object_store_id
);
145 void IndexedDBDatabase::AddIndex(int64 object_store_id
,
146 const IndexedDBIndexMetadata
& index
,
147 int64 new_max_index_id
) {
148 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
149 metadata_
.object_stores
.end());
150 IndexedDBObjectStoreMetadata object_store
=
151 metadata_
.object_stores
[object_store_id
];
153 DCHECK(object_store
.indexes
.find(index
.id
) == object_store
.indexes
.end());
154 object_store
.indexes
[index
.id
] = index
;
155 if (new_max_index_id
!= IndexedDBIndexMetadata::kInvalidId
) {
156 DCHECK_LT(object_store
.max_index_id
, new_max_index_id
);
157 object_store
.max_index_id
= new_max_index_id
;
159 metadata_
.object_stores
[object_store_id
] = object_store
;
162 void IndexedDBDatabase::RemoveIndex(int64 object_store_id
, int64 index_id
) {
163 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
164 metadata_
.object_stores
.end());
165 IndexedDBObjectStoreMetadata object_store
=
166 metadata_
.object_stores
[object_store_id
];
168 DCHECK(object_store
.indexes
.find(index_id
) != object_store
.indexes
.end());
169 object_store
.indexes
.erase(index_id
);
170 metadata_
.object_stores
[object_store_id
] = object_store
;
173 leveldb::Status
IndexedDBDatabase::OpenInternal() {
174 bool success
= false;
175 leveldb::Status s
= backing_store_
->GetIDBDatabaseMetaData(
176 metadata_
.name
, &metadata_
, &success
);
177 DCHECK(success
== (metadata_
.id
!= kInvalidId
)) << "success = " << success
178 << " id = " << metadata_
.id
;
182 return backing_store_
->GetObjectStores(metadata_
.id
,
183 &metadata_
.object_stores
);
185 return backing_store_
->CreateIDBDatabaseMetaData(
186 metadata_
.name
, metadata_
.version
, metadata_
.int_version
, &metadata_
.id
);
189 IndexedDBDatabase::~IndexedDBDatabase() {
190 DCHECK(transactions_
.empty());
191 DCHECK(pending_open_calls_
.empty());
192 DCHECK(pending_delete_calls_
.empty());
195 scoped_ptr
<IndexedDBConnection
> IndexedDBDatabase::CreateConnection(
196 scoped_refptr
<IndexedDBDatabaseCallbacks
> database_callbacks
,
197 int child_process_id
) {
198 scoped_ptr
<IndexedDBConnection
> connection(
199 new IndexedDBConnection(this, database_callbacks
));
200 connections_
.insert(connection
.get());
201 backing_store_
->GrantChildProcessPermissions(child_process_id
);
202 return connection
.Pass();
205 IndexedDBTransaction
* IndexedDBDatabase::GetTransaction(
206 int64 transaction_id
) const {
207 TransactionMap::const_iterator trans_iterator
=
208 transactions_
.find(transaction_id
);
209 if (trans_iterator
== transactions_
.end())
211 return trans_iterator
->second
;
214 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id
) const {
215 if (!ContainsKey(metadata_
.object_stores
, object_store_id
)) {
216 DLOG(ERROR
) << "Invalid object_store_id";
222 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id
,
223 int64 index_id
) const {
224 if (!ValidateObjectStoreId(object_store_id
))
226 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
227 metadata_
.object_stores
.find(object_store_id
)->second
;
228 if (!ContainsKey(object_store_metadata
.indexes
, index_id
)) {
229 DLOG(ERROR
) << "Invalid index_id";
235 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
236 int64 object_store_id
,
237 int64 index_id
) const {
238 if (!ValidateObjectStoreId(object_store_id
))
240 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
241 metadata_
.object_stores
.find(object_store_id
)->second
;
242 if (index_id
!= IndexedDBIndexMetadata::kInvalidId
&&
243 !ContainsKey(object_store_metadata
.indexes
, index_id
)) {
244 DLOG(ERROR
) << "Invalid index_id";
250 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
251 int64 object_store_id
,
252 int64 index_id
) const {
253 if (!ValidateObjectStoreId(object_store_id
))
255 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
256 metadata_
.object_stores
.find(object_store_id
)->second
;
257 if (ContainsKey(object_store_metadata
.indexes
, index_id
)) {
258 DLOG(ERROR
) << "Invalid index_id";
264 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id
,
265 int64 object_store_id
,
266 const base::string16
& name
,
267 const IndexedDBKeyPath
& key_path
,
268 bool auto_increment
) {
269 IDB_TRACE("IndexedDBDatabase::CreateObjectStore");
270 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
273 DCHECK_EQ(transaction
->mode(), indexed_db::TRANSACTION_VERSION_CHANGE
);
275 if (ContainsKey(metadata_
.object_stores
, object_store_id
)) {
276 DLOG(ERROR
) << "Invalid object_store_id";
280 IndexedDBObjectStoreMetadata
object_store_metadata(
285 IndexedDBDatabase::kMinimumIndexId
);
287 transaction
->ScheduleTask(
288 base::Bind(&IndexedDBDatabase::CreateObjectStoreOperation
,
290 object_store_metadata
),
291 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation
,
295 AddObjectStore(object_store_metadata
, object_store_id
);
298 void IndexedDBDatabase::CreateObjectStoreOperation(
299 const IndexedDBObjectStoreMetadata
& object_store_metadata
,
300 IndexedDBTransaction
* transaction
) {
301 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation");
303 backing_store_
->CreateObjectStore(transaction
->BackingStoreTransaction(),
304 transaction
->database()->id(),
305 object_store_metadata
.id
,
306 object_store_metadata
.name
,
307 object_store_metadata
.key_path
,
308 object_store_metadata
.auto_increment
);
310 IndexedDBDatabaseError
error(
311 blink::WebIDBDatabaseExceptionUnknownError
,
312 ASCIIToUTF16("Internal error creating object store '") +
313 object_store_metadata
.name
+ ASCIIToUTF16("'."));
314 transaction
->Abort(error
);
315 if (s
.IsCorruption())
316 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
322 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id
,
323 int64 object_store_id
) {
324 IDB_TRACE("IndexedDBDatabase::DeleteObjectStore");
325 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
328 DCHECK_EQ(transaction
->mode(), indexed_db::TRANSACTION_VERSION_CHANGE
);
330 if (!ValidateObjectStoreId(object_store_id
))
333 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
334 metadata_
.object_stores
[object_store_id
];
336 transaction
->ScheduleTask(
337 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation
,
339 object_store_metadata
),
340 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation
,
342 object_store_metadata
));
343 RemoveObjectStore(object_store_id
);
346 void IndexedDBDatabase::CreateIndex(int64 transaction_id
,
347 int64 object_store_id
,
349 const base::string16
& name
,
350 const IndexedDBKeyPath
& key_path
,
353 IDB_TRACE("IndexedDBDatabase::CreateIndex");
354 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
357 DCHECK_EQ(transaction
->mode(), indexed_db::TRANSACTION_VERSION_CHANGE
);
359 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id
, index_id
))
361 const IndexedDBIndexMetadata
index_metadata(
362 name
, index_id
, key_path
, unique
, multi_entry
);
364 transaction
->ScheduleTask(
365 base::Bind(&IndexedDBDatabase::CreateIndexOperation
,
369 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation
,
374 AddIndex(object_store_id
, index_metadata
, index_id
);
377 void IndexedDBDatabase::CreateIndexOperation(
378 int64 object_store_id
,
379 const IndexedDBIndexMetadata
& index_metadata
,
380 IndexedDBTransaction
* transaction
) {
381 IDB_TRACE("IndexedDBDatabase::CreateIndexOperation");
382 if (!backing_store_
->CreateIndex(transaction
->BackingStoreTransaction(),
383 transaction
->database()->id(),
387 index_metadata
.key_path
,
388 index_metadata
.unique
,
389 index_metadata
.multi_entry
).ok()) {
390 base::string16 error_string
=
391 ASCIIToUTF16("Internal error creating index '") +
392 index_metadata
.name
+ ASCIIToUTF16("'.");
393 transaction
->Abort(IndexedDBDatabaseError(
394 blink::WebIDBDatabaseExceptionUnknownError
, error_string
));
399 void IndexedDBDatabase::CreateIndexAbortOperation(
400 int64 object_store_id
,
402 IndexedDBTransaction
* transaction
) {
403 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
404 DCHECK(!transaction
);
405 RemoveIndex(object_store_id
, index_id
);
408 void IndexedDBDatabase::DeleteIndex(int64 transaction_id
,
409 int64 object_store_id
,
411 IDB_TRACE("IndexedDBDatabase::DeleteIndex");
412 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
415 DCHECK_EQ(transaction
->mode(), indexed_db::TRANSACTION_VERSION_CHANGE
);
417 if (!ValidateObjectStoreIdAndIndexId(object_store_id
, index_id
))
419 const IndexedDBIndexMetadata
& index_metadata
=
420 metadata_
.object_stores
[object_store_id
].indexes
[index_id
];
422 transaction
->ScheduleTask(
423 base::Bind(&IndexedDBDatabase::DeleteIndexOperation
,
427 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation
,
432 RemoveIndex(object_store_id
, index_id
);
435 void IndexedDBDatabase::DeleteIndexOperation(
436 int64 object_store_id
,
437 const IndexedDBIndexMetadata
& index_metadata
,
438 IndexedDBTransaction
* transaction
) {
439 IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation");
441 backing_store_
->DeleteIndex(transaction
->BackingStoreTransaction(),
442 transaction
->database()->id(),
446 base::string16 error_string
=
447 ASCIIToUTF16("Internal error deleting index '") +
448 index_metadata
.name
+ ASCIIToUTF16("'.");
449 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
451 transaction
->Abort(error
);
452 if (s
.IsCorruption())
453 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
458 void IndexedDBDatabase::DeleteIndexAbortOperation(
459 int64 object_store_id
,
460 const IndexedDBIndexMetadata
& index_metadata
,
461 IndexedDBTransaction
* transaction
) {
462 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
463 DCHECK(!transaction
);
464 AddIndex(object_store_id
, index_metadata
, IndexedDBIndexMetadata::kInvalidId
);
467 void IndexedDBDatabase::Commit(int64 transaction_id
) {
468 // The frontend suggests that we commit, but we may have previously initiated
469 // an abort, and so have disposed of the transaction. on_abort has already
470 // been dispatched to the frontend, so it will find out about that
472 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
474 transaction
->Commit();
477 void IndexedDBDatabase::Abort(int64 transaction_id
) {
478 // If the transaction is unknown, then it has already been aborted by the
479 // backend before this call so it is safe to ignore it.
480 IDB_TRACE("IndexedDBDatabase::Abort");
481 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
483 transaction
->Abort();
486 void IndexedDBDatabase::Abort(int64 transaction_id
,
487 const IndexedDBDatabaseError
& error
) {
488 IDB_TRACE("IndexedDBDatabase::Abort");
489 // If the transaction is unknown, then it has already been aborted by the
490 // backend before this call so it is safe to ignore it.
491 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
493 transaction
->Abort(error
);
496 void IndexedDBDatabase::Get(int64 transaction_id
,
497 int64 object_store_id
,
499 scoped_ptr
<IndexedDBKeyRange
> key_range
,
501 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
502 IDB_TRACE("IndexedDBDatabase::Get");
503 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
507 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
510 transaction
->ScheduleTask(base::Bind(
511 &IndexedDBDatabase::GetOperation
,
516 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
,
520 void IndexedDBDatabase::GetOperation(
521 int64 object_store_id
,
523 scoped_ptr
<IndexedDBKeyRange
> key_range
,
524 indexed_db::CursorType cursor_type
,
525 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
526 IndexedDBTransaction
* transaction
) {
527 IDB_TRACE("IndexedDBDatabase::GetOperation");
529 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
530 metadata_
.object_stores
.end());
531 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
532 metadata_
.object_stores
[object_store_id
];
534 const IndexedDBKey
* key
;
537 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
538 if (key_range
->IsOnlyKey()) {
539 key
= &key_range
->lower();
541 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
542 DCHECK_NE(cursor_type
, indexed_db::CURSOR_KEY_ONLY
);
543 // ObjectStore Retrieval Operation
544 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
545 transaction
->BackingStoreTransaction(),
549 indexed_db::CURSOR_NEXT
,
551 } else if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
552 // Index Value Retrieval Operation
553 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
554 transaction
->BackingStoreTransaction(),
559 indexed_db::CURSOR_NEXT
,
562 // Index Referenced Value Retrieval Operation
563 backing_store_cursor
= backing_store_
->OpenIndexCursor(
564 transaction
->BackingStoreTransaction(),
569 indexed_db::CURSOR_NEXT
,
574 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
575 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
576 "Internal error deleting data in range");
577 if (s
.IsCorruption()) {
578 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
583 if (!backing_store_cursor
) {
584 callbacks
->OnSuccess();
588 key
= &backing_store_cursor
->key();
591 scoped_ptr
<IndexedDBKey
> primary_key
;
592 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
593 // Object Store Retrieval Operation
594 IndexedDBValue value
;
595 s
= backing_store_
->GetRecord(transaction
->BackingStoreTransaction(),
601 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
602 "Internal error in GetRecord.");
603 callbacks
->OnError(error
);
605 if (s
.IsCorruption())
606 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
612 callbacks
->OnSuccess();
616 if (object_store_metadata
.auto_increment
&&
617 !object_store_metadata
.key_path
.IsNull()) {
618 callbacks
->OnSuccess(&value
, *key
, object_store_metadata
.key_path
);
622 callbacks
->OnSuccess(&value
);
626 // From here we are dealing only with indexes.
627 s
= backing_store_
->GetPrimaryKeyViaIndex(
628 transaction
->BackingStoreTransaction(),
635 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
636 "Internal error in GetPrimaryKeyViaIndex.");
637 callbacks
->OnError(error
);
638 if (s
.IsCorruption())
639 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
644 callbacks
->OnSuccess();
647 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
648 // Index Value Retrieval Operation
649 callbacks
->OnSuccess(*primary_key
);
653 // Index Referenced Value Retrieval Operation
654 IndexedDBValue value
;
655 s
= backing_store_
->GetRecord(transaction
->BackingStoreTransaction(),
661 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
662 "Internal error in GetRecord.");
663 callbacks
->OnError(error
);
664 if (s
.IsCorruption())
665 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
671 callbacks
->OnSuccess();
674 if (object_store_metadata
.auto_increment
&&
675 !object_store_metadata
.key_path
.IsNull()) {
676 callbacks
->OnSuccess(&value
, *primary_key
, object_store_metadata
.key_path
);
679 callbacks
->OnSuccess(&value
);
682 static scoped_ptr
<IndexedDBKey
> GenerateKey(
683 IndexedDBBackingStore
* backing_store
,
684 IndexedDBTransaction
* transaction
,
686 int64 object_store_id
) {
687 const int64 max_generator_value
=
688 9007199254740992LL; // Maximum integer storable as ECMAScript number.
689 int64 current_number
;
690 leveldb::Status s
= backing_store
->GetKeyGeneratorCurrentNumber(
691 transaction
->BackingStoreTransaction(),
696 LOG(ERROR
) << "Failed to GetKeyGeneratorCurrentNumber";
697 return make_scoped_ptr(new IndexedDBKey());
699 if (current_number
< 0 || current_number
> max_generator_value
)
700 return make_scoped_ptr(new IndexedDBKey());
702 return make_scoped_ptr(new IndexedDBKey(current_number
, WebIDBKeyTypeNumber
));
705 static leveldb::Status
UpdateKeyGenerator(IndexedDBBackingStore
* backing_store
,
706 IndexedDBTransaction
* transaction
,
708 int64 object_store_id
,
709 const IndexedDBKey
& key
,
710 bool check_current
) {
711 DCHECK_EQ(WebIDBKeyTypeNumber
, key
.type());
712 return backing_store
->MaybeUpdateKeyGeneratorCurrentNumber(
713 transaction
->BackingStoreTransaction(),
716 static_cast<int64
>(floor(key
.number())) + 1,
720 struct IndexedDBDatabase::PutOperationParams
{
721 PutOperationParams() {}
722 int64 object_store_id
;
723 IndexedDBValue value
;
724 ScopedVector
<webkit_blob::BlobDataHandle
> handles
;
725 scoped_ptr
<IndexedDBKey
> key
;
726 IndexedDBDatabase::PutMode put_mode
;
727 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
728 std::vector
<IndexKeys
> index_keys
;
731 DISALLOW_COPY_AND_ASSIGN(PutOperationParams
);
734 void IndexedDBDatabase::Put(int64 transaction_id
,
735 int64 object_store_id
,
736 IndexedDBValue
* value
,
737 ScopedVector
<webkit_blob::BlobDataHandle
>* handles
,
738 scoped_ptr
<IndexedDBKey
> key
,
740 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
741 const std::vector
<IndexKeys
>& index_keys
) {
742 IDB_TRACE("IndexedDBDatabase::Put");
743 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
746 DCHECK_NE(transaction
->mode(), indexed_db::TRANSACTION_READ_ONLY
);
748 if (!ValidateObjectStoreId(object_store_id
))
753 scoped_ptr
<PutOperationParams
> params(new PutOperationParams());
754 params
->object_store_id
= object_store_id
;
755 params
->value
.swap(*value
);
756 params
->handles
.swap(*handles
);
757 params
->key
= key
.Pass();
758 params
->put_mode
= put_mode
;
759 params
->callbacks
= callbacks
;
760 params
->index_keys
= index_keys
;
761 transaction
->ScheduleTask(base::Bind(
762 &IndexedDBDatabase::PutOperation
, this, base::Passed(¶ms
)));
765 void IndexedDBDatabase::PutOperation(scoped_ptr
<PutOperationParams
> params
,
766 IndexedDBTransaction
* transaction
) {
767 IDB_TRACE("IndexedDBDatabase::PutOperation");
768 DCHECK_NE(transaction
->mode(), indexed_db::TRANSACTION_READ_ONLY
);
769 bool key_was_generated
= false;
771 DCHECK(metadata_
.object_stores
.find(params
->object_store_id
) !=
772 metadata_
.object_stores
.end());
773 const IndexedDBObjectStoreMetadata
& object_store
=
774 metadata_
.object_stores
[params
->object_store_id
];
775 DCHECK(object_store
.auto_increment
|| params
->key
->IsValid());
777 scoped_ptr
<IndexedDBKey
> key
;
778 if (params
->put_mode
!= IndexedDBDatabase::CURSOR_UPDATE
&&
779 object_store
.auto_increment
&& !params
->key
->IsValid()) {
780 scoped_ptr
<IndexedDBKey
> auto_inc_key
= GenerateKey(
781 backing_store_
.get(), transaction
, id(), params
->object_store_id
);
782 key_was_generated
= true;
783 if (!auto_inc_key
->IsValid()) {
784 params
->callbacks
->OnError(
785 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError
,
786 "Maximum key generator value reached."));
789 key
= auto_inc_key
.Pass();
791 key
= params
->key
.Pass();
794 DCHECK(key
->IsValid());
796 IndexedDBBackingStore::RecordIdentifier record_identifier
;
797 if (params
->put_mode
== IndexedDBDatabase::ADD_ONLY
) {
799 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
800 transaction
->BackingStoreTransaction(),
802 params
->object_store_id
,
807 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
808 "Internal error checking key existence.");
809 params
->callbacks
->OnError(error
);
810 if (s
.IsCorruption())
811 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
816 params
->callbacks
->OnError(
817 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError
,
818 "Key already exists in the object store."));
823 ScopedVector
<IndexWriter
> index_writers
;
824 base::string16 error_message
;
825 bool obeys_constraints
= false;
826 bool backing_store_success
= MakeIndexWriters(transaction
,
827 backing_store_
.get(),
836 if (!backing_store_success
) {
837 params
->callbacks
->OnError(IndexedDBDatabaseError(
838 blink::WebIDBDatabaseExceptionUnknownError
,
839 "Internal error: backing store error updating index keys."));
842 if (!obeys_constraints
) {
843 params
->callbacks
->OnError(IndexedDBDatabaseError(
844 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
848 // Before this point, don't do any mutation. After this point, rollback the
849 // transaction in case of error.
851 backing_store_
->PutRecord(transaction
->BackingStoreTransaction(),
853 params
->object_store_id
,
859 IndexedDBDatabaseError
error(
860 blink::WebIDBDatabaseExceptionUnknownError
,
861 "Internal error: backing store error performing put/add.");
862 params
->callbacks
->OnError(error
);
863 if (s
.IsCorruption())
864 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
869 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
870 IndexWriter
* index_writer
= index_writers
[i
];
871 index_writer
->WriteIndexKeys(record_identifier
,
872 backing_store_
.get(),
873 transaction
->BackingStoreTransaction(),
875 params
->object_store_id
);
878 if (object_store
.auto_increment
&&
879 params
->put_mode
!= IndexedDBDatabase::CURSOR_UPDATE
&&
880 key
->type() == WebIDBKeyTypeNumber
) {
881 leveldb::Status s
= UpdateKeyGenerator(backing_store_
.get(),
884 params
->object_store_id
,
888 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
889 "Internal error updating key generator.");
890 params
->callbacks
->OnError(error
);
891 if (s
.IsCorruption())
892 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
898 params
->callbacks
->OnSuccess(*key
);
901 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id
,
902 int64 object_store_id
,
903 scoped_ptr
<IndexedDBKey
> primary_key
,
904 const std::vector
<IndexKeys
>& index_keys
) {
905 IDB_TRACE("IndexedDBDatabase::SetIndexKeys");
906 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
909 DCHECK_EQ(transaction
->mode(), indexed_db::TRANSACTION_VERSION_CHANGE
);
911 // TODO(alecflett): This method could be asynchronous, but we need to
912 // evaluate if it's worth the extra complexity.
913 IndexedDBBackingStore::RecordIdentifier record_identifier
;
915 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
916 transaction
->BackingStoreTransaction(),
923 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
924 "Internal error setting index keys.");
925 transaction
->Abort(error
);
926 if (s
.IsCorruption())
927 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
932 transaction
->Abort(IndexedDBDatabaseError(
933 blink::WebIDBDatabaseExceptionUnknownError
,
934 "Internal error setting index keys for object store."));
938 ScopedVector
<IndexWriter
> index_writers
;
939 base::string16 error_message
;
940 bool obeys_constraints
= false;
941 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
942 metadata_
.object_stores
.end());
943 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
944 metadata_
.object_stores
[object_store_id
];
945 bool backing_store_success
= MakeIndexWriters(transaction
,
948 object_store_metadata
,
955 if (!backing_store_success
) {
956 transaction
->Abort(IndexedDBDatabaseError(
957 blink::WebIDBDatabaseExceptionUnknownError
,
958 "Internal error: backing store error updating index keys."));
961 if (!obeys_constraints
) {
962 transaction
->Abort(IndexedDBDatabaseError(
963 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
967 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
968 IndexWriter
* index_writer
= index_writers
[i
];
969 index_writer
->WriteIndexKeys(record_identifier
,
971 transaction
->BackingStoreTransaction(),
977 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id
,
979 const std::vector
<int64
>& index_ids
) {
980 IDB_TRACE("IndexedDBDatabase::SetIndexesReady");
981 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
984 DCHECK_EQ(transaction
->mode(), indexed_db::TRANSACTION_VERSION_CHANGE
);
986 transaction
->ScheduleTask(
987 IndexedDBDatabase::PREEMPTIVE_TASK
,
988 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation
,
993 void IndexedDBDatabase::SetIndexesReadyOperation(
995 IndexedDBTransaction
* transaction
) {
996 IDB_TRACE("IndexedDBDatabase::SetIndexesReadyOperation");
997 for (size_t i
= 0; i
< index_count
; ++i
)
998 transaction
->DidCompletePreemptiveEvent();
1001 struct IndexedDBDatabase::OpenCursorOperationParams
{
1002 OpenCursorOperationParams() {}
1003 int64 object_store_id
;
1005 scoped_ptr
<IndexedDBKeyRange
> key_range
;
1006 indexed_db::CursorDirection direction
;
1007 indexed_db::CursorType cursor_type
;
1008 IndexedDBDatabase::TaskType task_type
;
1009 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
1012 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams
);
1015 void IndexedDBDatabase::OpenCursor(
1016 int64 transaction_id
,
1017 int64 object_store_id
,
1019 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1020 indexed_db::CursorDirection direction
,
1023 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1024 IDB_TRACE("IndexedDBDatabase::OpenCursor");
1025 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1029 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1032 scoped_ptr
<OpenCursorOperationParams
> params(new OpenCursorOperationParams());
1033 params
->object_store_id
= object_store_id
;
1034 params
->index_id
= index_id
;
1035 params
->key_range
= key_range
.Pass();
1036 params
->direction
= direction
;
1037 params
->cursor_type
=
1038 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
;
1039 params
->task_type
= task_type
;
1040 params
->callbacks
= callbacks
;
1041 transaction
->ScheduleTask(base::Bind(
1042 &IndexedDBDatabase::OpenCursorOperation
, this, base::Passed(¶ms
)));
1045 void IndexedDBDatabase::OpenCursorOperation(
1046 scoped_ptr
<OpenCursorOperationParams
> params
,
1047 IndexedDBTransaction
* transaction
) {
1048 IDB_TRACE("IndexedDBDatabase::OpenCursorOperation");
1050 // The frontend has begun indexing, so this pauses the transaction
1051 // until the indexing is complete. This can't happen any earlier
1052 // because we don't want to switch to early mode in case multiple
1053 // indexes are being created in a row, with Put()'s in between.
1054 if (params
->task_type
== IndexedDBDatabase::PREEMPTIVE_TASK
)
1055 transaction
->AddPreemptiveEvent();
1058 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1059 if (params
->index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1060 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1061 DCHECK_EQ(params
->task_type
, IndexedDBDatabase::NORMAL_TASK
);
1062 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1063 transaction
->BackingStoreTransaction(),
1065 params
->object_store_id
,
1070 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
1071 transaction
->BackingStoreTransaction(),
1073 params
->object_store_id
,
1079 DCHECK_EQ(params
->task_type
, IndexedDBDatabase::NORMAL_TASK
);
1080 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1081 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1082 transaction
->BackingStoreTransaction(),
1084 params
->object_store_id
,
1090 backing_store_cursor
= backing_store_
->OpenIndexCursor(
1091 transaction
->BackingStoreTransaction(),
1093 params
->object_store_id
,
1102 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
1103 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1104 "Internal error opening cursor operation");
1105 if (s
.IsCorruption()) {
1106 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1111 if (!backing_store_cursor
) {
1112 // Why is Success being called?
1113 params
->callbacks
->OnSuccess(static_cast<IndexedDBValue
*>(NULL
));
1117 scoped_refptr
<IndexedDBCursor
> cursor
=
1118 new IndexedDBCursor(backing_store_cursor
.Pass(),
1119 params
->cursor_type
,
1122 params
->callbacks
->OnSuccess(
1123 cursor
, cursor
->key(), cursor
->primary_key(), cursor
->Value());
1126 void IndexedDBDatabase::Count(int64 transaction_id
,
1127 int64 object_store_id
,
1129 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1130 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1131 IDB_TRACE("IndexedDBDatabase::Count");
1132 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1136 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1139 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation
,
1143 base::Passed(&key_range
),
1147 void IndexedDBDatabase::CountOperation(
1148 int64 object_store_id
,
1150 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1151 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1152 IndexedDBTransaction
* transaction
) {
1153 IDB_TRACE("IndexedDBDatabase::CountOperation");
1155 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1158 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1159 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1160 transaction
->BackingStoreTransaction(),
1164 indexed_db::CURSOR_NEXT
,
1167 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1168 transaction
->BackingStoreTransaction(),
1173 indexed_db::CURSOR_NEXT
,
1177 DLOG(ERROR
) << "Unable perform count operation: " << s
.ToString();
1178 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1179 "Internal error performing count operation");
1180 if (s
.IsCorruption()) {
1181 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1185 if (!backing_store_cursor
) {
1186 callbacks
->OnSuccess(count
);
1192 } while (backing_store_cursor
->Continue(&s
));
1194 // TODO(cmumford): Check for database corruption.
1196 callbacks
->OnSuccess(count
);
1199 void IndexedDBDatabase::DeleteRange(
1200 int64 transaction_id
,
1201 int64 object_store_id
,
1202 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1203 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1204 IDB_TRACE("IndexedDBDatabase::DeleteRange");
1205 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1208 DCHECK_NE(transaction
->mode(), indexed_db::TRANSACTION_READ_ONLY
);
1210 if (!ValidateObjectStoreId(object_store_id
))
1213 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation
,
1216 base::Passed(&key_range
),
1220 void IndexedDBDatabase::DeleteRangeOperation(
1221 int64 object_store_id
,
1222 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1223 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1224 IndexedDBTransaction
* transaction
) {
1225 IDB_TRACE("IndexedDBDatabase::DeleteRangeOperation");
1227 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
=
1228 backing_store_
->OpenObjectStoreCursor(
1229 transaction
->BackingStoreTransaction(),
1233 indexed_db::CURSOR_NEXT
,
1235 if (backing_store_cursor
&& s
.ok()) {
1237 if (!backing_store_
->DeleteRecord(
1238 transaction
->BackingStoreTransaction(),
1241 backing_store_cursor
->record_identifier())
1244 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
1245 "Internal error deleting data in range"));
1248 } while (backing_store_cursor
->Continue(&s
));
1252 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1253 ASCIIToUTF16("Internal error deleting range"));
1254 transaction
->Abort(error
);
1255 if (s
.IsCorruption()) {
1256 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1262 callbacks
->OnSuccess();
1265 void IndexedDBDatabase::Clear(int64 transaction_id
,
1266 int64 object_store_id
,
1267 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1268 IDB_TRACE("IndexedDBDatabase::Clear");
1269 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1272 DCHECK_NE(transaction
->mode(), indexed_db::TRANSACTION_READ_ONLY
);
1274 if (!ValidateObjectStoreId(object_store_id
))
1277 transaction
->ScheduleTask(base::Bind(
1278 &IndexedDBDatabase::ClearOperation
, this, object_store_id
, callbacks
));
1281 void IndexedDBDatabase::ClearOperation(
1282 int64 object_store_id
,
1283 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1284 IndexedDBTransaction
* transaction
) {
1285 IDB_TRACE("IndexedDBDatabase::ObjectStoreClearOperation");
1286 leveldb::Status s
= backing_store_
->ClearObjectStore(
1287 transaction
->BackingStoreTransaction(), id(), object_store_id
);
1289 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1290 "Internal error clearing object store");
1291 callbacks
->OnError(error
);
1292 if (s
.IsCorruption()) {
1293 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1298 callbacks
->OnSuccess();
1301 void IndexedDBDatabase::DeleteObjectStoreOperation(
1302 const IndexedDBObjectStoreMetadata
& object_store_metadata
,
1303 IndexedDBTransaction
* transaction
) {
1304 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation");
1306 backing_store_
->DeleteObjectStore(transaction
->BackingStoreTransaction(),
1307 transaction
->database()->id(),
1308 object_store_metadata
.id
);
1310 base::string16 error_string
=
1311 ASCIIToUTF16("Internal error deleting object store '") +
1312 object_store_metadata
.name
+ ASCIIToUTF16("'.");
1313 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1315 transaction
->Abort(error
);
1316 if (s
.IsCorruption())
1317 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1322 void IndexedDBDatabase::VersionChangeOperation(
1324 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1325 scoped_ptr
<IndexedDBConnection
> connection
,
1326 IndexedDBTransaction
* transaction
) {
1327 IDB_TRACE("IndexedDBDatabase::VersionChangeOperation");
1328 int64 old_version
= metadata_
.int_version
;
1329 DCHECK_GT(version
, old_version
);
1330 metadata_
.int_version
= version
;
1331 if (!backing_store_
->UpdateIDBDatabaseIntVersion(
1332 transaction
->BackingStoreTransaction(),
1334 metadata_
.int_version
)) {
1335 IndexedDBDatabaseError
error(
1336 blink::WebIDBDatabaseExceptionUnknownError
,
1338 "Internal error writing data to stable storage when "
1339 "updating version."));
1340 callbacks
->OnError(error
);
1341 transaction
->Abort(error
);
1344 DCHECK(!pending_second_half_open_
);
1345 pending_second_half_open_
.reset(
1346 new PendingSuccessCall(callbacks
, connection
.get(), version
));
1347 callbacks
->OnUpgradeNeeded(old_version
, connection
.Pass(), metadata());
1350 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction
* transaction
,
1352 DCHECK(transactions_
.find(transaction
->id()) != transactions_
.end());
1353 DCHECK_EQ(transactions_
[transaction
->id()], transaction
);
1354 transactions_
.erase(transaction
->id());
1356 if (transaction
->mode() == indexed_db::TRANSACTION_VERSION_CHANGE
) {
1357 if (pending_second_half_open_
) {
1359 DCHECK_EQ(pending_second_half_open_
->version(), metadata_
.int_version
);
1360 DCHECK(metadata_
.id
!= kInvalidId
);
1362 // Connection was already minted for OnUpgradeNeeded callback.
1363 scoped_ptr
<IndexedDBConnection
> connection
;
1364 pending_second_half_open_
->callbacks()->OnSuccess(connection
.Pass(),
1367 pending_second_half_open_
->callbacks()->OnError(
1368 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1369 "Version change transaction was aborted in "
1370 "upgradeneeded event handler."));
1372 pending_second_half_open_
.reset();
1375 // Connection queue is now unblocked.
1376 ProcessPendingCalls();
1380 void IndexedDBDatabase::TransactionCommitFailed() {
1381 // Factory may be null in unit tests.
1384 factory_
->HandleBackingStoreFailure(backing_store_
->origin_url());
1387 size_t IndexedDBDatabase::ConnectionCount() const {
1388 // This does not include pending open calls, as those should not block version
1389 // changes and deletes.
1390 return connections_
.size();
1393 size_t IndexedDBDatabase::PendingOpenCount() const {
1394 return pending_open_calls_
.size();
1397 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1398 return pending_run_version_change_transaction_call_
? 1 : 0;
1401 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1402 return pending_second_half_open_
? 1 : 0;
1405 size_t IndexedDBDatabase::PendingDeleteCount() const {
1406 return pending_delete_calls_
.size();
1409 void IndexedDBDatabase::ProcessPendingCalls() {
1410 if (pending_run_version_change_transaction_call_
&& ConnectionCount() == 1) {
1411 DCHECK(pending_run_version_change_transaction_call_
->version() >
1412 metadata_
.int_version
);
1413 scoped_ptr
<PendingUpgradeCall
> pending_call
=
1414 pending_run_version_change_transaction_call_
.Pass();
1415 RunVersionChangeTransactionFinal(pending_call
->callbacks(),
1416 pending_call
->ReleaseConnection(),
1417 pending_call
->transaction_id(),
1418 pending_call
->version());
1419 DCHECK_EQ(1u, ConnectionCount());
1420 // Fall through would be a no-op, since transaction must complete
1422 DCHECK(IsDeleteDatabaseBlocked());
1423 DCHECK(IsOpenConnectionBlocked());
1427 if (!IsDeleteDatabaseBlocked()) {
1428 PendingDeleteCallList pending_delete_calls
;
1429 pending_delete_calls_
.swap(pending_delete_calls
);
1430 while (!pending_delete_calls
.empty()) {
1431 // Only the first delete call will delete the database, but each must fire
1433 scoped_ptr
<PendingDeleteCall
> pending_delete_call(
1434 pending_delete_calls
.front());
1435 pending_delete_calls
.pop_front();
1436 DeleteDatabaseFinal(pending_delete_call
->callbacks());
1438 // delete_database_final should never re-queue calls.
1439 DCHECK(pending_delete_calls_
.empty());
1440 // Fall through when complete, as pending opens may be unblocked.
1443 if (!IsOpenConnectionBlocked()) {
1444 PendingOpenCallList pending_open_calls
;
1445 pending_open_calls_
.swap(pending_open_calls
);
1446 while (!pending_open_calls
.empty()) {
1447 OpenConnection(pending_open_calls
.front());
1448 pending_open_calls
.pop_front();
1453 void IndexedDBDatabase::CreateTransaction(
1454 int64 transaction_id
,
1455 IndexedDBConnection
* connection
,
1456 const std::vector
<int64
>& object_store_ids
,
1459 IDB_TRACE("IndexedDBDatabase::CreateTransaction");
1460 DCHECK(connections_
.count(connection
));
1461 DCHECK(transactions_
.find(transaction_id
) == transactions_
.end());
1462 if (transactions_
.find(transaction_id
) != transactions_
.end())
1465 // The transaction will add itself to this database's coordinator, which
1466 // manages the lifetime of the object.
1467 TransactionCreated(new IndexedDBTransaction(
1469 connection
->callbacks(),
1470 std::set
<int64
>(object_store_ids
.begin(), object_store_ids
.end()),
1471 static_cast<indexed_db::TransactionMode
>(mode
),
1473 new IndexedDBBackingStore::Transaction(backing_store_
)));
1476 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction
* transaction
) {
1477 transactions_
[transaction
->id()] = transaction
;
1480 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1481 return !pending_delete_calls_
.empty() ||
1482 transaction_coordinator_
.IsRunningVersionChangeTransaction() ||
1483 pending_run_version_change_transaction_call_
;
1486 void IndexedDBDatabase::OpenConnection(
1487 const IndexedDBPendingConnection
& connection
) {
1488 DCHECK(backing_store_
);
1490 // TODO(jsbell): Should have a priority queue so that higher version
1491 // requests are processed first. http://crbug.com/225850
1492 if (IsOpenConnectionBlocked()) {
1493 // The backing store only detects data loss when it is first opened. The
1494 // presence of existing connections means we didn't even check for data loss
1495 // so there'd better not be any.
1496 DCHECK_NE(blink::WebIDBDataLossTotal
, connection
.callbacks
->data_loss());
1497 pending_open_calls_
.push_back(connection
);
1501 if (metadata_
.id
== kInvalidId
) {
1502 // The database was deleted then immediately re-opened; OpenInternal()
1503 // recreates it in the backing store.
1504 if (OpenInternal().ok()) {
1505 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION
,
1506 metadata_
.int_version
);
1508 base::string16 message
;
1509 if (connection
.version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1510 message
= ASCIIToUTF16(
1511 "Internal error opening database with no version specified.");
1514 ASCIIToUTF16("Internal error opening database with version ") +
1515 Int64ToString16(connection
.version
);
1517 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1518 blink::WebIDBDatabaseExceptionUnknownError
, message
));
1523 // We infer that the database didn't exist from its lack of either type of
1525 bool is_new_database
=
1526 metadata_
.version
== kNoStringVersion
&&
1527 metadata_
.int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1529 if (connection
.version
== IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
) {
1530 // For unit tests only - skip upgrade steps. Calling from script with
1531 // DEFAULT_INT_VERSION throws exception.
1532 // TODO(jsbell): DCHECK that not in unit tests.
1533 DCHECK(is_new_database
);
1534 connection
.callbacks
->OnSuccess(
1535 CreateConnection(connection
.database_callbacks
,
1536 connection
.child_process_id
),
1541 // We may need to change the version.
1542 int64 local_version
= connection
.version
;
1543 if (local_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1544 if (!is_new_database
) {
1545 connection
.callbacks
->OnSuccess(
1546 CreateConnection(connection
.database_callbacks
,
1547 connection
.child_process_id
),
1551 // Spec says: If no version is specified and no database exists, set
1552 // database version to 1.
1556 if (local_version
> metadata_
.int_version
) {
1557 RunVersionChangeTransaction(connection
.callbacks
,
1558 CreateConnection(connection
.database_callbacks
,
1559 connection
.child_process_id
),
1560 connection
.transaction_id
,
1564 if (local_version
< metadata_
.int_version
) {
1565 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1566 blink::WebIDBDatabaseExceptionVersionError
,
1567 ASCIIToUTF16("The requested version (") +
1568 Int64ToString16(local_version
) +
1569 ASCIIToUTF16(") is less than the existing version (") +
1570 Int64ToString16(metadata_
.int_version
) + ASCIIToUTF16(").")));
1573 DCHECK_EQ(local_version
, metadata_
.int_version
);
1574 connection
.callbacks
->OnSuccess(
1575 CreateConnection(connection
.database_callbacks
,
1576 connection
.child_process_id
),
1580 void IndexedDBDatabase::RunVersionChangeTransaction(
1581 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1582 scoped_ptr
<IndexedDBConnection
> connection
,
1583 int64 transaction_id
,
1584 int64 requested_version
) {
1587 DCHECK(connections_
.count(connection
.get()));
1588 if (ConnectionCount() > 1) {
1589 DCHECK_NE(blink::WebIDBDataLossTotal
, callbacks
->data_loss());
1590 // Front end ensures the event is not fired at connections that have
1591 // close_pending set.
1592 for (ConnectionSet::const_iterator it
= connections_
.begin();
1593 it
!= connections_
.end();
1595 if (*it
!= connection
.get()) {
1596 (*it
)->callbacks()->OnVersionChange(metadata_
.int_version
,
1600 // TODO(jsbell): Remove the call to OnBlocked and instead wait
1601 // until the frontend tells us that all the "versionchange" events
1602 // have been delivered. http://crbug.com/100123
1603 callbacks
->OnBlocked(metadata_
.int_version
);
1605 DCHECK(!pending_run_version_change_transaction_call_
);
1606 pending_run_version_change_transaction_call_
.reset(new PendingUpgradeCall(
1607 callbacks
, connection
.Pass(), transaction_id
, requested_version
));
1610 RunVersionChangeTransactionFinal(
1611 callbacks
, connection
.Pass(), transaction_id
, requested_version
);
1614 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1615 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1616 scoped_ptr
<IndexedDBConnection
> connection
,
1617 int64 transaction_id
,
1618 int64 requested_version
) {
1620 std::vector
<int64
> object_store_ids
;
1621 CreateTransaction(transaction_id
,
1624 indexed_db::TRANSACTION_VERSION_CHANGE
);
1626 transactions_
[transaction_id
]
1627 ->ScheduleTask(base::Bind(&IndexedDBDatabase::VersionChangeOperation
,
1631 base::Passed(&connection
)),
1632 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation
,
1635 metadata_
.int_version
));
1637 DCHECK(!pending_second_half_open_
);
1640 void IndexedDBDatabase::DeleteDatabase(
1641 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1643 if (IsDeleteDatabaseBlocked()) {
1644 for (ConnectionSet::const_iterator it
= connections_
.begin();
1645 it
!= connections_
.end();
1647 // Front end ensures the event is not fired at connections that have
1648 // close_pending set.
1649 (*it
)->callbacks()->OnVersionChange(
1650 metadata_
.int_version
, IndexedDBDatabaseMetadata::NO_INT_VERSION
);
1652 // TODO(jsbell): Only fire OnBlocked if there are open
1653 // connections after the VersionChangeEvents are received, not
1654 // just set up to fire. http://crbug.com/100123
1655 callbacks
->OnBlocked(metadata_
.int_version
);
1656 pending_delete_calls_
.push_back(new PendingDeleteCall(callbacks
));
1659 DeleteDatabaseFinal(callbacks
);
1662 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1663 return !!ConnectionCount();
1666 void IndexedDBDatabase::DeleteDatabaseFinal(
1667 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1668 DCHECK(!IsDeleteDatabaseBlocked());
1669 DCHECK(backing_store_
);
1670 if (!backing_store_
->DeleteDatabase(metadata_
.name
).ok()) {
1672 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
1673 "Internal error deleting database."));
1676 int64 old_version
= metadata_
.int_version
;
1677 metadata_
.version
= kNoStringVersion
;
1678 metadata_
.id
= kInvalidId
;
1679 metadata_
.int_version
= IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1680 metadata_
.object_stores
.clear();
1681 callbacks
->OnSuccess(old_version
);
1683 factory_
->DatabaseDeleted(identifier_
);
1686 void IndexedDBDatabase::ForceClose() {
1687 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1688 scoped_refptr
<IndexedDBDatabase
> protect(this);
1689 ConnectionSet::const_iterator it
= connections_
.begin();
1690 while (it
!= connections_
.end()) {
1691 IndexedDBConnection
* connection
= *it
++;
1692 connection
->ForceClose();
1694 DCHECK(connections_
.empty());
1697 void IndexedDBDatabase::Close(IndexedDBConnection
* connection
, bool forced
) {
1698 DCHECK(connections_
.count(connection
));
1699 DCHECK(connection
->IsConnected());
1700 DCHECK(connection
->database() == this);
1702 IDB_TRACE("IndexedDBDatabase::Close");
1703 // Abort outstanding transactions from the closing connection. This
1704 // can not happen if the close is requested by the connection itself
1705 // as the front-end defers the close until all transactions are
1706 // complete, but can occur on process termination or forced close.
1708 TransactionMap
transactions(transactions_
);
1709 for (TransactionMap::const_iterator it
= transactions
.begin(),
1710 end
= transactions
.end();
1713 if (it
->second
->connection() == connection
->callbacks())
1715 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
1716 "Connection is closing."));
1720 connections_
.erase(connection
);
1721 if (pending_second_half_open_
&&
1722 pending_second_half_open_
->connection() == connection
) {
1723 pending_second_half_open_
->callbacks()->OnError(
1724 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1725 "The connection was closed."));
1726 pending_second_half_open_
.reset();
1729 ProcessPendingCalls();
1731 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1732 if (!ConnectionCount() && !pending_open_calls_
.size() &&
1733 !pending_delete_calls_
.size()) {
1734 DCHECK(transactions_
.empty());
1736 const GURL origin_url
= backing_store_
->origin_url();
1737 backing_store_
= NULL
;
1739 // factory_ should only be null in unit tests.
1740 // TODO(jsbell): DCHECK(factory_ || !in_unit_tests) - somehow.
1742 factory_
->ReleaseDatabase(identifier_
, forced
);
1748 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1749 int64 object_store_id
,
1750 IndexedDBTransaction
* transaction
) {
1751 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1752 DCHECK(!transaction
);
1753 RemoveObjectStore(object_store_id
);
1756 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1757 const IndexedDBObjectStoreMetadata
& object_store_metadata
,
1758 IndexedDBTransaction
* transaction
) {
1759 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
1760 DCHECK(!transaction
);
1761 AddObjectStore(object_store_metadata
,
1762 IndexedDBObjectStoreMetadata::kInvalidId
);
1765 void IndexedDBDatabase::VersionChangeAbortOperation(
1766 const base::string16
& previous_version
,
1767 int64 previous_int_version
,
1768 IndexedDBTransaction
* transaction
) {
1769 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
1770 DCHECK(!transaction
);
1771 metadata_
.version
= previous_version
;
1772 metadata_
.int_version
= previous_int_version
;
1775 } // namespace content