Commit 12c33772 authored by jsbell@chromium.org's avatar jsbell@chromium.org

IndexedDB: Prevent store/index deletion from racing ahead of use

In the Indexed DB spec, schema updates occur synchronously from the
perspective of script, e.g. you can create a store then use it
immediately without waiting for a request to complete. That model was
carried through to the back end, but with a subtle issue that the
deletion of a store or index could "race ahead" of a previously
requested use of it.

Change store/index deletion to take place in order with other
requests, so do all the work (in-memory and backing store) during the
scheduled operation, rather than on IPC receipt. Also change
store/index creation to do all the work (in-memory and backing store)
synchronously, since index population is already preemptively done.

And since this requires shuffling when the "abort" operations should
be queued, rework the API there slightly.

BUG=370056,368271, 362723
R=cmumford@chromium.org,ericu@chromium.org

Review URL: https://codereview.chromium.org/277583002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272282 0039d316-1c4b-4281-b951-d872f2087c98
parent 8ed02f6e
...@@ -277,6 +277,9 @@ void IndexedDBDatabase::CreateObjectStore(int64 transaction_id, ...@@ -277,6 +277,9 @@ void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
return; return;
} }
// Store creation is done synchronously, as it may be followed by
// index creation (also sync) since preemptive OpenCursor/SetIndexKeys
// may follow.
IndexedDBObjectStoreMetadata object_store_metadata( IndexedDBObjectStoreMetadata object_store_metadata(
name, name,
object_store_id, object_store_id,
...@@ -284,21 +287,6 @@ void IndexedDBDatabase::CreateObjectStore(int64 transaction_id, ...@@ -284,21 +287,6 @@ void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
auto_increment, auto_increment,
IndexedDBDatabase::kMinimumIndexId); IndexedDBDatabase::kMinimumIndexId);
transaction->ScheduleTask(
base::Bind(&IndexedDBDatabase::CreateObjectStoreOperation,
this,
object_store_metadata),
base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
this,
object_store_id));
AddObjectStore(object_store_metadata, object_store_id);
}
void IndexedDBDatabase::CreateObjectStoreOperation(
const IndexedDBObjectStoreMetadata& object_store_metadata,
IndexedDBTransaction* transaction) {
IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation");
leveldb::Status s = leveldb::Status s =
backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(), backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(),
transaction->database()->id(), transaction->database()->id(),
...@@ -317,6 +305,12 @@ void IndexedDBDatabase::CreateObjectStoreOperation( ...@@ -317,6 +305,12 @@ void IndexedDBDatabase::CreateObjectStoreOperation(
error); error);
return; return;
} }
AddObjectStore(object_store_metadata, object_store_id);
transaction->ScheduleAbortTask(
base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
this,
object_store_id));
} }
void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id, void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
...@@ -330,17 +324,10 @@ void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id, ...@@ -330,17 +324,10 @@ void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
if (!ValidateObjectStoreId(object_store_id)) if (!ValidateObjectStoreId(object_store_id))
return; return;
const IndexedDBObjectStoreMetadata& object_store_metadata =
metadata_.object_stores[object_store_id];
transaction->ScheduleTask( transaction->ScheduleTask(
base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation, base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation,
this, this,
object_store_metadata), object_store_id));
base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
this,
object_store_metadata));
RemoveObjectStore(object_store_id);
} }
void IndexedDBDatabase::CreateIndex(int64 transaction_id, void IndexedDBDatabase::CreateIndex(int64 transaction_id,
...@@ -358,27 +345,12 @@ void IndexedDBDatabase::CreateIndex(int64 transaction_id, ...@@ -358,27 +345,12 @@ void IndexedDBDatabase::CreateIndex(int64 transaction_id,
if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id)) if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
return; return;
// Index creation is done synchronously since preemptive
// OpenCursor/SetIndexKeys may follow.
const IndexedDBIndexMetadata index_metadata( const IndexedDBIndexMetadata index_metadata(
name, index_id, key_path, unique, multi_entry); name, index_id, key_path, unique, multi_entry);
transaction->ScheduleTask(
base::Bind(&IndexedDBDatabase::CreateIndexOperation,
this,
object_store_id,
index_metadata),
base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
this,
object_store_id,
index_id));
AddIndex(object_store_id, index_metadata, index_id);
}
void IndexedDBDatabase::CreateIndexOperation(
int64 object_store_id,
const IndexedDBIndexMetadata& index_metadata,
IndexedDBTransaction* transaction) {
IDB_TRACE("IndexedDBDatabase::CreateIndexOperation");
if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(), if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
transaction->database()->id(), transaction->database()->id(),
object_store_id, object_store_id,
...@@ -394,6 +366,13 @@ void IndexedDBDatabase::CreateIndexOperation( ...@@ -394,6 +366,13 @@ void IndexedDBDatabase::CreateIndexOperation(
blink::WebIDBDatabaseExceptionUnknownError, error_string)); blink::WebIDBDatabaseExceptionUnknownError, error_string));
return; return;
} }
AddIndex(object_store_id, index_metadata, index_id);
transaction->ScheduleAbortTask(
base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
this,
object_store_id,
index_id));
} }
void IndexedDBDatabase::CreateIndexAbortOperation( void IndexedDBDatabase::CreateIndexAbortOperation(
...@@ -416,32 +395,28 @@ void IndexedDBDatabase::DeleteIndex(int64 transaction_id, ...@@ -416,32 +395,28 @@ void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id)) if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
return; return;
const IndexedDBIndexMetadata& index_metadata =
metadata_.object_stores[object_store_id].indexes[index_id];
transaction->ScheduleTask( transaction->ScheduleTask(
base::Bind(&IndexedDBDatabase::DeleteIndexOperation, base::Bind(&IndexedDBDatabase::DeleteIndexOperation,
this, this,
object_store_id, object_store_id,
index_metadata), index_id));
base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
this,
object_store_id,
index_metadata));
RemoveIndex(object_store_id, index_id);
} }
void IndexedDBDatabase::DeleteIndexOperation( void IndexedDBDatabase::DeleteIndexOperation(
int64 object_store_id, int64 object_store_id,
const IndexedDBIndexMetadata& index_metadata, int64 index_id,
IndexedDBTransaction* transaction) { IndexedDBTransaction* transaction) {
IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation"); IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation");
const IndexedDBIndexMetadata index_metadata =
metadata_.object_stores[object_store_id].indexes[index_id];
leveldb::Status s = leveldb::Status s =
backing_store_->DeleteIndex(transaction->BackingStoreTransaction(), backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
transaction->database()->id(), transaction->database()->id(),
object_store_id, object_store_id,
index_metadata.id); index_id);
if (!s.ok()) { if (!s.ok()) {
base::string16 error_string = base::string16 error_string =
ASCIIToUTF16("Internal error deleting index '") + ASCIIToUTF16("Internal error deleting index '") +
...@@ -452,7 +427,15 @@ void IndexedDBDatabase::DeleteIndexOperation( ...@@ -452,7 +427,15 @@ void IndexedDBDatabase::DeleteIndexOperation(
if (s.IsCorruption()) if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error); error);
return;
} }
RemoveIndex(object_store_id, index_id);
transaction->ScheduleAbortTask(
base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
this,
object_store_id,
index_metadata));
} }
void IndexedDBDatabase::DeleteIndexAbortOperation( void IndexedDBDatabase::DeleteIndexAbortOperation(
...@@ -1299,13 +1282,16 @@ void IndexedDBDatabase::ClearOperation( ...@@ -1299,13 +1282,16 @@ void IndexedDBDatabase::ClearOperation(
} }
void IndexedDBDatabase::DeleteObjectStoreOperation( void IndexedDBDatabase::DeleteObjectStoreOperation(
const IndexedDBObjectStoreMetadata& object_store_metadata, int64 object_store_id,
IndexedDBTransaction* transaction) { IndexedDBTransaction* transaction) {
IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation"); IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation");
const IndexedDBObjectStoreMetadata object_store_metadata =
metadata_.object_stores[object_store_id];
leveldb::Status s = leveldb::Status s =
backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(), backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
transaction->database()->id(), transaction->database()->id(),
object_store_metadata.id); object_store_id);
if (!s.ok()) { if (!s.ok()) {
base::string16 error_string = base::string16 error_string =
ASCIIToUTF16("Internal error deleting object store '") + ASCIIToUTF16("Internal error deleting object store '") +
...@@ -1316,7 +1302,14 @@ void IndexedDBDatabase::DeleteObjectStoreOperation( ...@@ -1316,7 +1302,14 @@ void IndexedDBDatabase::DeleteObjectStoreOperation(
if (s.IsCorruption()) if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error); error);
return;
} }
RemoveObjectStore(object_store_id);
transaction->ScheduleAbortTask(
base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
this,
object_store_metadata));
} }
void IndexedDBDatabase::VersionChangeOperation( void IndexedDBDatabase::VersionChangeOperation(
...@@ -1327,11 +1320,9 @@ void IndexedDBDatabase::VersionChangeOperation( ...@@ -1327,11 +1320,9 @@ void IndexedDBDatabase::VersionChangeOperation(
IDB_TRACE("IndexedDBDatabase::VersionChangeOperation"); IDB_TRACE("IndexedDBDatabase::VersionChangeOperation");
int64 old_version = metadata_.int_version; int64 old_version = metadata_.int_version;
DCHECK_GT(version, old_version); DCHECK_GT(version, old_version);
metadata_.int_version = version;
if (!backing_store_->UpdateIDBDatabaseIntVersion( if (!backing_store_->UpdateIDBDatabaseIntVersion(
transaction->BackingStoreTransaction(), transaction->BackingStoreTransaction(), id(), version)) {
id(),
metadata_.int_version)) {
IndexedDBDatabaseError error( IndexedDBDatabaseError error(
blink::WebIDBDatabaseExceptionUnknownError, blink::WebIDBDatabaseExceptionUnknownError,
ASCIIToUTF16( ASCIIToUTF16(
...@@ -1341,6 +1332,15 @@ void IndexedDBDatabase::VersionChangeOperation( ...@@ -1341,6 +1332,15 @@ void IndexedDBDatabase::VersionChangeOperation(
transaction->Abort(error); transaction->Abort(error);
return; return;
} }
transaction->ScheduleAbortTask(
base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
this,
metadata_.version,
metadata_.int_version));
metadata_.int_version = version;
metadata_.version = kNoStringVersion;
DCHECK(!pending_second_half_open_); DCHECK(!pending_second_half_open_);
pending_second_half_open_.reset( pending_second_half_open_.reset(
new PendingSuccessCall(callbacks, connection.get(), version)); new PendingSuccessCall(callbacks, connection.get(), version));
...@@ -1623,17 +1623,12 @@ void IndexedDBDatabase::RunVersionChangeTransactionFinal( ...@@ -1623,17 +1623,12 @@ void IndexedDBDatabase::RunVersionChangeTransactionFinal(
object_store_ids, object_store_ids,
indexed_db::TRANSACTION_VERSION_CHANGE); indexed_db::TRANSACTION_VERSION_CHANGE);
transactions_[transaction_id] transactions_[transaction_id]->ScheduleTask(
->ScheduleTask(base::Bind(&IndexedDBDatabase::VersionChangeOperation, base::Bind(&IndexedDBDatabase::VersionChangeOperation,
this, this,
requested_version, requested_version,
callbacks, callbacks,
base::Passed(&connection)), base::Passed(&connection)));
base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
this,
metadata_.version,
metadata_.int_version));
DCHECK(!pending_second_half_open_); DCHECK(!pending_second_half_open_);
} }
......
...@@ -175,13 +175,10 @@ class CONTENT_EXPORT IndexedDBDatabase ...@@ -175,13 +175,10 @@ class CONTENT_EXPORT IndexedDBDatabase
size_t PendingDeleteCount() const; size_t PendingDeleteCount() const;
// Asynchronous tasks scheduled within transactions: // Asynchronous tasks scheduled within transactions:
void CreateObjectStoreOperation(
const IndexedDBObjectStoreMetadata& object_store_metadata,
IndexedDBTransaction* transaction);
void CreateObjectStoreAbortOperation(int64 object_store_id, void CreateObjectStoreAbortOperation(int64 object_store_id,
IndexedDBTransaction* transaction); IndexedDBTransaction* transaction);
void DeleteObjectStoreOperation( void DeleteObjectStoreOperation(
const IndexedDBObjectStoreMetadata& object_store_metadata, int64 object_store_id,
IndexedDBTransaction* transaction); IndexedDBTransaction* transaction);
void DeleteObjectStoreAbortOperation( void DeleteObjectStoreAbortOperation(
const IndexedDBObjectStoreMetadata& object_store_metadata, const IndexedDBObjectStoreMetadata& object_store_metadata,
...@@ -193,11 +190,8 @@ class CONTENT_EXPORT IndexedDBDatabase ...@@ -193,11 +190,8 @@ class CONTENT_EXPORT IndexedDBDatabase
void VersionChangeAbortOperation(const base::string16& previous_version, void VersionChangeAbortOperation(const base::string16& previous_version,
int64 previous_int_version, int64 previous_int_version,
IndexedDBTransaction* transaction); IndexedDBTransaction* transaction);
void CreateIndexOperation(int64 object_store_id,
const IndexedDBIndexMetadata& index_metadata,
IndexedDBTransaction* transaction);
void DeleteIndexOperation(int64 object_store_id, void DeleteIndexOperation(int64 object_store_id,
const IndexedDBIndexMetadata& index_metadata, int64 index_id,
IndexedDBTransaction* transaction); IndexedDBTransaction* transaction);
void CreateIndexAbortOperation(int64 object_store_id, void CreateIndexAbortOperation(int64 object_store_id,
int64 index_id, int64 index_id,
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/auto_reset.h" #include "base/auto_reset.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/run_loop.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "content/browser/indexed_db/indexed_db.h" #include "content/browser/indexed_db/indexed_db.h"
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
#include "content/browser/indexed_db/indexed_db_factory.h" #include "content/browser/indexed_db/indexed_db_factory.h"
#include "content/browser/indexed_db/indexed_db_fake_backing_store.h" #include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
#include "content/browser/indexed_db/indexed_db_transaction.h" #include "content/browser/indexed_db/indexed_db_transaction.h"
#include "content/browser/indexed_db/indexed_db_value.h"
#include "content/browser/indexed_db/mock_indexed_db_callbacks.h" #include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
#include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h" #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -24,7 +26,7 @@ ...@@ -24,7 +26,7 @@
using base::ASCIIToUTF16; using base::ASCIIToUTF16;
namespace { namespace {
const int FAKE_CHILD_PROCESS_ID = 0; const int kFakeChildProcessId = 0;
} }
namespace content { namespace content {
...@@ -71,7 +73,7 @@ TEST(IndexedDBDatabaseTest, ConnectionLifecycle) { ...@@ -71,7 +73,7 @@ TEST(IndexedDBDatabaseTest, ConnectionLifecycle) {
IndexedDBPendingConnection connection1( IndexedDBPendingConnection connection1(
request1, request1,
callbacks1, callbacks1,
FAKE_CHILD_PROCESS_ID, kFakeChildProcessId,
transaction_id1, transaction_id1,
IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION); IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
db->OpenConnection(connection1); db->OpenConnection(connection1);
...@@ -85,7 +87,7 @@ TEST(IndexedDBDatabaseTest, ConnectionLifecycle) { ...@@ -85,7 +87,7 @@ TEST(IndexedDBDatabaseTest, ConnectionLifecycle) {
IndexedDBPendingConnection connection2( IndexedDBPendingConnection connection2(
request2, request2,
callbacks2, callbacks2,
FAKE_CHILD_PROCESS_ID, kFakeChildProcessId,
transaction_id2, transaction_id2,
IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION); IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
db->OpenConnection(connection2); db->OpenConnection(connection2);
...@@ -129,7 +131,7 @@ TEST(IndexedDBDatabaseTest, ForcedClose) { ...@@ -129,7 +131,7 @@ TEST(IndexedDBDatabaseTest, ForcedClose) {
IndexedDBPendingConnection connection( IndexedDBPendingConnection connection(
request, request,
callbacks, callbacks,
FAKE_CHILD_PROCESS_ID, kFakeChildProcessId,
upgrade_transaction_id, upgrade_transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION); IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
database->OpenConnection(connection); database->OpenConnection(connection);
...@@ -193,7 +195,7 @@ TEST(IndexedDBDatabaseTest, PendingDelete) { ...@@ -193,7 +195,7 @@ TEST(IndexedDBDatabaseTest, PendingDelete) {
IndexedDBPendingConnection connection( IndexedDBPendingConnection connection(
request1, request1,
callbacks1, callbacks1,
FAKE_CHILD_PROCESS_ID, kFakeChildProcessId,
transaction_id1, transaction_id1,
IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION); IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
db->OpenConnection(connection); db->OpenConnection(connection);
...@@ -213,4 +215,191 @@ TEST(IndexedDBDatabaseTest, PendingDelete) { ...@@ -213,4 +215,191 @@ TEST(IndexedDBDatabaseTest, PendingDelete) {
EXPECT_TRUE(request2->success_called()); EXPECT_TRUE(request2->success_called());
} }
void DummyOperation(IndexedDBTransaction* transaction) {
}
class IndexedDBDatabaseOperationTest : public testing::Test {
public:
IndexedDBDatabaseOperationTest() : commit_success_(true) {}
virtual void SetUp() {
backing_store_ = new IndexedDBFakeBackingStore();
leveldb::Status s;
db_ = IndexedDBDatabase::Create(ASCIIToUTF16("db"),
backing_store_,
NULL /*factory*/,
IndexedDBDatabase::Identifier(),
&s);
ASSERT_TRUE(s.ok());
request_ = new MockIndexedDBCallbacks();
callbacks_ = new MockIndexedDBDatabaseCallbacks();
const int64 transaction_id = 1;
db_->OpenConnection(IndexedDBPendingConnection(
request_,
callbacks_,
kFakeChildProcessId,
transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION));
EXPECT_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
db_->metadata().int_version);
transaction_ = new IndexedDBTransaction(
transaction_id,
callbacks_,
std::set<int64>() /*scope*/,
indexed_db::TRANSACTION_VERSION_CHANGE,
db_,
new IndexedDBFakeBackingStore::FakeTransaction(commit_success_));
db_->TransactionCreated(transaction_);
// Add a dummy task which takes the place of the VersionChangeOperation
// which kicks off the upgrade. This ensures that the transaction has
// processed at least one task before the CreateObjectStore call.
transaction_->ScheduleTask(base::Bind(&DummyOperation));
}
void RunPostedTasks() { base::RunLoop().RunUntilIdle(); }
protected:
scoped_refptr<IndexedDBFakeBackingStore> backing_store_;
scoped_refptr<IndexedDBDatabase> db_;
scoped_refptr<MockIndexedDBCallbacks> request_;
scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks_;
scoped_refptr<IndexedDBTransaction> transaction_;
bool commit_success_;
private:
base::MessageLoop message_loop_;
DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationTest);
};
TEST_F(IndexedDBDatabaseOperationTest, CreateObjectStore) {
EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
const int64 store_id = 1001;
db_->CreateObjectStore(transaction_->id(),
store_id,
ASCIIToUTF16("store"),
IndexedDBKeyPath(),
false /*auto_increment*/);
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
RunPostedTasks();
transaction_->Commit();
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
}
TEST_F(IndexedDBDatabaseOperationTest, CreateIndex) {
EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
const int64 store_id = 1001;
db_->CreateObjectStore(transaction_->id(),
store_id,
ASCIIToUTF16("store"),
IndexedDBKeyPath(),
false /*auto_increment*/);
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
const int64 index_id = 2002;
db_->CreateIndex(transaction_->id(),
store_id,
index_id,
ASCIIToUTF16("index"),
IndexedDBKeyPath(),
false /*unique*/,
false /*multi_entry*/);
EXPECT_EQ(
1ULL,
db_->metadata().object_stores.find(store_id)->second.indexes.size());
RunPostedTasks();
transaction_->Commit();
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
EXPECT_EQ(
1ULL,
db_->metadata().object_stores.find(store_id)->second.indexes.size());
}
class IndexedDBDatabaseOperationAbortTest
: public IndexedDBDatabaseOperationTest {
public:
IndexedDBDatabaseOperationAbortTest() { commit_success_ = false; }
};
TEST_F(IndexedDBDatabaseOperationAbortTest, CreateObjectStore) {
EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
const int64 store_id = 1001;
db_->CreateObjectStore(transaction_->id(),
store_id,
ASCIIToUTF16("store"),
IndexedDBKeyPath(),
false /*auto_increment*/);
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
RunPostedTasks();
transaction_->Commit();
EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
}
TEST_F(IndexedDBDatabaseOperationAbortTest, CreateIndex) {
EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
const int64 store_id = 1001;
db_->CreateObjectStore(transaction_->id(),
store_id,
ASCIIToUTF16("store"),
IndexedDBKeyPath(),
false /*auto_increment*/);
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
const int64 index_id = 2002;
db_->CreateIndex(transaction_->id(),
store_id,
index_id,
ASCIIToUTF16("index"),
IndexedDBKeyPath(),
false /*unique*/,
false /*multi_entry*/);
EXPECT_EQ(
1ULL,
db_->metadata().object_stores.find(store_id)->second.indexes.size());
RunPostedTasks();
transaction_->Commit();
EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
}
TEST_F(IndexedDBDatabaseOperationTest, CreatePutDelete) {
EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
const int64 store_id = 1001;
// Creation is synchronous.
db_->CreateObjectStore(transaction_->id(),
store_id,
ASCIIToUTF16("store"),
IndexedDBKeyPath(),
false /*auto_increment*/);
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
// Put is asynchronous
IndexedDBValue value("value1", std::vector<IndexedDBBlobInfo>());
ScopedVector<webkit_blob::BlobDataHandle> handles;
scoped_ptr<IndexedDBKey> key(new IndexedDBKey("key"));
std::vector<IndexedDBDatabase::IndexKeys> index_keys;
scoped_refptr<MockIndexedDBCallbacks> request(
new MockIndexedDBCallbacks(false));
db_->Put(transaction_->id(),
store_id,
&value,
&handles,
key.Pass(),
IndexedDBDatabase::ADD_ONLY,
request,
index_keys);
// Deletion is asynchronous.
db_->DeleteObjectStore(transaction_->id(),
store_id);
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
// This will execute the Put then Delete.
RunPostedTasks();
EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
}
} // namespace content } // namespace content
...@@ -67,21 +67,32 @@ leveldb::Status IndexedDBFakeBackingStore::CreateObjectStore( ...@@ -67,21 +67,32 @@ leveldb::Status IndexedDBFakeBackingStore::CreateObjectStore(
const base::string16& name, const base::string16& name,
const IndexedDBKeyPath&, const IndexedDBKeyPath&,
bool auto_increment) { bool auto_increment) {
return leveldb::Status::IOError("test error"); return leveldb::Status::OK();
}
leveldb::Status IndexedDBFakeBackingStore::PutRecord(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
const IndexedDBKey& key,
IndexedDBValue& value,
ScopedVector<webkit_blob::BlobDataHandle>* handles,
RecordIdentifier* record) {
return leveldb::Status::OK();
} }
leveldb::Status IndexedDBFakeBackingStore::ClearObjectStore( leveldb::Status IndexedDBFakeBackingStore::ClearObjectStore(
Transaction*, Transaction*,
int64 database_id, int64 database_id,
int64 object_store_id) { int64 object_store_id) {
return leveldb::Status::IOError("test error"); return leveldb::Status::OK();
} }
leveldb::Status IndexedDBFakeBackingStore::DeleteRecord( leveldb::Status IndexedDBFakeBackingStore::DeleteRecord(
Transaction*, Transaction*,
int64 database_id, int64 database_id,
int64 object_store_id, int64 object_store_id,
const RecordIdentifier&) { const RecordIdentifier&) {
return leveldb::Status::IOError("test error"); return leveldb::Status::OK();
} }
leveldb::Status IndexedDBFakeBackingStore::GetKeyGeneratorCurrentNumber( leveldb::Status IndexedDBFakeBackingStore::GetKeyGeneratorCurrentNumber(
Transaction*, Transaction*,
...@@ -117,14 +128,14 @@ leveldb::Status IndexedDBFakeBackingStore::CreateIndex( ...@@ -117,14 +128,14 @@ leveldb::Status IndexedDBFakeBackingStore::CreateIndex(
const IndexedDBKeyPath&, const IndexedDBKeyPath&,
bool is_unique, bool is_unique,
bool is_multi_entry) { bool is_multi_entry) {
return leveldb::Status::IOError("test error"); return leveldb::Status::OK();
} }
leveldb::Status IndexedDBFakeBackingStore::DeleteIndex(Transaction*, leveldb::Status IndexedDBFakeBackingStore::DeleteIndex(Transaction*,
int64 database_id, int64 database_id,
int64 object_store_id, int64 object_store_id,
int64 index_id) { int64 index_id) {
return leveldb::Status::IOError("test error"); return leveldb::Status::OK();
} }
leveldb::Status IndexedDBFakeBackingStore::PutIndexDataForRecord( leveldb::Status IndexedDBFakeBackingStore::PutIndexDataForRecord(
Transaction*, Transaction*,
...@@ -133,7 +144,7 @@ leveldb::Status IndexedDBFakeBackingStore::PutIndexDataForRecord( ...@@ -133,7 +144,7 @@ leveldb::Status IndexedDBFakeBackingStore::PutIndexDataForRecord(
int64 index_id, int64 index_id,
const IndexedDBKey&, const IndexedDBKey&,
const RecordIdentifier&) { const RecordIdentifier&) {
return leveldb::Status::IOError("test error"); return leveldb::Status::OK();
} }
void IndexedDBFakeBackingStore::ReportBlobUnused(int64 database_id, void IndexedDBFakeBackingStore::ReportBlobUnused(int64 database_id,
......
...@@ -44,6 +44,15 @@ class IndexedDBFakeBackingStore : public IndexedDBBackingStore { ...@@ -44,6 +44,15 @@ class IndexedDBFakeBackingStore : public IndexedDBBackingStore {
const IndexedDBKeyPath&, const IndexedDBKeyPath&,
bool auto_increment) OVERRIDE; bool auto_increment) OVERRIDE;
virtual leveldb::Status PutRecord(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
const IndexedDBKey& key,
IndexedDBValue& value,
ScopedVector<webkit_blob::BlobDataHandle>* handles,
RecordIdentifier* record) OVERRIDE;
virtual leveldb::Status ClearObjectStore(Transaction*, virtual leveldb::Status ClearObjectStore(Transaction*,
int64 database_id, int64 database_id,
int64 object_store_id) OVERRIDE; int64 object_store_id) OVERRIDE;
......
...@@ -86,18 +86,6 @@ IndexedDBTransaction::~IndexedDBTransaction() { ...@@ -86,18 +86,6 @@ IndexedDBTransaction::~IndexedDBTransaction() {
DCHECK(abort_task_stack_.empty()); DCHECK(abort_task_stack_.empty());
} }
void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) {
if (state_ == FINISHED)
return;
timeout_timer_.Stop();
used_ = true;
task_queue_.push(task);
++diagnostics_.tasks_scheduled;
abort_task_stack_.push(abort_task);
RunTasksIfStarted();
}
void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type,
Operation task) { Operation task) {
if (state_ == FINISHED) if (state_ == FINISHED)
...@@ -114,6 +102,12 @@ void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, ...@@ -114,6 +102,12 @@ void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type,
RunTasksIfStarted(); RunTasksIfStarted();
} }
void IndexedDBTransaction::ScheduleAbortTask(Operation abort_task) {
DCHECK_NE(FINISHED, state_);
DCHECK(used_);
abort_task_stack_.push(abort_task);
}
void IndexedDBTransaction::RunTasksIfStarted() { void IndexedDBTransaction::RunTasksIfStarted() {
DCHECK(used_); DCHECK(used_);
......
...@@ -49,8 +49,8 @@ class CONTENT_EXPORT IndexedDBTransaction ...@@ -49,8 +49,8 @@ class CONTENT_EXPORT IndexedDBTransaction
void ScheduleTask(Operation task) { void ScheduleTask(Operation task) {
ScheduleTask(IndexedDBDatabase::NORMAL_TASK, task); ScheduleTask(IndexedDBDatabase::NORMAL_TASK, task);
} }
void ScheduleTask(Operation task, Operation abort_task);
void ScheduleTask(IndexedDBDatabase::TaskType, Operation task); void ScheduleTask(IndexedDBDatabase::TaskType, Operation task);
void ScheduleAbortTask(Operation abort_task);
void RegisterOpenCursor(IndexedDBCursor* cursor); void RegisterOpenCursor(IndexedDBCursor* cursor);
void UnregisterOpenCursor(IndexedDBCursor* cursor); void UnregisterOpenCursor(IndexedDBCursor* cursor);
void AddPreemptiveEvent() { pending_preemptive_events_++; } void AddPreemptiveEvent() { pending_preemptive_events_++; }
......
...@@ -14,6 +14,21 @@ ...@@ -14,6 +14,21 @@
namespace content { namespace content {
class AbortObserver {
public:
AbortObserver() : abort_task_called_(false) {}
void AbortTask(IndexedDBTransaction* transaction) {
abort_task_called_ = true;
}
bool abort_task_called() const { return abort_task_called_; }
private:
bool abort_task_called_;
DISALLOW_COPY_AND_ASSIGN(AbortObserver);
};
class IndexedDBTransactionTest : public testing::Test { class IndexedDBTransactionTest : public testing::Test {
public: public:
IndexedDBTransactionTest() { IndexedDBTransactionTest() {
...@@ -37,6 +52,11 @@ class IndexedDBTransactionTest : public testing::Test { ...@@ -37,6 +52,11 @@ class IndexedDBTransactionTest : public testing::Test {
void RunPostedTasks() { message_loop_.RunUntilIdle(); } void RunPostedTasks() { message_loop_.RunUntilIdle(); }
void DummyOperation(IndexedDBTransaction* transaction) {} void DummyOperation(IndexedDBTransaction* transaction) {}
void AbortableOperation(AbortObserver* observer,
IndexedDBTransaction* transaction) {
transaction->ScheduleAbortTask(
base::Bind(&AbortObserver::AbortTask, base::Unretained(observer)));
}
protected: protected:
scoped_refptr<IndexedDBFakeBackingStore> backing_store_; scoped_refptr<IndexedDBFakeBackingStore> backing_store_;
...@@ -132,21 +152,6 @@ TEST_F(IndexedDBTransactionTest, NoTimeoutReadOnly) { ...@@ -132,21 +152,6 @@ TEST_F(IndexedDBTransactionTest, NoTimeoutReadOnly) {
EXPECT_FALSE(transaction->IsTimeoutTimerRunning()); EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
} }
class AbortObserver {
public:
AbortObserver() : abort_task_called_(false) {}
void AbortTask(IndexedDBTransaction* transaction) {
abort_task_called_ = true;
}
bool abort_task_called() const { return abort_task_called_; }
private:
bool abort_task_called_;
DISALLOW_COPY_AND_ASSIGN(AbortObserver);
};
TEST_P(IndexedDBTransactionTestMode, ScheduleNormalTask) { TEST_P(IndexedDBTransactionTestMode, ScheduleNormalTask) {
const int64 id = 0; const int64 id = 0;
const std::set<int64> scope; const std::set<int64> scope;
...@@ -285,9 +290,9 @@ TEST_P(IndexedDBTransactionTestMode, AbortTasks) { ...@@ -285,9 +290,9 @@ TEST_P(IndexedDBTransactionTestMode, AbortTasks) {
AbortObserver observer; AbortObserver observer;
transaction->ScheduleTask( transaction->ScheduleTask(
base::Bind(&IndexedDBTransactionTest::DummyOperation, base::Bind(&IndexedDBTransactionTest::AbortableOperation,
base::Unretained(this)), base::Unretained(this),
base::Bind(&AbortObserver::AbortTask, base::Unretained(&observer))); base::Unretained(&observer)));
// Pump the message loop so that the transaction completes all pending tasks, // Pump the message loop so that the transaction completes all pending tasks,
// otherwise it will defer the commit. // otherwise it will defer the commit.
......
...@@ -23,6 +23,8 @@ void MockIndexedDBCallbacks::OnSuccess(int64) {} ...@@ -23,6 +23,8 @@ void MockIndexedDBCallbacks::OnSuccess(int64) {}
void MockIndexedDBCallbacks::OnSuccess(const std::vector<base::string16>&) {} void MockIndexedDBCallbacks::OnSuccess(const std::vector<base::string16>&) {}
void MockIndexedDBCallbacks::OnSuccess(const IndexedDBKey& key) {}
void MockIndexedDBCallbacks::OnSuccess( void MockIndexedDBCallbacks::OnSuccess(
scoped_ptr<IndexedDBConnection> connection, scoped_ptr<IndexedDBConnection> connection,
const IndexedDBDatabaseMetadata& metadata) { const IndexedDBDatabaseMetadata& metadata) {
......
...@@ -18,6 +18,7 @@ class MockIndexedDBCallbacks : public IndexedDBCallbacks { ...@@ -18,6 +18,7 @@ class MockIndexedDBCallbacks : public IndexedDBCallbacks {
virtual void OnSuccess() OVERRIDE; virtual void OnSuccess() OVERRIDE;
virtual void OnSuccess(int64) OVERRIDE; virtual void OnSuccess(int64) OVERRIDE;
virtual void OnSuccess(const std::vector<base::string16>&) OVERRIDE; virtual void OnSuccess(const std::vector<base::string16>&) OVERRIDE;
virtual void OnSuccess(const IndexedDBKey& key) OVERRIDE;
virtual void OnSuccess(scoped_ptr<IndexedDBConnection> connection, virtual void OnSuccess(scoped_ptr<IndexedDBConnection> connection,
const IndexedDBDatabaseMetadata& metadata) OVERRIDE; const IndexedDBDatabaseMetadata& metadata) OVERRIDE;
IndexedDBConnection* connection() { return connection_.get(); } IndexedDBConnection* connection() { return connection_.get(); }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment