Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_database.cc
blob860fc0c0043335df3c18b4d8780c70d5b7443334
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 <set>
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/metrics/histogram_macros.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "content/browser/indexed_db/indexed_db_blob_info.h"
19 #include "content/browser/indexed_db/indexed_db_connection.h"
20 #include "content/browser/indexed_db/indexed_db_context_impl.h"
21 #include "content/browser/indexed_db/indexed_db_cursor.h"
22 #include "content/browser/indexed_db/indexed_db_factory.h"
23 #include "content/browser/indexed_db/indexed_db_index_writer.h"
24 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
25 #include "content/browser/indexed_db/indexed_db_return_value.h"
26 #include "content/browser/indexed_db/indexed_db_tracing.h"
27 #include "content/browser/indexed_db/indexed_db_transaction.h"
28 #include "content/browser/indexed_db/indexed_db_value.h"
29 #include "content/common/indexed_db/indexed_db_key_path.h"
30 #include "content/common/indexed_db/indexed_db_key_range.h"
31 #include "storage/browser/blob/blob_data_handle.h"
32 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
33 #include "third_party/leveldatabase/env_chromium.h"
35 using base::ASCIIToUTF16;
36 using base::Int64ToString16;
37 using blink::WebIDBKeyTypeNumber;
39 namespace content {
41 namespace {
43 // Used for WebCore.IndexedDB.Schema.ObjectStore.KeyPathType and
44 // WebCore.IndexedDB.Schema.Index.KeyPathType histograms. Do not
45 // modify (delete, re-order, renumber) these values other than
46 // the _MAX value.
47 enum HistogramIDBKeyPathType {
48 KEY_PATH_TYPE_NONE = 0,
49 KEY_PATH_TYPE_STRING = 1,
50 KEY_PATH_TYPE_ARRAY = 2,
51 KEY_PATH_TYPE_MAX = 3, // Keep as last/max entry, for histogram range.
54 HistogramIDBKeyPathType HistogramKeyPathType(const IndexedDBKeyPath& key_path) {
55 switch (key_path.type()) {
56 case blink::WebIDBKeyPathTypeNull:
57 return KEY_PATH_TYPE_NONE;
58 case blink::WebIDBKeyPathTypeString:
59 return KEY_PATH_TYPE_STRING;
60 case blink::WebIDBKeyPathTypeArray:
61 return KEY_PATH_TYPE_ARRAY;
63 NOTREACHED();
64 return KEY_PATH_TYPE_NONE;
67 } // namespace
69 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
70 // in-progress connection.
71 class IndexedDBDatabase::PendingUpgradeCall {
72 public:
73 PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks,
74 scoped_ptr<IndexedDBConnection> connection,
75 int64 transaction_id,
76 int64 version)
77 : callbacks_(callbacks),
78 connection_(connection.Pass()),
79 version_(version),
80 transaction_id_(transaction_id) {}
81 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
82 // Takes ownership of the connection object.
83 scoped_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT {
84 return connection_.Pass();
86 int64 version() const { return version_; }
87 int64 transaction_id() const { return transaction_id_; }
89 private:
90 scoped_refptr<IndexedDBCallbacks> callbacks_;
91 scoped_ptr<IndexedDBConnection> connection_;
92 int64 version_;
93 const int64 transaction_id_;
96 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
97 // owned elsewhere, but we need to cancel the success call if that connection
98 // closes before it is sent.
99 class IndexedDBDatabase::PendingSuccessCall {
100 public:
101 PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks,
102 IndexedDBConnection* connection,
103 int64 version)
104 : callbacks_(callbacks), connection_(connection), version_(version) {}
105 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
106 IndexedDBConnection* connection() const { return connection_; }
107 int64 version() const { return version_; }
109 private:
110 scoped_refptr<IndexedDBCallbacks> callbacks_;
111 IndexedDBConnection* connection_;
112 int64 version_;
115 class IndexedDBDatabase::PendingDeleteCall {
116 public:
117 explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
118 : callbacks_(callbacks) {}
119 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
121 private:
122 scoped_refptr<IndexedDBCallbacks> callbacks_;
125 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
126 const base::string16& name,
127 IndexedDBBackingStore* backing_store,
128 IndexedDBFactory* factory,
129 const Identifier& unique_identifier,
130 leveldb::Status* s) {
131 scoped_refptr<IndexedDBDatabase> database =
132 new IndexedDBDatabase(name, backing_store, factory, unique_identifier);
133 *s = database->OpenInternal();
134 if (s->ok())
135 return database;
136 else
137 return NULL;
140 namespace {
141 const base::string16::value_type kNoStringVersion[] = {0};
144 IndexedDBDatabase::IndexedDBDatabase(const base::string16& name,
145 IndexedDBBackingStore* backing_store,
146 IndexedDBFactory* factory,
147 const Identifier& unique_identifier)
148 : backing_store_(backing_store),
149 metadata_(name,
150 kInvalidId,
151 kNoStringVersion,
152 IndexedDBDatabaseMetadata::NO_INT_VERSION,
153 kInvalidId),
154 identifier_(unique_identifier),
155 factory_(factory) {
156 DCHECK(factory != NULL);
159 void IndexedDBDatabase::AddObjectStore(
160 const IndexedDBObjectStoreMetadata& object_store,
161 int64 new_max_object_store_id) {
162 DCHECK(metadata_.object_stores.find(object_store.id) ==
163 metadata_.object_stores.end());
164 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
165 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
166 metadata_.max_object_store_id = new_max_object_store_id;
168 metadata_.object_stores[object_store.id] = object_store;
171 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) {
172 DCHECK(metadata_.object_stores.find(object_store_id) !=
173 metadata_.object_stores.end());
174 metadata_.object_stores.erase(object_store_id);
177 void IndexedDBDatabase::AddIndex(int64 object_store_id,
178 const IndexedDBIndexMetadata& index,
179 int64 new_max_index_id) {
180 DCHECK(metadata_.object_stores.find(object_store_id) !=
181 metadata_.object_stores.end());
182 IndexedDBObjectStoreMetadata object_store =
183 metadata_.object_stores[object_store_id];
185 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
186 object_store.indexes[index.id] = index;
187 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
188 DCHECK_LT(object_store.max_index_id, new_max_index_id);
189 object_store.max_index_id = new_max_index_id;
191 metadata_.object_stores[object_store_id] = object_store;
194 void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
195 DCHECK(metadata_.object_stores.find(object_store_id) !=
196 metadata_.object_stores.end());
197 IndexedDBObjectStoreMetadata object_store =
198 metadata_.object_stores[object_store_id];
200 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
201 object_store.indexes.erase(index_id);
202 metadata_.object_stores[object_store_id] = object_store;
205 leveldb::Status IndexedDBDatabase::OpenInternal() {
206 bool success = false;
207 leveldb::Status s = backing_store_->GetIDBDatabaseMetaData(
208 metadata_.name, &metadata_, &success);
209 DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
210 << " id = " << metadata_.id;
211 if (!s.ok())
212 return s;
213 if (success)
214 return backing_store_->GetObjectStores(metadata_.id,
215 &metadata_.object_stores);
217 return backing_store_->CreateIDBDatabaseMetaData(
218 metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id);
221 IndexedDBDatabase::~IndexedDBDatabase() {
222 DCHECK(transactions_.empty());
223 DCHECK(pending_open_calls_.empty());
224 DCHECK(pending_delete_calls_.empty());
227 scoped_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection(
228 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
229 int child_process_id) {
230 scoped_ptr<IndexedDBConnection> connection(
231 new IndexedDBConnection(this, database_callbacks));
232 connections_.insert(connection.get());
233 backing_store_->GrantChildProcessPermissions(child_process_id);
234 return connection.Pass();
237 IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
238 int64 transaction_id) const {
239 TransactionMap::const_iterator trans_iterator =
240 transactions_.find(transaction_id);
241 if (trans_iterator == transactions_.end())
242 return NULL;
243 return trans_iterator->second;
246 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const {
247 if (!ContainsKey(metadata_.object_stores, object_store_id)) {
248 DLOG(ERROR) << "Invalid object_store_id";
249 return false;
251 return true;
254 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id,
255 int64 index_id) const {
256 if (!ValidateObjectStoreId(object_store_id))
257 return false;
258 const IndexedDBObjectStoreMetadata& object_store_metadata =
259 metadata_.object_stores.find(object_store_id)->second;
260 if (!ContainsKey(object_store_metadata.indexes, index_id)) {
261 DLOG(ERROR) << "Invalid index_id";
262 return false;
264 return true;
267 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
268 int64 object_store_id,
269 int64 index_id) const {
270 if (!ValidateObjectStoreId(object_store_id))
271 return false;
272 const IndexedDBObjectStoreMetadata& object_store_metadata =
273 metadata_.object_stores.find(object_store_id)->second;
274 if (index_id != IndexedDBIndexMetadata::kInvalidId &&
275 !ContainsKey(object_store_metadata.indexes, index_id)) {
276 DLOG(ERROR) << "Invalid index_id";
277 return false;
279 return true;
282 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
283 int64 object_store_id,
284 int64 index_id) const {
285 if (!ValidateObjectStoreId(object_store_id))
286 return false;
287 const IndexedDBObjectStoreMetadata& object_store_metadata =
288 metadata_.object_stores.find(object_store_id)->second;
289 if (ContainsKey(object_store_metadata.indexes, index_id)) {
290 DLOG(ERROR) << "Invalid index_id";
291 return false;
293 return true;
296 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
297 int64 object_store_id,
298 const base::string16& name,
299 const IndexedDBKeyPath& key_path,
300 bool auto_increment) {
301 IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id);
302 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
303 if (!transaction)
304 return;
305 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
307 if (ContainsKey(metadata_.object_stores, object_store_id)) {
308 DLOG(ERROR) << "Invalid object_store_id";
309 return;
312 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.ObjectStore.KeyPathType",
313 HistogramKeyPathType(key_path), KEY_PATH_TYPE_MAX);
314 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.ObjectStore.AutoIncrement",
315 auto_increment);
317 // Store creation is done synchronously, as it may be followed by
318 // index creation (also sync) since preemptive OpenCursor/SetIndexKeys
319 // may follow.
320 IndexedDBObjectStoreMetadata object_store_metadata(
321 name,
322 object_store_id,
323 key_path,
324 auto_increment,
325 IndexedDBDatabase::kMinimumIndexId);
327 leveldb::Status s =
328 backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(),
329 transaction->database()->id(),
330 object_store_metadata.id,
331 object_store_metadata.name,
332 object_store_metadata.key_path,
333 object_store_metadata.auto_increment);
334 if (!s.ok()) {
335 IndexedDBDatabaseError error(
336 blink::WebIDBDatabaseExceptionUnknownError,
337 ASCIIToUTF16("Internal error creating object store '") +
338 object_store_metadata.name + ASCIIToUTF16("'."));
339 transaction->Abort(error);
340 if (s.IsCorruption())
341 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
342 error);
343 return;
346 AddObjectStore(object_store_metadata, object_store_id);
347 transaction->ScheduleAbortTask(
348 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
349 this,
350 object_store_id));
353 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
354 int64 object_store_id) {
355 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id);
356 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
357 if (!transaction)
358 return;
359 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
361 if (!ValidateObjectStoreId(object_store_id))
362 return;
364 transaction->ScheduleTask(
365 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation,
366 this,
367 object_store_id));
370 void IndexedDBDatabase::CreateIndex(int64 transaction_id,
371 int64 object_store_id,
372 int64 index_id,
373 const base::string16& name,
374 const IndexedDBKeyPath& key_path,
375 bool unique,
376 bool multi_entry) {
377 IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id);
378 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
379 if (!transaction)
380 return;
381 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
383 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
384 return;
386 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.Index.KeyPathType",
387 HistogramKeyPathType(key_path), KEY_PATH_TYPE_MAX);
388 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.Unique", unique);
389 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.MultiEntry",
390 multi_entry);
392 // Index creation is done synchronously since preemptive
393 // OpenCursor/SetIndexKeys may follow.
394 const IndexedDBIndexMetadata index_metadata(
395 name, index_id, key_path, unique, multi_entry);
397 if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
398 transaction->database()->id(),
399 object_store_id,
400 index_metadata.id,
401 index_metadata.name,
402 index_metadata.key_path,
403 index_metadata.unique,
404 index_metadata.multi_entry).ok()) {
405 base::string16 error_string =
406 ASCIIToUTF16("Internal error creating index '") +
407 index_metadata.name + ASCIIToUTF16("'.");
408 transaction->Abort(IndexedDBDatabaseError(
409 blink::WebIDBDatabaseExceptionUnknownError, error_string));
410 return;
413 AddIndex(object_store_id, index_metadata, index_id);
414 transaction->ScheduleAbortTask(
415 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
416 this,
417 object_store_id,
418 index_id));
421 void IndexedDBDatabase::CreateIndexAbortOperation(
422 int64 object_store_id,
423 int64 index_id,
424 IndexedDBTransaction* transaction) {
425 IDB_TRACE1("IndexedDBDatabase::CreateIndexAbortOperation",
426 "txn.id",
427 transaction->id());
428 DCHECK(!transaction);
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_TRACE1("IndexedDBDatabase::DeleteIndexAbortOperation",
493 "txn.id",
494 transaction->id());
495 AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId);
498 void IndexedDBDatabase::Commit(int64 transaction_id) {
499 // The frontend suggests that we commit, but we may have previously initiated
500 // an abort, and so have disposed of the transaction. on_abort has already
501 // been dispatched to the frontend, so it will find out about that
502 // asynchronously.
503 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
504 if (transaction) {
505 scoped_refptr<IndexedDBFactory> factory = factory_;
506 leveldb::Status s = transaction->Commit();
507 if (s.IsCorruption()) {
508 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
509 "Internal error committing transaction.");
510 factory->HandleBackingStoreCorruption(identifier_.first, error);
515 void IndexedDBDatabase::Abort(int64 transaction_id) {
516 // If the transaction is unknown, then it has already been aborted by the
517 // backend before this call so it is safe to ignore it.
518 IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id);
519 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
520 if (transaction)
521 transaction->Abort();
524 void IndexedDBDatabase::Abort(int64 transaction_id,
525 const IndexedDBDatabaseError& error) {
526 IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id);
527 // If the transaction is unknown, then it has already been aborted by the
528 // backend before this call so it is safe to ignore it.
529 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
530 if (transaction)
531 transaction->Abort(error);
534 void IndexedDBDatabase::Get(int64 transaction_id,
535 int64 object_store_id,
536 int64 index_id,
537 scoped_ptr<IndexedDBKeyRange> key_range,
538 bool key_only,
539 scoped_refptr<IndexedDBCallbacks> callbacks) {
540 IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id);
541 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
542 if (!transaction)
543 return;
545 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
546 return;
548 transaction->ScheduleTask(base::Bind(
549 &IndexedDBDatabase::GetOperation,
550 this,
551 object_store_id,
552 index_id,
553 Passed(&key_range),
554 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
555 callbacks));
558 void IndexedDBDatabase::GetOperation(
559 int64 object_store_id,
560 int64 index_id,
561 scoped_ptr<IndexedDBKeyRange> key_range,
562 indexed_db::CursorType cursor_type,
563 scoped_refptr<IndexedDBCallbacks> callbacks,
564 IndexedDBTransaction* transaction) {
565 IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction->id());
567 DCHECK(metadata_.object_stores.find(object_store_id) !=
568 metadata_.object_stores.end());
569 const IndexedDBObjectStoreMetadata& object_store_metadata =
570 metadata_.object_stores[object_store_id];
572 const IndexedDBKey* key;
574 leveldb::Status s;
575 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
576 if (key_range->IsOnlyKey()) {
577 key = &key_range->lower();
578 } else {
579 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
580 DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY);
581 // ObjectStore Retrieval Operation
582 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
583 transaction->BackingStoreTransaction(),
584 id(),
585 object_store_id,
586 *key_range,
587 blink::WebIDBCursorDirectionNext,
588 &s);
589 } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
590 // Index Value Retrieval Operation
591 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
592 transaction->BackingStoreTransaction(),
593 id(),
594 object_store_id,
595 index_id,
596 *key_range,
597 blink::WebIDBCursorDirectionNext,
598 &s);
599 } else {
600 // Index Referenced Value Retrieval Operation
601 backing_store_cursor = backing_store_->OpenIndexCursor(
602 transaction->BackingStoreTransaction(),
603 id(),
604 object_store_id,
605 index_id,
606 *key_range,
607 blink::WebIDBCursorDirectionNext,
608 &s);
611 if (!s.ok()) {
612 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
613 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
614 "Internal error deleting data in range");
615 if (s.IsCorruption()) {
616 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
617 error);
621 if (!backing_store_cursor) {
622 callbacks->OnSuccess();
623 return;
626 key = &backing_store_cursor->key();
629 scoped_ptr<IndexedDBKey> primary_key;
630 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
631 // Object Store Retrieval Operation
632 IndexedDBReturnValue value;
633 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
634 id(),
635 object_store_id,
636 *key,
637 &value);
638 if (!s.ok()) {
639 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
640 "Internal error in GetRecord.");
641 callbacks->OnError(error);
643 if (s.IsCorruption())
644 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
645 error);
646 return;
649 if (value.empty()) {
650 callbacks->OnSuccess();
651 return;
654 if (object_store_metadata.auto_increment &&
655 !object_store_metadata.key_path.IsNull()) {
656 value.primary_key = *key;
657 value.key_path = object_store_metadata.key_path;
660 callbacks->OnSuccess(&value);
661 return;
664 // From here we are dealing only with indexes.
665 s = backing_store_->GetPrimaryKeyViaIndex(
666 transaction->BackingStoreTransaction(),
667 id(),
668 object_store_id,
669 index_id,
670 *key,
671 &primary_key);
672 if (!s.ok()) {
673 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
674 "Internal error in GetPrimaryKeyViaIndex.");
675 callbacks->OnError(error);
676 if (s.IsCorruption())
677 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
678 error);
679 return;
681 if (!primary_key) {
682 callbacks->OnSuccess();
683 return;
685 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
686 // Index Value Retrieval Operation
687 callbacks->OnSuccess(*primary_key);
688 return;
691 // Index Referenced Value Retrieval Operation
692 IndexedDBReturnValue value;
693 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
694 id(),
695 object_store_id,
696 *primary_key,
697 &value);
698 if (!s.ok()) {
699 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
700 "Internal error in GetRecord.");
701 callbacks->OnError(error);
702 if (s.IsCorruption())
703 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
704 error);
705 return;
708 if (value.empty()) {
709 callbacks->OnSuccess();
710 return;
712 if (object_store_metadata.auto_increment &&
713 !object_store_metadata.key_path.IsNull()) {
714 value.primary_key = *primary_key;
715 value.key_path = object_store_metadata.key_path;
717 callbacks->OnSuccess(&value);
720 static scoped_ptr<IndexedDBKey> GenerateKey(
721 IndexedDBBackingStore* backing_store,
722 IndexedDBTransaction* transaction,
723 int64 database_id,
724 int64 object_store_id) {
725 const int64 max_generator_value =
726 9007199254740992LL; // Maximum integer storable as ECMAScript number.
727 int64 current_number;
728 leveldb::Status s = backing_store->GetKeyGeneratorCurrentNumber(
729 transaction->BackingStoreTransaction(),
730 database_id,
731 object_store_id,
732 &current_number);
733 if (!s.ok()) {
734 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
735 return make_scoped_ptr(new IndexedDBKey());
737 if (current_number < 0 || current_number > max_generator_value)
738 return make_scoped_ptr(new IndexedDBKey());
740 return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber));
743 static leveldb::Status UpdateKeyGenerator(IndexedDBBackingStore* backing_store,
744 IndexedDBTransaction* transaction,
745 int64 database_id,
746 int64 object_store_id,
747 const IndexedDBKey& key,
748 bool check_current) {
749 DCHECK_EQ(WebIDBKeyTypeNumber, key.type());
750 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
751 transaction->BackingStoreTransaction(),
752 database_id,
753 object_store_id,
754 static_cast<int64>(floor(key.number())) + 1,
755 check_current);
758 struct IndexedDBDatabase::PutOperationParams {
759 PutOperationParams() {}
760 int64 object_store_id;
761 IndexedDBValue value;
762 ScopedVector<storage::BlobDataHandle> handles;
763 scoped_ptr<IndexedDBKey> key;
764 blink::WebIDBPutMode put_mode;
765 scoped_refptr<IndexedDBCallbacks> callbacks;
766 std::vector<IndexKeys> index_keys;
768 private:
769 DISALLOW_COPY_AND_ASSIGN(PutOperationParams);
772 void IndexedDBDatabase::Put(int64 transaction_id,
773 int64 object_store_id,
774 IndexedDBValue* value,
775 ScopedVector<storage::BlobDataHandle>* handles,
776 scoped_ptr<IndexedDBKey> key,
777 blink::WebIDBPutMode put_mode,
778 scoped_refptr<IndexedDBCallbacks> callbacks,
779 const std::vector<IndexKeys>& index_keys) {
780 IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id);
781 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
782 if (!transaction)
783 return;
784 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
786 if (!ValidateObjectStoreId(object_store_id))
787 return;
789 DCHECK(key);
790 DCHECK(value);
791 scoped_ptr<PutOperationParams> params(new PutOperationParams());
792 params->object_store_id = object_store_id;
793 params->value.swap(*value);
794 params->handles.swap(*handles);
795 params->key = key.Pass();
796 params->put_mode = put_mode;
797 params->callbacks = callbacks;
798 params->index_keys = index_keys;
799 transaction->ScheduleTask(base::Bind(
800 &IndexedDBDatabase::PutOperation, this, base::Passed(&params)));
803 void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
804 IndexedDBTransaction* transaction) {
805 IDB_TRACE1("IndexedDBDatabase::PutOperation", "txn.id", transaction->id());
806 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
807 bool key_was_generated = false;
809 DCHECK(metadata_.object_stores.find(params->object_store_id) !=
810 metadata_.object_stores.end());
811 const IndexedDBObjectStoreMetadata& object_store =
812 metadata_.object_stores[params->object_store_id];
813 DCHECK(object_store.auto_increment || params->key->IsValid());
815 scoped_ptr<IndexedDBKey> key;
816 if (params->put_mode != blink::WebIDBPutModeCursorUpdate &&
817 object_store.auto_increment && !params->key->IsValid()) {
818 scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
819 backing_store_.get(), transaction, id(), params->object_store_id);
820 key_was_generated = true;
821 if (!auto_inc_key->IsValid()) {
822 params->callbacks->OnError(
823 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
824 "Maximum key generator value reached."));
825 return;
827 key = auto_inc_key.Pass();
828 } else {
829 key = params->key.Pass();
832 DCHECK(key->IsValid());
834 IndexedDBBackingStore::RecordIdentifier record_identifier;
835 if (params->put_mode == blink::WebIDBPutModeAddOnly) {
836 bool found = false;
837 leveldb::Status s = backing_store_->KeyExistsInObjectStore(
838 transaction->BackingStoreTransaction(),
839 id(),
840 params->object_store_id,
841 *key,
842 &record_identifier,
843 &found);
844 if (!s.ok()) {
845 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
846 "Internal error checking key existence.");
847 params->callbacks->OnError(error);
848 if (s.IsCorruption())
849 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
850 error);
851 return;
853 if (found) {
854 params->callbacks->OnError(
855 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
856 "Key already exists in the object store."));
857 return;
861 ScopedVector<IndexWriter> index_writers;
862 base::string16 error_message;
863 bool obeys_constraints = false;
864 bool backing_store_success = MakeIndexWriters(transaction,
865 backing_store_.get(),
866 id(),
867 object_store,
868 *key,
869 key_was_generated,
870 params->index_keys,
871 &index_writers,
872 &error_message,
873 &obeys_constraints);
874 if (!backing_store_success) {
875 params->callbacks->OnError(IndexedDBDatabaseError(
876 blink::WebIDBDatabaseExceptionUnknownError,
877 "Internal error: backing store error updating index keys."));
878 return;
880 if (!obeys_constraints) {
881 params->callbacks->OnError(IndexedDBDatabaseError(
882 blink::WebIDBDatabaseExceptionConstraintError, error_message));
883 return;
886 // Before this point, don't do any mutation. After this point, rollback the
887 // transaction in case of error.
888 leveldb::Status s =
889 backing_store_->PutRecord(transaction->BackingStoreTransaction(),
890 id(),
891 params->object_store_id,
892 *key,
893 &params->value,
894 &params->handles,
895 &record_identifier);
896 if (!s.ok()) {
897 IndexedDBDatabaseError error(
898 blink::WebIDBDatabaseExceptionUnknownError,
899 "Internal error: backing store error performing put/add.");
900 params->callbacks->OnError(error);
901 if (s.IsCorruption())
902 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
903 error);
904 return;
907 for (size_t i = 0; i < index_writers.size(); ++i) {
908 IndexWriter* index_writer = index_writers[i];
909 index_writer->WriteIndexKeys(record_identifier,
910 backing_store_.get(),
911 transaction->BackingStoreTransaction(),
912 id(),
913 params->object_store_id);
916 if (object_store.auto_increment &&
917 params->put_mode != blink::WebIDBPutModeCursorUpdate &&
918 key->type() == WebIDBKeyTypeNumber) {
919 leveldb::Status s = UpdateKeyGenerator(backing_store_.get(),
920 transaction,
921 id(),
922 params->object_store_id,
923 *key,
924 !key_was_generated);
925 if (!s.ok()) {
926 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
927 "Internal error updating key generator.");
928 params->callbacks->OnError(error);
929 if (s.IsCorruption())
930 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
931 error);
932 return;
936 params->callbacks->OnSuccess(*key);
939 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
940 int64 object_store_id,
941 scoped_ptr<IndexedDBKey> primary_key,
942 const std::vector<IndexKeys>& index_keys) {
943 IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id);
944 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
945 if (!transaction)
946 return;
947 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
949 // TODO(alecflett): This method could be asynchronous, but we need to
950 // evaluate if it's worth the extra complexity.
951 IndexedDBBackingStore::RecordIdentifier record_identifier;
952 bool found = false;
953 leveldb::Status s = backing_store_->KeyExistsInObjectStore(
954 transaction->BackingStoreTransaction(),
955 metadata_.id,
956 object_store_id,
957 *primary_key,
958 &record_identifier,
959 &found);
960 if (!s.ok()) {
961 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
962 "Internal error setting index keys.");
963 transaction->Abort(error);
964 if (s.IsCorruption())
965 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
966 error);
967 return;
969 if (!found) {
970 transaction->Abort(IndexedDBDatabaseError(
971 blink::WebIDBDatabaseExceptionUnknownError,
972 "Internal error setting index keys for object store."));
973 return;
976 ScopedVector<IndexWriter> index_writers;
977 base::string16 error_message;
978 bool obeys_constraints = false;
979 DCHECK(metadata_.object_stores.find(object_store_id) !=
980 metadata_.object_stores.end());
981 const IndexedDBObjectStoreMetadata& object_store_metadata =
982 metadata_.object_stores[object_store_id];
983 bool backing_store_success = MakeIndexWriters(transaction,
984 backing_store_.get(),
985 id(),
986 object_store_metadata,
987 *primary_key,
988 false,
989 index_keys,
990 &index_writers,
991 &error_message,
992 &obeys_constraints);
993 if (!backing_store_success) {
994 transaction->Abort(IndexedDBDatabaseError(
995 blink::WebIDBDatabaseExceptionUnknownError,
996 "Internal error: backing store error updating index keys."));
997 return;
999 if (!obeys_constraints) {
1000 transaction->Abort(IndexedDBDatabaseError(
1001 blink::WebIDBDatabaseExceptionConstraintError, error_message));
1002 return;
1005 for (size_t i = 0; i < index_writers.size(); ++i) {
1006 IndexWriter* index_writer = index_writers[i];
1007 index_writer->WriteIndexKeys(record_identifier,
1008 backing_store_.get(),
1009 transaction->BackingStoreTransaction(),
1010 id(),
1011 object_store_id);
1015 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
1016 int64,
1017 const std::vector<int64>& index_ids) {
1018 IDB_TRACE1("IndexedDBDatabase::SetIndexesReady", "txn.id", transaction_id);
1019 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1020 if (!transaction)
1021 return;
1022 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
1024 transaction->ScheduleTask(
1025 blink::WebIDBTaskTypePreemptive,
1026 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation,
1027 this,
1028 index_ids.size()));
1031 void IndexedDBDatabase::SetIndexesReadyOperation(
1032 size_t index_count,
1033 IndexedDBTransaction* transaction) {
1034 IDB_TRACE1("IndexedDBDatabase::SetIndexesReadyOperation",
1035 "txn.id",
1036 transaction->id());
1037 for (size_t i = 0; i < index_count; ++i)
1038 transaction->DidCompletePreemptiveEvent();
1041 struct IndexedDBDatabase::OpenCursorOperationParams {
1042 OpenCursorOperationParams() {}
1043 int64 object_store_id;
1044 int64 index_id;
1045 scoped_ptr<IndexedDBKeyRange> key_range;
1046 blink::WebIDBCursorDirection direction;
1047 indexed_db::CursorType cursor_type;
1048 blink::WebIDBTaskType task_type;
1049 scoped_refptr<IndexedDBCallbacks> callbacks;
1051 private:
1052 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams);
1055 void IndexedDBDatabase::OpenCursor(
1056 int64 transaction_id,
1057 int64 object_store_id,
1058 int64 index_id,
1059 scoped_ptr<IndexedDBKeyRange> key_range,
1060 blink::WebIDBCursorDirection direction,
1061 bool key_only,
1062 blink::WebIDBTaskType task_type,
1063 scoped_refptr<IndexedDBCallbacks> callbacks) {
1064 IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id);
1065 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1066 if (!transaction)
1067 return;
1069 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1070 return;
1072 scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams());
1073 params->object_store_id = object_store_id;
1074 params->index_id = index_id;
1075 params->key_range = key_range.Pass();
1076 params->direction = direction;
1077 params->cursor_type =
1078 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE;
1079 params->task_type = task_type;
1080 params->callbacks = callbacks;
1081 transaction->ScheduleTask(base::Bind(
1082 &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(&params)));
1085 void IndexedDBDatabase::OpenCursorOperation(
1086 scoped_ptr<OpenCursorOperationParams> params,
1087 IndexedDBTransaction* transaction) {
1088 IDB_TRACE1(
1089 "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction->id());
1091 // The frontend has begun indexing, so this pauses the transaction
1092 // until the indexing is complete. This can't happen any earlier
1093 // because we don't want to switch to early mode in case multiple
1094 // indexes are being created in a row, with Put()'s in between.
1095 if (params->task_type == blink::WebIDBTaskTypePreemptive)
1096 transaction->AddPreemptiveEvent();
1098 leveldb::Status s;
1099 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1100 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
1101 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1102 DCHECK_EQ(params->task_type, blink::WebIDBTaskTypeNormal);
1103 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1104 transaction->BackingStoreTransaction(),
1105 id(),
1106 params->object_store_id,
1107 *params->key_range,
1108 params->direction,
1109 &s);
1110 } else {
1111 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1112 transaction->BackingStoreTransaction(),
1113 id(),
1114 params->object_store_id,
1115 *params->key_range,
1116 params->direction,
1117 &s);
1119 } else {
1120 DCHECK_EQ(params->task_type, blink::WebIDBTaskTypeNormal);
1121 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1122 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1123 transaction->BackingStoreTransaction(),
1124 id(),
1125 params->object_store_id,
1126 params->index_id,
1127 *params->key_range,
1128 params->direction,
1129 &s);
1130 } else {
1131 backing_store_cursor = backing_store_->OpenIndexCursor(
1132 transaction->BackingStoreTransaction(),
1133 id(),
1134 params->object_store_id,
1135 params->index_id,
1136 *params->key_range,
1137 params->direction,
1138 &s);
1142 if (!s.ok()) {
1143 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
1144 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1145 "Internal error opening cursor operation");
1146 if (s.IsCorruption()) {
1147 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1148 error);
1152 if (!backing_store_cursor) {
1153 // Why is Success being called?
1154 params->callbacks->OnSuccess(nullptr);
1155 return;
1158 scoped_refptr<IndexedDBCursor> cursor =
1159 new IndexedDBCursor(backing_store_cursor.Pass(),
1160 params->cursor_type,
1161 params->task_type,
1162 transaction);
1163 params->callbacks->OnSuccess(
1164 cursor, cursor->key(), cursor->primary_key(), cursor->Value());
1167 void IndexedDBDatabase::Count(int64 transaction_id,
1168 int64 object_store_id,
1169 int64 index_id,
1170 scoped_ptr<IndexedDBKeyRange> key_range,
1171 scoped_refptr<IndexedDBCallbacks> callbacks) {
1172 IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id);
1173 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1174 if (!transaction)
1175 return;
1177 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1178 return;
1180 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation,
1181 this,
1182 object_store_id,
1183 index_id,
1184 base::Passed(&key_range),
1185 callbacks));
1188 void IndexedDBDatabase::CountOperation(
1189 int64 object_store_id,
1190 int64 index_id,
1191 scoped_ptr<IndexedDBKeyRange> key_range,
1192 scoped_refptr<IndexedDBCallbacks> callbacks,
1193 IndexedDBTransaction* transaction) {
1194 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction->id());
1195 uint32 count = 0;
1196 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1198 leveldb::Status s;
1199 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
1200 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1201 transaction->BackingStoreTransaction(),
1202 id(),
1203 object_store_id,
1204 *key_range,
1205 blink::WebIDBCursorDirectionNext,
1206 &s);
1207 } else {
1208 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1209 transaction->BackingStoreTransaction(),
1210 id(),
1211 object_store_id,
1212 index_id,
1213 *key_range,
1214 blink::WebIDBCursorDirectionNext,
1215 &s);
1217 if (!s.ok()) {
1218 DLOG(ERROR) << "Unable perform count operation: " << s.ToString();
1219 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1220 "Internal error performing count operation");
1221 if (s.IsCorruption()) {
1222 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1223 error);
1226 if (!backing_store_cursor) {
1227 callbacks->OnSuccess(count);
1228 return;
1231 do {
1232 ++count;
1233 } while (backing_store_cursor->Continue(&s));
1235 // TODO(cmumford): Check for database corruption.
1237 callbacks->OnSuccess(count);
1240 void IndexedDBDatabase::DeleteRange(
1241 int64 transaction_id,
1242 int64 object_store_id,
1243 scoped_ptr<IndexedDBKeyRange> key_range,
1244 scoped_refptr<IndexedDBCallbacks> callbacks) {
1245 IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id);
1246 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1247 if (!transaction)
1248 return;
1249 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
1251 if (!ValidateObjectStoreId(object_store_id))
1252 return;
1254 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation,
1255 this,
1256 object_store_id,
1257 base::Passed(&key_range),
1258 callbacks));
1261 void IndexedDBDatabase::DeleteRangeOperation(
1262 int64 object_store_id,
1263 scoped_ptr<IndexedDBKeyRange> key_range,
1264 scoped_refptr<IndexedDBCallbacks> callbacks,
1265 IndexedDBTransaction* transaction) {
1266 IDB_TRACE1(
1267 "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction->id());
1268 leveldb::Status s =
1269 backing_store_->DeleteRange(transaction->BackingStoreTransaction(),
1270 id(),
1271 object_store_id,
1272 *key_range);
1273 if (!s.ok()) {
1274 base::string16 error_string =
1275 ASCIIToUTF16("Internal error deleting data in range");
1276 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1277 error_string);
1278 transaction->Abort(error);
1279 if (s.IsCorruption()) {
1280 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1281 error);
1283 return;
1285 callbacks->OnSuccess();
1288 void IndexedDBDatabase::Clear(int64 transaction_id,
1289 int64 object_store_id,
1290 scoped_refptr<IndexedDBCallbacks> callbacks) {
1291 IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id);
1292 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1293 if (!transaction)
1294 return;
1295 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
1297 if (!ValidateObjectStoreId(object_store_id))
1298 return;
1300 transaction->ScheduleTask(base::Bind(
1301 &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks));
1304 void IndexedDBDatabase::ClearOperation(
1305 int64 object_store_id,
1306 scoped_refptr<IndexedDBCallbacks> callbacks,
1307 IndexedDBTransaction* transaction) {
1308 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction->id());
1309 leveldb::Status s = backing_store_->ClearObjectStore(
1310 transaction->BackingStoreTransaction(), id(), object_store_id);
1311 if (!s.ok()) {
1312 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1313 "Internal error clearing object store");
1314 callbacks->OnError(error);
1315 if (s.IsCorruption()) {
1316 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1317 error);
1319 return;
1321 callbacks->OnSuccess();
1324 void IndexedDBDatabase::DeleteObjectStoreOperation(
1325 int64 object_store_id,
1326 IndexedDBTransaction* transaction) {
1327 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation",
1328 "txn.id",
1329 transaction->id());
1331 const IndexedDBObjectStoreMetadata object_store_metadata =
1332 metadata_.object_stores[object_store_id];
1333 leveldb::Status s =
1334 backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
1335 transaction->database()->id(),
1336 object_store_id);
1337 if (!s.ok()) {
1338 base::string16 error_string =
1339 ASCIIToUTF16("Internal error deleting object store '") +
1340 object_store_metadata.name + ASCIIToUTF16("'.");
1341 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1342 error_string);
1343 transaction->Abort(error);
1344 if (s.IsCorruption())
1345 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1346 error);
1347 return;
1350 RemoveObjectStore(object_store_id);
1351 transaction->ScheduleAbortTask(
1352 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
1353 this,
1354 object_store_metadata));
1357 void IndexedDBDatabase::VersionChangeOperation(
1358 int64 version,
1359 scoped_refptr<IndexedDBCallbacks> callbacks,
1360 scoped_ptr<IndexedDBConnection> connection,
1361 IndexedDBTransaction* transaction) {
1362 IDB_TRACE1(
1363 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id());
1364 int64 old_version = metadata_.int_version;
1365 DCHECK_GT(version, old_version);
1367 if (!backing_store_->UpdateIDBDatabaseIntVersion(
1368 transaction->BackingStoreTransaction(), id(), version)) {
1369 IndexedDBDatabaseError error(
1370 blink::WebIDBDatabaseExceptionUnknownError,
1371 ASCIIToUTF16(
1372 "Internal error writing data to stable storage when "
1373 "updating version."));
1374 callbacks->OnError(error);
1375 transaction->Abort(error);
1376 return;
1379 transaction->ScheduleAbortTask(
1380 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
1381 this,
1382 metadata_.version,
1383 metadata_.int_version));
1384 metadata_.int_version = version;
1385 metadata_.version = kNoStringVersion;
1387 DCHECK(!pending_second_half_open_);
1388 pending_second_half_open_.reset(
1389 new PendingSuccessCall(callbacks, connection.get(), version));
1390 callbacks->OnUpgradeNeeded(old_version, connection.Pass(), metadata());
1393 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
1394 bool committed) {
1395 DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1396 DCHECK_EQ(transactions_[transaction->id()], transaction);
1397 transactions_.erase(transaction->id());
1399 if (transaction->mode() == blink::WebIDBTransactionModeVersionChange) {
1400 if (pending_second_half_open_) {
1401 if (committed) {
1402 DCHECK_EQ(pending_second_half_open_->version(), metadata_.int_version);
1403 DCHECK(metadata_.id != kInvalidId);
1405 // Connection was already minted for OnUpgradeNeeded callback.
1406 scoped_ptr<IndexedDBConnection> connection;
1407 pending_second_half_open_->callbacks()->OnSuccess(connection.Pass(),
1408 this->metadata());
1409 } else {
1410 pending_second_half_open_->callbacks()->OnError(
1411 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1412 "Version change transaction was aborted in "
1413 "upgradeneeded event handler."));
1415 pending_second_half_open_.reset();
1418 // Connection queue is now unblocked.
1419 ProcessPendingCalls();
1423 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status& status) {
1424 if (status.IsCorruption()) {
1425 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1426 "Error committing transaction");
1427 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), error);
1428 } else {
1429 factory_->HandleBackingStoreFailure(backing_store_->origin_url());
1433 size_t IndexedDBDatabase::ConnectionCount() const {
1434 // This does not include pending open calls, as those should not block version
1435 // changes and deletes.
1436 return connections_.size();
1439 size_t IndexedDBDatabase::PendingOpenCount() const {
1440 return pending_open_calls_.size();
1443 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1444 return pending_run_version_change_transaction_call_ ? 1 : 0;
1447 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1448 return pending_second_half_open_ ? 1 : 0;
1451 size_t IndexedDBDatabase::PendingDeleteCount() const {
1452 return pending_delete_calls_.size();
1455 void IndexedDBDatabase::ProcessPendingCalls() {
1456 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1457 DCHECK(pending_run_version_change_transaction_call_->version() >
1458 metadata_.int_version);
1459 scoped_ptr<PendingUpgradeCall> pending_call =
1460 pending_run_version_change_transaction_call_.Pass();
1461 RunVersionChangeTransactionFinal(pending_call->callbacks(),
1462 pending_call->ReleaseConnection(),
1463 pending_call->transaction_id(),
1464 pending_call->version());
1465 DCHECK_EQ(1u, ConnectionCount());
1466 // Fall through would be a no-op, since transaction must complete
1467 // asynchronously.
1468 DCHECK(IsDeleteDatabaseBlocked());
1469 DCHECK(IsOpenConnectionBlocked());
1470 return;
1473 if (!IsDeleteDatabaseBlocked()) {
1474 PendingDeleteCallList pending_delete_calls;
1475 pending_delete_calls_.swap(pending_delete_calls);
1476 while (!pending_delete_calls.empty()) {
1477 // Only the first delete call will delete the database, but each must fire
1478 // callbacks.
1479 scoped_ptr<PendingDeleteCall> pending_delete_call(
1480 pending_delete_calls.front());
1481 pending_delete_calls.pop_front();
1482 DeleteDatabaseFinal(pending_delete_call->callbacks());
1484 // delete_database_final should never re-queue calls.
1485 DCHECK(pending_delete_calls_.empty());
1486 // Fall through when complete, as pending opens may be unblocked.
1489 if (!IsOpenConnectionBlocked()) {
1490 PendingOpenCallList pending_open_calls;
1491 pending_open_calls_.swap(pending_open_calls);
1492 while (!pending_open_calls.empty()) {
1493 OpenConnection(pending_open_calls.front());
1494 pending_open_calls.pop_front();
1499 void IndexedDBDatabase::CreateTransaction(
1500 int64 transaction_id,
1501 IndexedDBConnection* connection,
1502 const std::vector<int64>& object_store_ids,
1503 blink::WebIDBTransactionMode mode) {
1504 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id);
1505 DCHECK(connections_.count(connection));
1506 DCHECK(transactions_.find(transaction_id) == transactions_.end());
1507 if (transactions_.find(transaction_id) != transactions_.end())
1508 return;
1510 // The transaction will add itself to this database's coordinator, which
1511 // manages the lifetime of the object.
1512 TransactionCreated(new IndexedDBTransaction(
1513 transaction_id,
1514 connection->callbacks(),
1515 std::set<int64>(object_store_ids.begin(), object_store_ids.end()),
1516 mode,
1517 this,
1518 new IndexedDBBackingStore::Transaction(backing_store_.get())));
1521 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
1522 transactions_[transaction->id()] = transaction;
1525 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1526 return !pending_delete_calls_.empty() ||
1527 transaction_coordinator_.IsRunningVersionChangeTransaction() ||
1528 pending_run_version_change_transaction_call_;
1531 void IndexedDBDatabase::OpenConnection(
1532 const IndexedDBPendingConnection& connection) {
1533 DCHECK(backing_store_.get());
1535 // TODO(jsbell): Should have a priority queue so that higher version
1536 // requests are processed first. http://crbug.com/225850
1537 if (IsOpenConnectionBlocked()) {
1538 // The backing store only detects data loss when it is first opened. The
1539 // presence of existing connections means we didn't even check for data loss
1540 // so there'd better not be any.
1541 DCHECK_NE(blink::WebIDBDataLossTotal, connection.callbacks->data_loss());
1542 pending_open_calls_.push_back(connection);
1543 return;
1546 if (metadata_.id == kInvalidId) {
1547 // The database was deleted then immediately re-opened; OpenInternal()
1548 // recreates it in the backing store.
1549 if (OpenInternal().ok()) {
1550 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
1551 metadata_.int_version);
1552 } else {
1553 base::string16 message;
1554 if (connection.version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1555 message = ASCIIToUTF16(
1556 "Internal error opening database with no version specified.");
1557 } else {
1558 message =
1559 ASCIIToUTF16("Internal error opening database with version ") +
1560 Int64ToString16(connection.version);
1562 connection.callbacks->OnError(IndexedDBDatabaseError(
1563 blink::WebIDBDatabaseExceptionUnknownError, message));
1564 return;
1568 // We infer that the database didn't exist from its lack of either type of
1569 // version.
1570 bool is_new_database =
1571 metadata_.version == kNoStringVersion &&
1572 metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
1574 if (connection.version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
1575 // For unit tests only - skip upgrade steps. Calling from script with
1576 // DEFAULT_INT_VERSION throws exception.
1577 // TODO(jsbell): DCHECK that not in unit tests.
1578 DCHECK(is_new_database);
1579 connection.callbacks->OnSuccess(
1580 CreateConnection(connection.database_callbacks,
1581 connection.child_process_id),
1582 this->metadata());
1583 return;
1586 // We may need to change the version.
1587 int64 local_version = connection.version;
1588 if (local_version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1589 if (!is_new_database) {
1590 connection.callbacks->OnSuccess(
1591 CreateConnection(connection.database_callbacks,
1592 connection.child_process_id),
1593 this->metadata());
1594 return;
1596 // Spec says: If no version is specified and no database exists, set
1597 // database version to 1.
1598 local_version = 1;
1601 if (local_version > metadata_.int_version) {
1602 RunVersionChangeTransaction(connection.callbacks,
1603 CreateConnection(connection.database_callbacks,
1604 connection.child_process_id),
1605 connection.transaction_id,
1606 local_version);
1607 return;
1609 if (local_version < metadata_.int_version) {
1610 connection.callbacks->OnError(IndexedDBDatabaseError(
1611 blink::WebIDBDatabaseExceptionVersionError,
1612 ASCIIToUTF16("The requested version (") +
1613 Int64ToString16(local_version) +
1614 ASCIIToUTF16(") is less than the existing version (") +
1615 Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
1616 return;
1618 DCHECK_EQ(local_version, metadata_.int_version);
1619 connection.callbacks->OnSuccess(
1620 CreateConnection(connection.database_callbacks,
1621 connection.child_process_id),
1622 this->metadata());
1625 void IndexedDBDatabase::RunVersionChangeTransaction(
1626 scoped_refptr<IndexedDBCallbacks> callbacks,
1627 scoped_ptr<IndexedDBConnection> connection,
1628 int64 transaction_id,
1629 int64 requested_version) {
1630 DCHECK(callbacks.get());
1631 DCHECK(connections_.count(connection.get()));
1632 if (ConnectionCount() > 1) {
1633 DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss());
1634 // Front end ensures the event is not fired at connections that have
1635 // close_pending set.
1636 for (const auto* iter : connections_) {
1637 if (iter != connection.get()) {
1638 iter->callbacks()->OnVersionChange(metadata_.int_version,
1639 requested_version);
1642 // OnBlocked will be fired at the request when one of the other
1643 // connections acks that the OnVersionChange was ignored.
1645 DCHECK(!pending_run_version_change_transaction_call_);
1646 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall(
1647 callbacks, connection.Pass(), transaction_id, requested_version));
1648 return;
1650 RunVersionChangeTransactionFinal(
1651 callbacks, connection.Pass(), transaction_id, requested_version);
1654 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1655 scoped_refptr<IndexedDBCallbacks> callbacks,
1656 scoped_ptr<IndexedDBConnection> connection,
1657 int64 transaction_id,
1658 int64 requested_version) {
1660 std::vector<int64> object_store_ids;
1661 CreateTransaction(transaction_id,
1662 connection.get(),
1663 object_store_ids,
1664 blink::WebIDBTransactionModeVersionChange);
1666 transactions_[transaction_id]->ScheduleTask(
1667 base::Bind(&IndexedDBDatabase::VersionChangeOperation,
1668 this,
1669 requested_version,
1670 callbacks,
1671 base::Passed(&connection)));
1672 DCHECK(!pending_second_half_open_);
1675 void IndexedDBDatabase::DeleteDatabase(
1676 scoped_refptr<IndexedDBCallbacks> callbacks) {
1678 if (IsDeleteDatabaseBlocked()) {
1679 for (const auto* connection : connections_) {
1680 // Front end ensures the event is not fired at connections that have
1681 // close_pending set.
1682 connection->callbacks()->OnVersionChange(
1683 metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION);
1685 // OnBlocked will be fired at the request when one of the other
1686 // connections acks that the OnVersionChange was ignored.
1688 pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
1689 return;
1691 DeleteDatabaseFinal(callbacks);
1694 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1695 return !!ConnectionCount();
1698 void IndexedDBDatabase::DeleteDatabaseFinal(
1699 scoped_refptr<IndexedDBCallbacks> callbacks) {
1700 DCHECK(!IsDeleteDatabaseBlocked());
1701 DCHECK(backing_store_.get());
1702 leveldb::Status s = backing_store_->DeleteDatabase(metadata_.name);
1703 if (!s.ok()) {
1704 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1705 "Internal error deleting database.");
1706 callbacks->OnError(error);
1707 if (s.IsCorruption()) {
1708 GURL origin_url = backing_store_->origin_url();
1709 backing_store_ = NULL;
1710 factory_->HandleBackingStoreCorruption(origin_url, error);
1712 return;
1714 int64 old_version = metadata_.int_version;
1715 metadata_.version = kNoStringVersion;
1716 metadata_.id = kInvalidId;
1717 metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1718 metadata_.object_stores.clear();
1719 callbacks->OnSuccess(old_version);
1720 factory_->DatabaseDeleted(identifier_);
1723 void IndexedDBDatabase::ForceClose() {
1724 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1725 scoped_refptr<IndexedDBDatabase> protect(this);
1726 ConnectionSet::const_iterator it = connections_.begin();
1727 while (it != connections_.end()) {
1728 IndexedDBConnection* connection = *it++;
1729 connection->ForceClose();
1731 DCHECK(connections_.empty());
1734 void IndexedDBDatabase::VersionChangeIgnored() {
1735 if (pending_run_version_change_transaction_call_)
1736 pending_run_version_change_transaction_call_->callbacks()->OnBlocked(
1737 metadata_.int_version);
1739 for (const auto& pending_delete_call : pending_delete_calls_)
1740 pending_delete_call->callbacks()->OnBlocked(metadata_.int_version);
1744 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
1745 DCHECK(connections_.count(connection));
1746 DCHECK(connection->IsConnected());
1747 DCHECK(connection->database() == this);
1749 IDB_TRACE("IndexedDBDatabase::Close");
1751 connections_.erase(connection);
1753 // Abort outstanding transactions from the closing connection. This
1754 // can not happen if the close is requested by the connection itself
1755 // as the front-end defers the close until all transactions are
1756 // complete, but can occur on process termination or forced close.
1758 TransactionMap transactions(transactions_);
1759 for (const auto& it : transactions) {
1760 if (it.second->connection() == connection->callbacks())
1761 it.second->Abort(
1762 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1763 "Connection is closing."));
1767 if (pending_second_half_open_ &&
1768 pending_second_half_open_->connection() == connection) {
1769 pending_second_half_open_->callbacks()->OnError(
1770 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1771 "The connection was closed."));
1772 pending_second_half_open_.reset();
1775 ProcessPendingCalls();
1777 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1778 if (!ConnectionCount() && !pending_open_calls_.size() &&
1779 !pending_delete_calls_.size()) {
1780 DCHECK(transactions_.empty());
1782 const GURL origin_url = backing_store_->origin_url();
1783 backing_store_ = NULL;
1785 factory_->ReleaseDatabase(identifier_, forced);
1789 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1790 int64 object_store_id,
1791 IndexedDBTransaction* transaction) {
1792 DCHECK(!transaction);
1793 IDB_TRACE1("IndexedDBDatabase::CreateObjectStoreAbortOperation",
1794 "txn.id",
1795 transaction->id());
1796 RemoveObjectStore(object_store_id);
1799 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1800 const IndexedDBObjectStoreMetadata& object_store_metadata,
1801 IndexedDBTransaction* transaction) {
1802 DCHECK(!transaction);
1803 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreAbortOperation",
1804 "txn.id",
1805 transaction->id());
1806 AddObjectStore(object_store_metadata,
1807 IndexedDBObjectStoreMetadata::kInvalidId);
1810 void IndexedDBDatabase::VersionChangeAbortOperation(
1811 const base::string16& previous_version,
1812 int64 previous_int_version,
1813 IndexedDBTransaction* transaction) {
1814 DCHECK(!transaction);
1815 IDB_TRACE1("IndexedDBDatabase::VersionChangeAbortOperation",
1816 "txn.id",
1817 transaction->id());
1818 metadata_.version = previous_version;
1819 metadata_.int_version = previous_int_version;
1822 } // namespace content