IndexedDB: Move method precondition checks to front end objects
authorjsbell@chromium.org <jsbell@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Jun 2012 22:04:09 +0000 (22:04 +0000)
committerjsbell@chromium.org <jsbell@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Jun 2012 22:04:09 +0000 (22:04 +0000)
https://bugs.webkit.org/show_bug.cgi?id=89377

Reviewed by Tony Chang.

Now that metadata exists on the front end, most of the pre-condition validation checks
done on IDB method calls from script can be moved to the front end which simplifies the
code significantly in the case of complex methods like IDBObjectStore::put().

Adds an internal "active" flag for transactions, although the behavior is not accurate
to the spec (it should only be true during event callbacks - http://webkit.org/b/89379).
The back-end methods can then be simplifed to just adding async tasks to the transaction,
and the front end methods can take care of all exception cases except for asynchronous
transaction abort which still requires plumbing back to the front end.

No functional changes - no new tests.

* Modules/indexeddb/IDBCursor.cpp:
(WebCore::IDBCursor::update): Migrate from IDBObjectStoreBackendImpl::put.
(WebCore::IDBCursor::advance): Add more explicit transaction-is-active check.
(WebCore::IDBCursor::continueFunction): Ditto.
(WebCore::IDBCursor::deleteFunction): Ditto.
(WebCore::IDBCursor::effectiveObjectStore): Convenience function (source may be store or index).
(WebCore):
* Modules/indexeddb/IDBCursor.h:
(WebCore::IDBCursor::isKeyCursor): Distinguish from IDBCursorWithValue.
(IDBCursor):
* Modules/indexeddb/IDBCursorBackendImpl.cpp:
(WebCore::IDBCursorBackendImpl::update): Remove migrated check.
* Modules/indexeddb/IDBCursorWithValue.h:
(IDBCursorWithValue):
* Modules/indexeddb/IDBDatabase.cpp: Migrate checks.
(WebCore::IDBDatabase::createObjectStore):
(WebCore::IDBDatabase::deleteObjectStore):
* Modules/indexeddb/IDBDatabaseBackendImpl.cpp: Replace checks with assertions.
(WebCore::IDBDatabaseBackendImpl::createObjectStore):
(WebCore::IDBDatabaseBackendImpl::deleteObjectStore):
* Modules/indexeddb/IDBIndex.cpp: Add transaction-is-active checks.
(WebCore::IDBIndex::openCursor):
(WebCore::IDBIndex::count):
(WebCore::IDBIndex::openKeyCursor):
(WebCore::IDBIndex::get):
(WebCore::IDBIndex::getKey):
* Modules/indexeddb/IDBObjectStore.cpp: Migrate cehcks.
(WebCore::IDBObjectStore::get):
(WebCore::IDBObjectStore::add): Delegates to put(PutMode)
(WebCore::IDBObjectStore::put): Delegates to put(PutMode)
(WebCore): Adds put(PutMode) which has the unified checks migrated from
IDBObjectStoreBackendImpl::put.
(WebCore::IDBObjectStore::deleteFunction):
(WebCore::IDBObjectStore::clear):
(WebCore::IDBObjectStore::createIndex):
(WebCore::IDBObjectStore::deleteIndex):
(WebCore::IDBObjectStore::openCursor):
(WebCore::IDBObjectStore::count):
* Modules/indexeddb/IDBObjectStore.h: Adds put(PutMode).
(IDBObjectStore):
* Modules/indexeddb/IDBObjectStoreBackendImpl.cpp:
(WebCore::IDBObjectStoreBackendImpl::getInternal): Fix trace symbol.
(WebCore::IDBObjectStoreBackendImpl::put): Remove migrated checks.
(WebCore::IDBObjectStoreBackendImpl::createIndex): Remove migrated checks.
(WebCore::IDBObjectStoreBackendImpl::deleteIndex): Remove migrated checks.
* Modules/indexeddb/IDBTransaction.cpp: Add active flag tracking.
(WebCore::IDBTransaction::IDBTransaction):
(WebCore::IDBTransaction::abort):
(WebCore::IDBTransaction::onAbort):
(WebCore::IDBTransaction::onComplete):
* Modules/indexeddb/IDBTransaction.h:
(WebCore::IDBTransaction::isActive):
(WebCore::IDBTransaction::isReadOnly): Group IDL/non-IDL methods.
(IDBTransaction):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@121291 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Modules/indexeddb/IDBCursor.cpp
Source/WebCore/Modules/indexeddb/IDBCursor.h
Source/WebCore/Modules/indexeddb/IDBCursorBackendImpl.cpp
Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h
Source/WebCore/Modules/indexeddb/IDBDatabase.cpp
Source/WebCore/Modules/indexeddb/IDBDatabaseBackendImpl.cpp
Source/WebCore/Modules/indexeddb/IDBIndex.cpp
Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp
Source/WebCore/Modules/indexeddb/IDBObjectStore.h
Source/WebCore/Modules/indexeddb/IDBObjectStoreBackendImpl.cpp
Source/WebCore/Modules/indexeddb/IDBTransaction.cpp
Source/WebCore/Modules/indexeddb/IDBTransaction.h

index 34759f0..597f6a2 100755 (executable)
@@ -1,3 +1,77 @@
+2012-06-26  Joshua Bell  <jsbell@chromium.org>
+
+        IndexedDB: Move method precondition checks to front end objects
+        https://bugs.webkit.org/show_bug.cgi?id=89377
+
+        Reviewed by Tony Chang.
+
+        Now that metadata exists on the front end, most of the pre-condition validation checks
+        done on IDB method calls from script can be moved to the front end which simplifies the
+        code significantly in the case of complex methods like IDBObjectStore::put().
+
+        Adds an internal "active" flag for transactions, although the behavior is not accurate
+        to the spec (it should only be true during event callbacks - http://webkit.org/b/89379).
+        The back-end methods can then be simplifed to just adding async tasks to the transaction,
+        and the front end methods can take care of all exception cases except for asynchronous
+        transaction abort which still requires plumbing back to the front end.
+
+        No functional changes - no new tests.
+
+        * Modules/indexeddb/IDBCursor.cpp:
+        (WebCore::IDBCursor::update): Migrate from IDBObjectStoreBackendImpl::put.
+        (WebCore::IDBCursor::advance): Add more explicit transaction-is-active check.
+        (WebCore::IDBCursor::continueFunction): Ditto.
+        (WebCore::IDBCursor::deleteFunction): Ditto.
+        (WebCore::IDBCursor::effectiveObjectStore): Convenience function (source may be store or index).
+        (WebCore):
+        * Modules/indexeddb/IDBCursor.h:
+        (WebCore::IDBCursor::isKeyCursor): Distinguish from IDBCursorWithValue.
+        (IDBCursor):
+        * Modules/indexeddb/IDBCursorBackendImpl.cpp:
+        (WebCore::IDBCursorBackendImpl::update): Remove migrated check.
+        * Modules/indexeddb/IDBCursorWithValue.h:
+        (IDBCursorWithValue):
+        * Modules/indexeddb/IDBDatabase.cpp: Migrate checks.
+        (WebCore::IDBDatabase::createObjectStore):
+        (WebCore::IDBDatabase::deleteObjectStore):
+        * Modules/indexeddb/IDBDatabaseBackendImpl.cpp: Replace checks with assertions.
+        (WebCore::IDBDatabaseBackendImpl::createObjectStore):
+        (WebCore::IDBDatabaseBackendImpl::deleteObjectStore):
+        * Modules/indexeddb/IDBIndex.cpp: Add transaction-is-active checks.
+        (WebCore::IDBIndex::openCursor):
+        (WebCore::IDBIndex::count):
+        (WebCore::IDBIndex::openKeyCursor):
+        (WebCore::IDBIndex::get):
+        (WebCore::IDBIndex::getKey):
+        * Modules/indexeddb/IDBObjectStore.cpp: Migrate cehcks.
+        (WebCore::IDBObjectStore::get):
+        (WebCore::IDBObjectStore::add): Delegates to put(PutMode)
+        (WebCore::IDBObjectStore::put): Delegates to put(PutMode)
+        (WebCore): Adds put(PutMode) which has the unified checks migrated from
+        IDBObjectStoreBackendImpl::put.
+        (WebCore::IDBObjectStore::deleteFunction):
+        (WebCore::IDBObjectStore::clear):
+        (WebCore::IDBObjectStore::createIndex):
+        (WebCore::IDBObjectStore::deleteIndex):
+        (WebCore::IDBObjectStore::openCursor):
+        (WebCore::IDBObjectStore::count):
+        * Modules/indexeddb/IDBObjectStore.h: Adds put(PutMode).
+        (IDBObjectStore):
+        * Modules/indexeddb/IDBObjectStoreBackendImpl.cpp:
+        (WebCore::IDBObjectStoreBackendImpl::getInternal): Fix trace symbol.
+        (WebCore::IDBObjectStoreBackendImpl::put): Remove migrated checks.
+        (WebCore::IDBObjectStoreBackendImpl::createIndex): Remove migrated checks.
+        (WebCore::IDBObjectStoreBackendImpl::deleteIndex): Remove migrated checks.
+        * Modules/indexeddb/IDBTransaction.cpp: Add active flag tracking.
+        (WebCore::IDBTransaction::IDBTransaction):
+        (WebCore::IDBTransaction::abort):
+        (WebCore::IDBTransaction::onAbort):
+        (WebCore::IDBTransaction::onComplete):
+        * Modules/indexeddb/IDBTransaction.h:
+        (WebCore::IDBTransaction::isActive):
+        (WebCore::IDBTransaction::isReadOnly): Group IDL/non-IDL methods.
+        (IDBTransaction):
+
 2012-06-26  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r121285.
index 2759514..af1c504 100644 (file)
 #if ENABLE(INDEXED_DATABASE)
 
 #include "IDBAny.h"
+#include "IDBBindingUtilities.h"
 #include "IDBCallbacks.h"
 #include "IDBCursorBackendInterface.h"
 #include "IDBKey.h"
+#include "IDBObjectStore.h"
 #include "IDBRequest.h"
 #include "IDBTracing.h"
 #include "IDBTransaction.h"
@@ -125,24 +127,37 @@ IDBAny* IDBCursor::source() const
 PassRefPtr<IDBRequest> IDBCursor::update(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, ExceptionCode& ec)
 {
     IDB_TRACE("IDBCursor::update");
+    RefPtr<SerializedScriptValue> value = prpValue;
 
-    if (!m_gotValue) {
+    if (!m_gotValue || isKeyCursor()) {
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
-
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     if (m_transaction->isReadOnly()) {
         ec = IDBDatabaseException::READ_ONLY_ERR;
         return 0;
     }
-
-    RefPtr<SerializedScriptValue> value = prpValue;
     if (value->blobURLs().size() > 0) {
         // FIXME: Add Blob/File/FileList support
         ec = IDBDatabaseException::IDB_DATA_CLONE_ERR;
         return 0;
     }
 
+    RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
+    const IDBKeyPath& keyPath = objectStore->metadata().keyPath;
+    const bool usesInLineKeys = !keyPath.isNull();
+    if (usesInLineKeys) {
+        RefPtr<IDBKey> keyPathKey = createIDBKeyFromSerializedValueAndKeyPath(value, keyPath);
+        if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) {
+            ec = IDBDatabaseException::DATA_ERR;
+            return 0;
+        }
+    }
+
     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
     m_backend->update(value, request, ec);
     if (ec) {
@@ -160,7 +175,7 @@ void IDBCursor::advance(unsigned long count, ExceptionCode& ec)
         return;
     }
 
-    if (!m_request) {
+    if (!m_transaction->isActive()) {
         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
         return;
     }
@@ -188,7 +203,7 @@ void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec)
         return;
     }
 
-    if (!m_request) {
+    if (!m_transaction->isActive()) {
         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
         return;
     }
@@ -213,6 +228,10 @@ void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec)
 PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec)
 {
     IDB_TRACE("IDBCursor::delete");
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     if (m_transaction->isReadOnly()) {
         ec = IDBDatabaseException::READ_ONLY_ERR;
         return 0;
@@ -252,6 +271,14 @@ void IDBCursor::setValueReady()
     m_valueIsDirty = true;
 }
 
+PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore()
+{
+    if (m_source->type() == IDBAny::IDBObjectStoreType)
+        return m_source->idbObjectStore();
+    RefPtr<IDBIndex> index = m_source->idbIndex();
+    return index->objectStore();
+}
+
 unsigned short IDBCursor::stringToDirection(const String& directionString, ExceptionCode& ec)
 {
     if (directionString == IDBCursor::directionNext())
index 3f9d84f..4f4564e 100644 (file)
@@ -91,8 +91,11 @@ public:
 
 protected:
     IDBCursor(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*);
+    virtual bool isKeyCursor() const { return true; }
 
 private:
+    PassRefPtr<IDBObjectStore> effectiveObjectStore();
+
     RefPtr<IDBCursorBackendInterface> m_backend;
     RefPtr<IDBRequest> m_request;
     RefPtr<IDBAny> m_source;
index b6246e9..0a14a6f 100644 (file)
@@ -93,10 +93,9 @@ void IDBCursorBackendImpl::update(PassRefPtr<SerializedScriptValue> value, PassR
 {
     IDB_TRACE("IDBCursorBackendImpl::update");
     ASSERT(m_transaction->mode() != IDBTransaction::READ_ONLY);
-    if (!m_cursor || m_cursorType == IndexKeyCursor) {
-        ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
-        return;
-    }
+
+    ASSERT(m_cursor);
+    ASSERT(m_cursorType != IndexKeyCursor);
 
     m_objectStore->put(value, m_cursor->primaryKey(), IDBObjectStoreBackendInterface::CursorUpdate, callbacks, m_transaction.get(), ec);
 }
index a1ba14f..9b396ba 100644 (file)
@@ -41,6 +41,9 @@ public:
     // 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.
 
+protected:
+    virtual bool isKeyCursor() const OVERRIDE { return false; }
+
 private:
     IDBCursorWithValue(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*);
 };
index 0a49400..ab123c3 100644 (file)
@@ -117,6 +117,10 @@ PassRefPtr<IDBObjectStore> IDBDatabase::createObjectStore(const String& name, co
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
+    if (!m_versionChangeTransaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
 
     IDBKeyPath keyPath;
     if (!options.isUndefinedOrNull()) {
@@ -128,6 +132,11 @@ PassRefPtr<IDBObjectStore> IDBDatabase::createObjectStore(const String& name, co
             keyPath = IDBKeyPath(keyPathString);
     }
 
+    if (m_metadata.objectStores.contains(name)) {
+        ec = IDBDatabaseException::CONSTRAINT_ERR;
+        return 0;
+    }
+
     if (!keyPath.isNull() && !keyPath.isValid()) {
         ec = IDBDatabaseException::IDB_SYNTAX_ERR;
         return 0;
@@ -162,6 +171,14 @@ void IDBDatabase::deleteObjectStore(const String& name, ExceptionCode& ec)
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return;
     }
+    if (!m_versionChangeTransaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return;
+    }
+    if (!m_metadata.objectStores.contains(name)) {
+        ec = IDBDatabaseException::IDB_NOT_FOUND_ERR;
+        return;
+    }
 
     m_backend->deleteObjectStore(name, m_versionChangeTransaction->backend(), ec);
     if (!ec) {
index 5cccaa2..505ba75 100644 (file)
@@ -145,10 +145,7 @@ IDBDatabaseMetadata IDBDatabaseBackendImpl::metadata() const
 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const IDBKeyPath& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
 {
     ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
-    if (m_objectStores.contains(name)) {
-        ec = IDBDatabaseException::CONSTRAINT_ERR;
-        return 0;
-    }
+    ASSERT(!m_objectStores.contains(name));
 
     RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(this, name, keyPath, autoIncrement);
     ASSERT(objectStore->name() == name);
@@ -186,12 +183,10 @@ PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(c
 void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
 {
     ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
-    RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name);
-    if (!objectStore) {
-        ec = IDBDatabaseException::IDB_NOT_FOUND_ERR;
-        return;
-    }
+    ASSERT(m_objectStores.contains(name));
+
     RefPtr<IDBDatabaseBackendImpl> database = this;
+    RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name);
     RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
     if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::deleteObjectStoreInternal, database, objectStore, transaction),
                                    createCallbackTask(&IDBDatabaseBackendImpl::addObjectStoreToMap, database, objectStore))) {
index 31e7f5d..fd93949 100644 (file)
@@ -65,6 +65,10 @@ PassRefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, Pas
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     unsigned short direction = IDBCursor::stringToDirection(directionString, ec);
     if (ec)
         return 0;
@@ -115,6 +119,10 @@ PassRefPtr<IDBRequest> IDBIndex::count(ScriptExecutionContext* context, PassRefP
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
     m_backend->count(keyRange, request, m_transaction->backend(), ec);
     if (ec) {
@@ -140,6 +148,10 @@ PassRefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context,
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
 
     unsigned short direction = IDBCursor::stringToDirection(directionString, ec);
     if (ec)
@@ -200,6 +212,10 @@ PassRefPtr<IDBRequest> IDBIndex::get(ScriptExecutionContext* context, PassRefPtr
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     if (!keyRange) {
         ec = IDBDatabaseException::DATA_ERR;
         return 0;
@@ -231,6 +247,10 @@ PassRefPtr<IDBRequest> IDBIndex::getKey(ScriptExecutionContext* context, PassRef
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     if (!keyRange) {
         ec = IDBDatabaseException::DATA_ERR;
         return 0;
index 68bc171..6c06415 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "DOMStringList.h"
 #include "IDBAny.h"
+#include "IDBBindingUtilities.h"
 #include "IDBDatabase.h"
 #include "IDBDatabaseException.h"
 #include "IDBIndex.h"
@@ -78,6 +79,10 @@ PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, Pass
         ec = IDBDatabaseException::DATA_ERR;
         return 0;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
     m_backend->get(keyRange, request, m_transaction->backend(), ec);
     if (ec) {
@@ -96,65 +101,80 @@ PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, Pass
     return get(context, keyRange.release(), ec);
 }
 
-PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> key, ExceptionCode& ec)
+PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, ExceptionCode& ec)
 {
     IDB_TRACE("IDBObjectStore::add");
+    return put(IDBObjectStoreBackendInterface::AddOnly, context, value, key, ec);
+}
+
+PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, ExceptionCode& ec)
+{
+    IDB_TRACE("IDBObjectStore::put");
+    return put(IDBObjectStoreBackendInterface::AddOrUpdate, context, value, key, ec);
+}
+
+PassRefPtr<IDBRequest> IDBObjectStore::put(IDBObjectStoreBackendInterface::PutMode putMode, ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, ExceptionCode& ec)
+{
+    IDB_TRACE("IDBObjectStore::put");
+    RefPtr<SerializedScriptValue> value = prpValue;
+    RefPtr<IDBKey> key = prpKey;
     if (m_deleted) {
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
-    if (m_transaction->isReadOnly()) {
-        ec = IDBDatabaseException::READ_ONLY_ERR;
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
         return 0;
     }
-
-    if (key && !key->isValid()) {
-        ec = IDBDatabaseException::DATA_ERR;
+    if (m_transaction->isReadOnly()) {
+        ec = IDBDatabaseException::READ_ONLY_ERR;
         return 0;
     }
-
-    RefPtr<SerializedScriptValue> value = prpValue;
     if (value->blobURLs().size() > 0) {
         // FIXME: Add Blob/File/FileList support
         ec = IDBDatabaseException::IDB_DATA_CLONE_ERR;
         return 0;
     }
 
-    RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
-    m_backend->put(value, key, IDBObjectStoreBackendInterface::AddOnly, request, m_transaction->backend(), ec);
-    if (ec) {
-        request->markEarlyDeath();
-        return 0;
-    }
-    return request.release();
-}
+    const IDBKeyPath& keyPath = m_metadata.keyPath;
+    const bool usesInLineKeys = !keyPath.isNull();
+    const bool hasKeyGenerator = autoIncrement();
 
-PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> key, ExceptionCode& ec)
-{
-    IDB_TRACE("IDBObjectStore::put");
-    if (m_deleted) {
-        ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
+    if (usesInLineKeys && key) {
+        ec = IDBDatabaseException::DATA_ERR;
         return 0;
     }
-    if (m_transaction->isReadOnly()) {
-        ec = IDBDatabaseException::READ_ONLY_ERR;
+    if (!usesInLineKeys && !hasKeyGenerator && !key) {
+        ec = IDBDatabaseException::DATA_ERR;
         return 0;
     }
-
+    if (usesInLineKeys) {
+        RefPtr<IDBKey> keyPathKey = createIDBKeyFromSerializedValueAndKeyPath(value, keyPath);
+        if (keyPathKey && !keyPathKey->isValid()) {
+            ec = IDBDatabaseException::DATA_ERR;
+            return 0;
+        }
+        if (!hasKeyGenerator && !keyPathKey) {
+            ec = IDBDatabaseException::DATA_ERR;
+            return 0;
+        }
+        if (hasKeyGenerator && !keyPathKey) {
+            RefPtr<IDBKey> dummyKey = IDBKey::createNumber(-1);
+            RefPtr<SerializedScriptValue> valueAfterInjection = injectIDBKeyIntoSerializedValue(dummyKey, value, keyPath);
+            if (!valueAfterInjection) {
+                ec = IDBDatabaseException::DATA_ERR;
+                return 0;
+            }
+        }
+    }
     if (key && !key->isValid()) {
         ec = IDBDatabaseException::DATA_ERR;
         return 0;
     }
 
-    RefPtr<SerializedScriptValue> value = prpValue;
-    if (value->blobURLs().size() > 0) {
-        // FIXME: Add Blob/File/FileList support
-        ec = IDBDatabaseException::IDB_DATA_CLONE_ERR;
-        return 0;
-    }
-
     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
-    m_backend->put(value, key, IDBObjectStoreBackendInterface::AddOrUpdate, request, m_transaction->backend(), ec);
+    // FIXME: Pass through keyPathKey as key to simplify back end implementation.
+    m_backend->put(value.release(), key.release(), putMode, request, m_transaction->backend(), ec);
     if (ec) {
         request->markEarlyDeath();
         return 0;
@@ -169,11 +189,14 @@ PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* co
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     if (m_transaction->isReadOnly()) {
         ec = IDBDatabaseException::READ_ONLY_ERR;
         return 0;
     }
-
     if (!keyRange) {
         ec = IDBDatabaseException::DATA_ERR;
         return 0;
@@ -204,6 +227,10 @@ PassRefPtr<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, Ex
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     if (m_transaction->isReadOnly()) {
         ec = IDBDatabaseException::READ_ONLY_ERR;
         return 0;
@@ -239,11 +266,22 @@ PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const IDBKe
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
-
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     if (!keyPath.isValid()) {
         ec = IDBDatabaseException::IDB_SYNTAX_ERR;
         return 0;
     }
+    if (name.isNull()) {
+        ec = IDBDatabaseException::IDB_TYPE_ERR;
+        return 0;
+    }
+    if (m_metadata.indexes.contains(name)) {
+        ec = IDBDatabaseException::CONSTRAINT_ERR;
+        return 0;
+    }
 
     bool unique = false;
     options.get("unique", unique);
@@ -304,6 +342,14 @@ void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec)
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return;
+    }
+    if (!m_metadata.indexes.contains(name)) {
+        ec = IDBDatabaseException::IDB_NOT_FOUND_ERR;
+        return;
+    }
 
     m_backend->deleteIndex(name, m_transaction->backend(), ec);
     if (!ec) {
@@ -325,6 +371,10 @@ PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* contex
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     unsigned short direction = IDBCursor::stringToDirection(directionString, ec);
     if (ec)
         return 0;
@@ -375,6 +425,10 @@ PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, Pa
         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
         return 0;
     }
+    if (!m_transaction->isActive()) {
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+        return 0;
+    }
     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
     m_backend->count(range, request, m_transaction->backend(), ec);
     if (ec) {
index 8181eb9..67b36f9 100644 (file)
@@ -95,6 +95,7 @@ public:
     PassRefPtr<IDBRequest> count(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
     PassRefPtr<IDBRequest> count(ScriptExecutionContext*, PassRefPtr<IDBKey>, ExceptionCode&);
 
+    PassRefPtr<IDBRequest> put(IDBObjectStoreBackendInterface::PutMode, ScriptExecutionContext*, PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, ExceptionCode&);
     void markDeleted() { m_deleted = true; }
     void transactionFinished();
 
@@ -103,7 +104,6 @@ public:
 
 private:
     IDBObjectStore(const IDBObjectStoreMetadata&, PassRefPtr<IDBObjectStoreBackendInterface>, IDBTransaction*);
-    void removeTransactionFromPendingList();
 
     IDBObjectStoreMetadata m_metadata;
     RefPtr<IDBObjectStoreBackendInterface> m_backend;
index a468654..af30ef3 100644 (file)
@@ -42,7 +42,7 @@
 #include "IDBKeyPathBackendImpl.h"
 #include "IDBKeyRange.h"
 #include "IDBTracing.h"
-#include "IDBTransactionBackendInterface.h"
+#include "IDBTransactionBackendImpl.h"
 #include "ScriptExecutionContext.h"
 
 namespace WebCore {
@@ -92,7 +92,7 @@ void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKeyRange> prpKeyRange, PassRef
 
 void IDBObjectStoreBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
 {
-    IDB_TRACE("IDBObjectStoreBackendImpl::getByRangeInternal");
+    IDB_TRACE("IDBObjectStoreBackendImpl::getInternal");
     RefPtr<IDBKey> key;
     if (keyRange->isOnlyKey())
         key = keyRange->lower();
@@ -148,53 +148,6 @@ void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue,
     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
     RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
 
-    if (putMode != CursorUpdate) {
-        const bool autoIncrement = objectStore->autoIncrement();
-        const bool hasKeyPath = !objectStore->m_keyPath.isNull();
-
-        if (hasKeyPath && key) {
-            ec = IDBDatabaseException::DATA_ERR;
-            return;
-        }
-        if (!hasKeyPath && !autoIncrement && !key) {
-            ec = IDBDatabaseException::DATA_ERR;
-            return;
-        }
-        if (hasKeyPath) {
-            RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath);
-            if (keyPathKey && !keyPathKey->isValid()) {
-                ec = IDBDatabaseException::DATA_ERR;
-                return;
-            }
-            if (!autoIncrement && !keyPathKey) {
-                ec = IDBDatabaseException::DATA_ERR;
-                return;
-            }
-            if (autoIncrement && !keyPathKey) {
-                RefPtr<IDBKey> dummyKey = IDBKey::createNumber(-1);
-                RefPtr<SerializedScriptValue> valueAfterInjection = injectKeyIntoKeyPath(dummyKey, value, objectStore->m_keyPath);
-                if (!valueAfterInjection) {
-                    ec = IDBDatabaseException::DATA_ERR;
-                    return;
-                }
-            }
-        }
-        if (key && !key->isValid()) {
-            ec = IDBDatabaseException::DATA_ERR;
-            return;
-        }
-    } else {
-        ASSERT(key);
-        const bool hasKeyPath = !objectStore->m_keyPath.isNull();
-        if (hasKeyPath) {
-            RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath);
-            if (!keyPathKey || !keyPathKey->isEqual(key.get())) {
-                ec = IDBDatabaseException::DATA_ERR;
-                return;
-            }
-        }
-    }
-
     if (!transaction->scheduleTask(
             createCallbackTask(&IDBObjectStoreBackendImpl::putInternal, objectStore, value, key, putMode, callbacks, transaction),
             // FIXME: One of these per put() is overkill, since it's simply a cache invalidation.
@@ -459,18 +412,8 @@ bool IDBObjectStoreBackendImpl::populateIndex(IDBBackingStore& backingStore, int
 
 PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
 {
-    if (name.isNull()) {
-        ec = IDBDatabaseException::IDB_TYPE_ERR;
-        return 0;
-    }
-    if (m_indexes.contains(name)) {
-        ec = IDBDatabaseException::CONSTRAINT_ERR;
-        return 0;
-    }
-    if (transaction->mode() != IDBTransaction::VERSION_CHANGE) {
-        ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
-        return 0;
-    }
+    ASSERT(transaction->mode() == IDBTransaction::VERSION_CHANGE);
+    ASSERT(!m_indexes.contains(name));
 
     RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_database, this, name, keyPath, unique, multiEntry);
     ASSERT(index->name() == name);
@@ -520,18 +463,11 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::index(const Stri
 
 void IDBObjectStoreBackendImpl::deleteIndex(const String& name, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
 {
-    if (transaction->mode() != IDBTransaction::VERSION_CHANGE) {
-        ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
-        return;
-    }
-
-    RefPtr<IDBIndexBackendImpl> index = m_indexes.get(name);
-    if (!index) {
-        ec = IDBDatabaseException::IDB_NOT_FOUND_ERR;
-        return;
-    }
+    ASSERT(transaction->mode() == IDBTransaction::VERSION_CHANGE);
+    ASSERT(m_indexes.contains(name));
 
     RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
+    RefPtr<IDBIndexBackendImpl> index = m_indexes.get(name);
     RefPtr<IDBTransactionBackendInterface> transactionPtr = transaction;
     if (!transaction->scheduleTask(
               createCallbackTask(&IDBObjectStoreBackendImpl::deleteIndexInternal,
index 7087c65..6ad3007 100644 (file)
@@ -84,6 +84,7 @@ IDBTransaction::IDBTransaction(ScriptExecutionContext* context, PassRefPtr<IDBTr
     , m_backend(backend)
     , m_database(db)
     , m_mode(mode)
+    , m_active(true)
     , m_transactionFinished(false)
     , m_contextStopped(false)
 {
@@ -193,6 +194,7 @@ void IDBTransaction::abort()
 {
     if (m_transactionFinished)
         return;
+    m_active = false;
     RefPtr<IDBTransaction> selfRef = this;
     if (m_backend)
         m_backend->abort();
@@ -242,6 +244,7 @@ void IDBTransaction::unregisterRequest(IDBRequest* request)
 void IDBTransaction::onAbort()
 {
     ASSERT(!m_transactionFinished);
+    m_active = false;
     while (!m_childRequests.isEmpty()) {
         IDBRequest* request = *m_childRequests.begin();
         m_childRequests.remove(request);
@@ -265,6 +268,7 @@ void IDBTransaction::onAbort()
 void IDBTransaction::onComplete()
 {
     ASSERT(!m_transactionFinished);
+    m_active = false;
     m_objectStoreCleanupMap.clear();
     closeOpenCursors();
     m_database->transactionFinished(this);
index 0b2dec6..2ce82b1 100644 (file)
@@ -68,12 +68,13 @@ public:
     static const AtomicString& modeToString(Mode, ExceptionCode&);
 
     IDBTransactionBackendInterface* backend() const;
+    bool isActive() const { return m_active; }
     bool isFinished() const;
+    bool isReadOnly() const { return m_mode == READ_ONLY; }
     bool isVersionChange() const { return m_mode == VERSION_CHANGE; }
 
     // Implement the IDBTransaction IDL
     const String& mode() const;
-    bool isReadOnly() const { return m_mode == READ_ONLY; }
     IDBDatabase* db() const;
     PassRefPtr<DOMError> error(ExceptionCode&) const;
     void setError(PassRefPtr<DOMError>);
@@ -135,6 +136,7 @@ private:
     RefPtr<IDBTransactionBackendInterface> m_backend;
     RefPtr<IDBDatabase> m_database;
     const Mode m_mode;
+    bool m_active;
     bool m_transactionFinished; // Is it possible that we'll fire any more events or allow any new requests? If not, we're finished.
     bool m_contextStopped;
     RefPtr<DOMError> m_error;