diff options
Diffstat (limited to 'Source/WebCore/storage')
63 files changed, 2059 insertions, 1350 deletions
diff --git a/Source/WebCore/storage/Database.cpp b/Source/WebCore/storage/Database.cpp index 75f616a..b072edb 100644 --- a/Source/WebCore/storage/Database.cpp +++ b/Source/WebCore/storage/Database.cpp @@ -258,8 +258,10 @@ void Database::changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) { - m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion))); + RefPtr<SQLTransaction> transaction = + SQLTransaction::create(this, callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion)); MutexLocker locker(m_transactionInProgressMutex); + m_transactionQueue.append(transaction.release()); if (!m_transactionInProgress) scheduleTransaction(); } @@ -277,8 +279,10 @@ void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, Pass void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback, bool readOnly) { - m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, 0, readOnly)); + RefPtr<SQLTransaction> transaction = + SQLTransaction::create(this, callback, errorCallback, successCallback, 0, readOnly); MutexLocker locker(m_transactionInProgressMutex); + m_transactionQueue.append(transaction.release()); if (!m_transactionInProgress) scheduleTransaction(); } diff --git a/Source/WebCore/storage/IDBAny.cpp b/Source/WebCore/storage/IDBAny.cpp index 3a049c0..1e076a9 100644 --- a/Source/WebCore/storage/IDBAny.cpp +++ b/Source/WebCore/storage/IDBAny.cpp @@ -28,7 +28,7 @@ #if ENABLE(INDEXED_DATABASE) -#include "IDBCursor.h" +#include "IDBCursorWithValue.h" #include "IDBDatabase.h" #include "IDBFactory.h" #include "IDBIndex.h" @@ -64,6 +64,13 @@ PassRefPtr<IDBCursor> IDBAny::idbCursor() return m_idbCursor; } + +PassRefPtr<IDBCursorWithValue> IDBAny::idbCursorWithValue() +{ + ASSERT(m_type == IDBCursorWithValueType); + return m_idbCursorWithValue; +} + PassRefPtr<IDBDatabase> IDBAny::idbDatabase() { ASSERT(m_type == IDBDatabaseType); @@ -112,6 +119,13 @@ void IDBAny::setNull() m_type = NullType; } +void IDBAny::set(PassRefPtr<IDBCursorWithValue> value) +{ + ASSERT(m_type == UndefinedType); + m_type = IDBCursorWithValueType; + m_idbCursorWithValue = value; +} + void IDBAny::set(PassRefPtr<IDBCursor> value) { ASSERT(m_type == UndefinedType); diff --git a/Source/WebCore/storage/IDBAny.h b/Source/WebCore/storage/IDBAny.h index 8e3f83e..e7e94f9 100644 --- a/Source/WebCore/storage/IDBAny.h +++ b/Source/WebCore/storage/IDBAny.h @@ -35,6 +35,7 @@ namespace WebCore { class IDBCursor; +class IDBCursorWithValue; class IDBDatabase; class IDBFactory; class IDBIndex; @@ -67,6 +68,7 @@ public: UndefinedType = 0, NullType, IDBCursorType, + IDBCursorWithValueType, IDBDatabaseType, IDBFactoryType, IDBIndexType, @@ -79,6 +81,7 @@ public: Type type() const { return m_type; } // Use type() to figure out which one of these you're allowed to call. PassRefPtr<IDBCursor> idbCursor(); + PassRefPtr<IDBCursorWithValue> idbCursorWithValue(); PassRefPtr<IDBDatabase> idbDatabase(); PassRefPtr<IDBFactory> idbFactory(); PassRefPtr<IDBIndex> idbIndex(); @@ -90,6 +93,7 @@ public: // Set can only be called once. void setNull(); void set(PassRefPtr<IDBCursor>); + void set(PassRefPtr<IDBCursorWithValue>); void set(PassRefPtr<IDBDatabase>); void set(PassRefPtr<IDBFactory>); void set(PassRefPtr<IDBIndex>); @@ -105,6 +109,7 @@ private: // Only one of the following should ever be in use at any given time. RefPtr<IDBCursor> m_idbCursor; + RefPtr<IDBCursorWithValue> m_idbCursorWithValue; RefPtr<IDBDatabase> m_idbDatabase; RefPtr<IDBFactory> m_idbFactory; RefPtr<IDBIndex> m_idbIndex; diff --git a/Source/WebCore/storage/IDBBackingStore.cpp b/Source/WebCore/storage/IDBBackingStore.cpp new file mode 100644 index 0000000..3859c4c --- /dev/null +++ b/Source/WebCore/storage/IDBBackingStore.cpp @@ -0,0 +1,996 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBBackingStore.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "FileSystem.h" +#include "IDBFactoryBackendImpl.h" +#include "IDBKey.h" +#include "IDBKeyRange.h" +#include "SQLiteDatabase.h" +#include "SQLiteStatement.h" +#include "SQLiteTransaction.h" +#include "SecurityOrigin.h" + +namespace WebCore { + +IDBBackingStore::IDBBackingStore(String identifier, IDBFactoryBackendImpl* factory) + : m_identifier(identifier) + , m_factory(factory) +{ + m_factory->addIDBBackingStore(identifier, this); +} + +IDBBackingStore::~IDBBackingStore() +{ + m_factory->removeIDBBackingStore(m_identifier); +} + +static bool runCommands(SQLiteDatabase& sqliteDatabase, const char** commands, size_t numberOfCommands) +{ + SQLiteTransaction transaction(sqliteDatabase, false); + transaction.begin(); + for (size_t i = 0; i < numberOfCommands; ++i) { + if (!sqliteDatabase.executeCommand(commands[i])) { + LOG_ERROR("Failed to run the following command for IndexedDB: %s", commands[i]); + return false; + } + } + transaction.commit(); + return true; +} + +static bool createTables(SQLiteDatabase& sqliteDatabase) +{ + if (sqliteDatabase.tableExists("Databases")) + return true; + static const char* commands[] = { + "CREATE TABLE Databases (id INTEGER PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL, version TEXT NOT NULL)", + "CREATE UNIQUE INDEX Databases_name ON Databases(name)", + + "CREATE TABLE ObjectStores (id INTEGER PRIMARY KEY, name TEXT NOT NULL, keyPath TEXT, doAutoIncrement INTEGER NOT NULL, databaseId INTEGER NOT NULL REFERENCES Databases(id))", + "CREATE UNIQUE INDEX ObjectStores_composit ON ObjectStores(databaseId, name)", + + "CREATE TABLE Indexes (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), name TEXT NOT NULL, keyPath TEXT, isUnique INTEGER NOT NULL)", + "CREATE UNIQUE INDEX Indexes_composit ON Indexes(objectStoreId, name)", + + "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, value TEXT NOT NULL)", + "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)", + + "CREATE TABLE IndexData (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))", + "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)", + "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId)", + "CREATE INDEX IndexData_indexId ON IndexData(indexId)", + }; + + return runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0])); +} + +static bool createMetaDataTable(SQLiteDatabase& sqliteDatabase) +{ + static const char* commands[] = { + "CREATE TABLE MetaData (name TEXT PRIMARY KEY, value NONE)", + "INSERT INTO MetaData VALUES ('version', 1)", + }; + + return runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0])); +} + +static bool getDatabaseSchemaVersion(SQLiteDatabase& sqliteDatabase, int* databaseVersion) +{ + SQLiteStatement query(sqliteDatabase, "SELECT value FROM MetaData WHERE name = 'version'"); + if (query.prepare() != SQLResultOk || query.step() != SQLResultRow) + return false; + + *databaseVersion = query.getColumnInt(0); + return query.finalize() == SQLResultOk; +} + +static bool migrateDatabase(SQLiteDatabase& sqliteDatabase) +{ + if (!sqliteDatabase.tableExists("MetaData")) { + if (!createMetaDataTable(sqliteDatabase)) + return false; + } + + int databaseVersion; + if (!getDatabaseSchemaVersion(sqliteDatabase, &databaseVersion)) + return false; + + if (databaseVersion == 1) { + static const char* commands[] = { + "DROP TABLE IF EXISTS ObjectStoreData2", + "CREATE TABLE ObjectStoreData2 (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, keyNumber REAL, value TEXT NOT NULL)", + "INSERT INTO ObjectStoreData2 SELECT * FROM ObjectStoreData", + "DROP TABLE ObjectStoreData", // This depends on SQLite not enforcing referential consistency. + "ALTER TABLE ObjectStoreData2 RENAME TO ObjectStoreData", + "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)", + "DROP TABLE IF EXISTS IndexData2", // This depends on SQLite not enforcing referential consistency. + "CREATE TABLE IndexData2 (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate REAL, keyNumber REAL, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))", + "INSERT INTO IndexData2 SELECT * FROM IndexData", + "DROP TABLE IndexData", + "ALTER TABLE IndexData2 RENAME TO IndexData", + "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)", + "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId)", + "CREATE INDEX IndexData_indexId ON IndexData(indexId)", + "UPDATE MetaData SET value = 2 WHERE name = 'version'", + }; + + if (!runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0]))) + return false; + + databaseVersion = 2; + } + + if (databaseVersion == 2) { + // We need to make the ObjectStoreData.value be a BLOB instead of TEXT. + static const char* commands[] = { + "DROP TABLE IF EXISTS ObjectStoreData", // This drops associated indices. + "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, keyNumber REAL, value BLOB NOT NULL)", + "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)", + "UPDATE MetaData SET value = 3 WHERE name = 'version'", + }; + + if (!runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0]))) + return false; + + databaseVersion = 3; + } + + return true; +} + +PassRefPtr<IDBBackingStore> IDBBackingStore::open(SecurityOrigin* securityOrigin, const String& pathBase, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl* factory) +{ + RefPtr<IDBBackingStore> backingStore(adoptRef(new IDBBackingStore(fileIdentifier, factory))); + + String path = ":memory:"; + if (!pathBase.isEmpty()) { + if (!makeAllDirectories(pathBase)) { + // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. + LOG_ERROR("Unabled to create LocalStorage database path %s", pathBase.utf8().data()); + return 0; + } + path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb"); + } + + if (!backingStore->m_db.open(path)) { + // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. + LOG_ERROR("Failed to open database file %s for IndexedDB", path.utf8().data()); + return 0; + } + + // FIXME: Error checking? + backingStore->m_db.setMaximumSize(maximumSize); + backingStore->m_db.turnOnIncrementalAutoVacuum(); + + if (!createTables(backingStore->m_db)) + return 0; + if (!migrateDatabase(backingStore->m_db)) + return 0; + + return backingStore.release(); +} + +bool IDBBackingStore::extractIDBDatabaseMetaData(const String& name, String& foundVersion, int64_t& foundId) +{ + SQLiteStatement databaseQuery(m_db, "SELECT id, version FROM Databases WHERE name = ?"); + if (databaseQuery.prepare() != SQLResultOk) { + ASSERT_NOT_REACHED(); + return false; + } + databaseQuery.bindText(1, name); + if (databaseQuery.step() != SQLResultRow) + return false; + + foundId = databaseQuery.getColumnInt64(0); + foundVersion = databaseQuery.getColumnText(1); + + if (databaseQuery.step() == SQLResultRow) + ASSERT_NOT_REACHED(); + return true; +} + +bool IDBBackingStore::setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId) +{ + ASSERT(!name.isNull()); + ASSERT(!version.isNull()); + + String sql = invalidRowId ? "INSERT INTO Databases (name, description, version) VALUES (?, '', ?)" : "UPDATE Databases SET name = ?, version = ? WHERE id = ?"; + SQLiteStatement query(m_db, sql); + if (query.prepare() != SQLResultOk) { + ASSERT_NOT_REACHED(); + return false; + } + + query.bindText(1, name); + query.bindText(2, version); + if (!invalidRowId) + query.bindInt64(3, rowId); + + if (query.step() != SQLResultDone) + return false; + + if (invalidRowId) + rowId = m_db.lastInsertRowID(); + + return true; +} + +void IDBBackingStore::getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags) +{ + SQLiteStatement query(m_db, "SELECT id, name, keyPath, doAutoIncrement FROM ObjectStores WHERE databaseId = ?"); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + ASSERT(foundIds.isEmpty()); + ASSERT(foundNames.isEmpty()); + ASSERT(foundKeyPaths.isEmpty()); + ASSERT(foundAutoIncrementFlags.isEmpty()); + + query.bindInt64(1, databaseId); + + while (query.step() == SQLResultRow) { + foundIds.append(query.getColumnInt64(0)); + foundNames.append(query.getColumnText(1)); + foundKeyPaths.append(query.getColumnText(2)); + foundAutoIncrementFlags.append(!!query.getColumnInt(3)); + } +} + +bool IDBBackingStore::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, int64_t databaseId, int64_t& assignedObjectStoreId) +{ + SQLiteStatement query(m_db, "INSERT INTO ObjectStores (name, keyPath, doAutoIncrement, databaseId) VALUES (?, ?, ?, ?)"); + if (query.prepare() != SQLResultOk) + return false; + + query.bindText(1, name); + query.bindText(2, keyPath); + query.bindInt(3, static_cast<int>(autoIncrement)); + query.bindInt64(4, databaseId); + + if (query.step() != SQLResultDone) + return false; + + assignedObjectStoreId = m_db.lastInsertRowID(); + return true; +} + +static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id) +{ + SQLiteStatement deleteQuery(db, sql); + bool ok = deleteQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + deleteQuery.bindInt64(1, id); + ok = deleteQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. +} + +void IDBBackingStore::deleteObjectStore(int64_t objectStoreId) +{ + doDelete(m_db, "DELETE FROM ObjectStores WHERE id = ?", objectStoreId); + doDelete(m_db, "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStoreId); + doDelete(m_db, "DELETE FROM IndexData WHERE indexId IN (SELECT id FROM Indexes WHERE objectStoreId = ?)", objectStoreId); + doDelete(m_db, "DELETE FROM Indexes WHERE objectStoreId = ?", objectStoreId); +} + +static String whereSyntaxForKey(const IDBKey& key, String qualifiedTableName = "") +{ + switch (key.type()) { + case IDBKey::StringType: + return qualifiedTableName + "keyString = ? AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL "; + case IDBKey::NumberType: + return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber = ? "; + case IDBKey::DateType: + return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate = ? AND " + qualifiedTableName + "keyNumber IS NULL "; + case IDBKey::NullType: + return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL "; + } + + ASSERT_NOT_REACHED(); + return ""; +} + +// Returns the number of items bound. +static int bindKeyToQuery(SQLiteStatement& query, int column, const IDBKey& key) +{ + switch (key.type()) { + case IDBKey::StringType: + query.bindText(column, key.string()); + return 1; + case IDBKey::DateType: + query.bindDouble(column, key.date()); + return 1; + case IDBKey::NumberType: + query.bindDouble(column, key.number()); + return 1; + case IDBKey::NullType: + return 0; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +static String lowerCursorWhereFragment(const IDBKey& key, String comparisonOperator, String qualifiedTableName = "") +{ + switch (key.type()) { + case IDBKey::StringType: + return "? " + comparisonOperator + " " + qualifiedTableName + "keyString AND "; + case IDBKey::DateType: + return "(? " + comparisonOperator + " " + qualifiedTableName + "keyDate OR NOT " + qualifiedTableName + "keyString IS NULL) AND "; + case IDBKey::NumberType: + return "(? " + comparisonOperator + " " + qualifiedTableName + "keyNumber OR NOT " + qualifiedTableName + "keyString IS NULL OR NOT " + qualifiedTableName + "keyDate IS NULL) AND "; + case IDBKey::NullType: + if (comparisonOperator == "<") + return "NOT(" + qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL) AND "; + return ""; // If it's =, the upper bound half will do the constraining. If it's <=, then that's a no-op. + } + ASSERT_NOT_REACHED(); + return ""; +} + +static String upperCursorWhereFragment(const IDBKey& key, String comparisonOperator, String qualifiedTableName = "") +{ + switch (key.type()) { + case IDBKey::StringType: + return "(" + qualifiedTableName + "keyString " + comparisonOperator + " ? OR " + qualifiedTableName + "keyString IS NULL) AND "; + case IDBKey::DateType: + return "(" + qualifiedTableName + "keyDate " + comparisonOperator + " ? OR " + qualifiedTableName + "keyDate IS NULL) AND " + qualifiedTableName + "keyString IS NULL AND "; + case IDBKey::NumberType: + return "(" + qualifiedTableName + "keyNumber " + comparisonOperator + " ? OR " + qualifiedTableName + "keyNumber IS NULL) AND " + qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND "; + case IDBKey::NullType: + if (comparisonOperator == "<") + return "0 != 0 AND "; + return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL AND "; + } + ASSERT_NOT_REACHED(); + return ""; +} + +String IDBBackingStore::getObjectStoreRecord(int64_t objectStoreId, const IDBKey& key) +{ + SQLiteStatement query(m_db, "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE objectStoreId = ? AND " + whereSyntaxForKey(key)); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + query.bindInt64(1, objectStoreId); + bindKeyToQuery(query, 2, key); + if (query.step() != SQLResultRow) + return String(); // Null String means record not found. + + ASSERT((key.type() == IDBKey::StringType) != query.isColumnNull(0)); + ASSERT((key.type() == IDBKey::DateType) != query.isColumnNull(1)); + ASSERT((key.type() == IDBKey::NumberType) != query.isColumnNull(2)); + + String record = query.getColumnBlobAsString(3); + ASSERT(query.step() != SQLResultRow); + + return record; +} + +static void bindKeyToQueryWithNulls(SQLiteStatement& query, int baseColumn, const IDBKey& key) +{ + switch (key.type()) { + case IDBKey::StringType: + query.bindText(baseColumn + 0, key.string()); + query.bindNull(baseColumn + 1); + query.bindNull(baseColumn + 2); + break; + case IDBKey::DateType: + query.bindNull(baseColumn + 0); + query.bindDouble(baseColumn + 1, key.date()); + query.bindNull(baseColumn + 2); + break; + case IDBKey::NumberType: + query.bindNull(baseColumn + 0); + query.bindNull(baseColumn + 1); + query.bindDouble(baseColumn + 2, key.number()); + break; + case IDBKey::NullType: + query.bindNull(baseColumn + 0); + query.bindNull(baseColumn + 1); + query.bindNull(baseColumn + 2); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +bool IDBBackingStore::putObjectStoreRecord(int64_t objectStoreId, const IDBKey& key, const String& value, int64_t& rowId, bool invalidRowId) +{ + String sql = !invalidRowId ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?" + : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)"; + SQLiteStatement query(m_db, sql); + if (query.prepare() != SQLResultOk) + return false; + + bindKeyToQueryWithNulls(query, 1, key); + query.bindBlob(4, value); + if (!invalidRowId) + query.bindInt64(5, rowId); + else + query.bindInt64(5, objectStoreId); + + if (query.step() != SQLResultDone) + return false; + + if (invalidRowId) + rowId = m_db.lastInsertRowID(); + + return true; +} + +void IDBBackingStore::clearObjectStore(int64_t objectStoreId) +{ + doDelete(m_db, "DELETE FROM IndexData WHERE objectStoreDataId IN (SELECT id FROM ObjectStoreData WHERE objectStoreId = ?)", objectStoreId); + doDelete(m_db, "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStoreId); +} + +void IDBBackingStore::deleteObjectStoreRecord(int64_t, int64_t objectStoreDataId) +{ + SQLiteStatement osQuery(m_db, "DELETE FROM ObjectStoreData WHERE id = ?"); + bool ok = osQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + osQuery.bindInt64(1, objectStoreDataId); + + ok = osQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); + + SQLiteStatement indexQuery(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ?"); + ok = indexQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + indexQuery.bindInt64(1, objectStoreDataId); + + ok = indexQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); +} + +double IDBBackingStore::nextAutoIncrementNumber(int64_t objectStoreId) +{ + SQLiteStatement query(m_db, "SELECT max(keyNumber) + 1 FROM ObjectStoreData WHERE objectStoreId = ? AND keyString IS NULL AND keyDate IS NULL"); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); + + query.bindInt64(1, objectStoreId); + + if (query.step() != SQLResultRow || query.isColumnNull(0)) + return 1; + + return query.getColumnDouble(0); +} + +bool IDBBackingStore::keyExistsInObjectStore(int64_t objectStoreId, const IDBKey& key, int64_t& foundObjectStoreDataId) +{ + String sql = String("SELECT id FROM ObjectStoreData WHERE objectStoreId = ? AND ") + whereSyntaxForKey(key); + SQLiteStatement query(m_db, sql); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + query.bindInt64(1, objectStoreId); + bindKeyToQuery(query, 2, key); + + if (query.step() != SQLResultRow) + return false; + + foundObjectStoreDataId = query.getColumnInt64(0); + return true; +} + +bool IDBBackingStore::forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback& callback) +{ + SQLiteStatement query(m_db, "SELECT id, value FROM ObjectStoreData WHERE objectStoreId = ?"); + if (query.prepare() != SQLResultOk) + return false; + + query.bindInt64(1, objectStoreId); + + while (query.step() == SQLResultRow) { + int64_t objectStoreDataId = query.getColumnInt64(0); + String value = query.getColumnBlobAsString(1); + if (!callback.callback(objectStoreDataId, value)) + return false; + } + + return true; +} + +void IDBBackingStore::getIndexes(int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags) +{ + SQLiteStatement query(m_db, "SELECT id, name, keyPath, isUnique FROM Indexes WHERE objectStoreId = ?"); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + ASSERT(foundIds.isEmpty()); + ASSERT(foundNames.isEmpty()); + ASSERT(foundKeyPaths.isEmpty()); + ASSERT(foundUniqueFlags.isEmpty()); + + query.bindInt64(1, objectStoreId); + + while (query.step() == SQLResultRow) { + foundIds.append(query.getColumnInt64(0)); + foundNames.append(query.getColumnText(1)); + foundKeyPaths.append(query.getColumnText(2)); + foundUniqueFlags.append(!!query.getColumnInt(3)); + } +} + +bool IDBBackingStore::createIndex(int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId) +{ + SQLiteStatement query(m_db, "INSERT INTO Indexes (objectStoreId, name, keyPath, isUnique) VALUES (?, ?, ?, ?)"); + if (query.prepare() != SQLResultOk) + return false; + + query.bindInt64(1, objectStoreId); + query.bindText(2, name); + query.bindText(3, keyPath); + query.bindInt(4, static_cast<int>(isUnique)); + + if (query.step() != SQLResultDone) + return false; + + indexId = m_db.lastInsertRowID(); + return true; +} + +void IDBBackingStore::deleteIndex(int64_t indexId) +{ + doDelete(m_db, "DELETE FROM Indexes WHERE id = ?", indexId); + doDelete(m_db, "DELETE FROM IndexData WHERE indexId = ?", indexId); +} + +bool IDBBackingStore::putIndexDataForRecord(int64_t indexId, const IDBKey& key, int64_t objectStoreDataId) +{ + SQLiteStatement query(m_db, "INSERT INTO IndexData (keyString, keyDate, keyNumber, indexId, objectStoreDataId) VALUES (?, ?, ?, ?, ?)"); + if (query.prepare() != SQLResultOk) + return false; + + bindKeyToQueryWithNulls(query, 1, key); + query.bindInt64(4, indexId); + query.bindInt64(5, objectStoreDataId); + + return query.step() == SQLResultDone; +} + +bool IDBBackingStore::deleteIndexDataForRecord(int64_t objectStoreDataId) +{ + SQLiteStatement query(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ?"); + if (query.prepare() != SQLResultOk) + return false; + + query.bindInt64(1, objectStoreDataId); + return query.step() == SQLResultDone; +} + +String IDBBackingStore::getObjectViaIndex(int64_t indexId, const IDBKey& key) +{ + String sql = String("SELECT ") + + "ObjectStoreData.value " + + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id " + + "WHERE IndexData.indexId = ? AND " + whereSyntaxForKey(key, "IndexData.") + + "ORDER BY IndexData.id LIMIT 1"; // Order by insertion order when all else fails. + SQLiteStatement query(m_db, sql); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + query.bindInt64(1, indexId); + bindKeyToQuery(query, 2, key); + + if (query.step() != SQLResultRow) + return String(); + + String foundValue = query.getColumnBlobAsString(0); + ASSERT(query.step() != SQLResultRow); + return foundValue; +} + +static PassRefPtr<IDBKey> keyFromQuery(SQLiteStatement& query, int baseColumn) +{ + if (query.columnCount() <= baseColumn) + return 0; + + if (!query.isColumnNull(baseColumn)) + return IDBKey::createString(query.getColumnText(baseColumn)); + + if (!query.isColumnNull(baseColumn + 1)) + return IDBKey::createDate(query.getColumnDouble(baseColumn + 1)); + + if (!query.isColumnNull(baseColumn + 2)) + return IDBKey::createNumber(query.getColumnDouble(baseColumn + 2)); + + return IDBKey::createNull(); +} + +PassRefPtr<IDBKey> IDBBackingStore::getPrimaryKeyViaIndex(int64_t indexId, const IDBKey& key) +{ + String sql = String("SELECT ") + + "ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber " + + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id " + + "WHERE IndexData.indexId = ? AND " + whereSyntaxForKey(key, "IndexData.") + + "ORDER BY IndexData.id LIMIT 1"; // Order by insertion order when all else fails. + SQLiteStatement query(m_db, sql); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + query.bindInt64(1, indexId); + bindKeyToQuery(query, 2, key); + + if (query.step() != SQLResultRow) + return 0; + + RefPtr<IDBKey> foundKey = keyFromQuery(query, 0); + ASSERT(query.step() != SQLResultRow); + return foundKey.release(); +} + +bool IDBBackingStore::keyExistsInIndex(int64_t indexId, const IDBKey& key) +{ + String sql = String("SELECT id FROM IndexData WHERE indexId = ? AND ") + whereSyntaxForKey(key); + SQLiteStatement query(m_db, sql); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + query.bindInt64(1, indexId); + bindKeyToQuery(query, 2, key); + + return query.step() == SQLResultRow; +} + +namespace { + +class CursorImplCommon : public IDBBackingStore::Cursor { +public: + CursorImplCommon(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint, bool iterateForward) + : m_query(sqliteDatabase, query) + , m_db(sqliteDatabase) + , m_uniquenessConstraint(uniquenessConstraint) + , m_iterateForward(iterateForward) + { + } + virtual ~CursorImplCommon() {} + + // IDBBackingStore::Cursor + virtual bool continueFunction(const IDBKey*); + virtual PassRefPtr<IDBKey> key() { return m_currentKey; } + virtual PassRefPtr<IDBKey> primaryKey() { return m_currentKey; } + virtual String value() = 0; + virtual int64_t objectStoreDataId() = 0; + virtual int64_t indexDataId() = 0; + + virtual void loadCurrentRow() = 0; + virtual bool currentRowExists() = 0; + + SQLiteStatement m_query; + +protected: + SQLiteDatabase& m_db; + bool m_uniquenessConstraint; + bool m_iterateForward; + int64_t m_currentId; + RefPtr<IDBKey> m_currentKey; +}; + +bool CursorImplCommon::continueFunction(const IDBKey* key) +{ + while (true) { + if (m_query.step() != SQLResultRow) + return false; + + RefPtr<IDBKey> oldKey = m_currentKey; + loadCurrentRow(); + + // Skip if this entry has been deleted from the object store. + if (!currentRowExists()) + continue; + + // If a key was supplied, we must loop until we find a key greater than or equal to it (or hit the end). + if (key) { + if (m_iterateForward) { + if (m_currentKey->isLessThan(key)) + continue; + } else { + if (key->isLessThan(m_currentKey.get())) + continue; + } + } + + // If we don't have a uniqueness constraint, we can stop now. + if (!m_uniquenessConstraint) + break; + if (!m_currentKey->isEqual(oldKey.get())) + break; + } + + return true; +} + +class ObjectStoreCursorImpl : public CursorImplCommon { +public: + ObjectStoreCursorImpl(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint, bool iterateForward) + : CursorImplCommon(sqliteDatabase, query, uniquenessConstraint, iterateForward) + { + } + + // CursorImplCommon. + virtual String value() { return m_currentValue; } + virtual int64_t objectStoreDataId() { return m_currentId; } + virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; } + virtual void loadCurrentRow(); + virtual bool currentRowExists(); + +private: + String m_currentValue; +}; + +void ObjectStoreCursorImpl::loadCurrentRow() +{ + m_currentId = m_query.getColumnInt64(0); + m_currentKey = keyFromQuery(m_query, 1); + m_currentValue = m_query.getColumnBlobAsString(4); +} + +bool ObjectStoreCursorImpl::currentRowExists() +{ + String sql = "SELECT id FROM ObjectStoreData WHERE id = ?"; + SQLiteStatement statement(m_db, sql); + + bool ok = statement.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); + + statement.bindInt64(1, m_currentId); + return statement.step() == SQLResultRow; +} + +class IndexKeyCursorImpl : public CursorImplCommon { +public: + IndexKeyCursorImpl(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint, bool iterateForward) + : CursorImplCommon(sqliteDatabase, query, uniquenessConstraint, iterateForward) + { + } + + // CursorImplCommon + virtual PassRefPtr<IDBKey> primaryKey() { return m_currentPrimaryKey; } + virtual String value() { ASSERT_NOT_REACHED(); return String(); } + virtual int64_t objectStoreDataId() { ASSERT_NOT_REACHED(); return 0; } + virtual int64_t indexDataId() { return m_currentId; } + virtual void loadCurrentRow(); + virtual bool currentRowExists(); + +private: + RefPtr<IDBKey> m_currentPrimaryKey; +}; + +void IndexKeyCursorImpl::loadCurrentRow() +{ + m_currentId = m_query.getColumnInt64(0); + m_currentKey = keyFromQuery(m_query, 1); + m_currentPrimaryKey = keyFromQuery(m_query, 4); +} + +bool IndexKeyCursorImpl::currentRowExists() +{ + String sql = "SELECT id FROM IndexData WHERE id = ?"; + SQLiteStatement statement(m_db, sql); + + bool ok = statement.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); + + statement.bindInt64(1, m_currentId); + return statement.step() == SQLResultRow; +} + +class IndexCursorImpl : public CursorImplCommon { +public: + IndexCursorImpl(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint, bool iterateForward) + : CursorImplCommon(sqliteDatabase, query, uniquenessConstraint, iterateForward) + { + } + + // CursorImplCommon + virtual PassRefPtr<IDBKey> primaryKey() { return m_currentPrimaryKey; } + virtual String value() { return m_currentValue; } + virtual int64_t objectStoreDataId() { ASSERT_NOT_REACHED(); return 0; } + virtual int64_t indexDataId() { return m_currentId; } + virtual void loadCurrentRow(); + virtual bool currentRowExists(); + +private: + RefPtr<IDBKey> m_currentPrimaryKey; + String m_currentValue; +}; + +void IndexCursorImpl::loadCurrentRow() +{ + m_currentId = m_query.getColumnInt64(0); + m_currentKey = keyFromQuery(m_query, 1); + m_currentValue = m_query.getColumnBlobAsString(4); + m_currentPrimaryKey = keyFromQuery(m_query, 5); +} + +bool IndexCursorImpl::currentRowExists() +{ + String sql = "SELECT id FROM IndexData WHERE id = ?"; + SQLiteStatement statement(m_db, sql); + + bool ok = statement.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); + + statement.bindInt64(1, m_currentId); + return statement.step() == SQLResultRow; +} + +} // namespace + +PassRefPtr<IDBBackingStore::Cursor> IDBBackingStore::openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange* range, IDBCursor::Direction direction) +{ + bool lowerBound = range && range->lower(); + bool upperBound = range && range->upper(); + + String sql = "SELECT id, keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE "; + if (lowerBound) + sql += lowerCursorWhereFragment(*range->lower(), range->lowerOpen() ? "<" : "<="); + if (upperBound) + sql += upperCursorWhereFragment(*range->upper(), range->upperOpen() ? "<" : "<="); + sql += "objectStoreId = ? ORDER BY "; + + if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE) + sql += "keyString, keyDate, keyNumber"; + else + sql += "keyString DESC, keyDate DESC, keyNumber DESC"; + + RefPtr<ObjectStoreCursorImpl> cursor = adoptRef(new ObjectStoreCursorImpl(m_db, sql, direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::PREV_NO_DUPLICATE, + direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT)); + + bool ok = cursor->m_query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + int currentColumn = 1; + if (lowerBound) + currentColumn += bindKeyToQuery(cursor->m_query, currentColumn, *range->lower()); + if (upperBound) + currentColumn += bindKeyToQuery(cursor->m_query, currentColumn, *range->upper()); + cursor->m_query.bindInt64(currentColumn, objectStoreId); + + if (cursor->m_query.step() != SQLResultRow) + return 0; + + cursor->loadCurrentRow(); + return cursor.release(); +} + +PassRefPtr<IDBBackingStore::Cursor> IDBBackingStore::openIndexKeyCursor(int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction) +{ + String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ") + + ("ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ") + + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id WHERE "; + + bool lowerBound = range && range->lower(); + bool upperBound = range && range->upper(); + + if (lowerBound) + sql += lowerCursorWhereFragment(*range->lower(), range->lowerOpen() ? "<" : "<=", "IndexData."); + if (upperBound) + sql += upperCursorWhereFragment(*range->upper(), range->upperOpen() ? "<" : "<=", "IndexData."); + sql += "IndexData.indexId = ? ORDER BY "; + + if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE) + sql += "IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, IndexData.id"; + else + sql += "IndexData.keyString DESC, IndexData.keyDate DESC, IndexData.keyNumber DESC, IndexData.id DESC"; + + RefPtr<IndexKeyCursorImpl> cursor = adoptRef(new IndexKeyCursorImpl(m_db, sql, direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::PREV_NO_DUPLICATE, + direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT)); + + bool ok = cursor->m_query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + int indexColumn = 1; + if (lowerBound) + indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->lower()); + if (upperBound) + indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->upper()); + cursor->m_query.bindInt64(indexColumn, indexId); + + if (cursor->m_query.step() != SQLResultRow) + return 0; + + cursor->loadCurrentRow(); + return cursor.release(); +} + +PassRefPtr<IDBBackingStore::Cursor> IDBBackingStore::openIndexCursor(int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction) +{ + String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ") + + ("ObjectStoreData.value, ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ") + + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id WHERE "; + + bool lowerBound = range && range->lower(); + bool upperBound = range && range->upper(); + + if (lowerBound) + sql += lowerCursorWhereFragment(*range->lower(), range->lowerOpen() ? "<" : "<=", "IndexData."); + if (upperBound) + sql += upperCursorWhereFragment(*range->upper(), range->upperOpen() ? "<" : "<=", "IndexData."); + sql += "IndexData.indexId = ? ORDER BY "; + + if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE) + sql += "IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, IndexData.id"; + else + sql += "IndexData.keyString DESC, IndexData.keyDate DESC, IndexData.keyNumber DESC, IndexData.id DESC"; + + RefPtr<IndexCursorImpl> cursor = adoptRef(new IndexCursorImpl(m_db, sql, direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::PREV_NO_DUPLICATE, + direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT)); + + bool ok = cursor->m_query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + int indexColumn = 1; + if (lowerBound) + indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->lower()); + if (upperBound) + indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->upper()); + cursor->m_query.bindInt64(indexColumn, indexId); + + if (cursor->m_query.step() != SQLResultRow) + return 0; + + cursor->loadCurrentRow(); + return cursor.release(); +} + +namespace { + +class TransactionImpl : public IDBBackingStore::Transaction { +public: + TransactionImpl(SQLiteDatabase& db) + : m_transaction(db) + { + } + + // IDBBackingStore::Transaction + virtual void begin() { m_transaction.begin(); } + virtual void commit() { m_transaction.commit(); } + virtual void rollback() { m_transaction.rollback(); } + +private: + SQLiteTransaction m_transaction; +}; + +} // namespace + +PassRefPtr<IDBBackingStore::Transaction> IDBBackingStore::createTransaction() +{ + return adoptRef(new TransactionImpl(m_db)); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBBackingStore.h b/Source/WebCore/storage/IDBBackingStore.h new file mode 100644 index 0000000..e75fee1 --- /dev/null +++ b/Source/WebCore/storage/IDBBackingStore.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef IDBBackingStore_h +#define IDBBackingStore_h + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursor.h" +#include "SQLiteDatabase.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class IDBFactoryBackendImpl; +class IDBKey; +class IDBKeyRange; +class SecurityOrigin; + +class IDBBackingStore : public RefCounted<IDBBackingStore> { +public: + static PassRefPtr<IDBBackingStore> open(SecurityOrigin*, const String& pathBase, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl*); + ~IDBBackingStore(); + + bool extractIDBDatabaseMetaData(const String& name, String& foundVersion, int64_t& foundId); + bool setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId); + + void getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags); + bool createObjectStore(const String& name, const String& keyPath, bool autoIncrement, int64_t databaseId, int64_t& assignedObjectStoreId); + void deleteObjectStore(int64_t objectStoreId); + String getObjectStoreRecord(int64_t objectStoreId, const IDBKey&); + bool putObjectStoreRecord(int64_t objectStoreId, const IDBKey&, const String& value, int64_t& rowId, bool invalidRowId); + void clearObjectStore(int64_t objectStoreId); + void deleteObjectStoreRecord(int64_t objectStoreId, int64_t objectStoreDataId); + double nextAutoIncrementNumber(int64_t objectStoreId); + bool keyExistsInObjectStore(int64_t objectStoreId, const IDBKey&, int64_t& foundObjectStoreDataId); + + class ObjectStoreRecordCallback { + public: + virtual bool callback(int64_t objectStoreDataId, const String& value) = 0; + virtual ~ObjectStoreRecordCallback() {}; + }; + bool forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback&); + + void getIndexes(int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags); + bool createIndex(int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId); + void deleteIndex(int64_t indexId); + bool putIndexDataForRecord(int64_t indexId, const IDBKey&, int64_t objectStoreDataId); + bool deleteIndexDataForRecord(int64_t objectStoreDataId); + String getObjectViaIndex(int64_t indexId, const IDBKey&); + PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t indexId, const IDBKey&); + bool keyExistsInIndex(int64_t indexId, const IDBKey&); + + class Cursor : public RefCounted<Cursor> { + public: + virtual bool continueFunction(const IDBKey* = 0) = 0; + virtual PassRefPtr<IDBKey> key() = 0; + virtual PassRefPtr<IDBKey> primaryKey() = 0; + virtual String value() = 0; + virtual int64_t objectStoreDataId() = 0; + virtual int64_t indexDataId() = 0; + virtual ~Cursor() {}; + }; + + PassRefPtr<Cursor> openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction); + PassRefPtr<Cursor> openIndexKeyCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction); + PassRefPtr<Cursor> openIndexCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction); + + class Transaction : public RefCounted<Transaction> { + public: + virtual void begin() = 0; + virtual void commit() = 0; + virtual void rollback() = 0; + }; + PassRefPtr<Transaction> createTransaction(); + +private: + IDBBackingStore(String identifier, IDBFactoryBackendImpl* factory); + + SQLiteDatabase m_db; + String m_identifier; + RefPtr<IDBFactoryBackendImpl> m_factory; +}; + +} // namespace WebCore + +#endif + +#endif // IDBBackingStore_h diff --git a/Source/WebCore/storage/IDBCallbacks.h b/Source/WebCore/storage/IDBCallbacks.h index 29fb1c4..f4e1b98 100644 --- a/Source/WebCore/storage/IDBCallbacks.h +++ b/Source/WebCore/storage/IDBCallbacks.h @@ -53,9 +53,9 @@ public: virtual void onSuccess(PassRefPtr<IDBDatabaseBackendInterface>) = 0; virtual void onSuccess(PassRefPtr<IDBIndexBackendInterface>) = 0; virtual void onSuccess(PassRefPtr<IDBKey>) = 0; - virtual void onSuccess(PassRefPtr<IDBObjectStoreBackendInterface>) = 0; virtual void onSuccess(PassRefPtr<IDBTransactionBackendInterface>) = 0; virtual void onSuccess(PassRefPtr<SerializedScriptValue>) = 0; + virtual void onBlocked() = 0; }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBCompleteEvent.h b/Source/WebCore/storage/IDBCompleteEvent.h deleted file mode 100644 index c004a72..0000000 --- a/Source/WebCore/storage/IDBCompleteEvent.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBCompleteEvent_h -#define IDBCompleteEvent_h - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBEvent.h" -#include "PlatformString.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> - -namespace WebCore { - -class IDBCompleteEvent : public IDBEvent { -public: - static PassRefPtr<IDBCompleteEvent> create(PassRefPtr<IDBAny> source); - // FIXME: Need to allow creation of these events from JS. - virtual ~IDBCompleteEvent(); - - virtual bool isIDBCompleteEvent() const { return true; } - -private: - IDBCompleteEvent(PassRefPtr<IDBAny> source); -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBCompleteEvent_h diff --git a/Source/WebCore/storage/IDBCursor.cpp b/Source/WebCore/storage/IDBCursor.cpp index 531cae1..9b1ebf6 100644 --- a/Source/WebCore/storage/IDBCursor.cpp +++ b/Source/WebCore/storage/IDBCursor.cpp @@ -39,13 +39,20 @@ namespace WebCore { -IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBTransaction* transaction) +PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) +{ + return adoptRef(new IDBCursor(backend, request, source, transaction)); +} + +IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) : m_backend(backend) , m_request(request) + , m_source(source) , m_transaction(transaction) { ASSERT(m_backend); ASSERT(m_request); + ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType); ASSERT(m_transaction); } @@ -63,17 +70,29 @@ PassRefPtr<IDBKey> IDBCursor::key() const return m_backend->key(); } -PassRefPtr<IDBAny> IDBCursor::value() const +PassRefPtr<IDBKey> IDBCursor::primaryKey() const +{ + return m_backend->primaryKey(); +} + +PassRefPtr<SerializedScriptValue> IDBCursor::value() const { return m_backend->value(); } +IDBAny* IDBCursor::source() const +{ + return m_source.get(); +} + PassRefPtr<IDBRequest> IDBCursor::update(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, ExceptionCode& ec) { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); m_backend->update(value, request, ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; + } return request.release(); } @@ -91,8 +110,10 @@ PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); m_backend->deleteFunction(request, ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; + } return request.release(); } diff --git a/Source/WebCore/storage/IDBCursor.h b/Source/WebCore/storage/IDBCursor.h index 9f5ffad..9ade9c8 100644 --- a/Source/WebCore/storage/IDBCursor.h +++ b/Source/WebCore/storage/IDBCursor.h @@ -29,6 +29,7 @@ #if ENABLE(INDEXED_DATABASE) #include "ExceptionCode.h" +#include "IDBKey.h" #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -38,7 +39,6 @@ namespace WebCore { class IDBAny; class IDBCallbacks; class IDBCursorBackendInterface; -class IDBKey; class IDBRequest; class IDBTransaction; class ScriptExecutionContext; @@ -52,11 +52,8 @@ public: PREV = 2, PREV_NO_DUPLICATE = 3, }; - static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBTransaction* transaction) - { - return adoptRef(new IDBCursor(backend, request, transaction)); - } - ~IDBCursor(); + static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*); + virtual ~IDBCursor(); // FIXME: Try to modify the code generator so this is unneeded. void continueFunction(ExceptionCode& ec) { continueFunction(0, ec); } @@ -64,16 +61,21 @@ public: // Implement the IDL unsigned short direction() const; PassRefPtr<IDBKey> key() const; - PassRefPtr<IDBAny> value() const; + PassRefPtr<IDBKey> primaryKey() const; + PassRefPtr<SerializedScriptValue> value() const; + IDBAny* source() const; + PassRefPtr<IDBRequest> update(ScriptExecutionContext*, PassRefPtr<SerializedScriptValue>, ExceptionCode&); void continueFunction(PassRefPtr<IDBKey>, ExceptionCode&); PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, ExceptionCode&); -private: - explicit IDBCursor(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBTransaction*); +protected: + IDBCursor(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*); +private: RefPtr<IDBCursorBackendInterface> m_backend; RefPtr<IDBRequest> m_request; + RefPtr<IDBAny> m_source; RefPtr<IDBTransaction> m_transaction; }; diff --git a/Source/WebCore/storage/IDBCursor.idl b/Source/WebCore/storage/IDBCursor.idl index 2e1459f..1f44cd2 100644 --- a/Source/WebCore/storage/IDBCursor.idl +++ b/Source/WebCore/storage/IDBCursor.idl @@ -35,7 +35,8 @@ module storage { readonly attribute unsigned short direction; readonly attribute IDBKey key; - readonly attribute IDBAny value; + readonly attribute IDBKey primaryKey; + readonly attribute IDBAny source; [CallWith=ScriptExecutionContext] IDBRequest update(in SerializedScriptValue value) raises (IDBDatabaseException); diff --git a/Source/WebCore/storage/IDBCursorBackendImpl.cpp b/Source/WebCore/storage/IDBCursorBackendImpl.cpp index 089bb67..6ae3616 100644 --- a/Source/WebCore/storage/IDBCursorBackendImpl.cpp +++ b/Source/WebCore/storage/IDBCursorBackendImpl.cpp @@ -29,32 +29,25 @@ #if ENABLE(INDEXED_DATABASE) #include "CrossThreadTask.h" +#include "IDBBackingStore.h" #include "IDBCallbacks.h" -#include "IDBDatabaseBackendImpl.h" #include "IDBDatabaseError.h" #include "IDBDatabaseException.h" -#include "IDBIndexBackendImpl.h" #include "IDBKeyRange.h" #include "IDBObjectStoreBackendImpl.h" #include "IDBRequest.h" -#include "IDBSQLiteDatabase.h" #include "IDBTransactionBackendInterface.h" -#include "SQLiteDatabase.h" -#include "SQLiteStatement.h" #include "SerializedScriptValue.h" namespace WebCore { -IDBCursorBackendImpl::IDBCursorBackendImpl(IDBSQLiteDatabase* database, PassRefPtr<IDBKeyRange> keyRange, IDBCursor::Direction direction, PassOwnPtr<SQLiteStatement> query, bool isSerializedScriptValueCursor, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore) - : m_database(database) - , m_keyRange(keyRange) +IDBCursorBackendImpl::IDBCursorBackendImpl(PassRefPtr<IDBBackingStore::Cursor> cursor, IDBCursor::Direction direction, CursorType cursorType, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore) + : m_cursor(cursor) , m_direction(direction) - , m_query(query) - , m_isSerializedScriptValueCursor(isSerializedScriptValueCursor) + , m_cursorType(cursorType) , m_transaction(transaction) , m_objectStore(objectStore) { - loadCurrentRow(); } IDBCursorBackendImpl::~IDBCursorBackendImpl() @@ -68,25 +61,28 @@ unsigned short IDBCursorBackendImpl::direction() const PassRefPtr<IDBKey> IDBCursorBackendImpl::key() const { - return m_currentKey; + return m_cursor->key(); } -PassRefPtr<IDBAny> IDBCursorBackendImpl::value() const +PassRefPtr<IDBKey> IDBCursorBackendImpl::primaryKey() const { - if (m_isSerializedScriptValueCursor) - return IDBAny::create(m_currentSerializedScriptValue.get()); - return IDBAny::create(m_currentIDBKeyValue.get()); + return m_cursor->primaryKey(); +} + +PassRefPtr<SerializedScriptValue> IDBCursorBackendImpl::value() const +{ + ASSERT(m_cursorType != IndexKeyCursor); + return SerializedScriptValue::createFromWire(m_cursor->value()); } void IDBCursorBackendImpl::update(PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBCallbacks> callbacks, ExceptionCode& ec) { - if (!m_query || m_currentId == InvalidId || !m_isSerializedScriptValueCursor) { + if (!m_cursor || m_cursorType == IndexKeyCursor) { ec = IDBDatabaseException::NOT_ALLOWED_ERR; return; } - RefPtr<IDBKey> key = m_currentIDBKeyValue ? m_currentIDBKeyValue : m_currentKey; - m_objectStore->put(value, key.release(), IDBObjectStoreBackendInterface::CursorUpdate, callbacks, m_transaction.get(), ec); + m_objectStore->put(value, m_cursor->primaryKey(), IDBObjectStoreBackendInterface::CursorUpdate, callbacks, m_transaction.get(), ec); } void IDBCursorBackendImpl::continueFunction(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec) @@ -98,51 +94,17 @@ void IDBCursorBackendImpl::continueFunction(PassRefPtr<IDBKey> prpKey, PassRefPt ec = IDBDatabaseException::NOT_ALLOWED_ERR; } -bool IDBCursorBackendImpl::currentRowExists() -{ - String sql = m_currentIDBKeyValue ? "SELECT id FROM IndexData WHERE id = ?" : "SELECT id FROM ObjectStoreData WHERE id = ?"; - SQLiteStatement statement(m_database->db(), sql); - - bool ok = statement.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); - - statement.bindInt64(1, m_currentId); - return statement.step() == SQLResultRow; -} - // IMPORTANT: If this ever 1) fires an 'error' event and 2) it's possible to fire another event afterwards, // IDBRequest::hasPendingActivity() will need to be modified to handle this!!! void IDBCursorBackendImpl::continueFunctionInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl> prpCursor, PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> callbacks) { RefPtr<IDBCursorBackendImpl> cursor = prpCursor; RefPtr<IDBKey> key = prpKey; - while (true) { - if (!cursor->m_query || cursor->m_query->step() != SQLResultRow) { - cursor->m_query = 0; - cursor->m_currentId = InvalidId; - cursor->m_currentKey = 0; - cursor->m_currentSerializedScriptValue = 0; - cursor->m_currentIDBKeyValue = 0; - callbacks->onSuccess(SerializedScriptValue::nullValue()); - return; - } - - RefPtr<IDBKey> oldKey = cursor->m_currentKey; - cursor->loadCurrentRow(); - - // Skip if this entry has been deleted from the object store. - if (!cursor->currentRowExists()) - continue; - - // If a key was supplied, we must loop until we find that key (or hit the end). - if (key && !key->isEqual(cursor->m_currentKey.get())) - continue; - - // If we don't have a uniqueness constraint, we can stop now. - if (cursor->m_direction == IDBCursor::NEXT || cursor->m_direction == IDBCursor::PREV) - break; - if (!cursor->m_currentKey->isEqual(oldKey.get())) - break; + + if (!cursor->m_cursor || !cursor->m_cursor->continueFunction(key.get())) { + cursor->m_cursor = 0; + callbacks->onSuccess(SerializedScriptValue::nullValue()); + return; } callbacks->onSuccess(cursor.get()); @@ -150,31 +112,12 @@ void IDBCursorBackendImpl::continueFunctionInternal(ScriptExecutionContext*, Pas void IDBCursorBackendImpl::deleteFunction(PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec) { - if (!m_query || m_currentId == InvalidId || !m_isSerializedScriptValueCursor) { + if (!m_cursor || m_cursorType == IndexKeyCursor) { ec = IDBDatabaseException::NOT_ALLOWED_ERR; return; } - RefPtr<IDBKey> key = m_currentIDBKeyValue ? m_currentIDBKeyValue : m_currentKey; - m_objectStore->deleteFunction(key.release(), prpCallbacks, m_transaction.get(), ec); -} - - -void IDBCursorBackendImpl::loadCurrentRow() -{ - // The column numbers depend on the query in IDBObjectStoreBackendImpl::openCursorInternal or - // IDBIndexBackendImpl::openCursorInternal. - m_currentId = m_query->getColumnInt64(0); - m_currentKey = IDBKey::fromQuery(*m_query, 1); - if (m_isSerializedScriptValueCursor) - m_currentSerializedScriptValue = SerializedScriptValue::createFromWire(m_query->getColumnBlobAsString(4)); - - m_currentIDBKeyValue = IDBKey::fromQuery(*m_query, 5); -} - -SQLiteDatabase& IDBCursorBackendImpl::database() const -{ - return m_database->db(); + m_objectStore->deleteFunction(m_cursor->primaryKey(), prpCallbacks, m_transaction.get(), ec); } } // namespace WebCore diff --git a/Source/WebCore/storage/IDBCursorBackendImpl.h b/Source/WebCore/storage/IDBCursorBackendImpl.h index f459139..2ce3881 100644 --- a/Source/WebCore/storage/IDBCursorBackendImpl.h +++ b/Source/WebCore/storage/IDBCursorBackendImpl.h @@ -29,6 +29,7 @@ #if ENABLE(INDEXED_DATABASE) +#include "IDBBackingStore.h" #include "IDBCursor.h" #include "IDBCursorBackendInterface.h" #include <wtf/OwnPtr.h> @@ -41,56 +42,34 @@ class IDBDatabaseBackendImpl; class IDBIndexBackendImpl; class IDBKeyRange; class IDBObjectStoreBackendInterface; -class IDBSQLiteDatabase; +class IDBBackingStore; class IDBTransactionBackendInterface; -class SQLiteDatabase; -class SQLiteStatement; class SerializedScriptValue; class IDBCursorBackendImpl : public IDBCursorBackendInterface { public: - static PassRefPtr<IDBCursorBackendImpl> create(IDBSQLiteDatabase* database, PassRefPtr<IDBKeyRange> keyRange, IDBCursor::Direction direction, PassOwnPtr<SQLiteStatement> query, bool isSerializedScriptValueCursor, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore) + static PassRefPtr<IDBCursorBackendImpl> create(PassRefPtr<IDBBackingStore::Cursor> cursor, IDBCursor::Direction direction, CursorType cursorType, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore) { - return adoptRef(new IDBCursorBackendImpl(database, keyRange, direction, query, isSerializedScriptValueCursor, transaction, objectStore)); + return adoptRef(new IDBCursorBackendImpl(cursor, direction, cursorType, transaction, objectStore)); } virtual ~IDBCursorBackendImpl(); virtual unsigned short direction() const; virtual PassRefPtr<IDBKey> key() const; - virtual PassRefPtr<IDBAny> value() const; + virtual PassRefPtr<IDBKey> primaryKey() const; + virtual PassRefPtr<SerializedScriptValue> value() const; virtual void update(PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBCallbacks>, ExceptionCode&); virtual void continueFunction(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, ExceptionCode&); virtual void deleteFunction(PassRefPtr<IDBCallbacks>, ExceptionCode&); private: - IDBCursorBackendImpl(IDBSQLiteDatabase*, PassRefPtr<IDBKeyRange>, IDBCursor::Direction, PassOwnPtr<SQLiteStatement> query, bool isSerializedScriptValueCursor, IDBTransactionBackendInterface*, IDBObjectStoreBackendInterface*); - - bool currentRowExists(); - void loadCurrentRow(); - SQLiteDatabase& database() const; + IDBCursorBackendImpl(PassRefPtr<IDBBackingStore::Cursor>, IDBCursor::Direction, CursorType, IDBTransactionBackendInterface*, IDBObjectStoreBackendInterface*); static void continueFunctionInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl>, PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>); - static const int64_t InvalidId = -1; - - RefPtr<IDBSQLiteDatabase> m_database; - - RefPtr<IDBKeyRange> m_keyRange; + RefPtr<IDBBackingStore::Cursor> m_cursor; IDBCursor::Direction m_direction; - OwnPtr<SQLiteStatement> m_query; - bool m_isSerializedScriptValueCursor; - int64_t m_currentId; - - // The key in the objectStore or index that this cursor iterates over. - RefPtr<IDBKey> m_currentKey; - - // m_isSerializedScriptValueCursor will only be available for object cursors. - RefPtr<SerializedScriptValue> m_currentSerializedScriptValue; - - // FIXME: make the primary key available via script for all types of cursors. - // For cursors on indices, this is the key in the objectstore that corresponds to the current entry in the index. - RefPtr<IDBKey> m_currentIDBKeyValue; - + CursorType m_cursorType; RefPtr<IDBTransactionBackendInterface> m_transaction; RefPtr<IDBObjectStoreBackendInterface> m_objectStore; }; diff --git a/Source/WebCore/storage/IDBCursorBackendInterface.h b/Source/WebCore/storage/IDBCursorBackendInterface.h index 0d132ca..6cdab42 100644 --- a/Source/WebCore/storage/IDBCursorBackendInterface.h +++ b/Source/WebCore/storage/IDBCursorBackendInterface.h @@ -45,9 +45,17 @@ class IDBCursorBackendInterface : public ThreadSafeShared<IDBCursorBackendInterf public: virtual ~IDBCursorBackendInterface() {} + enum CursorType { + InvalidCursorType = 0, + IndexCursor, + IndexKeyCursor, + ObjectStoreCursor + }; + virtual unsigned short direction() const = 0; virtual PassRefPtr<IDBKey> key() const = 0; - virtual PassRefPtr<IDBAny> value() const = 0; + virtual PassRefPtr<IDBKey> primaryKey() const = 0; + virtual PassRefPtr<SerializedScriptValue> value() const = 0; virtual void update(PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0; virtual void continueFunction(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0; diff --git a/Source/WebCore/storage/IDBErrorEvent.cpp b/Source/WebCore/storage/IDBCursorWithValue.cpp index e576fa8..d3c3bd8 100644 --- a/Source/WebCore/storage/IDBErrorEvent.cpp +++ b/Source/WebCore/storage/IDBCursorWithValue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,9 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -27,32 +24,29 @@ */ #include "config.h" -#include "IDBErrorEvent.h" +#include "IDBCursorWithValue.h" #if ENABLE(INDEXED_DATABASE) -#include "EventNames.h" -#include "IDBAny.h" -#include "IDBDatabaseError.h" +#include "IDBCursorBackendInterface.h" +#include "IDBKey.h" namespace WebCore { -PassRefPtr<IDBErrorEvent> IDBErrorEvent::create(PassRefPtr<IDBAny> source, const IDBDatabaseError& error) +PassRefPtr<IDBCursorWithValue> IDBCursorWithValue::create(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) { - return adoptRef(new IDBErrorEvent(source, error)); + return adoptRef(new IDBCursorWithValue(backend, request, source, transaction)); } -IDBErrorEvent::IDBErrorEvent(PassRefPtr<IDBAny> source, const IDBDatabaseError& error) - : IDBEvent(eventNames().errorEvent, source, true) - , m_code(error.code()) - , m_message(error.message()) +IDBCursorWithValue::IDBCursorWithValue(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) + : IDBCursor(backend, request, source, transaction) { } -IDBErrorEvent::~IDBErrorEvent() +IDBCursorWithValue::~IDBCursorWithValue() { } } // namespace WebCore -#endif +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBSQLiteDatabase.h b/Source/WebCore/storage/IDBCursorWithValue.h index 0556506..d0610bd 100644 --- a/Source/WebCore/storage/IDBSQLiteDatabase.h +++ b/Source/WebCore/storage/IDBCursorWithValue.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,40 +23,29 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBSQLiteDatabase_h -#define IDBSQLiteDatabase_h +#ifndef IDBCursorWithValue_h +#define IDBCursorWithValue_h #if ENABLE(INDEXED_DATABASE) -#include "SQLiteDatabase.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> +#include "IDBCursor.h" namespace WebCore { -class IDBFactoryBackendImpl; - -class IDBSQLiteDatabase : public RefCounted<IDBSQLiteDatabase> { +class IDBCursorWithValue : public IDBCursor { public: - static PassRefPtr<IDBSQLiteDatabase> create(String identifier, IDBFactoryBackendImpl* factory) - { - return adoptRef(new IDBSQLiteDatabase(identifier, factory)); - } - ~IDBSQLiteDatabase(); + static PassRefPtr<IDBCursorWithValue> create(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*); + virtual ~IDBCursorWithValue(); - SQLiteDatabase& db() { return m_db; } + // The value attribute defined in the IDL is simply implemented in IDBCursor (but not exposed via + // its IDL). This is to make the implementation more simple while matching what the spec says. private: - IDBSQLiteDatabase(String identifier, IDBFactoryBackendImpl* factory); - - SQLiteDatabase m_db; - String m_identifier; - RefPtr<IDBFactoryBackendImpl> m_factory; + IDBCursorWithValue(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*); }; } // namespace WebCore #endif -#endif // IDBSQLiteDatabase_h +#endif // IDBCursorWithValue_h diff --git a/Source/WebCore/storage/IDBEvent.idl b/Source/WebCore/storage/IDBCursorWithValue.idl index 4dd552e..811215a 100644 --- a/Source/WebCore/storage/IDBEvent.idl +++ b/Source/WebCore/storage/IDBCursorWithValue.idl @@ -10,9 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -30,7 +27,7 @@ module storage { interface [ Conditional=INDEXED_DATABASE - ] IDBEvent : Event { - readonly attribute IDBAny source; + ] IDBCursorWithValue : IDBCursor { + readonly attribute SerializedScriptValue value; }; } diff --git a/Source/WebCore/storage/IDBDatabase.cpp b/Source/WebCore/storage/IDBDatabase.cpp index 9a5eb6c..fe8d350 100644 --- a/Source/WebCore/storage/IDBDatabase.cpp +++ b/Source/WebCore/storage/IDBDatabase.cpp @@ -26,13 +26,17 @@ #include "config.h" #include "IDBDatabase.h" +#include "Document.h" +#include "EventQueue.h" #include "IDBAny.h" #include "IDBDatabaseError.h" #include "IDBDatabaseException.h" +#include "IDBEventDispatcher.h" #include "IDBFactoryBackendInterface.h" #include "IDBIndex.h" #include "IDBObjectStore.h" -#include "IDBRequest.h" +#include "IDBVersionChangeEvent.h" +#include "IDBVersionChangeRequest.h" #include "IDBTransaction.h" #include "ScriptExecutionContext.h" #include <limits> @@ -58,7 +62,6 @@ IDBDatabase::IDBDatabase(ScriptExecutionContext* context, PassRefPtr<IDBDatabase IDBDatabase::~IDBDatabase() { - ASSERT(m_stopped); } void IDBDatabase::setSetVersionTransaction(IDBTransaction* transaction) @@ -97,10 +100,10 @@ void IDBDatabase::deleteObjectStore(const String& name, ExceptionCode& ec) m_backend->deleteObjectStore(name, m_setVersionTransaction->backend(), ec); } -PassRefPtr<IDBRequest> IDBDatabase::setVersion(ScriptExecutionContext* context, const String& version, ExceptionCode& ec) +PassRefPtr<IDBVersionChangeRequest> IDBDatabase::setVersion(ScriptExecutionContext* context, const String& version, ExceptionCode& ec) { - RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), 0); - m_backend->setVersion(version, request, ec); + RefPtr<IDBVersionChangeRequest> request = IDBVersionChangeRequest::create(context, IDBAny::create(this), version); + m_backend->setVersion(version, request, this, ec); return request; } @@ -136,7 +139,27 @@ PassRefPtr<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* cont void IDBDatabase::close() { + if (m_noNewTransactions) + return; + + ASSERT(scriptExecutionContext()->isDocument()); + EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue(); + // Remove any pending versionchange events scheduled to fire on this + // connection. They would have been scheduled by the backend when another + // connection called setVersion, but the frontend connection is being + // closed before they could fire. + for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { + bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get()); + ASSERT_UNUSED(removed, removed); + } + m_noNewTransactions = true; + m_backend->close(this); +} + +void IDBDatabase::onVersionChange(const String& version) +{ + enqueueEvent(IDBVersionChangeEvent::create(version, eventNames().versionchangeEvent)); } bool IDBDatabase::hasPendingActivity() const @@ -149,6 +172,30 @@ bool IDBDatabase::hasPendingActivity() const return !m_stopped || ActiveDOMObject::hasPendingActivity(); } +void IDBDatabase::open() +{ + m_backend->open(this); +} + +void IDBDatabase::enqueueEvent(PassRefPtr<Event> event) +{ + ASSERT(scriptExecutionContext()->isDocument()); + EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue(); + event->setTarget(this); + eventQueue->enqueueEvent(event.get()); + m_enqueuedEvents.append(event); +} + +bool IDBDatabase::dispatchEvent(PassRefPtr<Event> event) +{ + ASSERT(event->type() == eventNames().versionchangeEvent); + for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { + if (m_enqueuedEvents[i].get() == event.get()) + m_enqueuedEvents.remove(i); + } + return EventTarget::dispatchEvent(event.get()); +} + void IDBDatabase::stop() { // Stop fires at a deterministic time, so we need to call close in it. diff --git a/Source/WebCore/storage/IDBDatabase.h b/Source/WebCore/storage/IDBDatabase.h index f90ddd3..51f1d23 100644 --- a/Source/WebCore/storage/IDBDatabase.h +++ b/Source/WebCore/storage/IDBDatabase.h @@ -32,6 +32,7 @@ #include "EventTarget.h" #include "ExceptionCode.h" #include "IDBDatabaseBackendInterface.h" +#include "IDBDatabaseCallbacks.h" #include "IDBObjectStore.h" #include "IDBTransaction.h" #include "OptionsObject.h" @@ -43,10 +44,10 @@ namespace WebCore { -class IDBAny; -class IDBRequest; +class IDBVersionChangeRequest; +class ScriptExecutionContext; -class IDBDatabase : public RefCounted<IDBDatabase>, public EventTarget, public ActiveDOMObject { +class IDBDatabase : public IDBDatabaseCallbacks, public EventTarget, public ActiveDOMObject { public: static PassRefPtr<IDBDatabase> create(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendInterface>); ~IDBDatabase(); @@ -66,11 +67,15 @@ public: PassRefPtr<IDBObjectStore> createObjectStore(const String& name, const OptionsObject&, ExceptionCode&); void deleteObjectStore(const String& name, ExceptionCode&); - PassRefPtr<IDBRequest> setVersion(ScriptExecutionContext*, const String& version, ExceptionCode&); + PassRefPtr<IDBVersionChangeRequest> setVersion(ScriptExecutionContext*, const String& version, ExceptionCode&); void close(); DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + DEFINE_ATTRIBUTE_EVENT_LISTENER(versionchange); + + // IDBDatabaseCallbacks + virtual void onVersionChange(const String& requestedVersion); // ActiveDOMObject virtual bool hasPendingActivity() const; @@ -80,8 +85,14 @@ public: virtual IDBDatabase* toIDBDatabase() { return this; } virtual ScriptExecutionContext* scriptExecutionContext() const; - using RefCounted<IDBDatabase>::ref; - using RefCounted<IDBDatabase>::deref; + + void open(); + void enqueueEvent(PassRefPtr<Event>); + bool dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) { return EventTarget::dispatchEvent(event, ec); } + virtual bool dispatchEvent(PassRefPtr<Event>); + + using RefCounted<IDBDatabaseCallbacks>::ref; + using RefCounted<IDBDatabaseCallbacks>::deref; private: IDBDatabase(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendInterface>); @@ -99,6 +110,10 @@ private: bool m_stopped; EventTargetData m_eventTargetData; + + // Keep track of the versionchange events waiting to be fired on this + // database so that we can cancel them if the database closes. + Vector<RefPtr<Event> > m_enqueuedEvents; }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBDatabase.idl b/Source/WebCore/storage/IDBDatabase.idl index 3922144..a07fda7 100644 --- a/Source/WebCore/storage/IDBDatabase.idl +++ b/Source/WebCore/storage/IDBDatabase.idl @@ -35,12 +35,13 @@ module storage { attribute EventListener onabort; attribute EventListener onerror; + attribute EventListener onversionchange; IDBObjectStore createObjectStore(in DOMString name, in [Optional] OptionsObject options) raises (IDBDatabaseException); void deleteObjectStore(in DOMString name) raises (IDBDatabaseException); - [CallWith=ScriptExecutionContext] IDBRequest setVersion(in DOMString version) + [CallWith=ScriptExecutionContext] IDBVersionChangeRequest setVersion(in DOMString version) raises (IDBDatabaseException); [CallWith=ScriptExecutionContext] IDBTransaction transaction(in [Optional] DOMStringList storeNames, in [Optional] unsigned short mode) raises (IDBDatabaseException); @@ -53,7 +54,7 @@ module storage { void removeEventListener(in DOMString type, in EventListener listener, in boolean useCapture); - boolean dispatchEvent(in Event evt) + boolean dispatchEvent(in Event evt) raises(EventException); }; diff --git a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp index 133c5ae..94e305b 100644 --- a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp +++ b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp @@ -30,65 +30,39 @@ #include "CrossThreadTask.h" #include "DOMStringList.h" +#include "IDBBackingStore.h" #include "IDBDatabaseException.h" #include "IDBFactoryBackendImpl.h" #include "IDBObjectStoreBackendImpl.h" -#include "IDBSQLiteDatabase.h" #include "IDBTransactionBackendImpl.h" #include "IDBTransactionCoordinator.h" -#include "SQLiteStatement.h" -#include "SQLiteTransaction.h" namespace WebCore { -static bool extractMetaData(SQLiteDatabase& sqliteDatabase, const String& name, String& foundVersion, int64& foundId) -{ - SQLiteStatement databaseQuery(sqliteDatabase, "SELECT id, version FROM Databases WHERE name = ?"); - if (databaseQuery.prepare() != SQLResultOk) { - ASSERT_NOT_REACHED(); - return false; +class IDBDatabaseBackendImpl::PendingSetVersionCall : public RefCounted<PendingSetVersionCall> { +public: + static PassRefPtr<PendingSetVersionCall> create(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks) + { + return adoptRef(new PendingSetVersionCall(version, callbacks, databaseCallbacks)); } - databaseQuery.bindText(1, name); - if (databaseQuery.step() != SQLResultRow) - return false; - - foundId = databaseQuery.getColumnInt64(0); - foundVersion = databaseQuery.getColumnText(1); - - if (databaseQuery.step() == SQLResultRow) - ASSERT_NOT_REACHED(); - return true; -} - -static bool setMetaData(SQLiteDatabase& sqliteDatabase, const String& name, const String& version, int64_t& rowId) -{ - ASSERT(!name.isNull()); - ASSERT(!version.isNull()); - - String sql = rowId != IDBDatabaseBackendImpl::InvalidId ? "UPDATE Databases SET name = ?, version = ? WHERE id = ?" - : "INSERT INTO Databases (name, description, version) VALUES (?, '', ?)"; - SQLiteStatement query(sqliteDatabase, sql); - if (query.prepare() != SQLResultOk) { - ASSERT_NOT_REACHED(); - return false; + String version() { return m_version; } + PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; } + PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks() { return m_databaseCallbacks; } + +private: + PendingSetVersionCall(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks) + : m_version(version) + , m_callbacks(callbacks) + , m_databaseCallbacks(databaseCallbacks) + { } + String m_version; + RefPtr<IDBCallbacks> m_callbacks; + RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks; +}; - query.bindText(1, name); - query.bindText(2, version); - if (rowId != IDBDatabaseBackendImpl::InvalidId) - query.bindInt64(3, rowId); - - if (query.step() != SQLResultDone) - return false; - - if (rowId == IDBDatabaseBackendImpl::InvalidId) - rowId = sqliteDatabase.lastInsertRowID(); - - return true; -} - -IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBSQLiteDatabase* sqliteDatabase, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier) - : m_sqliteDatabase(sqliteDatabase) +IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBBackingStore* backingStore, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier) + : m_backingStore(backingStore) , m_id(InvalidId) , m_name(name) , m_version("") @@ -98,9 +72,9 @@ IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBSQLiteData { ASSERT(!m_name.isNull()); - bool success = extractMetaData(m_sqliteDatabase->db(), m_name, m_version, m_id); + bool success = m_backingStore->extractIDBDatabaseMetaData(m_name, m_version, m_id); ASSERT_UNUSED(success, success == (m_id != InvalidId)); - if (!setMetaData(m_sqliteDatabase->db(), m_name, m_version, m_id)) + if (!m_backingStore->setIDBDatabaseMetaData(m_name, m_version, m_id, m_id == InvalidId)) ASSERT_NOT_REACHED(); // FIXME: Need better error handling. loadObjectStores(); } @@ -110,9 +84,9 @@ IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl() m_factory->removeIDBDatabaseBackend(m_identifier); } -SQLiteDatabase& IDBDatabaseBackendImpl::sqliteDatabase() const +PassRefPtr<IDBBackingStore> IDBDatabaseBackendImpl::backingStore() const { - return m_sqliteDatabase->db(); + return m_backingStore; } PassRefPtr<DOMStringList> IDBDatabaseBackendImpl::objectStoreNames() const @@ -132,7 +106,7 @@ PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObject return 0; } - RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_sqliteDatabase.get(), name, keyPath, autoIncrement); + RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_backingStore.get(), name, keyPath, autoIncrement); ASSERT(objectStore->name() == name); RefPtr<IDBDatabaseBackendImpl> database = this; @@ -149,21 +123,14 @@ PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObject void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction) { - SQLiteStatement insert(database->sqliteDatabase(), "INSERT INTO ObjectStores (name, keyPath, doAutoIncrement, databaseId) VALUES (?, ?, ?, ?)"); - if (insert.prepare() != SQLResultOk) { - transaction->abort(); - return; - } - insert.bindText(1, objectStore->name()); - insert.bindText(2, objectStore->keyPath()); - insert.bindInt(3, static_cast<int>(objectStore->autoIncrement())); - insert.bindInt64(4, database->id()); - if (insert.step() != SQLResultDone) { + int64_t objectStoreId; + + if (!database->m_backingStore->createObjectStore(objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement(), database->id(), objectStoreId)) { transaction->abort(); return; } - int64_t id = database->sqliteDatabase().lastInsertRowID(); - objectStore->setId(id); + + objectStore->setId(objectStoreId); transaction->didCompleteTaskEvents(); } @@ -172,16 +139,6 @@ PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(c return m_objectStores.get(name); } -static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id) -{ - SQLiteStatement deleteQuery(db, sql); - bool ok = deleteQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. - deleteQuery.bindInt64(1, id); - ok = deleteQuery.step() == SQLResultDone; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. -} - void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) { RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name); @@ -201,19 +158,31 @@ void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactio void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction) { - doDelete(database->sqliteDatabase(), "DELETE FROM ObjectStores WHERE id = ?", objectStore->id()); - doDelete(database->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStore->id()); - doDelete(database->sqliteDatabase(), "DELETE FROM IndexData WHERE indexId IN (SELECT id FROM Indexes WHERE objectStoreId = ?)", objectStore->id()); - doDelete(database->sqliteDatabase(), "DELETE FROM Indexes WHERE objectStoreId = ?", objectStore->id()); - + database->m_backingStore->deleteObjectStore(objectStore->id()); transaction->didCompleteTaskEvents(); } -void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec) +void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, ExceptionCode& ec) { - RefPtr<IDBDatabaseBackendImpl> database = this; RefPtr<IDBCallbacks> callbacks = prpCallbacks; + RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks; + if (!m_databaseCallbacksSet.contains(databaseCallbacks)) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::ABORT_ERR, "Connection was closed before set version transaction was created")); + return; + } + for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) { + if (*it != databaseCallbacks) + (*it)->onVersionChange(version); + } + if (m_databaseCallbacksSet.size() > 1) { + callbacks->onBlocked(); + RefPtr<PendingSetVersionCall> pendingSetVersionCall = PendingSetVersionCall::create(version, callbacks, databaseCallbacks); + m_pendingSetVersionCalls.append(pendingSetVersionCall); + return; + } + RefPtr<DOMStringList> objectStoreNames = DOMStringList::create(); + RefPtr<IDBDatabaseBackendImpl> database = this; RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStoreNames.get(), IDBTransaction::VERSION_CHANGE, this); if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::setVersionInternal, database, version, callbacks, transaction), createCallbackTask(&IDBDatabaseBackendImpl::resetVersion, database, m_version))) { @@ -225,7 +194,7 @@ void IDBDatabaseBackendImpl::setVersionInternal(ScriptExecutionContext*, PassRef { int64_t databaseId = database->id(); database->m_version = version; - if (!setMetaData(database->m_sqliteDatabase->db(), database->m_name, database->m_version, databaseId)) { + if (!database->m_backingStore->setIDBDatabaseMetaData(database->m_name, database->m_version, databaseId, databaseId == InvalidId)) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); @@ -247,29 +216,43 @@ PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(D return IDBTransactionBackendImpl::create(objectStoreNames, mode, this); } -void IDBDatabaseBackendImpl::close() +void IDBDatabaseBackendImpl::open(PassRefPtr<IDBDatabaseCallbacks> callbacks) { - // FIXME: Implement. + m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(callbacks)); } -void IDBDatabaseBackendImpl::loadObjectStores() +void IDBDatabaseBackendImpl::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks) { - SQLiteStatement objectStoresQuery(sqliteDatabase(), "SELECT id, name, keyPath, doAutoIncrement FROM ObjectStores WHERE databaseId = ?"); - bool ok = objectStoresQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - - objectStoresQuery.bindInt64(1, m_id); - - while (objectStoresQuery.step() == SQLResultRow) { - int64_t id = objectStoresQuery.getColumnInt64(0); - String name = objectStoresQuery.getColumnText(1); - String keyPath = objectStoresQuery.getColumnText(2); - bool autoIncrement = !!objectStoresQuery.getColumnInt(3); + RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks; + ASSERT(m_databaseCallbacksSet.contains(callbacks)); + m_databaseCallbacksSet.remove(callbacks); + if (m_databaseCallbacksSet.size() > 1) + return; - m_objectStores.set(name, IDBObjectStoreBackendImpl::create(m_sqliteDatabase.get(), id, name, keyPath, autoIncrement)); + while (!m_pendingSetVersionCalls.isEmpty()) { + ExceptionCode ec = 0; + RefPtr<PendingSetVersionCall> pendingSetVersionCall = m_pendingSetVersionCalls.takeFirst(); + setVersion(pendingSetVersionCall->version(), pendingSetVersionCall->callbacks(), pendingSetVersionCall->databaseCallbacks(), ec); + ASSERT(!ec); } } +void IDBDatabaseBackendImpl::loadObjectStores() +{ + Vector<int64_t> ids; + Vector<String> names; + Vector<String> keyPaths; + Vector<bool> autoIncrementFlags; + m_backingStore->getObjectStores(m_id, ids, names, keyPaths, autoIncrementFlags); + + ASSERT(names.size() == ids.size()); + ASSERT(keyPaths.size() == ids.size()); + ASSERT(autoIncrementFlags.size() == ids.size()); + + for (size_t i = 0; i < ids.size(); i++) + m_objectStores.set(names[i], IDBObjectStoreBackendImpl::create(m_backingStore.get(), ids[i], names[i], keyPaths[i], autoIncrementFlags[i])); +} + void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore) { ASSERT(database->m_objectStores.contains(objectStore->name())); diff --git a/Source/WebCore/storage/IDBDatabaseBackendImpl.h b/Source/WebCore/storage/IDBDatabaseBackendImpl.h index 8942968..f779125 100644 --- a/Source/WebCore/storage/IDBDatabaseBackendImpl.h +++ b/Source/WebCore/storage/IDBDatabaseBackendImpl.h @@ -28,31 +28,33 @@ #include "IDBCallbacks.h" #include "IDBDatabase.h" +#include <wtf/Deque.h> #include <wtf/HashMap.h> -#include <wtf/text/StringHash.h> +#include <wtf/ListHashSet.h> #if ENABLE(INDEXED_DATABASE) namespace WebCore { +class IDBBackingStore; +class IDBDatabase; class IDBFactoryBackendImpl; class IDBObjectStoreBackendImpl; -class IDBSQLiteDatabase; class IDBTransactionCoordinator; -class SQLiteDatabase; class IDBDatabaseBackendImpl : public IDBDatabaseBackendInterface { public: - static PassRefPtr<IDBDatabaseBackendImpl> create(const String& name, IDBSQLiteDatabase* database, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier) + static PassRefPtr<IDBDatabaseBackendImpl> create(const String& name, IDBBackingStore* database, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier) { return adoptRef(new IDBDatabaseBackendImpl(name, database, coordinator, factory, uniqueIdentifier)); } virtual ~IDBDatabaseBackendImpl(); - SQLiteDatabase& sqliteDatabase() const; + PassRefPtr<IDBBackingStore> backingStore() const; static const int64_t InvalidId = 0; int64_t id() const { return m_id; } + void open(PassRefPtr<IDBDatabaseCallbacks>); virtual String name() const { return m_name; } virtual String version() const { return m_version; } @@ -60,15 +62,15 @@ public: virtual PassRefPtr<IDBObjectStoreBackendInterface> createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface*, ExceptionCode&); virtual void deleteObjectStore(const String& name, IDBTransactionBackendInterface*, ExceptionCode&); - virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, ExceptionCode&); + virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, ExceptionCode&); virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* objectStoreNames, unsigned short mode, ExceptionCode&); - virtual void close(); + virtual void close(PassRefPtr<IDBDatabaseCallbacks>); PassRefPtr<IDBObjectStoreBackendInterface> objectStore(const String& name); IDBTransactionCoordinator* transactionCoordinator() const { return m_transactionCoordinator.get(); } private: - IDBDatabaseBackendImpl(const String& name, IDBSQLiteDatabase* database, IDBTransactionCoordinator*, IDBFactoryBackendImpl*, const String& uniqueIdentifier); + IDBDatabaseBackendImpl(const String& name, IDBBackingStore* database, IDBTransactionCoordinator*, IDBFactoryBackendImpl*, const String& uniqueIdentifier); void loadObjectStores(); @@ -81,7 +83,7 @@ private: static void addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl>, PassRefPtr<IDBObjectStoreBackendImpl>); static void resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl>, const String& version); - RefPtr<IDBSQLiteDatabase> m_sqliteDatabase; + RefPtr<IDBBackingStore> m_backingStore; int64 m_id; String m_name; String m_version; @@ -94,6 +96,12 @@ private: ObjectStoreMap m_objectStores; RefPtr<IDBTransactionCoordinator> m_transactionCoordinator; + + class PendingSetVersionCall; + Deque<RefPtr<PendingSetVersionCall> > m_pendingSetVersionCalls; + + typedef ListHashSet<RefPtr<IDBDatabaseCallbacks> > DatabaseCallbacksSet; + DatabaseCallbacksSet m_databaseCallbacksSet; }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBDatabaseBackendInterface.h b/Source/WebCore/storage/IDBDatabaseBackendInterface.h index 9cc7230..ad6fdc7 100644 --- a/Source/WebCore/storage/IDBDatabaseBackendInterface.h +++ b/Source/WebCore/storage/IDBDatabaseBackendInterface.h @@ -38,9 +38,9 @@ namespace WebCore { class DOMStringList; class Frame; class IDBCallbacks; +class IDBDatabaseCallbacks; class IDBObjectStoreBackendInterface; class IDBTransactionBackendInterface; -class IDBTransactionCallbacks; // This class is shared by IDBDatabase (async) and IDBDatabaseSync (sync). // This is implemented by IDBDatabaseBackendImpl and optionally others (in order to proxy @@ -56,9 +56,11 @@ public: virtual PassRefPtr<IDBObjectStoreBackendInterface> createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface*, ExceptionCode&) = 0; virtual void deleteObjectStore(const String& name, IDBTransactionBackendInterface*, ExceptionCode&) = 0; - virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0; + virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, ExceptionCode&) = 0; virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* storeNames, unsigned short mode, ExceptionCode&) = 0; - virtual void close() = 0; + virtual void close(PassRefPtr<IDBDatabaseCallbacks>) = 0; + + virtual void open(PassRefPtr<IDBDatabaseCallbacks>) = 0; }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBSQLiteDatabase.cpp b/Source/WebCore/storage/IDBDatabaseCallbacks.h index e881917..b3f244f 100644 --- a/Source/WebCore/storage/IDBSQLiteDatabase.cpp +++ b/Source/WebCore/storage/IDBDatabaseCallbacks.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,26 +23,25 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "IDBSQLiteDatabase.h" +#ifndef IDBDatabaseCallbacks_h +#define IDBDatabaseCallbacks_h #if ENABLE(INDEXED_DATABASE) -#include "IDBFactoryBackendImpl.h" +#include "PlatformString.h" +#include <wtf/RefCounted.h> namespace WebCore { -IDBSQLiteDatabase::IDBSQLiteDatabase(String identifier, IDBFactoryBackendImpl* factory) - : m_identifier(identifier) - , m_factory(factory) -{ -} +class IDBDatabaseCallbacks : public RefCounted<IDBDatabaseCallbacks> { +public: + virtual ~IDBDatabaseCallbacks() { } -IDBSQLiteDatabase::~IDBSQLiteDatabase() -{ - m_factory->removeSQLiteDatabase(m_identifier); -} + virtual void onVersionChange(const String& version) = 0; +}; } // namespace WebCore -#endif // ENABLE(INDEXED_DATABASE) +#endif + +#endif // IDBDatabaseCallbacks_h diff --git a/Source/WebCore/storage/IDBDatabaseException.h b/Source/WebCore/storage/IDBDatabaseException.h index 174fe5b..21991ee 100644 --- a/Source/WebCore/storage/IDBDatabaseException.h +++ b/Source/WebCore/storage/IDBDatabaseException.h @@ -43,6 +43,7 @@ public: static const int IDBDatabaseExceptionMax = 1299; enum IDBDatabaseExceptionCode { + NO_ERR = IDBDatabaseExceptionOffset + 0, UNKNOWN_ERR = IDBDatabaseExceptionOffset + 1, NON_TRANSIENT_ERR = IDBDatabaseExceptionOffset + 2, NOT_FOUND_ERR = IDBDatabaseExceptionOffset + 3, @@ -54,7 +55,8 @@ public: TRANSIENT_ERR = IDBDatabaseExceptionOffset + 9, TIMEOUT_ERR = IDBDatabaseExceptionOffset + 10, DEADLOCK_ERR = IDBDatabaseExceptionOffset + 11, - READ_ONLY_ERR = IDBDatabaseExceptionOffset + 12 + READ_ONLY_ERR = IDBDatabaseExceptionOffset + 12, + ABORT_ERR = IDBDatabaseExceptionOffset + 13 }; static int ErrorCodeToExceptionCode(int errorCode) diff --git a/Source/WebCore/storage/IDBDatabaseException.idl b/Source/WebCore/storage/IDBDatabaseException.idl index 9027e05..d603057 100644 --- a/Source/WebCore/storage/IDBDatabaseException.idl +++ b/Source/WebCore/storage/IDBDatabaseException.idl @@ -39,6 +39,7 @@ module storage { [DontEnum] DOMString toString(); #endif + const unsigned short NO_ERR = 0; const unsigned short UNKNOWN_ERR = 1; const unsigned short NON_TRANSIENT_ERR = 2; const unsigned short NOT_FOUND_ERR = 3; @@ -51,6 +52,7 @@ module storage { const unsigned short TIMEOUT_ERR = 10; const unsigned short DEADLOCK_ERR = 11; const unsigned short READ_ONLY_ERR = 12; + const unsigned short ABORT_ERR = 13; }; } diff --git a/Source/WebCore/storage/IDBErrorEvent.h b/Source/WebCore/storage/IDBErrorEvent.h deleted file mode 100644 index 648da8b..0000000 --- a/Source/WebCore/storage/IDBErrorEvent.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBErrorEvent_h -#define IDBErrorEvent_h - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBEvent.h" -#include "PlatformString.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> - -namespace WebCore { - -class IDBAny; -class IDBDatabaseError; - -class IDBErrorEvent : public IDBEvent { -public: - static PassRefPtr<IDBErrorEvent> create(PassRefPtr<IDBAny> source, const IDBDatabaseError&); - // FIXME: Need to allow creation of these events from JS. - virtual ~IDBErrorEvent(); - - unsigned short code() const { return m_code; } - String message() { return m_message; } - - virtual bool isIDBErrorEvent() const { return true; } - -private: - IDBErrorEvent(PassRefPtr<IDBAny> source, const IDBDatabaseError&); - - unsigned short m_code; - String m_message; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBEvent_h diff --git a/Source/WebCore/storage/IDBEvent.h b/Source/WebCore/storage/IDBEvent.h deleted file mode 100644 index d28885b..0000000 --- a/Source/WebCore/storage/IDBEvent.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBEvent_h -#define IDBEvent_h - -#if ENABLE(INDEXED_DATABASE) - -#include "Event.h" -#include "EventTarget.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class IDBAny; - -class IDBEvent : public Event { -public: - virtual ~IDBEvent(); - - PassRefPtr<IDBAny> source(); - bool dispatch(Vector<RefPtr<EventTarget> >&); // The target first and then its ancestors in order of how the event bubbles. - -protected: - IDBEvent(const AtomicString& type, PassRefPtr<IDBAny> source, bool canBubble); - -private: - RefPtr<IDBAny> m_source; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBEvent_h diff --git a/Source/WebCore/storage/IDBEvent.cpp b/Source/WebCore/storage/IDBEventDispatcher.cpp index a7f3db1..7263467 100644 --- a/Source/WebCore/storage/IDBEvent.cpp +++ b/Source/WebCore/storage/IDBEventDispatcher.cpp @@ -27,53 +27,39 @@ */ #include "config.h" -#include "IDBEvent.h" +#include "IDBEventDispatcher.h" #if ENABLE(INDEXED_DATABASE) -#include "IDBAny.h" +#include "Event.h" +#include "EventTarget.h" namespace WebCore { -IDBEvent::IDBEvent(const AtomicString& type, PassRefPtr<IDBAny> source, bool canBubble) - : Event(type, canBubble, true) - , m_source(source) -{ -} - -IDBEvent::~IDBEvent() -{ -} - -PassRefPtr<IDBAny> IDBEvent::source() -{ - return m_source; -} - -bool IDBEvent::dispatch(Vector<RefPtr<EventTarget> >& eventTargets) +bool IDBEventDispatcher::dispatch(Event* event, Vector<RefPtr<EventTarget> >& eventTargets) { size_t size = eventTargets.size(); ASSERT(size); - setEventPhase(Event::CAPTURING_PHASE); + event->setEventPhase(Event::CAPTURING_PHASE); for (size_t i = size - 1; i; --i) { // Don't do the first element. - setCurrentTarget(eventTargets[i].get()); - eventTargets[i]->fireEventListeners(this); - if (propagationStopped()) + event->setCurrentTarget(eventTargets[i].get()); + eventTargets[i]->fireEventListeners(event); + if (event->propagationStopped()) goto doneDispatching; } - setEventPhase(Event::AT_TARGET); - setCurrentTarget(eventTargets[0].get()); - eventTargets[0]->fireEventListeners(this); - if (propagationStopped() || !bubbles() || cancelBubble()) + event->setEventPhase(Event::AT_TARGET); + event->setCurrentTarget(eventTargets[0].get()); + eventTargets[0]->fireEventListeners(event); + if (event->propagationStopped() || !event->bubbles() || event->cancelBubble()) goto doneDispatching; - setEventPhase(Event::BUBBLING_PHASE); + event->setEventPhase(Event::BUBBLING_PHASE); for (size_t i = 1; i < size; ++i) { // Don't do the first element. - setCurrentTarget(eventTargets[i].get()); - eventTargets[i]->fireEventListeners(this); - if (propagationStopped() || cancelBubble()) + event->setCurrentTarget(eventTargets[i].get()); + eventTargets[i]->fireEventListeners(event); + if (event->propagationStopped() || event->cancelBubble()) goto doneDispatching; } @@ -93,12 +79,12 @@ bool IDBEvent::dispatch(Vector<RefPtr<EventTarget> >& eventTargets) // // (I think that so far webkit hasn't implemented the window.onerror // feature yet, so you probably don't want to fire the separate error - // event on the window until that has been implemented)." + // event on the window until that has been implemented)." -- Jonas Sicking doneDispatching: - setCurrentTarget(0); - setEventPhase(0); - return !defaultPrevented(); + event->setCurrentTarget(0); + event->setEventPhase(0); + return !event->defaultPrevented(); } } // namespace WebCore diff --git a/Source/WebCore/storage/IDBAbortEvent.cpp b/Source/WebCore/storage/IDBEventDispatcher.h index 980d656..00bf154 100644 --- a/Source/WebCore/storage/IDBAbortEvent.cpp +++ b/Source/WebCore/storage/IDBEventDispatcher.h @@ -26,30 +26,29 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "IDBAbortEvent.h" +#ifndef IDBEventDispatcher_h +#define IDBEventDispatcher_h #if ENABLE(INDEXED_DATABASE) -#include "EventNames.h" -#include "IDBAny.h" +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> namespace WebCore { -PassRefPtr<IDBAbortEvent> IDBAbortEvent::create(PassRefPtr<IDBAny> source) -{ - return adoptRef(new IDBAbortEvent(source)); -} +class Event; +class EventTarget; -IDBAbortEvent::IDBAbortEvent(PassRefPtr<IDBAny> source) - : IDBEvent(eventNames().abortEvent, source, true) -{ -} +class IDBEventDispatcher { +public: + static bool dispatch(Event*, Vector<RefPtr<EventTarget> >&); // The target first and then its ancestors in order of how the event bubbles. -IDBAbortEvent::~IDBAbortEvent() -{ -} +private: + IDBEventDispatcher(); +}; } // namespace WebCore -#endif +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBEventDispatcher_h diff --git a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp index 0e883cf..9127455 100644 --- a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp +++ b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp @@ -30,13 +30,10 @@ #include "IDBFactoryBackendImpl.h" #include "DOMStringList.h" -#include "FileSystem.h" +#include "IDBBackingStore.h" #include "IDBDatabaseBackendImpl.h" #include "IDBDatabaseException.h" -#include "IDBSQLiteDatabase.h" #include "IDBTransactionCoordinator.h" -#include "SQLiteStatement.h" -#include "SQLiteTransaction.h" #include "SecurityOrigin.h" #include <wtf/Threading.h> #include <wtf/UnusedParam.h> @@ -45,11 +42,11 @@ namespace WebCore { -IDBFactoryBackendImpl::IDBFactoryBackendImpl() +IDBFactoryBackendImpl::IDBFactoryBackendImpl() : m_transactionCoordinator(IDBTransactionCoordinator::create()) { } - + IDBFactoryBackendImpl::~IDBFactoryBackendImpl() { } @@ -60,151 +57,16 @@ void IDBFactoryBackendImpl::removeIDBDatabaseBackend(const String& uniqueIdentif m_databaseBackendMap.remove(uniqueIdentifier); } -void IDBFactoryBackendImpl::removeSQLiteDatabase(const String& uniqueIdentifier) -{ - ASSERT(m_sqliteDatabaseMap.contains(uniqueIdentifier)); - m_sqliteDatabaseMap.remove(uniqueIdentifier); -} - -static PassRefPtr<IDBSQLiteDatabase> openSQLiteDatabase(SecurityOrigin* securityOrigin, const String& pathBase, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl* factory) -{ - String path = ":memory:"; - if (!pathBase.isEmpty()) { - if (!makeAllDirectories(pathBase)) { - // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. - LOG_ERROR("Unabled to create LocalStorage database path %s", pathBase.utf8().data()); - return 0; - } - - path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb"); - } - - RefPtr<IDBSQLiteDatabase> sqliteDatabase = IDBSQLiteDatabase::create(fileIdentifier, factory); - if (!sqliteDatabase->db().open(path)) { - // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. - LOG_ERROR("Failed to open database file %s for IndexedDB", path.utf8().data()); - return 0; - } - - // FIXME: Error checking? - sqliteDatabase->db().setMaximumSize(maximumSize); - sqliteDatabase->db().turnOnIncrementalAutoVacuum(); - - return sqliteDatabase.release(); -} - -static bool runCommands(SQLiteDatabase& sqliteDatabase, const char** commands, size_t numberOfCommands) -{ - SQLiteTransaction transaction(sqliteDatabase, false); - transaction.begin(); - for (size_t i = 0; i < numberOfCommands; ++i) { - if (!sqliteDatabase.executeCommand(commands[i])) { - LOG_ERROR("Failed to run the following command for IndexedDB: %s", commands[i]); - return false; - } - } - transaction.commit(); - return true; -} - -static bool createTables(SQLiteDatabase& sqliteDatabase) -{ - if (sqliteDatabase.tableExists("Databases")) - return true; - static const char* commands[] = { - "CREATE TABLE Databases (id INTEGER PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL, version TEXT NOT NULL)", - "CREATE UNIQUE INDEX Databases_name ON Databases(name)", - - "CREATE TABLE ObjectStores (id INTEGER PRIMARY KEY, name TEXT NOT NULL, keyPath TEXT, doAutoIncrement INTEGER NOT NULL, databaseId INTEGER NOT NULL REFERENCES Databases(id))", - "CREATE UNIQUE INDEX ObjectStores_composit ON ObjectStores(databaseId, name)", - - "CREATE TABLE Indexes (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), name TEXT NOT NULL, keyPath TEXT, isUnique INTEGER NOT NULL)", - "CREATE UNIQUE INDEX Indexes_composit ON Indexes(objectStoreId, name)", - - "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, value TEXT NOT NULL)", - "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)", - - "CREATE TABLE IndexData (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))", - "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)", - "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId)", - "CREATE INDEX IndexData_indexId ON IndexData(indexId)", - }; - - return runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0])); -} - -static bool createMetaDataTable(SQLiteDatabase& sqliteDatabase) +void IDBFactoryBackendImpl::addIDBBackingStore(const String& uniqueIdentifier, IDBBackingStore* backingStore) { - static const char* commands[] = { - "CREATE TABLE MetaData (name TEXT PRIMARY KEY, value NONE)", - "INSERT INTO MetaData VALUES ('version', 1)", - }; - - return runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0])); + ASSERT(!m_backingStoreMap.contains(uniqueIdentifier)); + m_backingStoreMap.set(uniqueIdentifier, backingStore); } -static bool getDatabaseVersion(SQLiteDatabase& sqliteDatabase, int* databaseVersion) +void IDBFactoryBackendImpl::removeIDBBackingStore(const String& uniqueIdentifier) { - SQLiteStatement query(sqliteDatabase, "SELECT value FROM MetaData WHERE name = 'version'"); - if (query.prepare() != SQLResultOk || query.step() != SQLResultRow) - return false; - - *databaseVersion = query.getColumnInt(0); - return query.finalize() == SQLResultOk; -} - -static bool migrateDatabase(SQLiteDatabase& sqliteDatabase) -{ - if (!sqliteDatabase.tableExists("MetaData")) { - if (!createMetaDataTable(sqliteDatabase)) - return false; - } - - int databaseVersion; - if (!getDatabaseVersion(sqliteDatabase, &databaseVersion)) - return false; - - if (databaseVersion == 1) { - static const char* commands[] = { - "DROP TABLE IF EXISTS ObjectStoreData2", - "CREATE TABLE ObjectStoreData2 (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, keyNumber REAL, value TEXT NOT NULL)", - "INSERT INTO ObjectStoreData2 SELECT * FROM ObjectStoreData", - "DROP TABLE ObjectStoreData", // This depends on SQLite not enforcing referential consistency. - "ALTER TABLE ObjectStoreData2 RENAME TO ObjectStoreData", - "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)", - "DROP TABLE IF EXISTS IndexData2", // This depends on SQLite not enforcing referential consistency. - "CREATE TABLE IndexData2 (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate REAL, keyNumber REAL, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))", - "INSERT INTO IndexData2 SELECT * FROM IndexData", - "DROP TABLE IndexData", - "ALTER TABLE IndexData2 RENAME TO IndexData", - "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)", - "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId)", - "CREATE INDEX IndexData_indexId ON IndexData(indexId)", - "UPDATE MetaData SET value = 2 WHERE name = 'version'", - }; - - if (!runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0]))) - return false; - - databaseVersion = 2; - } - - if (databaseVersion == 2) { - // We need to make the ObjectStoreData.value be a BLOB instead of TEXT. - static const char* commands[] = { - "DROP TABLE IF EXISTS ObjectStoreData", // This drops associated indices. - "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, keyNumber REAL, value BLOB NOT NULL)", - "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)", - "UPDATE MetaData SET value = 3 WHERE name = 'version'", - }; - - if (!runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0]))) - return false; - - databaseVersion = 3; - } - - return true; + ASSERT(m_backingStoreMap.contains(uniqueIdentifier)); + m_backingStoreMap.remove(uniqueIdentifier); } void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> securityOrigin, Frame*, const String& dataDir, int64_t maximumSize) @@ -219,22 +81,19 @@ void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> ca // FIXME: Everything from now on should be done on another thread. - RefPtr<IDBSQLiteDatabase> sqliteDatabase; - SQLiteDatabaseMap::iterator it2 = m_sqliteDatabaseMap.find(fileIdentifier); - if (it2 != m_sqliteDatabaseMap.end()) - sqliteDatabase = it2->second; + RefPtr<IDBBackingStore> backingStore; + IDBBackingStoreMap::iterator it2 = m_backingStoreMap.find(fileIdentifier); + if (it2 != m_backingStoreMap.end()) + backingStore = it2->second; else { - sqliteDatabase = openSQLiteDatabase(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this); - - if (!sqliteDatabase || !createTables(sqliteDatabase->db()) || !migrateDatabase(sqliteDatabase->db())) { + backingStore = IDBBackingStore::open(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this); + if (!backingStore) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error.")); - m_sqliteDatabaseMap.set(fileIdentifier, 0); return; } - m_sqliteDatabaseMap.set(fileIdentifier, sqliteDatabase.get()); } - RefPtr<IDBDatabaseBackendImpl> databaseBackend = IDBDatabaseBackendImpl::create(name, sqliteDatabase.get(), m_transactionCoordinator.get(), this, uniqueIdentifier); + RefPtr<IDBDatabaseBackendImpl> databaseBackend = IDBDatabaseBackendImpl::create(name, backingStore.get(), m_transactionCoordinator.get(), this, uniqueIdentifier); callbacks->onSuccess(databaseBackend.get()); m_databaseBackendMap.set(uniqueIdentifier, databaseBackend.get()); } diff --git a/Source/WebCore/storage/IDBFactoryBackendImpl.h b/Source/WebCore/storage/IDBFactoryBackendImpl.h index dcaf848..071bcf8 100644 --- a/Source/WebCore/storage/IDBFactoryBackendImpl.h +++ b/Source/WebCore/storage/IDBFactoryBackendImpl.h @@ -38,8 +38,8 @@ namespace WebCore { class DOMStringList; +class IDBBackingStore; class IDBDatabaseBackendImpl; -class IDBSQLiteDatabase; class IDBTransactionCoordinator; class IDBFactoryBackendImpl : public IDBFactoryBackendInterface { @@ -52,7 +52,8 @@ public: // Notifications from weak pointers. void removeIDBDatabaseBackend(const String& uniqueIdentifier); - void removeSQLiteDatabase(const String& uniqueIdentifier); + void addIDBBackingStore(const String& uniqueIdentifier, IDBBackingStore*); + void removeIDBBackingStore(const String& uniqueIdentifier); virtual void open(const String& name, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, Frame*, const String& dataDir, int64_t maximumSize); @@ -62,8 +63,8 @@ private: typedef HashMap<String, IDBDatabaseBackendImpl*> IDBDatabaseBackendMap; IDBDatabaseBackendMap m_databaseBackendMap; - typedef HashMap<String, IDBSQLiteDatabase*> SQLiteDatabaseMap; - SQLiteDatabaseMap m_sqliteDatabaseMap; + typedef HashMap<String, IDBBackingStore*> IDBBackingStoreMap; + IDBBackingStoreMap m_backingStoreMap; RefPtr<IDBTransactionCoordinator> m_transactionCoordinator; @@ -76,4 +77,3 @@ private: #endif #endif // IDBFactoryBackendImpl_h - diff --git a/Source/WebCore/storage/IDBIndex.cpp b/Source/WebCore/storage/IDBIndex.cpp index 615767b..5f2ecf2 100644 --- a/Source/WebCore/storage/IDBIndex.cpp +++ b/Source/WebCore/storage/IDBIndex.cpp @@ -33,6 +33,7 @@ #include "IDBIndexBackendInterface.h" #include "IDBKey.h" #include "IDBKeyRange.h" +#include "IDBObjectStore.h" #include "IDBRequest.h" #include "IDBTransaction.h" @@ -40,11 +41,13 @@ namespace WebCore { static const unsigned short defaultDirection = IDBCursor::NEXT; -IDBIndex::IDBIndex(PassRefPtr<IDBIndexBackendInterface> backend, IDBTransaction* transaction) +IDBIndex::IDBIndex(PassRefPtr<IDBIndexBackendInterface> backend, IDBObjectStore* objectStore, IDBTransaction* transaction) : m_backend(backend) + , m_objectStore(objectStore) , m_transaction(transaction) { ASSERT(m_backend); + ASSERT(m_objectStore); ASSERT(m_transaction); } @@ -61,9 +64,12 @@ PassRefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, Pas } RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + request->setCursorType(IDBCursorBackendInterface::IndexCursor); m_backend->openCursor(keyRange, direction, request, m_transaction->backend(), ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; + } return request; } @@ -76,9 +82,12 @@ PassRefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context, } RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + request->setCursorType(IDBCursorBackendInterface::IndexKeyCursor); m_backend->openKeyCursor(keyRange, direction, request, m_transaction->backend(), ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; + } return request; } @@ -86,8 +95,10 @@ PassRefPtr<IDBRequest> IDBIndex::get(ScriptExecutionContext* context, PassRefPtr { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); m_backend->get(key, request, m_transaction->backend(), ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; + } return request; } @@ -95,8 +106,10 @@ PassRefPtr<IDBRequest> IDBIndex::getKey(ScriptExecutionContext* context, PassRef { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); m_backend->getKey(key, request, m_transaction->backend(), ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; + } return request; } diff --git a/Source/WebCore/storage/IDBIndex.h b/Source/WebCore/storage/IDBIndex.h index 10b46f1..2af1805 100644 --- a/Source/WebCore/storage/IDBIndex.h +++ b/Source/WebCore/storage/IDBIndex.h @@ -37,17 +37,19 @@ namespace WebCore { +class IDBObjectStore; + class IDBIndex : public RefCounted<IDBIndex> { public: - static PassRefPtr<IDBIndex> create(PassRefPtr<IDBIndexBackendInterface> backend, IDBTransaction* transaction) + static PassRefPtr<IDBIndex> create(PassRefPtr<IDBIndexBackendInterface> backend, IDBObjectStore* objectStore, IDBTransaction* transaction) { - return adoptRef(new IDBIndex(backend, transaction)); + return adoptRef(new IDBIndex(backend, objectStore, transaction)); } ~IDBIndex(); // Implement the IDL String name() const { return m_backend->name(); } - String storeName() const { return m_backend->storeName(); } + IDBObjectStore* objectStore() const { return m_objectStore.get(); } String keyPath() const { return m_backend->keyPath(); } bool unique() const { return m_backend->unique(); } @@ -64,9 +66,10 @@ public: PassRefPtr<IDBRequest> getKey(ScriptExecutionContext*, PassRefPtr<IDBKey>, ExceptionCode&); private: - IDBIndex(PassRefPtr<IDBIndexBackendInterface>, IDBTransaction*); + IDBIndex(PassRefPtr<IDBIndexBackendInterface>, IDBObjectStore*, IDBTransaction*); RefPtr<IDBIndexBackendInterface> m_backend; + RefPtr<IDBObjectStore> m_objectStore; RefPtr<IDBTransaction> m_transaction; }; diff --git a/Source/WebCore/storage/IDBIndex.idl b/Source/WebCore/storage/IDBIndex.idl index 13089cc..6db50e1 100644 --- a/Source/WebCore/storage/IDBIndex.idl +++ b/Source/WebCore/storage/IDBIndex.idl @@ -29,7 +29,7 @@ module storage { Conditional=INDEXED_DATABASE ] IDBIndex { readonly attribute DOMString name; - readonly attribute DOMString storeName; + readonly attribute IDBObjectStore objectStore; readonly attribute DOMString keyPath; readonly attribute boolean unique; diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.cpp b/Source/WebCore/storage/IDBIndexBackendImpl.cpp index 6eba189..62e5364 100644 --- a/Source/WebCore/storage/IDBIndexBackendImpl.cpp +++ b/Source/WebCore/storage/IDBIndexBackendImpl.cpp @@ -29,6 +29,7 @@ #if ENABLE(INDEXED_DATABASE) #include "CrossThreadTask.h" +#include "IDBBackingStore.h" #include "IDBCallbacks.h" #include "IDBCursorBackendImpl.h" #include "IDBDatabaseBackendImpl.h" @@ -36,14 +37,11 @@ #include "IDBKey.h" #include "IDBKeyRange.h" #include "IDBObjectStoreBackendImpl.h" -#include "IDBSQLiteDatabase.h" -#include "SQLiteDatabase.h" -#include "SQLiteStatement.h" namespace WebCore { -IDBIndexBackendImpl::IDBIndexBackendImpl(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique) - : m_database(database) +IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique) + : m_backingStore(backingStore) , m_id(id) , m_name(name) , m_storeName(storeName) @@ -52,8 +50,8 @@ IDBIndexBackendImpl::IDBIndexBackendImpl(IDBSQLiteDatabase* database, int64_t id { } -IDBIndexBackendImpl::IDBIndexBackendImpl(IDBSQLiteDatabase* database, const String& name, const String& storeName, const String& keyPath, bool unique) - : m_database(database) +IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, const String& name, const String& storeName, const String& keyPath, bool unique) + : m_backingStore(backingStore) , m_id(InvalidId) , m_name(name) , m_storeName(storeName) @@ -66,40 +64,26 @@ IDBIndexBackendImpl::~IDBIndexBackendImpl() { } -void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBKeyRange> range, unsigned short untypedDirection, bool objectCursor, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) +void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBKeyRange> range, unsigned short untypedDirection, IDBCursorBackendInterface::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) { - // Several files depend on this order of selects. - String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ") - + ("ObjectStoreData.value, ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ") - + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id WHERE "; - - bool lowerBound = range && range->lower(); - bool upperBound = range && range->upper(); - - if (lowerBound) - sql += range->lower()->lowerCursorWhereFragment(range->lowerWhereClauseComparisonOperator(), "IndexData."); - if (upperBound) - sql += range->upper()->upperCursorWhereFragment(range->upperWhereClauseComparisonOperator(), "IndexData."); - sql += "IndexData.indexId = ? ORDER BY "; - IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(untypedDirection); - if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE) - sql += "IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, IndexData.id"; - else - sql += "IndexData.keyString DESC, IndexData.keyDate DESC, IndexData.keyNumber DESC, IndexData.id DESC"; - - OwnPtr<SQLiteStatement> query = adoptPtr(new SQLiteStatement(index->sqliteDatabase(), sql)); - bool ok = query->prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - - int indexColumn = 1; - if (lowerBound) - indexColumn += range->lower()->bind(*query, indexColumn); - if (upperBound) - indexColumn += range->upper()->bind(*query, indexColumn); - query->bindInt64(indexColumn, index->id()); - - if (query->step() != SQLResultRow) { + + RefPtr<IDBBackingStore::Cursor> backingStoreCursor; + + switch (cursorType) { + case IDBCursorBackendInterface::IndexKeyCursor: + backingStoreCursor = index->m_backingStore->openIndexKeyCursor(index->id(), range.get(), direction); + break; + case IDBCursorBackendInterface::IndexCursor: + backingStoreCursor = index->m_backingStore->openIndexCursor(index->id(), range.get(), direction); + break; + case IDBCursorBackendInterface::ObjectStoreCursor: + case IDBCursorBackendInterface::InvalidCursorType: + ASSERT_NOT_REACHED(); + break; + } + + if (!backingStoreCursor) { callbacks->onSuccess(SerializedScriptValue::nullValue()); return; } @@ -108,7 +92,7 @@ void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr RefPtr<IDBObjectStoreBackendInterface> objectStore = transaction->objectStore(index->m_storeName, ec); ASSERT(objectStore && !ec); - RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(index->m_database.get(), range, direction, query.release(), objectCursor, transaction.get(), objectStore.get()); + RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(backingStoreCursor.get(), direction, cursorType, transaction.get(), objectStore.get()); callbacks->onSuccess(cursor.release()); } @@ -118,7 +102,7 @@ void IDBIndexBackendImpl::openCursor(PassRefPtr<IDBKeyRange> prpKeyRange, unsign RefPtr<IDBKeyRange> keyRange = prpKeyRange; RefPtr<IDBCallbacks> callbacks = prpCallbacks; RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr; - if (!transaction->scheduleTask(createCallbackTask(&openCursorInternal, index, keyRange, direction, true, callbacks, transaction))) + if (!transaction->scheduleTask(createCallbackTask(&openCursorInternal, index, keyRange, direction, IDBCursorBackendInterface::IndexCursor, callbacks, transaction))) ec = IDBDatabaseException::NOT_ALLOWED_ERR; } @@ -128,33 +112,28 @@ void IDBIndexBackendImpl::openKeyCursor(PassRefPtr<IDBKeyRange> prpKeyRange, uns RefPtr<IDBKeyRange> keyRange = prpKeyRange; RefPtr<IDBCallbacks> callbacks = prpCallbacks; RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr; - if (!transaction->scheduleTask(createCallbackTask(&openCursorInternal, index, keyRange, direction, false, callbacks, transaction))) + if (!transaction->scheduleTask(createCallbackTask(&openCursorInternal, index, keyRange, direction, IDBCursorBackendInterface::IndexKeyCursor, callbacks, transaction))) ec = IDBDatabaseException::NOT_ALLOWED_ERR; } void IDBIndexBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBKey> key, bool getObject, PassRefPtr<IDBCallbacks> callbacks) { - String sql = String("SELECT ") - + (getObject ? "ObjectStoreData.value " : "ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ") - + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id " - + "WHERE IndexData.indexId = ? AND " + key->whereSyntax("IndexData.") - + "ORDER BY IndexData.id LIMIT 1"; // Order by insertion order when all else fails. - SQLiteStatement query(index->sqliteDatabase(), sql); - bool ok = query.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - - query.bindInt64(1, index->id()); - key->bind(query, 2); - if (query.step() != SQLResultRow) { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index.")); - return; + // FIXME: Split getInternal into two functions, getting rid off |getObject|. + if (getObject) { + String value = index->m_backingStore->getObjectViaIndex(index->id(), *key); + if (value.isNull()) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index.")); + return; + } + callbacks->onSuccess(SerializedScriptValue::createFromWire(value)); + } else { + RefPtr<IDBKey> keyResult = index->m_backingStore->getPrimaryKeyViaIndex(index->id(), *key); + if (!keyResult) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index.")); + return; + } + callbacks->onSuccess(keyResult.get()); } - - if (getObject) - callbacks->onSuccess(SerializedScriptValue::createFromWire(query.getColumnText(0))); - else - callbacks->onSuccess(IDBKey::fromQuery(query, 0)); - ASSERT(query.step() != SQLResultRow); } void IDBIndexBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) @@ -175,34 +154,12 @@ void IDBIndexBackendImpl::getKey(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallba ec = IDBDatabaseException::NOT_ALLOWED_ERR; } -static String whereClause(IDBKey* key) -{ - return "WHERE indexId = ? AND " + key->whereSyntax(); -} - -static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key) -{ - query.bindInt64(1, id); - key->bind(query, 2); -} - bool IDBIndexBackendImpl::addingKeyAllowed(IDBKey* key) { if (!m_unique) return true; - SQLiteStatement query(sqliteDatabase(), "SELECT id FROM IndexData " + whereClause(key)); - bool ok = query.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - bindWhereClause(query, m_id, key); - bool existingValue = query.step() == SQLResultRow; - - return !existingValue; -} - -SQLiteDatabase& IDBIndexBackendImpl::sqliteDatabase() const -{ - return m_database->db(); + return !m_backingStore->keyExistsInIndex(m_id, *key); } } // namespace WebCore diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.h b/Source/WebCore/storage/IDBIndexBackendImpl.h index e640b5a..e2a06b8 100644 --- a/Source/WebCore/storage/IDBIndexBackendImpl.h +++ b/Source/WebCore/storage/IDBIndexBackendImpl.h @@ -26,27 +26,27 @@ #ifndef IDBIndexBackendImpl_h #define IDBIndexBackendImpl_h -#include "IDBIndexBackendInterface.h" - #if ENABLE(INDEXED_DATABASE) +#include "IDBCursorBackendInterface.h" +#include "IDBIndexBackendInterface.h" + namespace WebCore { +class IDBBackingStore; class IDBKey; class IDBObjectStoreBackendImpl; -class IDBSQLiteDatabase; -class SQLiteDatabase; class ScriptExecutionContext; class IDBIndexBackendImpl : public IDBIndexBackendInterface { public: - static PassRefPtr<IDBIndexBackendImpl> create(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique) + static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique) { - return adoptRef(new IDBIndexBackendImpl(database, id, name, storeName, keyPath, unique)); + return adoptRef(new IDBIndexBackendImpl(backingStore, id, name, storeName, keyPath, unique)); } - static PassRefPtr<IDBIndexBackendImpl> create(IDBSQLiteDatabase* database, const String& name, const String& storeName, const String& keyPath, bool unique) + static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, const String& name, const String& storeName, const String& keyPath, bool unique) { - return adoptRef(new IDBIndexBackendImpl(database, name, storeName, keyPath, unique)); + return adoptRef(new IDBIndexBackendImpl(backingStore, name, storeName, keyPath, unique)); } virtual ~IDBIndexBackendImpl(); @@ -56,6 +56,7 @@ public: return m_id; } void setId(int64_t id) { m_id = id; } + bool hasValidId() const { return m_id != InvalidId; }; bool addingKeyAllowed(IDBKey*); @@ -71,17 +72,15 @@ public: virtual void getKey(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); private: - IDBIndexBackendImpl(IDBSQLiteDatabase*, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique); - IDBIndexBackendImpl(IDBSQLiteDatabase*, const String& name, const String& storeName, const String& keyPath, bool unique); - - SQLiteDatabase& sqliteDatabase() const; + IDBIndexBackendImpl(IDBBackingStore*, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique); + IDBIndexBackendImpl(IDBBackingStore*, const String& name, const String& storeName, const String& keyPath, bool unique); - static void openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKeyRange>, unsigned short direction, bool objectCursor, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); + static void openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKeyRange>, unsigned short direction, IDBCursorBackendInterface::CursorType, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); static void getInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKey>, bool getObject, PassRefPtr<IDBCallbacks>); static const int64_t InvalidId = 0; - RefPtr<IDBSQLiteDatabase> m_database; + RefPtr<IDBBackingStore> m_backingStore; int64_t m_id; String m_name; diff --git a/Source/WebCore/storage/IDBKey.cpp b/Source/WebCore/storage/IDBKey.cpp index 5f45543..35156e8 100644 --- a/Source/WebCore/storage/IDBKey.cpp +++ b/Source/WebCore/storage/IDBKey.cpp @@ -28,9 +28,6 @@ #if ENABLE(INDEXED_DATABASE) -#include "SQLiteStatement.h" -#include "SerializedScriptValue.h" - namespace WebCore { IDBKey::IDBKey() @@ -42,35 +39,21 @@ IDBKey::~IDBKey() { } -PassRefPtr<IDBKey> IDBKey::fromQuery(SQLiteStatement& query, int baseColumn) -{ - if (query.columnCount() <= baseColumn) - return 0; - - if (!query.isColumnNull(baseColumn)) - return IDBKey::createString(query.getColumnText(baseColumn)); - - if (!query.isColumnNull(baseColumn + 1)) - return IDBKey::createDate(query.getColumnDouble(baseColumn + 1)); - - if (!query.isColumnNull(baseColumn + 2)) - return IDBKey::createNumber(query.getColumnDouble(baseColumn + 2)); - - return IDBKey::createNull(); -} - -bool IDBKey::isEqual(IDBKey* other) +bool IDBKey::isLessThan(const IDBKey* other) const { - if (!other || other->m_type != m_type) + ASSERT(other); + if (other->m_type < m_type) + return true; + if (other->m_type > m_type) return false; switch (m_type) { case StringType: - return other->m_string == m_string; + return codePointCompare(other->m_string, m_string) > 0; case DateType: - return other->m_date == m_date; + return other->m_date > m_date; case NumberType: - return other->m_number == m_number; + return other->m_number > m_number; case NullType: return true; } @@ -79,106 +62,24 @@ bool IDBKey::isEqual(IDBKey* other) return false; } -String IDBKey::whereSyntax(String qualifiedTableName) const -{ - switch (m_type) { - case IDBKey::StringType: - return qualifiedTableName + "keyString = ? AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL "; - case IDBKey::NumberType: - return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber = ? "; - case IDBKey::DateType: - return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate = ? AND " + qualifiedTableName + "keyNumber IS NULL "; - case IDBKey::NullType: - return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL "; - } - - ASSERT_NOT_REACHED(); - return ""; -} - -String IDBKey::lowerCursorWhereFragment(String comparisonOperator, String qualifiedTableName) +bool IDBKey::isEqual(const IDBKey* other) const { - switch (m_type) { - case StringType: - return "? " + comparisonOperator + " " + qualifiedTableName + "keyString AND "; - case DateType: - return "(? " + comparisonOperator + " " + qualifiedTableName + "keyDate OR NOT " + qualifiedTableName + "keyString IS NULL) AND "; - case NumberType: - return "(? " + comparisonOperator + " " + qualifiedTableName + "keyNumber OR NOT " + qualifiedTableName + "keyString IS NULL OR NOT " + qualifiedTableName + "keyDate IS NULL) AND "; - case NullType: - if (comparisonOperator == "<") - return "NOT(" + qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL) AND "; - return ""; // If it's =, the upper bound half will do the constraining. If it's <=, then that's a no-op. - } - ASSERT_NOT_REACHED(); - return ""; -} + if (!other || other->m_type != m_type) + return false; -String IDBKey::upperCursorWhereFragment(String comparisonOperator, String qualifiedTableName) -{ switch (m_type) { case StringType: - return "(" + qualifiedTableName + "keyString " + comparisonOperator + " ? OR " + qualifiedTableName + "keyString IS NULL) AND "; + return other->m_string == m_string; case DateType: - return "(" + qualifiedTableName + "keyDate " + comparisonOperator + " ? OR " + qualifiedTableName + "keyDate IS NULL) AND " + qualifiedTableName + "keyString IS NULL AND "; + return other->m_date == m_date; case NumberType: - return "(" + qualifiedTableName + "keyNumber " + comparisonOperator + " ? OR " + qualifiedTableName + "keyNumber IS NULL) AND " + qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND "; + return other->m_number == m_number; case NullType: - if (comparisonOperator == "<") - return "0 != 0 AND "; - return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL AND "; - } - ASSERT_NOT_REACHED(); - return ""; -} - -// Returns the number of items bound. -int IDBKey::bind(SQLiteStatement& query, int column) const -{ - switch (m_type) { - case IDBKey::StringType: - query.bindText(column, m_string); - return 1; - case IDBKey::DateType: - query.bindDouble(column, m_date); - return 1; - case IDBKey::NumberType: - query.bindDouble(column, m_number); - return 1; - case IDBKey::NullType: - return 0; + return true; } ASSERT_NOT_REACHED(); - return 0; -} - -void IDBKey::bindWithNulls(SQLiteStatement& query, int baseColumn) const -{ - switch (m_type) { - case IDBKey::StringType: - query.bindText(baseColumn + 0, m_string); - query.bindNull(baseColumn + 1); - query.bindNull(baseColumn + 2); - break; - case IDBKey::DateType: - query.bindNull(baseColumn + 0); - query.bindDouble(baseColumn + 1, m_date); - query.bindNull(baseColumn + 2); - break; - case IDBKey::NumberType: - query.bindNull(baseColumn + 0); - query.bindNull(baseColumn + 1); - query.bindDouble(baseColumn + 2, m_number); - break; - case IDBKey::NullType: - query.bindNull(baseColumn + 0); - query.bindNull(baseColumn + 1); - query.bindNull(baseColumn + 2); - break; - default: - ASSERT_NOT_REACHED(); - } + return false; } } // namespace WebCore diff --git a/Source/WebCore/storage/IDBKey.h b/Source/WebCore/storage/IDBKey.h index 9118743..5816ce3 100644 --- a/Source/WebCore/storage/IDBKey.h +++ b/Source/WebCore/storage/IDBKey.h @@ -34,8 +34,6 @@ namespace WebCore { -class SQLiteStatement; - class IDBKey : public ThreadSafeShared<IDBKey> { public: static PassRefPtr<IDBKey> createNull() @@ -99,14 +97,8 @@ public: return m_number; } - static PassRefPtr<IDBKey> fromQuery(SQLiteStatement& query, int baseColumn); - - bool isEqual(IDBKey* other); - String whereSyntax(String qualifiedTableName = "") const; - String lowerCursorWhereFragment(String comparisonOperator, String qualifiedTableName = ""); - String upperCursorWhereFragment(String comparisonOperator, String qualifiedTableName = ""); - int bind(SQLiteStatement& query, int column) const; - void bindWithNulls(SQLiteStatement& query, int baseColumn) const; + bool isLessThan(const IDBKey* other) const; + bool isEqual(const IDBKey* other) const; using ThreadSafeShared<IDBKey>::ref; using ThreadSafeShared<IDBKey>::deref; diff --git a/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp b/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp index b7c45c3..4f0fcee 100644 --- a/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp +++ b/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp @@ -37,4 +37,10 @@ void IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(const Ve // FIXME: Implement this method once JSC supports WireFormat for SerializedScriptValue. } +PassRefPtr<SerializedScriptValue> IDBKeyPathBackendImpl::injectIDBKeyIntoSerializedValue(PassRefPtr<IDBKey> key, PassRefPtr<SerializedScriptValue> value, const String& keyPath) +{ + // FIXME: Implement this method once JSC supports WireFormat for SerializedScriptValue. + return 0; +} + #endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBKeyPathBackendImpl.h b/Source/WebCore/storage/IDBKeyPathBackendImpl.h index 32af5e3..1c76232 100644 --- a/Source/WebCore/storage/IDBKeyPathBackendImpl.h +++ b/Source/WebCore/storage/IDBKeyPathBackendImpl.h @@ -38,6 +38,7 @@ class SerializedScriptValue; class IDBKeyPathBackendImpl { public: static void createIDBKeysFromSerializedValuesAndKeyPath(const Vector<RefPtr<SerializedScriptValue>, 0>& values, const String& keyPath, Vector<RefPtr<IDBKey>, 0>& keys); + static PassRefPtr<SerializedScriptValue> injectIDBKeyIntoSerializedValue(PassRefPtr<IDBKey>, PassRefPtr<SerializedScriptValue>, const String& keyPath); }; } diff --git a/Source/WebCore/storage/IDBKeyRange.cpp b/Source/WebCore/storage/IDBKeyRange.cpp index 142b3bd..4f4144b 100644 --- a/Source/WebCore/storage/IDBKeyRange.cpp +++ b/Source/WebCore/storage/IDBKeyRange.cpp @@ -56,31 +56,11 @@ PassRefPtr<IDBKeyRange> IDBKeyRange::upperBound(PassRefPtr<IDBKey> bound, bool o return IDBKeyRange::create(0, bound, false, open); } -PassRefPtr<IDBKeyRange> IDBKeyRange::bound(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, const OptionsObject& options) +PassRefPtr<IDBKeyRange> IDBKeyRange::bound(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, bool lowerOpen, bool upperOpen) { - bool lowerOpen = false; - bool upperOpen = false; - options.getKeyBool("lowerOpen", lowerOpen); - options.getKeyBool("upperOpen", upperOpen); return IDBKeyRange::create(lower, upper, lowerOpen, upperOpen); } -String IDBKeyRange::lowerWhereClauseComparisonOperator() const -{ - ASSERT(m_lower); - if (m_lowerOpen) - return "<"; - return "<="; -} - -String IDBKeyRange::upperWhereClauseComparisonOperator() const -{ - ASSERT(m_upper); - if (m_upperOpen) - return "<"; - return "<="; -} - } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBKeyRange.h b/Source/WebCore/storage/IDBKeyRange.h index 8af48fe..636a5c1 100644 --- a/Source/WebCore/storage/IDBKeyRange.h +++ b/Source/WebCore/storage/IDBKeyRange.h @@ -43,19 +43,15 @@ public: } ~IDBKeyRange() { } - PassRefPtr<IDBKey> lower() const { return m_lower; } PassRefPtr<IDBKey> upper() const { return m_upper; } bool lowerOpen() const { return m_lowerOpen; } bool upperOpen() const { return m_upperOpen; } - String lowerWhereClauseComparisonOperator() const; - String upperWhereClauseComparisonOperator() const; - static PassRefPtr<IDBKeyRange> only(PassRefPtr<IDBKey> value); static PassRefPtr<IDBKeyRange> lowerBound(PassRefPtr<IDBKey> bound, bool open = false); static PassRefPtr<IDBKeyRange> upperBound(PassRefPtr<IDBKey> bound, bool open = false); - static PassRefPtr<IDBKeyRange> bound(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, const OptionsObject& = OptionsObject()); + static PassRefPtr<IDBKeyRange> bound(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, bool lowerOpen = false, bool upperOpen = false); private: IDBKeyRange(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, bool lowerOpen, bool upperOpen); diff --git a/Source/WebCore/storage/IDBKeyRange.idl b/Source/WebCore/storage/IDBKeyRange.idl index d7fa075..6dd4c85 100644 --- a/Source/WebCore/storage/IDBKeyRange.idl +++ b/Source/WebCore/storage/IDBKeyRange.idl @@ -37,7 +37,7 @@ module storage { [ClassMethod] IDBKeyRange only(in IDBKey value); [ClassMethod] IDBKeyRange lowerBound(in IDBKey bound, in [Optional] boolean open); [ClassMethod] IDBKeyRange upperBound(in IDBKey bound, in [Optional] boolean open); - [ClassMethod] IDBKeyRange bound(in IDBKey lower, in IDBKey upper, in [Optional] OptionsObject options); + [ClassMethod] IDBKeyRange bound(in IDBKey lower, in IDBKey upper, in [Optional] boolean lowerOpen, in [Optional] boolean upperOpen); }; } diff --git a/Source/WebCore/storage/IDBObjectStore.cpp b/Source/WebCore/storage/IDBObjectStore.cpp index 53ae279..1a3e741 100644 --- a/Source/WebCore/storage/IDBObjectStore.cpp +++ b/Source/WebCore/storage/IDBObjectStore.cpp @@ -71,8 +71,10 @@ PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, Pass { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); m_objectStore->get(key, request, m_transaction->backend(), ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; + } return request.release(); } @@ -80,36 +82,44 @@ PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, Pass { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); m_objectStore->put(value, key, IDBObjectStoreBackendInterface::AddOnly, request, m_transaction->backend(), ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; - return request; + } + return request.release(); } PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, ExceptionCode& ec) { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); m_objectStore->put(value, key, IDBObjectStoreBackendInterface::AddOrUpdate, request, m_transaction->backend(), ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; - return request; + } + return request.release(); } PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec) { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); m_objectStore->deleteFunction(key, request, m_transaction->backend(), ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; - return request; + } + return request.release(); } PassRefPtr<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec) { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); m_objectStore->clear(request, m_transaction->backend(), ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; - return request; + } + return request.release(); } PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const String& keyPath, const OptionsObject& options, ExceptionCode& ec) @@ -121,7 +131,7 @@ PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const Strin ASSERT(!index != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa. if (!index) return 0; - return IDBIndex::create(index.release(), m_transaction.get()); + return IDBIndex::create(index.release(), this, m_transaction.get()); } PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec) @@ -130,7 +140,7 @@ PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec ASSERT(!index != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa. if (!index) return 0; - return IDBIndex::create(index.release(), m_transaction.get()); + return IDBIndex::create(index.release(), this, m_transaction.get()); } void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec) @@ -147,9 +157,12 @@ PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* contex } RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + request->setCursorType(IDBCursorBackendInterface::ObjectStoreCursor); m_objectStore->openCursor(range, direction, request, m_transaction->backend(), ec); - if (ec) + if (ec) { + request->markEarlyDeath(); return 0; + } return request.release(); } diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp index 921bbab..0433ed7 100644 --- a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp +++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp @@ -30,6 +30,7 @@ #include "CrossThreadTask.h" #include "DOMStringList.h" +#include "IDBBackingStore.h" #include "IDBBindingUtilities.h" #include "IDBCallbacks.h" #include "IDBCursorBackendImpl.h" @@ -40,12 +41,8 @@ #include "IDBKeyPath.h" #include "IDBKeyPathBackendImpl.h" #include "IDBKeyRange.h" -#include "IDBSQLiteDatabase.h" #include "IDBTransactionBackendInterface.h" #include "ScriptExecutionContext.h" -#include "SQLiteDatabase.h" -#include "SQLiteStatement.h" -#include "SQLiteTransaction.h" namespace WebCore { @@ -53,8 +50,8 @@ IDBObjectStoreBackendImpl::~IDBObjectStoreBackendImpl() { } -IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& keyPath, bool autoIncrement) - : m_database(database) +IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, int64_t id, const String& name, const String& keyPath, bool autoIncrement) + : m_backingStore(backingStore) , m_id(id) , m_name(name) , m_keyPath(keyPath) @@ -64,8 +61,8 @@ IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBSQLiteDatabase* database loadIndexes(); } -IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBSQLiteDatabase* database, const String& name, const String& keyPath, bool autoIncrement) - : m_database(database) +IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, const String& name, const String& keyPath, bool autoIncrement) + : m_backingStore(backingStore) , m_id(InvalidId) , m_name(name) , m_keyPath(keyPath) @@ -82,17 +79,6 @@ PassRefPtr<DOMStringList> IDBObjectStoreBackendImpl::indexNames() const return indexNames.release(); } -static String whereClause(IDBKey* key) -{ - return "WHERE objectStoreId = ? AND " + key->whereSyntax(); -} - -static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key) -{ - query.bindInt64(1, id); - key->bind(query, 2); -} - void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) { RefPtr<IDBObjectStoreBackendImpl> objectStore = this; @@ -104,22 +90,13 @@ void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCal void IDBObjectStoreBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) { - SQLiteStatement query(objectStore->sqliteDatabase(), "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData " + whereClause(key.get())); - bool ok = query.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - - bindWhereClause(query, objectStore->id(), key.get()); - if (query.step() != SQLResultRow) { + String wireData = objectStore->m_backingStore->getObjectStoreRecord(objectStore->id(), *key); + if (wireData.isNull()) { callbacks->onSuccess(SerializedScriptValue::undefinedValue()); return; } - ASSERT((key->type() == IDBKey::StringType) != query.isColumnNull(0)); - ASSERT((key->type() == IDBKey::DateType) != query.isColumnNull(1)); - ASSERT((key->type() == IDBKey::NumberType) != query.isColumnNull(2)); - - callbacks->onSuccess(SerializedScriptValue::createFromWire(query.getColumnBlobAsString(3))); - ASSERT(query.step() != SQLResultRow); + callbacks->onSuccess(SerializedScriptValue::createFromWire(wireData)); } static PassRefPtr<IDBKey> fetchKeyFromKeyPath(SerializedScriptValue* value, const String& keyPath) @@ -134,49 +111,9 @@ static PassRefPtr<IDBKey> fetchKeyFromKeyPath(SerializedScriptValue* value, cons return keys[0].release(); } -static bool putObjectStoreData(SQLiteDatabase& db, IDBKey* key, SerializedScriptValue* value, int64_t objectStoreId, int64_t& dataRowId) +static PassRefPtr<SerializedScriptValue> injectKeyIntoKeyPath(PassRefPtr<IDBKey> key, PassRefPtr<SerializedScriptValue> value, const String& keyPath) { - String sql = dataRowId != IDBObjectStoreBackendImpl::InvalidId ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?" - : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)"; - SQLiteStatement query(db, sql); - if (query.prepare() != SQLResultOk) - return false; - key->bindWithNulls(query, 1); - query.bindBlob(4, value->toWireString()); - if (dataRowId != IDBDatabaseBackendImpl::InvalidId) - query.bindInt64(5, dataRowId); - else - query.bindInt64(5, objectStoreId); - - if (query.step() != SQLResultDone) - return false; - - if (dataRowId == IDBDatabaseBackendImpl::InvalidId) - dataRowId = db.lastInsertRowID(); - - return true; -} - -static bool deleteIndexData(SQLiteDatabase& db, int64_t objectStoreDataId) -{ - SQLiteStatement deleteQuery(db, "DELETE FROM IndexData WHERE objectStoreDataId = ?"); - if (deleteQuery.prepare() != SQLResultOk) - return false; - deleteQuery.bindInt64(1, objectStoreDataId); - - return deleteQuery.step() == SQLResultDone; -} - -static bool putIndexData(SQLiteDatabase& db, IDBKey* key, int64_t indexId, int64_t objectStoreDataId) -{ - SQLiteStatement putQuery(db, "INSERT INTO IndexData (keyString, keyDate, keyNumber, indexId, objectStoreDataId) VALUES (?, ?, ?, ?, ?)"); - if (putQuery.prepare() != SQLResultOk) - return false; - key->bindWithNulls(putQuery, 1); - putQuery.bindInt64(4, indexId); - putQuery.bindInt64(5, objectStoreDataId); - - return putQuery.step() == SQLResultDone; + return IDBKeyPathBackendImpl::injectIDBKeyIntoSerializedValue(key, value, keyPath); } void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) @@ -197,7 +134,7 @@ void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, ec = IDBDatabaseException::NOT_ALLOWED_ERR; } -PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::selectKeyForPut(IDBObjectStoreBackendImpl* objectStore, SerializedScriptValue* value, IDBKey* key, PutMode putMode, IDBCallbacks* callbacks) +PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::selectKeyForPut(IDBObjectStoreBackendImpl* objectStore, IDBKey* key, PutMode putMode, IDBCallbacks* callbacks, RefPtr<SerializedScriptValue>& value) { if (putMode == CursorUpdate) ASSERT(key); @@ -220,19 +157,24 @@ PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::selectKeyForPut(IDBObjectStoreBack if (!hasKeyPath) return objectStore->genAutoIncrementKey(); - RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value, objectStore->m_keyPath); + RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath); if (keyPathKey) { objectStore->resetAutoIncrementKeyCache(); return keyPathKey; } - // FIXME: Generate auto increment key, and inject it through the key path. - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Adding data to object stores with auto increment and in-line keys not yet supported.")); - return 0; + RefPtr<IDBKey> autoIncKey = objectStore->genAutoIncrementKey(); + RefPtr<SerializedScriptValue> valueAfterInjection = injectKeyIntoKeyPath(autoIncKey, value, objectStore->m_keyPath); + if (!valueAfterInjection) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "The generated key could not be inserted into the object using the keyPath.")); + return 0; + } + value = valueAfterInjection; + return autoIncKey.release(); } if (hasKeyPath) { - RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value, objectStore->m_keyPath); + RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath); if (!keyPathKey) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "The key could not be fetched from the keyPath.")); @@ -258,7 +200,7 @@ PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::selectKeyForPut(IDBObjectStoreBack void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) { RefPtr<SerializedScriptValue> value = prpValue; - RefPtr<IDBKey> key = selectKeyForPut(objectStore.get(), value.get(), prpKey.get(), putMode, callbacks.get()); + RefPtr<IDBKey> key = selectKeyForPut(objectStore.get(), prpKey.get(), putMode, callbacks.get(), value); if (!key) return; @@ -285,12 +227,9 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr< indexKeys.append(key.release()); } - SQLiteStatement getQuery(objectStore->sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key.get())); - bool ok = getQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + int64_t dataRowId = InvalidId; + bool isExistingValue = objectStore->m_backingStore->keyExistsInObjectStore(objectStore->id(), *key, dataRowId); - bindWhereClause(getQuery, objectStore->id(), key.get()); - bool isExistingValue = getQuery.step() == SQLResultRow; if (putMode == AddOnly && isExistingValue) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store.")); return; @@ -298,15 +237,14 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr< // Before this point, don't do any mutation. After this point, rollback the transaction in case of error. - int64_t dataRowId = isExistingValue ? getQuery.getColumnInt(0) : InvalidId; - if (!putObjectStoreData(objectStore->sqliteDatabase(), key.get(), value.get(), objectStore->id(), dataRowId)) { + if (!objectStore->m_backingStore->putObjectStoreRecord(objectStore->id(), *key, value->toWireString(), dataRowId, dataRowId == InvalidId)) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); return; } - if (!deleteIndexData(objectStore->sqliteDatabase(), dataRowId)) { + if (!objectStore->m_backingStore->deleteIndexDataForRecord(dataRowId)) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); @@ -315,7 +253,9 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr< int i = 0; for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it, ++i) { - if (!putIndexData(objectStore->sqliteDatabase(), indexKeys[i].get(), it->second->id(), dataRowId)) { + if (!it->second->hasValidId()) + continue; // The index object has been created, but does not exist in the database yet. + if (!objectStore->m_backingStore->putIndexDataForRecord(it->second->id(), *indexKeys[i], dataRowId)) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); @@ -343,35 +283,13 @@ void IDBObjectStoreBackendImpl::deleteFunction(PassRefPtr<IDBKey> prpKey, PassRe void IDBObjectStoreBackendImpl::deleteInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) { - SQLiteStatement idQuery(objectStore->sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key.get())); - bool ok = idQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - bindWhereClause(idQuery, objectStore->id(), key.get()); - if (idQuery.step() != SQLResultRow) { + int64_t id; + if (!objectStore->m_backingStore->keyExistsInObjectStore(objectStore->id(), *key, id)) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the object store.")); return; } - int64_t id = idQuery.getColumnInt64(0); - - SQLiteStatement osQuery(objectStore->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE id = ?"); - ok = osQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - - osQuery.bindInt64(1, id); - - ok = osQuery.step() == SQLResultDone; - ASSERT_UNUSED(ok, ok); - - SQLiteStatement indexQuery(objectStore->sqliteDatabase(), "DELETE FROM IndexData WHERE objectStoreDataId = ?"); - ok = indexQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - - indexQuery.bindInt64(1, id); - - ok = indexQuery.step() == SQLResultDone; - ASSERT_UNUSED(ok, ok); - + objectStore->m_backingStore->deleteObjectStoreRecord(objectStore->id(), id); callbacks->onSuccess(SerializedScriptValue::nullValue()); } @@ -389,22 +307,46 @@ void IDBObjectStoreBackendImpl::clear(PassRefPtr<IDBCallbacks> prpCallbacks, IDB ec = IDBDatabaseException::NOT_ALLOWED_ERR; } -static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id) +void IDBObjectStoreBackendImpl::clearInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBCallbacks> callbacks) { - SQLiteStatement deleteQuery(db, sql); - bool ok = deleteQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. - deleteQuery.bindInt64(1, id); - ok = deleteQuery.step() == SQLResultDone; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + objectStore->m_backingStore->clearObjectStore(objectStore->id()); + callbacks->onSuccess(SerializedScriptValue::undefinedValue()); } -void IDBObjectStoreBackendImpl::clearInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBCallbacks> callbacks) -{ - doDelete(objectStore->sqliteDatabase(), "DELETE FROM IndexData WHERE objectStoreDataId IN (SELECT id FROM ObjectStoreData WHERE objectStoreId = ?)", objectStore->id()); - doDelete(objectStore->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStore->id()); +namespace { +class PopulateIndexCallback : public IDBBackingStore::ObjectStoreRecordCallback { +public: + PopulateIndexCallback(IDBBackingStore& backingStore, const String& indexKeyPath, int64_t indexId) + : m_backingStore(backingStore) + , m_indexKeyPath(indexKeyPath) + , m_indexId(indexId) + { + } - callbacks->onSuccess(SerializedScriptValue::undefinedValue()); + virtual bool callback(int64_t objectStoreDataId, const String& value) + { + RefPtr<SerializedScriptValue> objectValue = SerializedScriptValue::createFromWire(value); + RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(objectValue.get(), m_indexKeyPath); + + if (!m_backingStore.putIndexDataForRecord(m_indexId, *indexKey, objectStoreDataId)) + return false; + + return true; + } + +private: + IDBBackingStore& m_backingStore; + const String& m_indexKeyPath; + int64_t m_indexId; +}; +} + +static bool populateIndex(IDBBackingStore& backingStore, int64_t objectStoreId, int64_t indexId, const String& indexKeyPath) +{ + PopulateIndexCallback callback(backingStore, indexKeyPath, indexId); + if (!backingStore.forEachObjectStoreRecord(objectStoreId, callback)) + return false; + return true; } PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(const String& name, const String& keyPath, bool unique, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) @@ -418,7 +360,7 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(cons return 0; } - RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_database.get(), name, m_name, keyPath, unique); + RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_backingStore.get(), name, m_name, keyPath, unique); ASSERT(index->name() == name); RefPtr<IDBObjectStoreBackendImpl> objectStore = this; @@ -435,21 +377,19 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(cons void IDBObjectStoreBackendImpl::createIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction) { - SQLiteStatement insert(objectStore->sqliteDatabase(), "INSERT INTO Indexes (objectStoreId, name, keyPath, isUnique) VALUES (?, ?, ?, ?)"); - if (insert.prepare() != SQLResultOk) { + int64_t id; + if (!objectStore->m_backingStore->createIndex(objectStore->m_id, index->name(), index->keyPath(), index->unique(), id)) { transaction->abort(); return; } - insert.bindInt64(1, objectStore->m_id); - insert.bindText(2, index->name()); - insert.bindText(3, index->keyPath()); - insert.bindInt(4, static_cast<int>(index->unique())); - if (insert.step() != SQLResultDone) { + + index->setId(id); + + if (!populateIndex(*objectStore->m_backingStore, objectStore->m_id, id, index->keyPath())) { transaction->abort(); return; } - int64_t id = objectStore->sqliteDatabase().lastInsertRowID(); - index->setId(id); + transaction->didCompleteTaskEvents(); } @@ -488,9 +428,7 @@ void IDBObjectStoreBackendImpl::deleteIndex(const String& name, IDBTransactionBa void IDBObjectStoreBackendImpl::deleteIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction) { - doDelete(objectStore->sqliteDatabase(), "DELETE FROM Indexes WHERE id = ?", index->id()); - doDelete(objectStore->sqliteDatabase(), "DELETE FROM IndexData WHERE indexId = ?", index->id()); - + objectStore->m_backingStore->deleteIndex(index->id()); transaction->didCompleteTaskEvents(); } @@ -506,64 +444,32 @@ void IDBObjectStoreBackendImpl::openCursor(PassRefPtr<IDBKeyRange> prpRange, uns void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> range, unsigned short tmpDirection, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) { - bool lowerBound = range && range->lower(); - bool upperBound = range && range->upper(); - - // Several files depend on this order of selects. - String sql = "SELECT id, keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE "; - if (lowerBound) - sql += range->lower()->lowerCursorWhereFragment(range->lowerWhereClauseComparisonOperator()); - if (upperBound) - sql += range->upper()->upperCursorWhereFragment(range->upperWhereClauseComparisonOperator()); - sql += "objectStoreId = ? ORDER BY "; - IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(tmpDirection); - if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE) - sql += "keyString, keyDate, keyNumber"; - else - sql += "keyString DESC, keyDate DESC, keyNumber DESC"; - - OwnPtr<SQLiteStatement> query = adoptPtr(new SQLiteStatement(objectStore->sqliteDatabase(), sql)); - bool ok = query->prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - - int currentColumn = 1; - if (lowerBound) - currentColumn += range->lower()->bind(*query, currentColumn); - if (upperBound) - currentColumn += range->upper()->bind(*query, currentColumn); - query->bindInt64(currentColumn, objectStore->id()); - - if (query->step() != SQLResultRow) { + + RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->m_backingStore->openObjectStoreCursor(objectStore->id(), range.get(), direction); + if (!backingStoreCursor) { callbacks->onSuccess(SerializedScriptValue::nullValue()); return; } - RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(objectStore->m_database.get(), range, direction, query.release(), true, transaction.get(), objectStore.get()); + RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(backingStoreCursor.release(), direction, IDBCursorBackendInterface::ObjectStoreCursor, transaction.get(), objectStore.get()); callbacks->onSuccess(cursor.release()); } void IDBObjectStoreBackendImpl::loadIndexes() { - SQLiteStatement indexQuery(sqliteDatabase(), "SELECT id, name, keyPath, isUnique FROM Indexes WHERE objectStoreId = ?"); - bool ok = indexQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - - indexQuery.bindInt64(1, m_id); + Vector<int64_t> ids; + Vector<String> names; + Vector<String> keyPaths; + Vector<bool> uniqueFlags; + m_backingStore->getIndexes(m_id, ids, names, keyPaths, uniqueFlags); - while (indexQuery.step() == SQLResultRow) { - int64_t id = indexQuery.getColumnInt64(0); - String name = indexQuery.getColumnText(1); - String keyPath = indexQuery.getColumnText(2); - bool unique = !!indexQuery.getColumnInt(3); + ASSERT(names.size() == ids.size()); + ASSERT(keyPaths.size() == ids.size()); + ASSERT(uniqueFlags.size() == ids.size()); - m_indexes.set(name, IDBIndexBackendImpl::create(m_database.get(), id, name, m_name, keyPath, unique)); - } -} - -SQLiteDatabase& IDBObjectStoreBackendImpl::sqliteDatabase() const -{ - return m_database->db(); + for (size_t i = 0; i < ids.size(); i++) + m_indexes.set(names[i], IDBIndexBackendImpl::create(m_backingStore.get(), ids[i], names[i], m_name, keyPaths[i], uniqueFlags[i])); } void IDBObjectStoreBackendImpl::removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index) @@ -584,19 +490,7 @@ PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::genAutoIncrementKey() if (m_autoIncrementNumber > 0) return IDBKey::createNumber(m_autoIncrementNumber++); - String sql = "SELECT max(keyNumber) + 1 FROM ObjectStoreData WHERE objectStoreId = ? AND keyString IS NULL AND keyDate IS NULL"; - - SQLiteStatement query(sqliteDatabase(), sql); - bool ok = query.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); - - query.bindInt64(1, id()); - - if (query.step() != SQLResultRow || query.isColumnNull(0)) - m_autoIncrementNumber = 1; - else - m_autoIncrementNumber = static_cast<int>(query.getColumnDouble(0)); - + m_autoIncrementNumber = static_cast<int>(m_backingStore->nextAutoIncrementNumber(id())); return IDBKey::createNumber(m_autoIncrementNumber++); } diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h index b54f9fd..4479d1e 100644 --- a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h +++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h @@ -34,22 +34,21 @@ namespace WebCore { +class IDBBackingStore; class IDBDatabaseBackendImpl; class IDBIndexBackendImpl; -class IDBSQLiteDatabase; class IDBTransactionBackendInterface; -class SQLiteDatabase; class ScriptExecutionContext; class IDBObjectStoreBackendImpl : public IDBObjectStoreBackendInterface { public: - static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& keyPath, bool autoIncrement) + static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, int64_t id, const String& name, const String& keyPath, bool autoIncrement) { - return adoptRef(new IDBObjectStoreBackendImpl(database, id, name, keyPath, autoIncrement)); + return adoptRef(new IDBObjectStoreBackendImpl(backingStore, id, name, keyPath, autoIncrement)); } - static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBSQLiteDatabase* database, const String& name, const String& keyPath, bool autoIncrement) + static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, const String& name, const String& keyPath, bool autoIncrement) { - return adoptRef(new IDBObjectStoreBackendImpl(database, name, keyPath, autoIncrement)); + return adoptRef(new IDBObjectStoreBackendImpl(backingStore, name, keyPath, autoIncrement)); } virtual ~IDBObjectStoreBackendImpl(); @@ -78,14 +77,13 @@ public: virtual void openCursor(PassRefPtr<IDBKeyRange> range, unsigned short direction, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); private: - IDBObjectStoreBackendImpl(IDBSQLiteDatabase*, int64_t id, const String& name, const String& keyPath, bool autoIncrement); - IDBObjectStoreBackendImpl(IDBSQLiteDatabase*, const String& name, const String& keyPath, bool autoIncrement); + IDBObjectStoreBackendImpl(IDBBackingStore*, int64_t id, const String& name, const String& keyPath, bool autoIncrement); + IDBObjectStoreBackendImpl(IDBBackingStore*, const String& name, const String& keyPath, bool autoIncrement); void loadIndexes(); - SQLiteDatabase& sqliteDatabase() const; PassRefPtr<IDBKey> genAutoIncrementKey(); void resetAutoIncrementKeyCache() { m_autoIncrementNumber = -1; } - static PassRefPtr<IDBKey> selectKeyForPut(IDBObjectStoreBackendImpl*, SerializedScriptValue*, IDBKey*, PutMode, IDBCallbacks*); + static PassRefPtr<IDBKey> selectKeyForPut(IDBObjectStoreBackendImpl*, IDBKey*, PutMode, IDBCallbacks*, RefPtr<SerializedScriptValue>&); static void getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>); static void putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, PutMode, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); @@ -99,7 +97,7 @@ private: static void removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBIndexBackendImpl>); static void addIndexToMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBIndexBackendImpl>); - RefPtr<IDBSQLiteDatabase> m_database; + RefPtr<IDBBackingStore> m_backingStore; int64_t m_id; String m_name; diff --git a/Source/WebCore/storage/IDBRequest.cpp b/Source/WebCore/storage/IDBRequest.cpp index e7498ec..e1837fc 100644 --- a/Source/WebCore/storage/IDBRequest.cpp +++ b/Source/WebCore/storage/IDBRequest.cpp @@ -36,13 +36,12 @@ #include "EventListener.h" #include "EventNames.h" #include "EventQueue.h" -#include "IDBCursor.h" +#include "IDBCursorWithValue.h" #include "IDBDatabase.h" +#include "IDBEventDispatcher.h" #include "IDBIndex.h" -#include "IDBErrorEvent.h" -#include "IDBObjectStore.h" #include "IDBPendingTransactionMonitor.h" -#include "IDBSuccessEvent.h" +#include "IDBTransaction.h" namespace WebCore { @@ -53,47 +52,157 @@ PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassR IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction) : ActiveDOMObject(context, this) + , m_errorCode(0) , m_source(source) , m_transaction(transaction) , m_readyState(LOADING) , m_finished(false) + , m_cursorType(IDBCursorBackendInterface::InvalidCursorType) { - if (m_transaction) + if (m_transaction) { + m_transaction->registerRequest(this); IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend()); + } } IDBRequest::~IDBRequest() { + ASSERT(m_readyState == DONE || m_readyState == EarlyDeath); + if (m_transaction) + m_transaction->unregisterRequest(this); +} + +PassRefPtr<IDBAny> IDBRequest::result(ExceptionCode& ec) const +{ + if (m_readyState != DONE) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return 0; + } + return m_result; +} + +unsigned short IDBRequest::errorCode(ExceptionCode& ec) const +{ + if (m_readyState != DONE) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return 0; + } + return m_errorCode; +} + +String IDBRequest::webkitErrorMessage(ExceptionCode& ec) const +{ + if (m_readyState != DONE) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return String(); + } + return m_errorMessage; +} + +PassRefPtr<IDBAny> IDBRequest::source() const +{ + return m_source; +} + +PassRefPtr<IDBTransaction> IDBRequest::transaction() const +{ + return m_transaction; +} + +unsigned short IDBRequest::readyState() const +{ + ASSERT(m_readyState == LOADING || m_readyState == DONE); + return m_readyState; +} + +void IDBRequest::markEarlyDeath() +{ + ASSERT(m_readyState == LOADING); + m_readyState = EarlyDeath; } bool IDBRequest::resetReadyState(IDBTransaction* transaction) { ASSERT(!m_finished); ASSERT(scriptExecutionContext()); + ASSERT(transaction == m_transaction); if (m_readyState != DONE) return false; - m_transaction = transaction; m_readyState = LOADING; + m_result.clear(); + m_errorCode = 0; + m_errorMessage = String(); IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend()); return true; } +IDBAny* IDBRequest::source() +{ + return m_source.get(); +} + +void IDBRequest::abort() +{ + if (m_readyState != LOADING) { + ASSERT(m_readyState == DONE); + return; + } + + ASSERT(scriptExecutionContext()->isDocument()); + EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue(); + for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { + bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get()); + ASSERT_UNUSED(removed, removed); + } + m_enqueuedEvents.clear(); + + m_errorCode = 0; + m_errorMessage = String(); + m_result.clear(); + onError(IDBDatabaseError::create(IDBDatabaseException::ABORT_ERR, "The transaction was aborted, so the request cannot be fulfilled.")); +} + +void IDBRequest::setCursorType(IDBCursorBackendInterface::CursorType cursorType) +{ + ASSERT(m_cursorType == IDBCursorBackendInterface::InvalidCursorType); + m_cursorType = cursorType; +} + void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error) { - enqueueEvent(IDBErrorEvent::create(m_source, *error)); + ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result); + m_errorCode = error->code(); + m_errorMessage = error->message(); + enqueueEvent(Event::create(eventNames().errorEvent, true, true)); +} + +static PassRefPtr<Event> createSuccessEvent() +{ + return Event::create(eventNames().successEvent, false, false); } void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend) { - enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(IDBCursor::create(backend, this, m_transaction.get())))); + ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result); + ASSERT(m_cursorType != IDBCursorBackendInterface::InvalidCursorType); + if (m_cursorType == IDBCursorBackendInterface::IndexKeyCursor) + m_result = IDBAny::create(IDBCursor::create(backend, this, m_source.get(), m_transaction.get())); + else + m_result = IDBAny::create(IDBCursorWithValue::create(backend, this, m_source.get(), m_transaction.get())); + enqueueEvent(createSuccessEvent()); } void IDBRequest::onSuccess(PassRefPtr<IDBDatabaseBackendInterface> backend) { - enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(IDBDatabase::create(scriptExecutionContext(), backend)))); + ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result); + RefPtr<IDBDatabase> idbDatabase = IDBDatabase::create(scriptExecutionContext(), backend); + idbDatabase->open(); + + m_result = IDBAny::create(idbDatabase.release()); + enqueueEvent(createSuccessEvent()); } void IDBRequest::onSuccess(PassRefPtr<IDBIndexBackendInterface> backend) @@ -103,16 +212,14 @@ void IDBRequest::onSuccess(PassRefPtr<IDBIndexBackendInterface> backend) void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey) { - enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(idbKey))); -} - -void IDBRequest::onSuccess(PassRefPtr<IDBObjectStoreBackendInterface> backend) -{ - ASSERT_NOT_REACHED(); // FIXME: This method should go away. + ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result); + m_result = IDBAny::create(idbKey); + enqueueEvent(createSuccessEvent()); } void IDBRequest::onSuccess(PassRefPtr<IDBTransactionBackendInterface> prpBackend) { + ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result); if (!scriptExecutionContext()) return; @@ -125,12 +232,16 @@ void IDBRequest::onSuccess(PassRefPtr<IDBTransactionBackendInterface> prpBackend m_source->idbDatabase()->setSetVersionTransaction(frontend.get()); IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend()); - enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(frontend.release()))); + + m_result = IDBAny::create(frontend.release()); + enqueueEvent(createSuccessEvent()); } void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptValue) { - enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(serializedScriptValue))); + ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result); + m_result = IDBAny::create(serializedScriptValue); + enqueueEvent(createSuccessEvent()); } bool IDBRequest::hasPendingActivity() const @@ -141,6 +252,11 @@ bool IDBRequest::hasPendingActivity() const return !m_finished || ActiveDOMObject::hasPendingActivity(); } +void IDBRequest::onBlocked() +{ + ASSERT_NOT_REACHED(); +} + ScriptExecutionContext* IDBRequest::scriptExecutionContext() const { return ActiveDOMObject::scriptExecutionContext(); @@ -149,10 +265,17 @@ ScriptExecutionContext* IDBRequest::scriptExecutionContext() const bool IDBRequest::dispatchEvent(PassRefPtr<Event> event) { ASSERT(!m_finished); + ASSERT(m_enqueuedEvents.size()); ASSERT(scriptExecutionContext()); ASSERT(event->target() == this); ASSERT(m_readyState < DONE); - m_readyState = DONE; + if (event->type() != eventNames().blockedEvent) + m_readyState = DONE; + + for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { + if (m_enqueuedEvents[i].get() == event.get()) + m_enqueuedEvents.remove(i); + } Vector<RefPtr<EventTarget> > targets; targets.append(this); @@ -165,26 +288,29 @@ bool IDBRequest::dispatchEvent(PassRefPtr<Event> event) targets.append(m_transaction->db()); } - ASSERT(event->isIDBErrorEvent() || event->isIDBSuccessEvent()); - bool dontPreventDefault = static_cast<IDBEvent*>(event.get())->dispatch(targets); + // FIXME: When we allow custom event dispatching, this will probably need to change. + ASSERT(event->type() == eventNames().successEvent || event->type() == eventNames().errorEvent || event->type() == eventNames().blockedEvent); + bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets); - // If the event's result was of type IDBCursor, then it's possible for us to - // fire again (unless the transaction completes). - if (event->isIDBSuccessEvent()) { - RefPtr<IDBAny> any = static_cast<IDBSuccessEvent*>(event.get())->result(); - if (any->type() != IDBAny::IDBCursorType) - m_finished = true; - } else + // If the result was of type IDBCursor, then we'll fire again. + if (m_result && m_result->type() != IDBAny::IDBCursorType && m_result->type() != IDBAny::IDBCursorWithValueType) m_finished = true; if (m_transaction) { - if (dontPreventDefault && event->isIDBErrorEvent()) + // If an error event and the default wasn't prevented... + if (dontPreventDefault && event->type() == eventNames().errorEvent) m_transaction->backend()->abort(); m_transaction->backend()->didCompleteTaskEvents(); } return dontPreventDefault; } +void IDBRequest::uncaughtExceptionInEventHandler() +{ + if (m_transaction) + m_transaction->backend()->abort(); +} + void IDBRequest::enqueueEvent(PassRefPtr<Event> event) { ASSERT(!m_finished); @@ -195,7 +321,8 @@ void IDBRequest::enqueueEvent(PassRefPtr<Event> event) ASSERT(scriptExecutionContext()->isDocument()); EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue(); event->setTarget(this); - eventQueue->enqueueEvent(event); + eventQueue->enqueueEvent(event.get()); + m_enqueuedEvents.append(event); } EventTargetData* IDBRequest::eventTargetData() diff --git a/Source/WebCore/storage/IDBRequest.h b/Source/WebCore/storage/IDBRequest.h index 5c31318..b6b4e5b 100644 --- a/Source/WebCore/storage/IDBRequest.h +++ b/Source/WebCore/storage/IDBRequest.h @@ -36,12 +36,12 @@ #include "EventListener.h" #include "EventNames.h" #include "EventTarget.h" +#include "ExceptionCode.h" #include "IDBAny.h" #include "IDBCallbacks.h" namespace WebCore { -class IDBEvent; class IDBTransaction; class IDBRequest : public IDBCallbacks, public EventTarget, public ActiveDOMObject { @@ -49,16 +49,28 @@ public: static PassRefPtr<IDBRequest> create(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransaction*); virtual ~IDBRequest(); + PassRefPtr<IDBAny> result(ExceptionCode&) const; + unsigned short errorCode(ExceptionCode&) const; + String webkitErrorMessage(ExceptionCode&) const; + PassRefPtr<IDBAny> source() const; + PassRefPtr<IDBTransaction> transaction() const; + // Defined in the IDL enum ReadyState { LOADING = 1, - DONE = 2 + DONE = 2, + EarlyDeath = 3 }; - unsigned short readyState() const { return m_readyState; } + unsigned short readyState() const; + DEFINE_ATTRIBUTE_EVENT_LISTENER(success); DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + void markEarlyDeath(); bool resetReadyState(IDBTransaction*); + void setCursorType(IDBCursorBackendInterface::CursorType); + IDBAny* source(); + void abort(); // IDBCallbacks virtual void onError(PassRefPtr<IDBDatabaseError>); @@ -66,9 +78,9 @@ public: virtual void onSuccess(PassRefPtr<IDBCursorBackendInterface>); virtual void onSuccess(PassRefPtr<IDBIndexBackendInterface>); virtual void onSuccess(PassRefPtr<IDBKey>); - virtual void onSuccess(PassRefPtr<IDBObjectStoreBackendInterface>); virtual void onSuccess(PassRefPtr<IDBTransactionBackendInterface>); virtual void onSuccess(PassRefPtr<SerializedScriptValue>); + virtual void onBlocked(); // ActiveDOMObject virtual bool hasPendingActivity() const; @@ -78,15 +90,19 @@ public: virtual ScriptExecutionContext* scriptExecutionContext() const; virtual bool dispatchEvent(PassRefPtr<Event>); bool dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) { return EventTarget::dispatchEvent(event, ec); } + virtual void uncaughtExceptionInEventHandler(); using ThreadSafeShared<IDBCallbacks>::ref; using ThreadSafeShared<IDBCallbacks>::deref; -private: +protected: IDBRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransaction*); - void enqueueEvent(PassRefPtr<Event>); + RefPtr<IDBAny> m_result; + unsigned short m_errorCode; + String m_errorMessage; +private: // EventTarget virtual void refEventTarget() { ref(); } virtual void derefEventTarget() { deref(); } @@ -98,6 +114,10 @@ private: ReadyState m_readyState; bool m_finished; // Is it possible that we'll fire any more events? If not, we're finished. + Vector<RefPtr<Event> > m_enqueuedEvents; + + // Only used if the result type will be a cursor. + IDBCursorBackendInterface::CursorType m_cursorType; EventTargetData m_eventTargetData; }; diff --git a/Source/WebCore/storage/IDBRequest.idl b/Source/WebCore/storage/IDBRequest.idl index 58872f0..6ce2036 100644 --- a/Source/WebCore/storage/IDBRequest.idl +++ b/Source/WebCore/storage/IDBRequest.idl @@ -32,6 +32,14 @@ module storage { Conditional=INDEXED_DATABASE, EventTarget ] IDBRequest { + readonly attribute IDBAny result + getter raises (IDBDatabaseException); + readonly attribute unsigned short errorCode + getter raises (IDBDatabaseException); + readonly attribute [ConvertNullStringTo=Undefined] DOMString webkitErrorMessage + getter raises (IDBDatabaseException); + readonly attribute IDBAny source; + readonly attribute IDBTransaction transaction; // States const unsigned short LOADING = 1; diff --git a/Source/WebCore/storage/IDBSuccessEvent.h b/Source/WebCore/storage/IDBSuccessEvent.h deleted file mode 100644 index 5be660a..0000000 --- a/Source/WebCore/storage/IDBSuccessEvent.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBSuccessEvent_h -#define IDBSuccessEvent_h - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBEvent.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> - -namespace WebCore { - -class IDBAny; - -class IDBSuccessEvent : public IDBEvent { -public: - static PassRefPtr<IDBSuccessEvent> create(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result); - // FIXME: Need to allow creation of these events from JS. - virtual ~IDBSuccessEvent(); - - PassRefPtr<IDBAny> result(); - - virtual bool isIDBSuccessEvent() const { return true; } - -private: - IDBSuccessEvent(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result); - - RefPtr<IDBAny> m_result; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBEvent_h diff --git a/Source/WebCore/storage/IDBTransaction.cpp b/Source/WebCore/storage/IDBTransaction.cpp index 1f696b3..710bb76 100644 --- a/Source/WebCore/storage/IDBTransaction.cpp +++ b/Source/WebCore/storage/IDBTransaction.cpp @@ -31,10 +31,9 @@ #include "Document.h" #include "EventException.h" #include "EventQueue.h" -#include "IDBAbortEvent.h" -#include "IDBCompleteEvent.h" #include "IDBDatabase.h" #include "IDBDatabaseException.h" +#include "IDBEventDispatcher.h" #include "IDBIndex.h" #include "IDBObjectStore.h" #include "IDBObjectStoreBackendInterface.h" @@ -104,14 +103,31 @@ void IDBTransaction::abort() m_backend->abort(); } +void IDBTransaction::registerRequest(IDBRequest* request) +{ + m_childRequests.add(request); +} + +void IDBTransaction::unregisterRequest(IDBRequest* request) +{ + // If we aborted the request, it will already have been removed. + m_childRequests.remove(request); +} + void IDBTransaction::onAbort() { - enqueueEvent(IDBAbortEvent::create(IDBAny::create(this))); + while (!m_childRequests.isEmpty()) { + IDBRequest* request = *m_childRequests.begin(); + m_childRequests.remove(request); + request->abort(); + } + + enqueueEvent(Event::create(eventNames().abortEvent, true, false)); } void IDBTransaction::onComplete() { - enqueueEvent(IDBCompleteEvent::create(IDBAny::create(this))); + enqueueEvent(Event::create(eventNames().completeEvent, false, false)); } bool IDBTransaction::hasPendingActivity() const @@ -139,8 +155,9 @@ bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event) targets.append(this); targets.append(db()); - ASSERT(event->isIDBAbortEvent() || event->isIDBCompleteEvent()); - return static_cast<IDBEvent*>(event.get())->dispatch(targets); + // FIXME: When we allow custom event dispatching, this will probably need to change. + ASSERT(event->type() == eventNames().completeEvent || event->type() == eventNames().abortEvent); + return IDBEventDispatcher::dispatch(event.get(), targets); } bool IDBTransaction::canSuspend() const diff --git a/Source/WebCore/storage/IDBTransaction.h b/Source/WebCore/storage/IDBTransaction.h index ff4feb6..b5778ad 100644 --- a/Source/WebCore/storage/IDBTransaction.h +++ b/Source/WebCore/storage/IDBTransaction.h @@ -62,6 +62,9 @@ public: PassRefPtr<IDBObjectStore> objectStore(const String& name, ExceptionCode&); void abort(); + void registerRequest(IDBRequest*); + void unregisterRequest(IDBRequest*); + DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); DEFINE_ATTRIBUTE_EVENT_LISTENER(complete); DEFINE_ATTRIBUTE_EVENT_LISTENER(error); @@ -100,6 +103,8 @@ private: unsigned short m_mode; bool m_finished; // Is it possible that we'll fire any more events or allow any new transactions? If not, we're finished. + ListHashSet<IDBRequest*> m_childRequests; + EventTargetData m_eventTargetData; }; diff --git a/Source/WebCore/storage/IDBTransactionBackendImpl.cpp b/Source/WebCore/storage/IDBTransactionBackendImpl.cpp index 1357838..b5b883f 100644 --- a/Source/WebCore/storage/IDBTransactionBackendImpl.cpp +++ b/Source/WebCore/storage/IDBTransactionBackendImpl.cpp @@ -28,10 +28,10 @@ #if ENABLE(INDEXED_DATABASE) +#include "IDBBackingStore.h" #include "IDBDatabaseBackendImpl.h" #include "IDBDatabaseException.h" #include "IDBTransactionCoordinator.h" -#include "SQLiteDatabase.h" namespace WebCore { @@ -45,7 +45,7 @@ IDBTransactionBackendImpl::IDBTransactionBackendImpl(DOMStringList* objectStores , m_mode(mode) , m_state(Unused) , m_database(database) - , m_transaction(new SQLiteTransaction(database->sqliteDatabase())) + , m_transaction(database->backingStore()->createTransaction()) , m_taskTimer(this, &IDBTransactionBackendImpl::taskTimerFired) , m_taskEventTimer(this, &IDBTransactionBackendImpl::taskEventTimerFired) , m_pendingEvents(0) diff --git a/Source/WebCore/storage/IDBTransactionBackendImpl.h b/Source/WebCore/storage/IDBTransactionBackendImpl.h index 1297e74..912a239 100644 --- a/Source/WebCore/storage/IDBTransactionBackendImpl.h +++ b/Source/WebCore/storage/IDBTransactionBackendImpl.h @@ -29,9 +29,9 @@ #if ENABLE(INDEXED_DATABASE) #include "DOMStringList.h" +#include "IDBBackingStore.h" #include "IDBTransactionBackendInterface.h" #include "IDBTransactionCallbacks.h" -#include "SQLiteTransaction.h" #include "Timer.h" #include <wtf/Deque.h> #include <wtf/RefPtr.h> @@ -81,7 +81,7 @@ private: TaskQueue m_taskQueue; TaskQueue m_abortTaskQueue; - OwnPtr<SQLiteTransaction> m_transaction; + RefPtr<IDBBackingStore::Transaction> m_transaction; // FIXME: delete the timer once we have threads instead. Timer<IDBTransactionBackendImpl> m_taskTimer; diff --git a/Source/WebCore/storage/IDBTransactionBackendInterface.h b/Source/WebCore/storage/IDBTransactionBackendInterface.h index 9b1fce6..1b370af 100644 --- a/Source/WebCore/storage/IDBTransactionBackendInterface.h +++ b/Source/WebCore/storage/IDBTransactionBackendInterface.h @@ -38,7 +38,6 @@ namespace WebCore { class IDBObjectStoreBackendInterface; class IDBTransactionCallbacks; -class SQLiteDatabase; // This class is shared by IDBTransaction (async) and IDBTransactionSync (sync). // This is implemented by IDBTransactionBackendImpl and optionally others (in order to proxy @@ -61,4 +60,3 @@ public: #endif #endif // IDBTransactionBackendInterface_h - diff --git a/Source/WebCore/storage/IDBTransactionCoordinator.cpp b/Source/WebCore/storage/IDBTransactionCoordinator.cpp index f867f42..edaff2c 100644 --- a/Source/WebCore/storage/IDBTransactionCoordinator.cpp +++ b/Source/WebCore/storage/IDBTransactionCoordinator.cpp @@ -31,7 +31,6 @@ #include "IDBDatabaseBackendImpl.h" #include "IDBObjectStoreBackendInterface.h" #include "IDBTransactionBackendImpl.h" -#include "SQLiteDatabase.h" #include "ScriptExecutionContext.h" namespace WebCore { diff --git a/Source/WebCore/storage/IDBCompleteEvent.cpp b/Source/WebCore/storage/IDBVersionChangeEvent.cpp index 20ee57a..5fd9d8b 100644 --- a/Source/WebCore/storage/IDBCompleteEvent.cpp +++ b/Source/WebCore/storage/IDBVersionChangeEvent.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,9 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -27,7 +24,7 @@ */ #include "config.h" -#include "IDBCompleteEvent.h" +#include "IDBVersionChangeEvent.h" #if ENABLE(INDEXED_DATABASE) @@ -36,20 +33,26 @@ namespace WebCore { -PassRefPtr<IDBCompleteEvent> IDBCompleteEvent::create(PassRefPtr<IDBAny> source) +PassRefPtr<IDBVersionChangeEvent> IDBVersionChangeEvent::create(const String& version, const AtomicString& eventType) { - return adoptRef(new IDBCompleteEvent(source)); + return adoptRef(new IDBVersionChangeEvent(version, eventType)); } -IDBCompleteEvent::IDBCompleteEvent(PassRefPtr<IDBAny> source) - : IDBEvent(eventNames().completeEvent, source, false) +IDBVersionChangeEvent::IDBVersionChangeEvent(const String& version, const AtomicString& eventType) + : Event(eventType, false /*canBubble*/, false /*cancelable*/) + , m_version(version) { } -IDBCompleteEvent::~IDBCompleteEvent() +IDBVersionChangeEvent::~IDBVersionChangeEvent() { } +String IDBVersionChangeEvent::version() +{ + return m_version; +} + } // namespace WebCore #endif diff --git a/Source/WebCore/storage/IDBAbortEvent.h b/Source/WebCore/storage/IDBVersionChangeEvent.h index fc27989..efd360f 100644 --- a/Source/WebCore/storage/IDBAbortEvent.h +++ b/Source/WebCore/storage/IDBVersionChangeEvent.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,9 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -26,32 +23,38 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBAbortEvent_h -#define IDBAbortEvent_h +#ifndef IDBVersionChangeEvent_h +#define IDBVersionChangeEvent_h #if ENABLE(INDEXED_DATABASE) -#include "IDBEvent.h" +#include "Event.h" #include "PlatformString.h" #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> namespace WebCore { -class IDBAbortEvent : public IDBEvent { +class IDBAny; + +class IDBVersionChangeEvent : public Event { public: - static PassRefPtr<IDBAbortEvent> create(PassRefPtr<IDBAny> source); + static PassRefPtr<IDBVersionChangeEvent> create(const String& version, const AtomicString& eventType); // FIXME: Need to allow creation of these events from JS. - virtual ~IDBAbortEvent(); + virtual ~IDBVersionChangeEvent(); + + virtual bool isIDBVersionChangeEvent() const { return true; } - virtual bool isIDBAbortEvent() const { return true; } + virtual String version(); private: - IDBAbortEvent(PassRefPtr<IDBAny> source); + IDBVersionChangeEvent(const String& version, const AtomicString& eventType); + + String m_version; }; } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) -#endif // IDBAbortEvent_h +#endif // IDBVersionChangeEvent_h diff --git a/Source/WebCore/storage/IDBSuccessEvent.idl b/Source/WebCore/storage/IDBVersionChangeEvent.idl index b4ea7d2..c6a4171 100644 --- a/Source/WebCore/storage/IDBSuccessEvent.idl +++ b/Source/WebCore/storage/IDBVersionChangeEvent.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,9 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -30,7 +27,7 @@ module storage { interface [ Conditional=INDEXED_DATABASE - ] IDBSuccessEvent : IDBEvent { - readonly attribute IDBAny result; + ] IDBVersionChangeEvent : Event { + readonly attribute DOMString version; }; } diff --git a/Source/WebCore/storage/IDBSuccessEvent.cpp b/Source/WebCore/storage/IDBVersionChangeRequest.cpp index 110b78b..8c5416e 100644 --- a/Source/WebCore/storage/IDBSuccessEvent.cpp +++ b/Source/WebCore/storage/IDBVersionChangeRequest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,9 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -27,33 +24,34 @@ */ #include "config.h" -#include "IDBSuccessEvent.h" +#include "IDBVersionChangeRequest.h" #if ENABLE(INDEXED_DATABASE) -#include "EventNames.h" -#include "IDBAny.h" +#include "IDBVersionChangeEvent.h" +#include "ScriptExecutionContext.h" namespace WebCore { -PassRefPtr<IDBSuccessEvent> IDBSuccessEvent::create(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result) +PassRefPtr<IDBVersionChangeRequest> IDBVersionChangeRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, const String& version) { - return adoptRef(new IDBSuccessEvent(source, result)); + return adoptRef(new IDBVersionChangeRequest(context, source, version)); } -IDBSuccessEvent::IDBSuccessEvent(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result) - : IDBEvent(eventNames().successEvent, source, false) - , m_result(result) +IDBVersionChangeRequest::IDBVersionChangeRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, const String& version) + : IDBRequest(context, source, 0) + , m_version(version) { } -IDBSuccessEvent::~IDBSuccessEvent() +IDBVersionChangeRequest::~IDBVersionChangeRequest() { } -PassRefPtr<IDBAny> IDBSuccessEvent::result() +void IDBVersionChangeRequest::onBlocked() { - return m_result; + ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result); + enqueueEvent(IDBVersionChangeEvent::create(m_version, eventNames().blockedEvent)); } } // namespace WebCore diff --git a/Source/WebCore/storage/IDBVersionChangeRequest.h b/Source/WebCore/storage/IDBVersionChangeRequest.h new file mode 100644 index 0000000..32cf4c0 --- /dev/null +++ b/Source/WebCore/storage/IDBVersionChangeRequest.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef IDBVersionChangeRequest_h +#define IDBVersionChangeRequest_h + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBRequest.h" + +namespace WebCore { + +class IDBVersionChangeRequest : public IDBRequest { +public: + static PassRefPtr<IDBVersionChangeRequest> create(ScriptExecutionContext*, PassRefPtr<IDBAny> source, const String& version); + virtual ~IDBVersionChangeRequest(); + + virtual void onBlocked(); + + DEFINE_ATTRIBUTE_EVENT_LISTENER(blocked); + +private: + IDBVersionChangeRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source, const String& version); + + String m_version; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBRequest_h diff --git a/Source/WebCore/storage/IDBErrorEvent.idl b/Source/WebCore/storage/IDBVersionChangeRequest.idl index 5c58f6f..ffac735 100644 --- a/Source/WebCore/storage/IDBErrorEvent.idl +++ b/Source/WebCore/storage/IDBVersionChangeRequest.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,9 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -29,9 +26,9 @@ module storage { interface [ - Conditional=INDEXED_DATABASE - ] IDBErrorEvent : IDBEvent { - readonly attribute unsigned short code; - readonly attribute DOMString message; + Conditional=INDEXED_DATABASE, + EventTarget + ] IDBVersionChangeRequest : IDBRequest { + attribute EventListener onblocked; }; } diff --git a/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp b/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp index 38b2983..e6a1ad5 100644 --- a/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp +++ b/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp @@ -28,7 +28,9 @@ #if ENABLE(INDEXED_DATABASE) +#include "IDBKey.h" #include "PlatformBridge.h" +#include "SerializedScriptValue.h" namespace WebCore { @@ -37,6 +39,11 @@ void IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(const Ve PlatformBridge::createIDBKeysFromSerializedValuesAndKeyPath(values, keyPath, keys); } +PassRefPtr<SerializedScriptValue> IDBKeyPathBackendImpl::injectIDBKeyIntoSerializedValue(PassRefPtr<IDBKey> key, PassRefPtr<SerializedScriptValue> value, const String& keyPath) +{ + return PlatformBridge::injectIDBKeyIntoSerializedValue(key, value, keyPath); +} + } // namespace WebCore #endif |