summaryrefslogtreecommitdiffstats
path: root/content/browser/indexed_db/indexed_db_database_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/indexed_db/indexed_db_database_impl.cc')
-rw-r--r--content/browser/indexed_db/indexed_db_database_impl.cc1823
1 files changed, 1823 insertions, 0 deletions
diff --git a/content/browser/indexed_db/indexed_db_database_impl.cc b/content/browser/indexed_db/indexed_db_database_impl.cc
new file mode 100644
index 0000000..dcde2d0
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_database_impl.cc
@@ -0,0 +1,1823 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_database_impl.h"
+
+#include <math.h>
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_cursor_impl.h"
+#include "content/browser/indexed_db/indexed_db_factory_impl.h"
+#include "content/browser/indexed_db/indexed_db_index_writer.h"
+#include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/indexed_db_transaction.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+#include "content/common/indexed_db/indexed_db_key_range.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebIDBDatabaseException.h"
+
+using base::Int64ToString16;
+using WebKit::WebIDBKey;
+
+namespace content {
+
+class CreateObjectStoreOperation : public IndexedDBTransaction::Operation {
+ public:
+ CreateObjectStoreOperation(
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ const IndexedDBObjectStoreMetadata& object_store_metadata)
+ : backing_store_(backing_store),
+ object_store_metadata_(object_store_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const IndexedDBObjectStoreMetadata object_store_metadata_;
+};
+
+class DeleteObjectStoreOperation : public IndexedDBTransaction::Operation {
+ public:
+ DeleteObjectStoreOperation(
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ const IndexedDBObjectStoreMetadata& object_store_metadata)
+ : backing_store_(backing_store),
+ object_store_metadata_(object_store_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const IndexedDBObjectStoreMetadata object_store_metadata_;
+};
+
+class IndexedDBDatabaseImpl::VersionChangeOperation
+ : public IndexedDBTransaction::Operation {
+ public:
+ VersionChangeOperation(
+ scoped_refptr<IndexedDBDatabaseImpl> database,
+ int64 transaction_id,
+ int64 version,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks)
+ : database_(database),
+ transaction_id_(transaction_id),
+ version_(version),
+ callbacks_(callbacks),
+ database_callbacks_(database_callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ scoped_refptr<IndexedDBDatabaseImpl> database_;
+ int64 transaction_id_;
+ int64 version_;
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_;
+};
+
+class CreateObjectStoreAbortOperation : public IndexedDBTransaction::Operation {
+ public:
+ CreateObjectStoreAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database,
+ int64 object_store_id)
+ : database_(database), object_store_id_(object_store_id) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBDatabaseImpl> database_;
+ const int64 object_store_id_;
+};
+
+class DeleteObjectStoreAbortOperation : public IndexedDBTransaction::Operation {
+ public:
+ DeleteObjectStoreAbortOperation(
+ scoped_refptr<IndexedDBDatabaseImpl> database,
+ const IndexedDBObjectStoreMetadata& object_store_metadata)
+ : database_(database), object_store_metadata_(object_store_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ scoped_refptr<IndexedDBDatabaseImpl> database_;
+ IndexedDBObjectStoreMetadata object_store_metadata_;
+};
+
+class IndexedDBDatabaseImpl::VersionChangeAbortOperation
+ : public IndexedDBTransaction::Operation {
+ public:
+ VersionChangeAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database,
+ const string16& previous_version,
+ int64 previous_int_version)
+ : database_(database),
+ previous_version_(previous_version),
+ previous_int_version_(previous_int_version) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ scoped_refptr<IndexedDBDatabaseImpl> database_;
+ string16 previous_version_;
+ int64 previous_int_version_;
+};
+
+class CreateIndexOperation : public IndexedDBTransaction::Operation {
+ public:
+ CreateIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 object_store_id,
+ const IndexedDBIndexMetadata& index_metadata)
+ : backing_store_(backing_store),
+ object_store_id_(object_store_id),
+ index_metadata_(index_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 object_store_id_;
+ const IndexedDBIndexMetadata index_metadata_;
+};
+
+class DeleteIndexOperation : public IndexedDBTransaction::Operation {
+ public:
+ DeleteIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 object_store_id,
+ const IndexedDBIndexMetadata& index_metadata)
+ : backing_store_(backing_store),
+ object_store_id_(object_store_id),
+ index_metadata_(index_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 object_store_id_;
+ const IndexedDBIndexMetadata index_metadata_;
+};
+
+class CreateIndexAbortOperation : public IndexedDBTransaction::Operation {
+ public:
+ CreateIndexAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database,
+ int64 object_store_id,
+ int64 index_id)
+ : database_(database),
+ object_store_id_(object_store_id),
+ index_id_(index_id) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBDatabaseImpl> database_;
+ const int64 object_store_id_;
+ const int64 index_id_;
+};
+
+class DeleteIndexAbortOperation : public IndexedDBTransaction::Operation {
+ public:
+ DeleteIndexAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database,
+ int64 object_store_id,
+ const IndexedDBIndexMetadata& index_metadata)
+ : database_(database),
+ object_store_id_(object_store_id),
+ index_metadata_(index_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBDatabaseImpl> database_;
+ const int64 object_store_id_;
+ const IndexedDBIndexMetadata index_metadata_;
+};
+
+class GetOperation : public IndexedDBTransaction::Operation {
+ public:
+ GetOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ const IndexedDBDatabaseMetadata& metadata,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ indexed_db::CursorType cursor_type,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : backing_store_(backing_store),
+ database_id_(metadata.id),
+ object_store_id_(object_store_id),
+ index_id_(index_id),
+ key_path_(metadata.object_stores.find(object_store_id)
+ ->second.key_path),
+ auto_increment_(metadata.object_stores.find(object_store_id)
+ ->second.auto_increment),
+ key_range_(key_range.Pass()),
+ cursor_type_(cursor_type),
+ callbacks_(callbacks) {
+ DCHECK(metadata.object_stores.find(object_store_id) !=
+ metadata.object_stores.end());
+ DCHECK(metadata.object_stores.find(object_store_id)->second.id ==
+ object_store_id);
+ }
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const int64 object_store_id_;
+ const int64 index_id_;
+ const IndexedDBKeyPath key_path_;
+ const bool auto_increment_;
+ const scoped_ptr<IndexedDBKeyRange> key_range_;
+ const indexed_db::CursorType cursor_type_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class PutOperation : public IndexedDBTransaction::Operation {
+ public:
+ PutOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 database_id,
+ const IndexedDBObjectStoreMetadata& object_store,
+ std::vector<char>* value,
+ scoped_ptr<IndexedDBKey> key,
+ IndexedDBDatabase::PutMode put_mode,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexedDBDatabase::IndexKeys>& index_keys)
+ : backing_store_(backing_store),
+ database_id_(database_id),
+ object_store_(object_store),
+ key_(key.Pass()),
+ put_mode_(put_mode),
+ callbacks_(callbacks),
+ index_ids_(index_ids),
+ index_keys_(index_keys) {
+ value_.swap(*value);
+ }
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const IndexedDBObjectStoreMetadata object_store_;
+ std::vector<char> value_;
+ scoped_ptr<IndexedDBKey> key_;
+ const IndexedDBDatabase::PutMode put_mode_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+ const std::vector<int64> index_ids_;
+ const std::vector<IndexedDBDatabase::IndexKeys> index_keys_;
+};
+
+class SetIndexesReadyOperation : public IndexedDBTransaction::Operation {
+ public:
+ explicit SetIndexesReadyOperation(size_t index_count)
+ : index_count_(index_count) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const size_t index_count_;
+};
+
+class OpenCursorOperation : public IndexedDBTransaction::Operation {
+ public:
+ OpenCursorOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ indexed_db::CursorDirection direction,
+ indexed_db::CursorType cursor_type,
+ IndexedDBDatabase::TaskType task_type,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : backing_store_(backing_store),
+ database_id_(database_id),
+ object_store_id_(object_store_id),
+ index_id_(index_id),
+ key_range_(key_range.Pass()),
+ direction_(direction),
+ cursor_type_(cursor_type),
+ task_type_(task_type),
+ callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const int64 object_store_id_;
+ const int64 index_id_;
+ const scoped_ptr<IndexedDBKeyRange> key_range_;
+ const indexed_db::CursorDirection direction_;
+ const indexed_db::CursorType cursor_type_;
+ const IndexedDBDatabase::TaskType task_type_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class CountOperation : public IndexedDBTransaction::Operation {
+ public:
+ CountOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : backing_store_(backing_store),
+ database_id_(database_id),
+ object_store_id_(object_store_id),
+ index_id_(index_id),
+ key_range_(key_range.Pass()),
+ callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const int64 object_store_id_;
+ const int64 index_id_;
+ const scoped_ptr<IndexedDBKeyRange> key_range_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class DeleteRangeOperation : public IndexedDBTransaction::Operation {
+ public:
+ DeleteRangeOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 database_id,
+ int64 object_store_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : backing_store_(backing_store),
+ database_id_(database_id),
+ object_store_id_(object_store_id),
+ key_range_(key_range.Pass()),
+ callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const int64 object_store_id_;
+ const scoped_ptr<IndexedDBKeyRange> key_range_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class ClearOperation : public IndexedDBTransaction::Operation {
+ public:
+ ClearOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 database_id,
+ int64 object_store_id,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : backing_store_(backing_store),
+ database_id_(database_id),
+ object_store_id_(object_store_id),
+ callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const int64 object_store_id_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class IndexedDBDatabaseImpl::PendingOpenCall {
+ public:
+ PendingOpenCall(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 version)
+ : callbacks_(callbacks),
+ database_callbacks_(database_callbacks),
+ version_(version),
+ transaction_id_(transaction_id) {}
+ scoped_refptr<IndexedDBCallbacksWrapper> Callbacks() { return callbacks_; }
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> DatabaseCallbacks() {
+ return database_callbacks_;
+ }
+ int64 Version() { return version_; }
+ int64 TransactionId() const { return transaction_id_; }
+
+ private:
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_;
+ int64 version_;
+ const int64 transaction_id_;
+};
+
+class IndexedDBDatabaseImpl::PendingDeleteCall {
+ public:
+ explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : callbacks_(callbacks) {}
+ scoped_refptr<IndexedDBCallbacksWrapper> Callbacks() { return callbacks_; }
+
+ private:
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+scoped_refptr<IndexedDBDatabaseImpl> IndexedDBDatabaseImpl::Create(
+ const string16& name,
+ IndexedDBBackingStore* database,
+ IndexedDBFactoryImpl* factory,
+ const string16& unique_identifier) {
+ scoped_refptr<IndexedDBDatabaseImpl> backend =
+ new IndexedDBDatabaseImpl(name, database, factory, unique_identifier);
+ if (!backend->OpenInternal())
+ return 0;
+ return backend;
+}
+
+namespace {
+const base::string16::value_type kNoStringVersion[] = {0};
+}
+
+IndexedDBDatabaseImpl::IndexedDBDatabaseImpl(
+ const string16& name,
+ IndexedDBBackingStore* backing_store,
+ IndexedDBFactoryImpl* factory,
+ const string16& unique_identifier)
+ : backing_store_(backing_store),
+ metadata_(name,
+ kInvalidId,
+ kNoStringVersion,
+ IndexedDBDatabaseMetadata::NO_INT_VERSION,
+ kInvalidId),
+ identifier_(unique_identifier),
+ factory_(factory),
+ running_version_change_transaction_(NULL),
+ closing_connection_(false) {
+ DCHECK(!metadata_.name.empty());
+}
+
+void IndexedDBDatabaseImpl::AddObjectStore(
+ const IndexedDBObjectStoreMetadata& object_store,
+ int64 new_max_object_store_id) {
+ DCHECK(metadata_.object_stores.find(object_store.id) ==
+ metadata_.object_stores.end());
+ if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
+ DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
+ metadata_.max_object_store_id = new_max_object_store_id;
+ }
+ metadata_.object_stores[object_store.id] = object_store;
+}
+
+void IndexedDBDatabaseImpl::RemoveObjectStore(int64 object_store_id) {
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ metadata_.object_stores.erase(object_store_id);
+}
+
+void IndexedDBDatabaseImpl::AddIndex(int64 object_store_id,
+ const IndexedDBIndexMetadata& index,
+ int64 new_max_index_id) {
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ IndexedDBObjectStoreMetadata object_store =
+ metadata_.object_stores[object_store_id];
+
+ DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
+ object_store.indexes[index.id] = index;
+ if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
+ DCHECK_LT(object_store.max_index_id, new_max_index_id);
+ object_store.max_index_id = new_max_index_id;
+ }
+ metadata_.object_stores[object_store_id] = object_store;
+}
+
+void IndexedDBDatabaseImpl::RemoveIndex(int64 object_store_id, int64 index_id) {
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ IndexedDBObjectStoreMetadata object_store =
+ metadata_.object_stores[object_store_id];
+
+ DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
+ object_store.indexes.erase(index_id);
+ metadata_.object_stores[object_store_id] = object_store;
+}
+
+bool IndexedDBDatabaseImpl::OpenInternal() {
+ bool success = false;
+ bool ok = backing_store_->GetIDBDatabaseMetaData(
+ metadata_.name, &metadata_, success);
+ DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
+ << " id_ = " << metadata_.id;
+ if (!ok)
+ return false;
+ if (success)
+ return backing_store_->GetObjectStores(metadata_.id,
+ &metadata_.object_stores);
+
+ return backing_store_->CreateIDBDatabaseMetaData(
+ metadata_.name, metadata_.version, metadata_.int_version, metadata_.id);
+}
+
+IndexedDBDatabaseImpl::~IndexedDBDatabaseImpl() {
+ DCHECK(transactions_.empty());
+ DCHECK(pending_open_calls_.empty());
+ DCHECK(pending_delete_calls_.empty());
+}
+
+scoped_refptr<IndexedDBBackingStore> IndexedDBDatabaseImpl::BackingStore()
+ const {
+ return backing_store_;
+}
+
+void IndexedDBDatabaseImpl::CreateObjectStore(int64 transaction_id,
+ int64 object_store_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool auto_increment) {
+ IDB_TRACE("IndexedDBDatabaseImpl::create_object_store");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ DCHECK(metadata_.object_stores.find(object_store_id) ==
+ metadata_.object_stores.end());
+ IndexedDBObjectStoreMetadata object_store_metadata(
+ name,
+ object_store_id,
+ key_path,
+ auto_increment,
+ IndexedDBDatabase::kMinimumIndexId);
+
+ transaction->ScheduleTask(
+ new CreateObjectStoreOperation(backing_store_, object_store_metadata),
+ new CreateObjectStoreAbortOperation(this, object_store_id));
+
+ AddObjectStore(object_store_metadata, object_store_id);
+}
+
+void CreateObjectStoreOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("CreateObjectStoreOperation");
+ if (!backing_store_->CreateObjectStore(
+ transaction->BackingStoreTransaction(),
+ transaction->database()->id(),
+ object_store_metadata_.id,
+ object_store_metadata_.name,
+ object_store_metadata_.key_path,
+ object_store_metadata_.auto_increment)) {
+ string16 error_string =
+ ASCIIToUTF16("Internal error creating object store '") +
+ object_store_metadata_.name + ASCIIToUTF16("'.");
+
+ scoped_refptr<IndexedDBDatabaseError> error =
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError, error_string);
+ transaction->Abort(error);
+ return;
+ }
+}
+
+void IndexedDBDatabaseImpl::DeleteObjectStore(int64 transaction_id,
+ int64 object_store_id) {
+ IDB_TRACE("IndexedDBDatabaseImpl::delete_object_store");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ const IndexedDBObjectStoreMetadata& object_store_metadata =
+ metadata_.object_stores[object_store_id];
+
+ transaction->ScheduleTask(
+ new DeleteObjectStoreOperation(backing_store_, object_store_metadata),
+ new DeleteObjectStoreAbortOperation(this, object_store_metadata));
+ RemoveObjectStore(object_store_id);
+}
+
+void IndexedDBDatabaseImpl::CreateIndex(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool unique,
+ bool multi_entry) {
+ IDB_TRACE("IndexedDBDatabaseImpl::create_index");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ const IndexedDBObjectStoreMetadata object_store =
+ metadata_.object_stores[object_store_id];
+
+ DCHECK(object_store.indexes.find(index_id) == object_store.indexes.end());
+ const IndexedDBIndexMetadata index_metadata(
+ name, index_id, key_path, unique, multi_entry);
+
+ transaction->ScheduleTask(
+ new CreateIndexOperation(backing_store_, object_store_id, index_metadata),
+ new CreateIndexAbortOperation(this, object_store_id, index_id));
+
+ AddIndex(object_store_id, index_metadata, index_id);
+}
+
+void CreateIndexOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("CreateIndexOperation");
+ if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
+ transaction->database()->id(),
+ object_store_id_,
+ index_metadata_.id,
+ index_metadata_.name,
+ index_metadata_.key_path,
+ index_metadata_.unique,
+ index_metadata_.multi_entry)) {
+ string16 error_string = ASCIIToUTF16("Internal error creating index '") +
+ index_metadata_.name + ASCIIToUTF16("'.");
+ transaction->Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
+ return;
+ }
+}
+
+void CreateIndexAbortOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("CreateIndexAbortOperation");
+ DCHECK(!transaction);
+ database_->RemoveIndex(object_store_id_, index_id_);
+}
+
+void IndexedDBDatabaseImpl::DeleteIndex(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id) {
+ IDB_TRACE("IndexedDBDatabaseImpl::delete_index");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ IndexedDBObjectStoreMetadata object_store =
+ metadata_.object_stores[object_store_id];
+
+ DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
+ const IndexedDBIndexMetadata& index_metadata = object_store.indexes[index_id];
+
+ transaction->ScheduleTask(
+ new DeleteIndexOperation(backing_store_, object_store_id, index_metadata),
+ new DeleteIndexAbortOperation(this, object_store_id, index_metadata));
+
+ RemoveIndex(object_store_id, index_id);
+}
+
+void DeleteIndexOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("DeleteIndexOperation");
+ bool ok = backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
+ transaction->database()->id(),
+ object_store_id_,
+ index_metadata_.id);
+ if (!ok) {
+ string16 error_string = ASCIIToUTF16("Internal error deleting index '") +
+ index_metadata_.name + ASCIIToUTF16("'.");
+ scoped_refptr<IndexedDBDatabaseError> error =
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError, error_string);
+ transaction->Abort(error);
+ }
+}
+
+void DeleteIndexAbortOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("DeleteIndexAbortOperation");
+ DCHECK(!transaction);
+ database_->AddIndex(
+ object_store_id_, index_metadata_, IndexedDBIndexMetadata::kInvalidId);
+}
+
+void IndexedDBDatabaseImpl::Commit(int64 transaction_id) {
+ // The frontend suggests that we commit, but we may have previously initiated
+ // an abort, and so have disposed of the transaction. on_abort has already
+ // been dispatched to the frontend, so it will find out about that
+ // asynchronously.
+ if (transactions_.find(transaction_id) != transactions_.end())
+ transactions_[transaction_id]->Commit();
+}
+
+void IndexedDBDatabaseImpl::Abort(int64 transaction_id) {
+ // If the transaction is unknown, then it has already been aborted by the
+ // backend before this call so it is safe to ignore it.
+ if (transactions_.find(transaction_id) != transactions_.end())
+ transactions_[transaction_id]->Abort();
+}
+
+void IndexedDBDatabaseImpl::Abort(int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseError> error) {
+ // If the transaction is unknown, then it has already been aborted by the
+ // backend before this call so it is safe to ignore it.
+ if (transactions_.find(transaction_id) != transactions_.end())
+ transactions_[transaction_id]->Abort(error);
+}
+
+void IndexedDBDatabaseImpl::Get(
+ int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ bool key_only,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBDatabaseImpl::get");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+
+ transaction->ScheduleTask(new GetOperation(
+ backing_store_,
+ metadata_,
+ object_store_id,
+ index_id,
+ key_range.Pass(),
+ key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
+ callbacks));
+}
+
+void GetOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("GetOperation");
+
+ const IndexedDBKey* key;
+
+ scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
+ if (key_range_->IsOnlyKey()) {
+ key = &key_range_->lower();
+ } else {
+ if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
+ DCHECK_NE(cursor_type_, indexed_db::CURSOR_KEY_ONLY);
+ // ObjectStore Retrieval Operation
+ backing_store_cursor = backing_store_->OpenObjectStoreCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ } else if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
+ // Index Value Retrieval Operation
+ backing_store_cursor = backing_store_->OpenIndexKeyCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ } else {
+ // Index Referenced Value Retrieval Operation
+ backing_store_cursor = backing_store_->OpenIndexCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ }
+
+ if (!backing_store_cursor) {
+ callbacks_->OnSuccess();
+ return;
+ }
+
+ key = &backing_store_cursor->key();
+ }
+
+ scoped_ptr<IndexedDBKey> primary_key;
+ bool ok;
+ if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
+ // Object Store Retrieval Operation
+ std::vector<char> value;
+ ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *key,
+ value);
+ if (!ok) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error in get_record.")));
+ return;
+ }
+
+ if (value.empty()) {
+ callbacks_->OnSuccess();
+ return;
+ }
+
+ if (auto_increment_ && !key_path_.IsNull()) {
+ callbacks_->OnSuccess(&value, *key, key_path_);
+ return;
+ }
+
+ callbacks_->OnSuccess(&value);
+ return;
+ }
+
+ // From here we are dealing only with indexes.
+ ok = backing_store_->GetPrimaryKeyViaIndex(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key,
+ &primary_key);
+ if (!ok) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error in get_primary_key_via_index.")));
+ return;
+ }
+ if (!primary_key) {
+ callbacks_->OnSuccess();
+ return;
+ }
+ if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
+ // Index Value Retrieval Operation
+ callbacks_->OnSuccess(*primary_key);
+ return;
+ }
+
+ // Index Referenced Value Retrieval Operation
+ std::vector<char> value;
+ ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *primary_key,
+ value);
+ if (!ok) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error in get_record.")));
+ return;
+ }
+
+ if (value.empty()) {
+ callbacks_->OnSuccess();
+ return;
+ }
+ if (auto_increment_ && !key_path_.IsNull()) {
+ callbacks_->OnSuccess(&value, *primary_key, key_path_);
+ return;
+ }
+ callbacks_->OnSuccess(&value);
+}
+
+static scoped_ptr<IndexedDBKey> GenerateKey(
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ scoped_refptr<IndexedDBTransaction> transaction,
+ int64 database_id,
+ int64 object_store_id) {
+ const int64 max_generator_value =
+ 9007199254740992LL; // Maximum integer storable as ECMAScript number.
+ int64 current_number;
+ bool ok = backing_store->GetKeyGeneratorCurrentNumber(
+ transaction->BackingStoreTransaction(),
+ database_id,
+ object_store_id,
+ current_number);
+ if (!ok) {
+ LOG(ERROR) << "Failed to get_key_generator_current_number";
+ return make_scoped_ptr(new IndexedDBKey());
+ }
+ if (current_number < 0 || current_number > max_generator_value)
+ return make_scoped_ptr(new IndexedDBKey());
+
+ return make_scoped_ptr(
+ new IndexedDBKey(current_number, WebIDBKey::NumberType));
+}
+
+static bool UpdateKeyGenerator(
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ scoped_refptr<IndexedDBTransaction> transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey* key,
+ bool check_current) {
+ DCHECK(key && key->type() == WebIDBKey::NumberType);
+ return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
+ transaction->BackingStoreTransaction(),
+ database_id,
+ object_store_id,
+ static_cast<int64>(floor(key->number())) + 1,
+ check_current);
+}
+
+void IndexedDBDatabaseImpl::Put(
+ int64 transaction_id,
+ int64 object_store_id,
+ std::vector<char>* value,
+ scoped_ptr<IndexedDBKey> key,
+ PutMode put_mode,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexKeys>& index_keys) {
+ IDB_TRACE("IndexedDBDatabaseImpl::put");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
+
+ const IndexedDBObjectStoreMetadata object_store_metadata =
+ metadata_.object_stores[object_store_id];
+
+ DCHECK(key);
+ DCHECK(object_store_metadata.auto_increment || key->IsValid());
+ transaction->ScheduleTask(new PutOperation(backing_store_,
+ id(),
+ object_store_metadata,
+ value,
+ key.Pass(),
+ put_mode,
+ callbacks,
+ index_ids,
+ index_keys));
+}
+
+void PutOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("PutOperation");
+ DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
+ DCHECK_EQ(index_ids_.size(), index_keys_.size());
+ bool key_was_generated = false;
+
+ scoped_ptr<IndexedDBKey> key;
+ if (put_mode_ != IndexedDBDatabase::CURSOR_UPDATE &&
+ object_store_.auto_increment && !key_->IsValid()) {
+ scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
+ backing_store_, transaction, database_id_, object_store_.id);
+ key_was_generated = true;
+ if (!auto_inc_key->IsValid()) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionConstraintError,
+ ASCIIToUTF16("Maximum key generator value reached.")));
+ return;
+ }
+ key = auto_inc_key.Pass();
+ } else {
+ key = key_.Pass();
+ }
+
+ DCHECK(key->IsValid());
+
+ IndexedDBBackingStore::RecordIdentifier record_identifier;
+ if (put_mode_ == IndexedDBDatabase::ADD_ONLY) {
+ bool found = false;
+ bool ok = backing_store_->KeyExistsInObjectStore(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_.id,
+ *key.get(),
+ &record_identifier,
+ found);
+ if (!ok) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error checking key existence.")));
+ return;
+ }
+ if (found) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionConstraintError,
+ ASCIIToUTF16("Key already exists in the object store.")));
+ return;
+ }
+ }
+
+ ScopedVector<IndexedDBObjectStoreImpl::IndexWriter> index_writers;
+ string16 error_message;
+ bool obeys_constraints = false;
+ bool backing_store_success =
+ IndexedDBObjectStoreImpl::MakeIndexWriters(transaction,
+ backing_store_.get(),
+ database_id_,
+ object_store_,
+ *key,
+ key_was_generated,
+ index_ids_,
+ index_keys_,
+ &index_writers,
+ &error_message,
+ &obeys_constraints);
+ if (!backing_store_success) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error: backing store error updating index keys.")));
+ return;
+ }
+ if (!obeys_constraints) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionConstraintError, error_message));
+ return;
+ }
+
+ // Before this point, don't do any mutation. After this point, rollback the
+ // transaction in case of error.
+ backing_store_success =
+ backing_store_->PutRecord(transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_.id,
+ *key.get(),
+ value_,
+ &record_identifier);
+ if (!backing_store_success) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error: backing store error performing put/add.")));
+ return;
+ }
+
+ for (size_t i = 0; i < index_writers.size(); ++i) {
+ IndexedDBObjectStoreImpl::IndexWriter* index_writer = index_writers[i];
+ index_writer->WriteIndexKeys(record_identifier,
+ backing_store_,
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_.id);
+ }
+
+ if (object_store_.auto_increment &&
+ put_mode_ != IndexedDBDatabase::CURSOR_UPDATE &&
+ key->type() == WebIDBKey::NumberType) {
+ bool ok = UpdateKeyGenerator(backing_store_,
+ transaction,
+ database_id_,
+ object_store_.id,
+ key.get(),
+ !key_was_generated);
+ if (!ok) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error updating key generator.")));
+ return;
+ }
+ }
+
+ callbacks_->OnSuccess(*key);
+}
+
+void IndexedDBDatabaseImpl::SetIndexKeys(
+ int64 transaction_id,
+ int64 object_store_id,
+ scoped_ptr<IndexedDBKey> primary_key,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexKeys>& index_keys) {
+ IDB_TRACE("IndexedDBDatabaseImpl::set_index_keys");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ scoped_refptr<IndexedDBBackingStore> store = BackingStore();
+ // TODO(jsbell): This method could be asynchronous, but we need to
+ // evaluate if it's worth the extra complexity.
+ IndexedDBBackingStore::RecordIdentifier record_identifier;
+ bool found = false;
+ bool ok =
+ store->KeyExistsInObjectStore(transaction->BackingStoreTransaction(),
+ metadata_.id,
+ object_store_id,
+ *primary_key,
+ &record_identifier,
+ found);
+ if (!ok) {
+ transaction->Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error setting index keys.")));
+ return;
+ }
+ if (!found) {
+ scoped_refptr<IndexedDBDatabaseError> error =
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error setting index keys for object store."));
+ transaction->Abort(error);
+ return;
+ }
+
+ ScopedVector<IndexedDBObjectStoreImpl::IndexWriter> index_writers;
+ string16 error_message;
+ bool obeys_constraints = false;
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ const IndexedDBObjectStoreMetadata& object_store_metadata =
+ metadata_.object_stores[object_store_id];
+ bool backing_store_success =
+ IndexedDBObjectStoreImpl::MakeIndexWriters(transaction,
+ store.get(),
+ id(),
+ object_store_metadata,
+ *primary_key,
+ false,
+ index_ids,
+ index_keys,
+ &index_writers,
+ &error_message,
+ &obeys_constraints);
+ if (!backing_store_success) {
+ transaction->Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error: backing store error updating index keys.")));
+ return;
+ }
+ if (!obeys_constraints) {
+ transaction->Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionConstraintError, error_message));
+ return;
+ }
+
+ for (size_t i = 0; i < index_writers.size(); ++i) {
+ IndexedDBObjectStoreImpl::IndexWriter* index_writer = index_writers[i];
+ index_writer->WriteIndexKeys(record_identifier,
+ store.get(),
+ transaction->BackingStoreTransaction(),
+ id(),
+ object_store_id);
+ }
+}
+
+void IndexedDBDatabaseImpl::SetIndexesReady(
+ int64 transaction_id,
+ int64,
+ const std::vector<int64>& index_ids) {
+ IDB_TRACE("IndexedDBObjectStoreImpl::set_indexes_ready");
+
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+
+ transaction->ScheduleTask(IndexedDBDatabase::PREEMPTIVE_TASK,
+ new SetIndexesReadyOperation(index_ids.size()));
+}
+
+void SetIndexesReadyOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("SetIndexesReadyOperation");
+ for (size_t i = 0; i < index_count_; ++i)
+ transaction->DidCompletePreemptiveEvent();
+}
+
+void IndexedDBDatabaseImpl::OpenCursor(
+ int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ indexed_db::CursorDirection direction,
+ bool key_only,
+ TaskType task_type,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBDatabaseImpl::open_cursor");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+
+ transaction->ScheduleTask(new OpenCursorOperation(
+ backing_store_,
+ id(),
+ object_store_id,
+ index_id,
+ key_range.Pass(),
+ direction,
+ key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
+ task_type,
+ callbacks));
+}
+
+void OpenCursorOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("OpenCursorOperation");
+
+ // The frontend has begun indexing, so this pauses the transaction
+ // until the indexing is complete. This can't happen any earlier
+ // because we don't want to switch to early mode in case multiple
+ // indexes are being created in a row, with Put()'s in between.
+ if (task_type_ == IndexedDBDatabase::PREEMPTIVE_TASK)
+ transaction->AddPreemptiveEvent();
+
+ scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
+ if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
+ DCHECK_NE(cursor_type_, indexed_db::CURSOR_KEY_ONLY);
+ backing_store_cursor = backing_store_->OpenObjectStoreCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *key_range_,
+ direction_);
+ } else {
+ DCHECK_EQ(task_type_, IndexedDBDatabase::NORMAL_TASK);
+ if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
+ backing_store_cursor = backing_store_->OpenIndexKeyCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key_range_,
+ direction_);
+ } else {
+ backing_store_cursor = backing_store_->OpenIndexCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key_range_,
+ direction_);
+ }
+ }
+
+ if (!backing_store_cursor) {
+ callbacks_->OnSuccess(static_cast<std::vector<char>*>(NULL));
+ return;
+ }
+
+ IndexedDBDatabase::TaskType task_type(
+ static_cast<IndexedDBDatabase::TaskType>(task_type_));
+ scoped_refptr<IndexedDBCursorImpl> cursor =
+ IndexedDBCursorImpl::Create(backing_store_cursor.Pass(),
+ cursor_type_,
+ task_type,
+ transaction,
+ object_store_id_);
+ callbacks_->OnSuccess(
+ cursor, cursor->key(), cursor->primary_key(), cursor->Value());
+}
+
+void IndexedDBDatabaseImpl::Count(
+ int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBDatabaseImpl::count");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ transaction->ScheduleTask(new CountOperation(backing_store_,
+ id(),
+ object_store_id,
+ index_id,
+ key_range.Pass(),
+ callbacks));
+}
+
+void CountOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("CountOperation");
+ uint32 count = 0;
+ scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
+
+ if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
+ backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ } else {
+ backing_store_cursor = backing_store_->OpenIndexKeyCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ }
+ if (!backing_store_cursor) {
+ callbacks_->OnSuccess(count);
+ return;
+ }
+
+ do {
+ ++count;
+ } while (backing_store_cursor->ContinueFunction(0));
+
+ callbacks_->OnSuccess(count);
+}
+
+void IndexedDBDatabaseImpl::DeleteRange(
+ int64 transaction_id,
+ int64 object_store_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBDatabaseImpl::delete_range");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+
+ transaction->ScheduleTask(new DeleteRangeOperation(
+ backing_store_, id(), object_store_id, key_range.Pass(), callbacks));
+}
+
+void DeleteRangeOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("DeleteRangeOperation");
+ scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor =
+ backing_store_->OpenObjectStoreCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ if (backing_store_cursor) {
+ do {
+ if (!backing_store_->DeleteRecord(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ backing_store_cursor->record_identifier())) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error deleting data in range")));
+ return;
+ }
+ } while (backing_store_cursor->ContinueFunction(0));
+ }
+
+ callbacks_->OnSuccess();
+}
+
+void IndexedDBDatabaseImpl::Clear(
+ int64 transaction_id,
+ int64 object_store_id,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBDatabaseImpl::clear");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
+
+ transaction->ScheduleTask(
+ new ClearOperation(backing_store_, id(), object_store_id, callbacks));
+}
+
+void ClearOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("ObjectStoreClearOperation");
+ if (!backing_store_->ClearObjectStore(transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_)) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error clearing object store")));
+ return;
+ }
+ callbacks_->OnSuccess();
+}
+
+void DeleteObjectStoreOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("DeleteObjectStoreOperation");
+ bool ok =
+ backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
+ transaction->database()->id(),
+ object_store_metadata_.id);
+ if (!ok) {
+ string16 error_string =
+ ASCIIToUTF16("Internal error deleting object store '") +
+ object_store_metadata_.name + ASCIIToUTF16("'.");
+ scoped_refptr<IndexedDBDatabaseError> error =
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError, error_string);
+ transaction->Abort(error);
+ }
+}
+
+void IndexedDBDatabaseImpl::VersionChangeOperation::Perform(
+ IndexedDBTransaction* transaction) {
+ IDB_TRACE("VersionChangeOperation");
+ int64 database_id = database_->id();
+ int64 old_version = database_->metadata_.int_version;
+ DCHECK_GT(version_, old_version);
+ database_->metadata_.int_version = version_;
+ if (!database_->backing_store_->UpdateIDBDatabaseIntVersion(
+ transaction->BackingStoreTransaction(),
+ database_id,
+ database_->metadata_.int_version)) {
+ scoped_refptr<IndexedDBDatabaseError> error =
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error writing data to stable storage when "
+ "updating version."));
+ callbacks_->OnError(error);
+ transaction->Abort(error);
+ return;
+ }
+ DCHECK(!database_->pending_second_half_open_);
+ database_->pending_second_half_open_.reset(new PendingOpenCall(
+ callbacks_, database_callbacks_, transaction_id_, version_));
+ callbacks_->OnUpgradeNeeded(old_version, database_, database_->metadata());
+}
+
+void IndexedDBDatabaseImpl::TransactionStarted(
+ IndexedDBTransaction* transaction) {
+
+ if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
+ DCHECK(!running_version_change_transaction_);
+ running_version_change_transaction_ = transaction;
+ }
+}
+
+void IndexedDBDatabaseImpl::TransactionFinished(
+ IndexedDBTransaction* transaction) {
+
+ DCHECK(transactions_.find(transaction->id()) != transactions_.end());
+ DCHECK_EQ(transactions_[transaction->id()], transaction);
+ transactions_.erase(transaction->id());
+ if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
+ DCHECK_EQ(transaction, running_version_change_transaction_);
+ running_version_change_transaction_ = NULL;
+ }
+}
+
+void IndexedDBDatabaseImpl::TransactionFinishedAndAbortFired(
+ IndexedDBTransaction* transaction) {
+ if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
+ if (pending_second_half_open_) {
+ pending_second_half_open_->Callbacks()
+ ->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionAbortError,
+ ASCIIToUTF16("Version change transaction was aborted in "
+ "upgradeneeded event handler.")));
+ pending_second_half_open_.reset();
+ }
+ ProcessPendingCalls();
+ }
+}
+
+void IndexedDBDatabaseImpl::TransactionFinishedAndCompleteFired(
+ IndexedDBTransaction* transaction) {
+ if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
+ DCHECK(pending_second_half_open_);
+ if (pending_second_half_open_) {
+ DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version);
+ DCHECK(metadata_.id != kInvalidId);
+ pending_second_half_open_->Callbacks()->OnSuccess(this, this->metadata());
+ pending_second_half_open_.reset();
+ }
+ ProcessPendingCalls();
+ }
+}
+
+size_t IndexedDBDatabaseImpl::ConnectionCount() const {
+ // This does not include pending open calls, as those should not block version
+ // changes and deletes.
+ return database_callbacks_set_.size();
+}
+
+void IndexedDBDatabaseImpl::ProcessPendingCalls() {
+ if (pending_second_half_open_) {
+ DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version);
+ DCHECK(metadata_.id != kInvalidId);
+ scoped_ptr<PendingOpenCall> pending_call = pending_second_half_open_.Pass();
+ pending_call->Callbacks()->OnSuccess(this, this->metadata());
+ // Fall through when complete, as pending opens may be unblocked.
+ }
+
+ if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
+ DCHECK(pending_run_version_change_transaction_call_->Version() >
+ metadata_.int_version);
+ scoped_ptr<PendingOpenCall> pending_call =
+ pending_run_version_change_transaction_call_.Pass();
+ RunVersionChangeTransactionFinal(pending_call->Callbacks(),
+ pending_call->DatabaseCallbacks(),
+ pending_call->TransactionId(),
+ pending_call->Version());
+ DCHECK_EQ(static_cast<size_t>(1), ConnectionCount());
+ // Fall through would be a no-op, since transaction must complete
+ // asynchronously.
+ DCHECK(IsDeleteDatabaseBlocked());
+ DCHECK(IsOpenConnectionBlocked());
+ return;
+ }
+
+ if (!IsDeleteDatabaseBlocked()) {
+ PendingDeleteCallList pending_delete_calls;
+ pending_delete_calls_.swap(pending_delete_calls);
+ while (!pending_delete_calls.empty()) {
+ // Only the first delete call will delete the database, but each must fire
+ // callbacks.
+ scoped_ptr<PendingDeleteCall> pending_delete_call(
+ pending_delete_calls.front());
+ pending_delete_calls.pop_front();
+ DeleteDatabaseFinal(pending_delete_call->Callbacks());
+ }
+ // delete_database_final should never re-queue calls.
+ DCHECK(pending_delete_calls_.empty());
+ // Fall through when complete, as pending opens may be unblocked.
+ }
+
+ if (!IsOpenConnectionBlocked()) {
+ PendingOpenCallList pending_open_calls;
+ pending_open_calls_.swap(pending_open_calls);
+ while (!pending_open_calls.empty()) {
+ scoped_ptr<PendingOpenCall> pending_open_call(pending_open_calls.front());
+ pending_open_calls.pop_front();
+ OpenConnection(pending_open_call->Callbacks(),
+ pending_open_call->DatabaseCallbacks(),
+ pending_open_call->TransactionId(),
+ pending_open_call->Version());
+ }
+ }
+}
+
+void IndexedDBDatabaseImpl::CreateTransaction(
+ int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
+ const std::vector<int64>& object_store_ids,
+ uint16 mode) {
+
+ DCHECK(database_callbacks_set_.has(callbacks));
+
+ scoped_refptr<IndexedDBTransaction> transaction =
+ IndexedDBTransaction::Create(
+ transaction_id,
+ callbacks,
+ object_store_ids,
+ static_cast<indexed_db::TransactionMode>(mode),
+ this);
+ DCHECK(transactions_.find(transaction_id) == transactions_.end());
+ transactions_[transaction_id] = transaction;
+}
+
+bool IndexedDBDatabaseImpl::IsOpenConnectionBlocked() const {
+ return !pending_delete_calls_.empty() ||
+ running_version_change_transaction_ ||
+ pending_run_version_change_transaction_call_;
+}
+
+void IndexedDBDatabaseImpl::OpenConnection(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 version) {
+ DCHECK(backing_store_.get());
+
+ // TODO(jsbell): Should have a priority queue so that higher version
+ // requests are processed first. http://crbug.com/225850
+ if (IsOpenConnectionBlocked()) {
+ pending_open_calls_.push_back(new PendingOpenCall(
+ callbacks, database_callbacks, transaction_id, version));
+ return;
+ }
+
+ if (metadata_.id == kInvalidId) {
+ // The database was deleted then immediately re-opened; OpenInternal()
+ // recreates it in the backing store.
+ if (OpenInternal()) {
+ DCHECK_EQ(metadata_.int_version,
+ IndexedDBDatabaseMetadata::NO_INT_VERSION);
+ } else {
+ string16 message;
+ scoped_refptr<IndexedDBDatabaseError> error;
+ if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
+ message = ASCIIToUTF16(
+ "Internal error opening database with no version specified.");
+ else
+ message =
+ ASCIIToUTF16("Internal error opening database with version ") +
+ Int64ToString16(version);
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError, message));
+ return;
+ }
+ }
+
+ // We infer that the database didn't exist from its lack of either type of
+ // version.
+ bool is_new_database =
+ metadata_.version == kNoStringVersion &&
+ metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
+
+ if (version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
+ // For unit tests only - skip upgrade steps. Calling from script with
+ // DEFAULT_INT_VERSION throws exception.
+ // TODO(jsbell): Assert that we're executing a unit test.
+ DCHECK(is_new_database);
+ database_callbacks_set_.insert(database_callbacks);
+ callbacks->OnSuccess(this, this->metadata());
+ return;
+ }
+
+ if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
+ if (!is_new_database) {
+ database_callbacks_set_.insert(database_callbacks);
+ callbacks->OnSuccess(this, this->metadata());
+ return;
+ }
+ // Spec says: If no version is specified and no database exists, set
+ // database version to 1.
+ version = 1;
+ }
+
+ if (version > metadata_.int_version) {
+ database_callbacks_set_.insert(database_callbacks);
+ RunVersionChangeTransaction(
+ callbacks, database_callbacks, transaction_id, version);
+ return;
+ }
+ if (version < metadata_.int_version) {
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionVersionError,
+ ASCIIToUTF16("The requested version (") + Int64ToString16(version) +
+ ASCIIToUTF16(") is less than the existing version (") +
+ Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
+ return;
+ }
+ DCHECK_EQ(version, metadata_.int_version);
+ database_callbacks_set_.insert(database_callbacks);
+ callbacks->OnSuccess(this, this->metadata());
+}
+
+void IndexedDBDatabaseImpl::RunVersionChangeTransaction(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 requested_version) {
+
+ DCHECK(callbacks);
+ DCHECK(database_callbacks_set_.has(database_callbacks));
+ if (ConnectionCount() > 1) {
+ // Front end ensures the event is not fired at connections that have
+ // close_pending set.
+ for (DatabaseCallbacksSet::const_iterator it =
+ database_callbacks_set_.begin();
+ it != database_callbacks_set_.end();
+ ++it) {
+ if (*it != database_callbacks.get())
+ (*it)->OnVersionChange(metadata_.int_version, requested_version);
+ }
+ // TODO(jsbell): Remove the call to on_blocked and instead wait
+ // until the frontend tells us that all the "versionchange" events
+ // have been delivered. http://crbug.com/100123
+ callbacks->OnBlocked(metadata_.int_version);
+
+ DCHECK(!pending_run_version_change_transaction_call_);
+ pending_run_version_change_transaction_call_.reset(new PendingOpenCall(
+ callbacks, database_callbacks, transaction_id, requested_version));
+ return;
+ }
+ RunVersionChangeTransactionFinal(
+ callbacks, database_callbacks, transaction_id, requested_version);
+}
+
+void IndexedDBDatabaseImpl::RunVersionChangeTransactionFinal(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 requested_version) {
+
+ std::vector<int64> object_store_ids;
+ CreateTransaction(transaction_id,
+ database_callbacks,
+ object_store_ids,
+ indexed_db::TRANSACTION_VERSION_CHANGE);
+ scoped_refptr<IndexedDBTransaction> transaction =
+ transactions_[transaction_id];
+
+ transaction->ScheduleTask(
+ new VersionChangeOperation(this,
+ transaction_id,
+ requested_version,
+ callbacks,
+ database_callbacks),
+ new VersionChangeAbortOperation(
+ this, metadata_.version, metadata_.int_version));
+
+ DCHECK(!pending_second_half_open_);
+}
+
+void IndexedDBDatabaseImpl::DeleteDatabase(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+
+ if (IsDeleteDatabaseBlocked()) {
+ for (DatabaseCallbacksSet::const_iterator it =
+ database_callbacks_set_.begin();
+ it != database_callbacks_set_.end();
+ ++it) {
+ // Front end ensures the event is not fired at connections that have
+ // close_pending set.
+ (*it)->OnVersionChange(metadata_.int_version,
+ IndexedDBDatabaseMetadata::NO_INT_VERSION);
+ }
+ // TODO(jsbell): Only fire on_blocked if there are open
+ // connections after the VersionChangeEvents are received, not
+ // just set up to fire. http://crbug.com/100123
+ callbacks->OnBlocked(metadata_.int_version);
+ pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
+ return;
+ }
+ DeleteDatabaseFinal(callbacks);
+}
+
+bool IndexedDBDatabaseImpl::IsDeleteDatabaseBlocked() const {
+ return !!ConnectionCount();
+}
+
+void IndexedDBDatabaseImpl::DeleteDatabaseFinal(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ DCHECK(!IsDeleteDatabaseBlocked());
+ DCHECK(backing_store_);
+ if (!backing_store_->DeleteDatabase(metadata_.name)) {
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error deleting database.")));
+ return;
+ }
+ metadata_.version = kNoStringVersion;
+ metadata_.id = kInvalidId;
+ metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
+ metadata_.object_stores.clear();
+ callbacks->OnSuccess();
+}
+
+void IndexedDBDatabaseImpl::Close(
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks) {
+ DCHECK(callbacks);
+ DCHECK(database_callbacks_set_.has(callbacks));
+
+ // Close outstanding transactions from the closing connection. This
+ // can not happen if the close is requested by the connection itself
+ // as the front-end defers the close until all transactions are
+ // complete, so something unusual has happened e.g. unexpected
+ // process termination.
+ {
+ TransactionMap transactions(transactions_);
+ for (TransactionMap::const_iterator it = transactions.begin(),
+ end = transactions.end();
+ it != end;
+ ++it) {
+ if (it->second->connection() == callbacks)
+ it->second->Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Connection is closing.")));
+ }
+ }
+
+ database_callbacks_set_.erase(callbacks);
+ if (pending_second_half_open_ &&
+ pending_second_half_open_->DatabaseCallbacks() == callbacks) {
+ pending_second_half_open_->Callbacks()
+ ->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionAbortError,
+ ASCIIToUTF16("The connection was closed.")));
+ pending_second_half_open_.reset();
+ }
+
+ // process_pending_calls allows the inspector to process a pending open call
+ // and call close, reentering IndexedDBDatabaseImpl::close. Then the
+ // backend would be removed both by the inspector closing its connection, and
+ // by the connection that first called close.
+ // To avoid that situation, don't proceed in case of reentrancy.
+ if (closing_connection_)
+ return;
+ base::AutoReset<bool> ClosingConnection(&closing_connection_, true);
+ ProcessPendingCalls();
+
+ // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
+ if (!ConnectionCount() && !pending_open_calls_.size() &&
+ !pending_delete_calls_.size()) {
+ DCHECK(transactions_.empty());
+
+ backing_store_ = NULL;
+
+ // This check should only be false in unit tests.
+ // TODO(jsbell): Assert factory_ || we're executing a unit test.
+ if (factory_)
+ factory_->RemoveIDBDatabaseBackend(identifier_);
+ }
+}
+
+void CreateObjectStoreAbortOperation::Perform(
+ IndexedDBTransaction* transaction) {
+ IDB_TRACE("CreateObjectStoreAbortOperation");
+ DCHECK(!transaction);
+ database_->RemoveObjectStore(object_store_id_);
+}
+
+void DeleteObjectStoreAbortOperation::Perform(
+ IndexedDBTransaction* transaction) {
+ IDB_TRACE("DeleteObjectStoreAbortOperation");
+ DCHECK(!transaction);
+ database_->AddObjectStore(object_store_metadata_,
+ IndexedDBObjectStoreMetadata::kInvalidId);
+}
+
+void IndexedDBDatabaseImpl::VersionChangeAbortOperation::Perform(
+ IndexedDBTransaction* transaction) {
+ IDB_TRACE("VersionChangeAbortOperation");
+ DCHECK(!transaction);
+ database_->metadata_.version = previous_version_;
+ database_->metadata_.int_version = previous_int_version_;
+}
+
+} // namespace content