[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBCursor.cpp
index 9980828..ff2c219 100644 (file)
 
 #if ENABLE(INDEXED_DATABASE)
 
-#include "DOMRequestState.h"
 #include "ExceptionCode.h"
 #include "IDBBindingUtilities.h"
+#include "IDBDatabase.h"
 #include "IDBDatabaseException.h"
 #include "IDBGetResult.h"
+#include "IDBIndex.h"
+#include "IDBIterateCursorData.h"
 #include "IDBObjectStore.h"
 #include "IDBRequest.h"
 #include "IDBTransaction.h"
 #include "Logging.h"
 #include "ScriptExecutionContext.h"
+#include <heap/StrongInlines.h>
+#include <runtime/JSCJSValueInlines.h>
 #include <wtf/NeverDestroyed.h>
 
+using namespace JSC;
+
 namespace WebCore {
 
 const AtomicString& IDBCursor::directionNext()
@@ -66,42 +72,46 @@ const AtomicString& IDBCursor::directionPrevUnique()
     return prevunique;
 }
 
-IndexedDB::CursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionCode& ec)
+std::optional<IndexedDB::CursorDirection> IDBCursor::stringToDirection(const String& directionString)
 {
-    if (directionString == IDBCursor::directionNext())
+    if (directionString == directionNext())
         return IndexedDB::CursorDirection::Next;
-    if (directionString == IDBCursor::directionNextUnique())
+    if (directionString == directionNextUnique())
         return IndexedDB::CursorDirection::NextNoDuplicate;
-    if (directionString == IDBCursor::directionPrev())
+    if (directionString == directionPrev())
         return IndexedDB::CursorDirection::Prev;
-    if (directionString == IDBCursor::directionPrevUnique())
+    if (directionString == directionPrevUnique())
         return IndexedDB::CursorDirection::PrevNoDuplicate;
 
-    ec = TypeError;
-    return IndexedDB::CursorDirection::Next;
+    return std::nullopt;
 }
 
 const AtomicString& IDBCursor::directionToString(IndexedDB::CursorDirection direction)
 {
     switch (direction) {
     case IndexedDB::CursorDirection::Next:
-        return IDBCursor::directionNext();
+        return directionNext();
 
     case IndexedDB::CursorDirection::NextNoDuplicate:
-        return IDBCursor::directionNextUnique();
+        return directionNextUnique();
 
     case IndexedDB::CursorDirection::Prev:
-        return IDBCursor::directionPrev();
+        return directionPrev();
 
     case IndexedDB::CursorDirection::PrevNoDuplicate:
-        return IDBCursor::directionPrevUnique();
+        return directionPrevUnique();
 
     default:
         ASSERT_NOT_REACHED();
-        return IDBCursor::directionNext();
+        return directionNext();
     }
 }
 
+Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
+{
+    return adoptRef(*new IDBCursor(transaction, objectStore, info));
+}
+
 Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info)
 {
     return adoptRef(*new IDBCursor(transaction, index, info));
@@ -110,32 +120,37 @@ Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBIndex& index, c
 IDBCursor::IDBCursor(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
     : ActiveDOMObject(transaction.scriptExecutionContext())
     , m_info(info)
-    , m_source(IDBAny::create(objectStore).leakRef())
     , m_objectStore(&objectStore)
 {
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+
     suspendIfNeeded();
 }
 
 IDBCursor::IDBCursor(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info)
     : ActiveDOMObject(transaction.scriptExecutionContext())
     , m_info(info)
-    , m_source(IDBAny::create(index).leakRef())
     , m_index(&index)
 {
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+
     suspendIfNeeded();
 }
 
 IDBCursor::~IDBCursor()
 {
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
 }
 
 bool IDBCursor::sourcesDeleted() const
 {
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+
     if (m_objectStore)
         return m_objectStore->isDeleted();
 
     ASSERT(m_index);
-    return m_index->isDeleted() || m_index->modernObjectStore().isDeleted();
+    return m_index->isDeleted() || m_index->objectStore().isDeleted();
 }
 
 IDBObjectStore& IDBCursor::effectiveObjectStore() const
@@ -144,281 +159,268 @@ IDBObjectStore& IDBCursor::effectiveObjectStore() const
         return *m_objectStore;
 
     ASSERT(m_index);
-    return m_index->modernObjectStore();
+    return m_index->objectStore();
 }
 
 IDBTransaction& IDBCursor::transaction() const
 {
-    return effectiveObjectStore().modernTransaction();
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+    return effectiveObjectStore().transaction();
 }
 
 const String& IDBCursor::direction() const
 {
-    return IDBCursor::directionToString(m_info.cursorDirection());
-}
-
-const Deprecated::ScriptValue& IDBCursor::key() const
-{
-    return m_deprecatedCurrentKey;
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+    return directionToString(m_info.cursorDirection());
 }
 
-const Deprecated::ScriptValue& IDBCursor::primaryKey() const
-{
-    return m_deprecatedCurrentPrimaryKey;
-}
-
-const Deprecated::ScriptValue& IDBCursor::value() const
-{
-    return m_deprecatedCurrentValue;
-}
-
-IDBAny* IDBCursor::source()
-{
-    return &m_source.get();
-}
-
-RefPtr<WebCore::IDBRequest> IDBCursor::update(JSC::ExecState& exec, Deprecated::ScriptValue& value, ExceptionCodeWithMessage& ec)
+ExceptionOr<Ref<IDBRequest>> IDBCursor::update(ExecState& state, JSValue value)
 {
     LOG(IndexedDB, "IDBCursor::update");
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
 
-    if (sourcesDeleted()) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        ec.message = ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor's source or effective object store has been deleted.");
-        return nullptr;
-    }
+    if (sourcesDeleted())
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
 
-    if (!transaction().isActive()) {
-        ec.code = IDBDatabaseException::TransactionInactiveError;
-        ec.message = ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The transaction is inactive or finished.");
-        return nullptr;
-    }
+    if (!transaction().isActive())
+        return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The transaction is inactive or finished.") };
 
-    if (transaction().isReadOnly()) {
-        ec.code = IDBDatabaseException::ReadOnlyError;
-        ec.message = ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The record may not be updated inside a read-only transaction.");
-        return nullptr;
-    }
+    if (transaction().isReadOnly())
+        return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The record may not be updated inside a read-only transaction.") };
 
-    if (!m_gotValue) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        ec.message = ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is being iterated or has iterated past its end.");
-        return nullptr;
-    }
+    if (!m_gotValue)
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
 
-    if (isKeyCursor()) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        ec.message = ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is a key cursor.");
-        return nullptr;
-    }
+    if (!isKeyCursorWithValue())
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is a key cursor.") };
 
     auto& objectStore = effectiveObjectStore();
-    auto& keyPath = objectStore.info().keyPath();
-    const bool usesInLineKeys = !keyPath.isNull();
+    auto& optionalKeyPath = objectStore.info().keyPath();
+    const bool usesInLineKeys = !!optionalKeyPath;
     if (usesInLineKeys) {
-        RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(exec, value, keyPath);
+        RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, optionalKeyPath.value());
         IDBKeyData keyPathKeyData(keyPathKey.get());
-        if (!keyPathKey || keyPathKeyData != m_currentPrimaryKeyData) {
-            ec.code = IDBDatabaseException::DataError;
-            ec.message = ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key.");
-            return nullptr;
-        }
+        if (!keyPathKey || keyPathKeyData != m_currentPrimaryKeyData)
+            return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key.") };
     }
 
-    auto request = effectiveObjectStore().putForCursorUpdate(exec, value.jsValue(), m_deprecatedCurrentPrimaryKey.jsValue(), ec);
-    if (ec.code)
-        return nullptr;
+    auto putResult = effectiveObjectStore().putForCursorUpdate(state, value, m_currentPrimaryKey.get());
+    if (putResult.hasException())
+        return putResult.releaseException();
 
-    ASSERT(request);
+    auto request = putResult.releaseReturnValue();
     request->setSource(*this);
     ++m_outstandingRequestCount;
 
-    return request;
+    return WTFMove(request);
 }
 
-void IDBCursor::advance(unsigned long count, ExceptionCodeWithMessage& ec)
+ExceptionOr<void> IDBCursor::advance(unsigned count)
 {
     LOG(IndexedDB, "IDBCursor::advance");
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
 
-    if (!m_request) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        return;
-    }
-    
-    if (!count) {
-        ec.code = TypeError;
-        ec.message = ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': A count argument with value 0 (zero) was supplied, must be greater than 0.");
-        return;
-    }
+    if (!m_request)
+        return Exception { IDBDatabaseException::InvalidStateError };
 
-    if (sourcesDeleted()) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        ec.message = ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor's source or effective object store has been deleted.");
-        return;
-    }
+    if (!count)
+        return Exception { TypeError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': A count argument with value 0 (zero) was supplied, must be greater than 0.") };
 
-    if (!transaction().isActive()) {
-        ec.code = IDBDatabaseException::TransactionInactiveError;
-        ec.message = ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The transaction is inactive or finished.");
-        return;
-    }
+    if (!transaction().isActive())
+        return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The transaction is inactive or finished.") };
 
-    if (!m_gotValue) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        ec.message = ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor is being iterated or has iterated past its end.");
-        return;
-    }
+    if (sourcesDeleted())
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
+
+    if (!m_gotValue)
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
 
     m_gotValue = false;
 
     uncheckedIterateCursor(IDBKeyData(), count);
+
+    return { };
 }
 
