Chromium Code Reviews| Index: content/browser/indexed_db/indexed_db_database_impl.cc |
| 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 0000000000000000000000000000000000000000..3b07c794dc68199dd2dd6380afcf58bad0bd3c54 |
| --- /dev/null |
| +++ b/content/browser/indexed_db/indexed_db_database_impl.cc |
| @@ -0,0 +1,1827 @@ |
| +// 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_t transaction_id, |
| + int64_t 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_t transaction_id_; |
| + int64_t version_; |
| + scoped_refptr<IndexedDBCallbacksWrapper> callbacks_; |
| + scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_; |
| +}; |
| + |
| +class CreateObjectStoreAbortOperation : public IndexedDBTransaction::Operation { |
| + public: |
| + CreateObjectStoreAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database, |
| + int64_t 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_t 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_t 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_t previous_int_version_; |
| +}; |
| + |
| +class CreateIndexOperation : public IndexedDBTransaction::Operation { |
| + public: |
| + CreateIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store, |
| + int64_t 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_t object_store_id_; |
| + const IndexedDBIndexMetadata index_metadata_; |
| +}; |
| + |
| +class DeleteIndexOperation : public IndexedDBTransaction::Operation { |
| + public: |
| + DeleteIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store, |
| + int64_t 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_t object_store_id_; |
| + const IndexedDBIndexMetadata index_metadata_; |
| +}; |
| + |
| +class CreateIndexAbortOperation : public IndexedDBTransaction::Operation { |
| + public: |
| + CreateIndexAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database, |
| + int64_t object_store_id, |
| + int64_t 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_t object_store_id_; |
| + const int64_t index_id_; |
| +}; |
| + |
| +class DeleteIndexAbortOperation : public IndexedDBTransaction::Operation { |
| + public: |
| + DeleteIndexAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database, |
| + int64_t 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_t object_store_id_; |
| + const IndexedDBIndexMetadata index_metadata_; |
| +}; |
| + |
| +class GetOperation : public IndexedDBTransaction::Operation { |
| + public: |
| + GetOperation(scoped_refptr<IndexedDBBackingStore> backing_store, |
| + const IndexedDBDatabaseMetadata& metadata, |
| + int64_t object_store_id, |
| + int64_t 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_t database_id_; |
| + const int64_t object_store_id_; |
| + const int64_t 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_t 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_t>& 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_t 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_t> 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_t database_id, |
| + int64_t object_store_id, |
| + int64_t 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_t database_id_; |
| + const int64_t object_store_id_; |
| + const int64_t 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_t database_id, |
| + int64_t object_store_id, |
| + int64_t 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_t database_id_; |
| + const int64_t object_store_id_; |
| + const int64_t 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_t database_id, |
| + int64_t 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_t database_id_; |
| + const int64_t 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_t database_id, |
| + int64_t 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_t database_id_; |
| + const int64_t object_store_id_; |
| + const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_; |
| +}; |
| + |
| +class IndexedDBDatabaseImpl::PendingOpenCall { |
| + public: |
| + PendingOpenCall( |
| + scoped_refptr<IndexedDBCallbacksWrapper> callbacks, |
| + scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks, |
| + int64_t transaction_id, |
| + int64_t 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_t Version() { return version_; } |
| + int64_t TransactionId() const { return transaction_id_; } |
| + |
| + private: |
| + scoped_refptr<IndexedDBCallbacksWrapper> callbacks_; |
| + scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_; |
| + int64_t version_; |
| + const int64_t 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_t 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(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_t 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_t object_store_id, |
| + const IndexedDBIndexMetadata& index, |
| + int64_t 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(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_t object_store_id, |
| + int64_t 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_t transaction_id, |
| + int64_t 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_t transaction_id, |
| + int64_t 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_t transaction_id, |
| + int64_t object_store_id, |
| + int64_t 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_t transaction_id, |
| + int64_t object_store_id, |
| + int64_t 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_t 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_t 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_t 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_t transaction_id, |
| + int64_t object_store_id, |
| + int64_t 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(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); |
| + } |
| + |
| + // 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_t database_id, |
| + int64_t object_store_id) { |
| + const int64_t max_generator_value = |
| + 9007199254740992LL; // Maximum integer storable as ECMAScript number. |
| + int64_t 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_t database_id, |
| + int64_t object_store_id, |
| + const IndexedDBKey* key, |
| + bool check_current) { |
| + DCHECK_EQ(key && key->type(), WebIDBKey::NumberType); |
| + return backing_store->MaybeUpdateKeyGeneratorCurrentNumber( |
| + transaction->BackingStoreTransaction(), |
| + database_id, |
| + object_store_id, |
| + static_cast<int64_t>(floor(key->number())) + 1, |
| + check_current); |
| +} |
| + |
| +void IndexedDBDatabaseImpl::Put( |
| + int64_t transaction_id, |
| + int64_t object_store_id, |
| + std::vector<char>* value, |
| + scoped_ptr<IndexedDBKey> key, |
| + PutMode put_mode, |
| + scoped_refptr<IndexedDBCallbacksWrapper> callbacks, |
| + const std::vector<int64_t>& 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(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(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_t transaction_id, |
| + int64_t object_store_id, |
| + scoped_ptr<IndexedDBKey> primary_key, |
| + const std::vector<int64_t>& 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_t transaction_id, |
| + int64_t, |
| + const std::vector<int64_t>& 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_t transaction_id, |
| + int64_t object_store_id, |
| + int64_t 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(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_t transaction_id, |
| + int64_t object_store_id, |
| + int64_t 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_t 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_t transaction_id, |
| + int64_t 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_t transaction_id, |
| + int64_t 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(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_t database_id = database_->id(); |
| + int64_t old_version = database_->metadata_.int_version; |
| + DCHECK(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(ConnectionCount() == 1); |
| + // 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_t transaction_id, |
| + scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks, |
| + const std::vector<int64_t>& object_store_ids, |
| + unsigned short 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_t transaction_id, |
| + int64_t version) { |
| + DCHECK(backing_store_.get()); |
| + |
| + // TODO(jsbell): Should have a priority queue so that higher version requests |
| + // are |
|
dgrogan
2013/05/22 18:22:06
There are a few of these bad comment wraps.
jsbell
2013/05/22 22:21:14
Done.
|
| + // 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_t transaction_id, |
| + int64_t 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 |
|
dgrogan
2013/05/22 18:22:06
comment wrapping
jsbell
2013/05/22 19:13:04
Done.
|
| + // 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_t transaction_id, |
| + int64_t requested_version) { |
| + |
| + std::vector<int64_t> 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 |
|
dgrogan
2013/05/22 18:22:06
comment wrapping
jsbell
2013/05/22 19:13:04
Done.
|
| + // 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 |