-void IDBCursor::continueFunction(ScriptExecutionContext&, ExceptionCodeWithMessage& ec)
+ExceptionOr<void> IDBCursor::continuePrimaryKey(ExecState& state, JSValue keyValue, JSValue primaryKeyValue)
 {
-    continueFunction(IDBKeyData(), ec);
+    if (!transaction().isActive())
+        return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The transaction is inactive or finished.") };
+
+    if (sourcesDeleted())
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
+
+    if (!m_index)
+        return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's source is not an index.") };
+
+    auto direction = m_info.cursorDirection();
+    if (direction != IndexedDB::CursorDirection::Next && direction != IndexedDB::CursorDirection::Prev)
+        return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's direction must be either \"next\" or \"prev\".") };
+
+    if (!m_gotValue)
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
+
+    RefPtr<IDBKey> key = scriptValueToIDBKey(state, keyValue);
+    if (!key->isValid())
+        return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is not a valid key.") };
+
+    RefPtr<IDBKey> primaryKey = scriptValueToIDBKey(state, primaryKeyValue);
+    if (!primaryKey->isValid())
+        return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The second parameter is not a valid key.") };
+
+    IDBKeyData keyData = { key.get() };
+    IDBKeyData primaryKeyData = { primaryKey.get() };
+
+    if (keyData < m_currentKeyData && direction == IndexedDB::CursorDirection::Next)
+        return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is less than this cursor's position and this cursor's direction is \"next\".") };
+
+    if (keyData > m_currentKeyData && direction == IndexedDB::CursorDirection::Prev)
+        return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is greater than this cursor's position and this cursor's direction is \"prev\".") };
+
+    if (keyData == m_currentKeyData) {
+        if (primaryKeyData <= m_currentPrimaryKeyData && direction == IndexedDB::CursorDirection::Next)
+            return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The key parameters represent a position less-than-or-equal-to this cursor's position and this cursor's direction is \"next\".") };
+        if (primaryKeyData >= m_currentPrimaryKeyData && direction == IndexedDB::CursorDirection::Prev)
+            return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The key parameters represent a position greater-than-or-equal-to this cursor's position and this cursor's direction is \"prev\".") };
+    }
+
+    m_gotValue = false;
+
+    uncheckedIterateCursor(keyData, primaryKeyData);
+
+    return { };
 }
 
-void IDBCursor::continueFunction(ScriptExecutionContext& context, const Deprecated::ScriptValue& keyValue, ExceptionCodeWithMessage& ec)
+ExceptionOr<void> IDBCursor::continueFunction(ExecState& execState, JSValue keyValue)
 {
-    DOMRequestState requestState(&context);
     RefPtr<IDBKey> key;
-    if (!keyValue.jsValue().isUndefined())
-        key = scriptValueToIDBKey(&requestState, keyValue);
+    if (!keyValue.isUndefined())
+        key = scriptValueToIDBKey(execState, keyValue);
 
-    continueFunction(key.get(), ec);
+    return continueFunction(key.get());
 }
 
-void IDBCursor::continueFunction(const IDBKeyData& key, ExceptionCodeWithMessage& ec)
+ExceptionOr<void> IDBCursor::continueFunction(const IDBKeyData& key)
 {
     LOG(IndexedDB, "IDBCursor::continueFunction (to key %s)", key.loggingString().utf8().data());
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
 
-    if (!m_request) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        return;
-    }
+    if (!m_request)
+        return Exception { IDBDatabaseException::InvalidStateError };
 
-    if (sourcesDeleted()) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor's source or effective object store has been deleted.");
-        return;
-    }
+    if (!transaction().isActive())
+        return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The transaction is inactive or finished.") };
 
-    if (!transaction().isActive()) {
-        ec.code = IDBDatabaseException::TransactionInactiveError;
-        ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The transaction is inactive or finished.");
-        return;
-    }
+    if (sourcesDeleted())
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
 
-    if (!m_gotValue) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor is being iterated or has iterated past its end.");
-        return;
-    }
+    if (!m_gotValue)
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
 
-    if (!key.isNull() && !key.isValid()) {
-        ec.code = IDBDatabaseException::DataError;
-        ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is not a valid key.");
-        return;
-    }
+    if (!key.isNull() && !key.isValid())
+        return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is not a valid key.") };
 
     if (m_info.isDirectionForward()) {
-        if (!key.isNull() && key.compare(m_currentKeyData) <= 0) {
-            ec.code = IDBDatabaseException::DataError;
-            ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is less than or equal to this cursor's position.");
-            return;
-        }
-    } else if (!key.isNull() && key.compare(m_currentKeyData) >= 0) {
-        ec.code = IDBDatabaseException::DataError;
-        ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is greater than or equal to this cursor's position.");
-        return;
+        if (!key.isNull() && key.compare(m_currentKeyData) <= 0)
+            return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is less than or equal to this cursor's position.") };
+    } else {
+        if (!key.isNull() && key.compare(m_currentKeyData) >= 0)
+            return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is greater than or equal to this cursor's position.") };
     }
 
     m_gotValue = false;
 
     uncheckedIterateCursor(key, 0);
+
+    return { };
 }
 
-void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, unsigned long count)
+void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, unsigned count)
 {
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+
     ++m_outstandingRequestCount;
 
     m_request->willIterateCursor(*this);
-    transaction().iterateCursor(*this, key, count);
+    transaction().iterateCursor(*this, { key, { }, count });
 }
 
-RefPtr<WebCore::IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext& context, ExceptionCodeWithMessage& ec)
+void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, const IDBKeyData& primaryKey)
+{
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+
+    ++m_outstandingRequestCount;
+
+    m_request->willIterateCursor(*this);
+    transaction().iterateCursor(*this, { key, primaryKey, 0 });
+}
+
+ExceptionOr<Ref<WebCore::IDBRequest>> IDBCursor::deleteFunction(ExecState& state)
 {
     LOG(IndexedDB, "IDBCursor::deleteFunction");
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
 
-    if (sourcesDeleted()) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        ec.message = ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor's source or effective object store has been deleted.");
-        return nullptr;
-    }
+    if (sourcesDeleted())
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
 
-    if (!transaction().isActive()) {
-        ec.code = IDBDatabaseException::TransactionInactiveError;
-        ec.message = ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The transaction is inactive or finished.");
-        return nullptr;
-    }
+    if (!transaction().isActive())
+        return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The transaction is inactive or finished.") };
 
-    if (transaction().isReadOnly()) {
-        ec.code = IDBDatabaseException::ReadOnlyError;
-        ec.message = ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The record may not be deleted inside a read-only transaction.");
-        return nullptr;
-    }
+    if (transaction().isReadOnly())
+        return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The record may not be deleted inside a read-only transaction.") };
 
-    if (!m_gotValue) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        ec.message = ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is being iterated or has iterated past its end.");
-        return nullptr;
-    }
+    if (!m_gotValue)
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
 
-    if (isKeyCursor()) {
-        ec.code = IDBDatabaseException::InvalidStateError;
-        ec.message = ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is a key cursor.");
-        return nullptr;
-    }
+    if (!isKeyCursorWithValue())
+        return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is a key cursor.") };
 
-    auto request = effectiveObjectStore().modernDelete(context, m_deprecatedCurrentPrimaryKey.jsValue(), ec);
-    if (ec.code)
-        return nullptr;
+    auto result = effectiveObjectStore().deleteFunction(state, m_currentPrimaryKey.get());
+    if (result.hasException())
+        return result.releaseException();
 
-    ASSERT(request);
+    auto request = result.releaseReturnValue();
     request->setSource(*this);
     ++m_outstandingRequestCount;
 
-    return request;
+    return WTFMove(request);
 }
 
 void IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult)
 {
     LOG(IndexedDB, "IDBCursor::setGetResult - current key %s", getResult.keyData().loggingString().substring(0, 100).utf8().data());
+    ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
 
     auto* context = request.scriptExecutionContext();
     if (!context)
         return;
 
+    auto* exec = context->execState();
+    if (!exec)
+        return;
+
     if (!getResult.isDefined()) {
-        m_deprecatedCurrentKey = { };
+        m_currentKey = { };
         m_currentKeyData = { };
-        m_deprecatedCurrentPrimaryKey = { };
+        m_currentPrimaryKey = { };
         m_currentPrimaryKeyData = { };
-        m_deprecatedCurrentValue = { };
+        m_currentValue = { };
 
         m_gotValue = false;
         return;
     }
 
-    m_deprecatedCurrentKey = idbKeyDataToScriptValue(context, getResult.keyData());
+    auto& vm = context->vm();
+
+    m_currentKey = { vm, idbKeyDataToScriptValue(*exec, getResult.keyData()) };
     m_currentKeyData = getResult.keyData();
-    m_deprecatedCurrentPrimaryKey = idbKeyDataToScriptValue(context, getResult.primaryKeyData());
+    m_currentPrimaryKey = { vm, idbKeyDataToScriptValue(*exec, getResult.primaryKeyData()) };
     m_currentPrimaryKeyData = getResult.primaryKeyData();
 
-    if (isKeyCursor())
-        m_deprecatedCurrentValue = { };
+    if (isKeyCursorWithValue())
+        m_currentValue = { vm, deserializeIDBValueToJSValue(*exec, getResult.value()) };
     else
-        m_deprecatedCurrentValue = deserializeIDBValueData(*context, getResult.value().data());
+        m_currentValue = { };
 
     m_gotValue = true;
 }