diff options
Diffstat (limited to 'Source/WebCore/Modules/indexeddb/IDBCursor.cpp')
-rw-r--r-- | Source/WebCore/Modules/indexeddb/IDBCursor.cpp | 508 |
1 files changed, 278 insertions, 230 deletions
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursor.cpp b/Source/WebCore/Modules/indexeddb/IDBCursor.cpp index f6ea30ed5..7f490e986 100644 --- a/Source/WebCore/Modules/indexeddb/IDBCursor.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBCursor.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple 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. * - * 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. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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" @@ -28,309 +28,357 @@ #if ENABLE(INDEXED_DATABASE) -#include "IDBAny.h" +#include "ExceptionCode.h" #include "IDBBindingUtilities.h" -#include "IDBCallbacks.h" -#include "IDBCursorBackend.h" -#include "IDBKey.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 "ScriptCallStack.h" #include "ScriptExecutionContext.h" -#include <limits> +#include <heap/HeapInlines.h> +#include <heap/StrongInlines.h> +#include <runtime/JSCJSValueInlines.h> + +using namespace JSC; namespace WebCore { -PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) +Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info) { - return adoptRef(new IDBCursor(backend, direction, request, source, transaction)); + return adoptRef(*new IDBCursor(transaction, objectStore, info)); } -const AtomicString& IDBCursor::directionNext() +Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info) { - DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral)); - return next; + return adoptRef(*new IDBCursor(transaction, index, info)); } -const AtomicString& IDBCursor::directionNextUnique() +IDBCursor::IDBCursor(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info) + : ActiveDOMObject(transaction.scriptExecutionContext()) + , m_info(info) + , m_source(&objectStore) { - DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral)); - return nextunique; -} + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); -const AtomicString& IDBCursor::directionPrev() -{ - DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral)); - return prev; + suspendIfNeeded(); } -const AtomicString& IDBCursor::directionPrevUnique() +IDBCursor::IDBCursor(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info) + : ActiveDOMObject(transaction.scriptExecutionContext()) + , m_info(info) + , m_source(&index) { - DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral)); - return prevunique; -} - + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); -IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) - : m_backend(backend) - , m_request(request) - , m_direction(direction) - , m_source(source) - , m_transaction(transaction) - , m_transactionNotifier(transaction, this) - , m_gotValue(false) -{ - ASSERT(m_backend); - ASSERT(m_request); - ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType); - ASSERT(m_transaction); + suspendIfNeeded(); } IDBCursor::~IDBCursor() { + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); } -const String& IDBCursor::direction() const +bool IDBCursor::sourcesDeleted() const { - LOG(StorageAPI, "IDBCursor::direction"); - return directionToString(m_direction); -} + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); -const Deprecated::ScriptValue& IDBCursor::key() const -{ - LOG(StorageAPI, "IDBCursor::key"); - return m_currentKeyValue; + return WTF::switchOn(m_source, + [] (const RefPtr<IDBObjectStore>& objectStore) { return objectStore->isDeleted(); }, + [] (const RefPtr<IDBIndex>& index) { return index->isDeleted() || index->objectStore().isDeleted(); } + ); } -const Deprecated::ScriptValue& IDBCursor::primaryKey() const +IDBObjectStore& IDBCursor::effectiveObjectStore() const { - LOG(StorageAPI, "IDBCursor::primaryKey"); - return m_currentPrimaryKeyValue; + return WTF::switchOn(m_source, + [] (const RefPtr<IDBObjectStore>& objectStore) -> IDBObjectStore& { return *objectStore; }, + [] (const RefPtr<IDBIndex>& index) -> IDBObjectStore& { return index->objectStore(); } + ); } -const Deprecated::ScriptValue& IDBCursor::value() const +IDBTransaction& IDBCursor::transaction() const { - LOG(StorageAPI, "IDBCursor::value"); - return m_currentValue; + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); + return effectiveObjectStore().transaction(); } -IDBAny* IDBCursor::source() const +ExceptionOr<Ref<IDBRequest>> IDBCursor::update(ExecState& state, JSValue value) { - return m_source.get(); -} + LOG(IndexedDB, "IDBCursor::update"); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); -PassRefPtr<IDBRequest> IDBCursor::update(JSC::ExecState* state, Deprecated::ScriptValue& value, ExceptionCode& ec) -{ - LOG(StorageAPI, "IDBCursor::update"); + 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 (!m_gotValue || isKeyCursor()) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - if (m_transaction->isReadOnly()) { - ec = IDBDatabaseException::ReadOnlyError; - return 0; - } + if (!transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The transaction is inactive or finished.") }; + + 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.") }; - RefPtr<IDBObjectStore> objectStore = effectiveObjectStore(); - const IDBKeyPath& keyPath = objectStore->metadata().keyPath; - const bool usesInLineKeys = !keyPath.isNull(); + 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 (!isKeyCursorWithValue()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is a key cursor.") }; + + auto& objectStore = effectiveObjectStore(); + auto& optionalKeyPath = objectStore.info().keyPath(); + const bool usesInLineKeys = !!optionalKeyPath; if (usesInLineKeys) { - RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, keyPath); - if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) { - ec = IDBDatabaseException::DataError; - return 0; - } + RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, optionalKeyPath.value()); + IDBKeyData keyPathKeyData(keyPathKey.get()); + 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.") }; } - return objectStore->put(IDBDatabaseBackend::CursorUpdate, IDBAny::create(this), state, value, m_currentPrimaryKey, ec); + auto putResult = effectiveObjectStore().putForCursorUpdate(state, value, m_currentPrimaryKey.get()); + if (putResult.hasException()) + return putResult.releaseException(); + + auto request = putResult.releaseReturnValue(); + request->setSource(*this); + ++m_outstandingRequestCount; + + return WTFMove(request); } -void IDBCursor::advance(unsigned long count, ExceptionCode& ec) +ExceptionOr<void> IDBCursor::advance(unsigned count) { - ec = 0; - LOG(StorageAPI, "IDBCursor::advance"); - if (!m_gotValue) { - ec = IDBDatabaseException::InvalidStateError; - return; - } + LOG(IndexedDB, "IDBCursor::advance"); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return; - } + if (!m_request) + return Exception { IDBDatabaseException::InvalidStateError }; - if (!count) { - ec = TypeError; - 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()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The transaction is inactive or finished.") }; + + 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 { }; +} + +ExceptionOr<void> IDBCursor::continuePrimaryKey(ExecState& state, JSValue keyValue, JSValue primaryKeyValue) +{ + 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 (!WTF::holds_alternative<RefPtr<IDBIndex>>(m_source)) + 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_request->setPendingCursor(this); m_gotValue = false; - m_backend->advance(count, m_request, ec); - ASSERT(!ec); + + uncheckedIterateCursor(keyData, primaryKeyData); + + return { }; } -void IDBCursor::continueFunction(ScriptExecutionContext* context, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec) +ExceptionOr<void> IDBCursor::continueFunction(ExecState& execState, JSValue keyValue) { - DOMRequestState requestState(context); - RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue); - continueFunction(key.release(), ec); + RefPtr<IDBKey> key; + if (!keyValue.isUndefined()) + key = scriptValueToIDBKey(execState, keyValue); + + return continueFunction(key.get()); } -void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec) +ExceptionOr<void> IDBCursor::continueFunction(const IDBKeyData& key) { - ec = 0; - LOG(StorageAPI, "IDBCursor::continue"); - if (key && !key->isValid()) { - ec = IDBDatabaseException::DataError; - return; - } + LOG(IndexedDB, "IDBCursor::continueFunction (to key %s)", key.loggingString().utf8().data()); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return; - } + if (!m_request) + return Exception { IDBDatabaseException::InvalidStateError }; - if (!m_gotValue) { - ec = IDBDatabaseException::InvalidStateError; - return; - } + if (!transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The transaction is inactive or finished.") }; + + 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) + 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()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is not a valid key.") }; - if (key) { - ASSERT(m_currentKey); - if (m_direction == IndexedDB::CursorDirection::Next || m_direction == IndexedDB::CursorDirection::NextNoDuplicate) { - if (!m_currentKey->isLessThan(key.get())) { - ec = IDBDatabaseException::DataError; - return; - } - } else { - if (!key->isLessThan(m_currentKey.get())) { - ec = IDBDatabaseException::DataError; - return; - } - } + if (m_info.isDirectionForward()) { + 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.") }; } - // FIXME: We're not using the context from when continue was called, which means the callback - // will be on the original context openCursor was called on. Is this right? - m_request->setPendingCursor(this); m_gotValue = false; - m_backend->continueFunction(key, m_request, ec); - ASSERT(!ec); + + uncheckedIterateCursor(key, 0); + + return { }; } -PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec) +void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, unsigned count) { - ec = 0; - LOG(StorageAPI, "IDBCursor::delete"); - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - if (m_transaction->isReadOnly()) { - ec = IDBDatabaseException::ReadOnlyError; - return 0; - } + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); - if (!m_gotValue || isKeyCursor()) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_backend->deleteFunction(request, ec); - ASSERT(!ec); - return request.release(); + ++m_outstandingRequestCount; + + m_request->willIterateCursor(*this); + transaction().iterateCursor(*this, { key, { }, count }); } -void IDBCursor::postSuccessHandlerCallback() +void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, const IDBKeyData& primaryKey) { - m_backend->postSuccessHandlerCallback(); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); + + ++m_outstandingRequestCount; + + m_request->willIterateCursor(*this); + transaction().iterateCursor(*this, { key, primaryKey, 0 }); } -void IDBCursor::close() +ExceptionOr<Ref<WebCore::IDBRequest>> IDBCursor::deleteFunction(ExecState& state) { - m_transactionNotifier.cursorFinished(); - if (m_request) { - m_request->finishCursor(); - m_request.clear(); - } + LOG(IndexedDB, "IDBCursor::deleteFunction"); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); + + 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()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The transaction is inactive or finished.") }; + + 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) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") }; + + if (!isKeyCursorWithValue()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is a key cursor.") }; + + auto result = effectiveObjectStore().deleteFunction(state, m_currentPrimaryKey.get()); + if (result.hasException()) + return result.releaseException(); + + auto request = result.releaseReturnValue(); + request->setSource(*this); + ++m_outstandingRequestCount; + + return WTFMove(request); } -void IDBCursor::setValueReady(DOMRequestState* state, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, Deprecated::ScriptValue& value) +void IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult) { - m_currentKey = key; - m_currentKeyValue = idbKeyToScriptValue(state, m_currentKey); - - m_currentPrimaryKey = primaryKey; - m_currentPrimaryKeyValue = idbKeyToScriptValue(state, m_currentPrimaryKey); - - if (!isKeyCursor()) { - RefPtr<IDBObjectStore> objectStore = effectiveObjectStore(); - const IDBObjectStoreMetadata metadata = objectStore->metadata(); - if (metadata.autoIncrement && !metadata.keyPath.isNull()) { -#ifndef NDEBUG - RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, metadata.keyPath); - ASSERT(!expectedKey || expectedKey->isEqual(m_currentPrimaryKey.get())); -#endif - bool injected = injectIDBKeyIntoScriptValue(m_request->requestState(), m_currentPrimaryKey, value, metadata.keyPath); - // FIXME: There is no way to report errors here. Move this into onSuccessWithContinuation so that we can abort the transaction there. See: https://bugs.webkit.org/show_bug.cgi?id=92278 - ASSERT_UNUSED(injected, injected); - } + 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_currentKey = { }; + m_currentKeyData = { }; + m_currentPrimaryKey = { }; + m_currentPrimaryKeyData = { }; + m_currentValue = { }; + + m_gotValue = false; + return; } - m_currentValue = value; + + auto& vm = context->vm(); + + // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object + // of the IDBCursor wrapper can be used, rather than the lexicalGlobalObject. + m_currentKey = { vm, toJS(*exec, *exec->lexicalGlobalObject(), getResult.keyData().maybeCreateIDBKey().get()) }; + m_currentKeyData = getResult.keyData(); + m_currentPrimaryKey = { vm, toJS(*exec, *exec->lexicalGlobalObject(), getResult.primaryKeyData().maybeCreateIDBKey().get()) }; + m_currentPrimaryKeyData = getResult.primaryKeyData(); + + if (isKeyCursorWithValue()) + m_currentValue = { vm, deserializeIDBValueToJSValue(*exec, getResult.value()) }; + else + m_currentValue = { }; m_gotValue = true; } -PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore() +const char* IDBCursor::activeDOMObjectName() const { - if (m_source->type() == IDBAny::IDBObjectStoreType) - return m_source->idbObjectStore(); - RefPtr<IDBIndex> index = m_source->idbIndex(); - return index->objectStore(); + return "IDBCursor"; } -IndexedDB::CursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionCode& ec) +bool IDBCursor::canSuspendForDocumentSuspension() const { - if (directionString == IDBCursor::directionNext()) - return IndexedDB::CursorDirection::Next; - if (directionString == IDBCursor::directionNextUnique()) - return IndexedDB::CursorDirection::NextNoDuplicate; - if (directionString == IDBCursor::directionPrev()) - return IndexedDB::CursorDirection::Prev; - if (directionString == IDBCursor::directionPrevUnique()) - return IndexedDB::CursorDirection::PrevNoDuplicate; - - ec = TypeError; - return IndexedDB::CursorDirection::Next; + return false; } -const AtomicString& IDBCursor::directionToString(IndexedDB::CursorDirection direction) +bool IDBCursor::hasPendingActivity() const { - switch (direction) { - case IndexedDB::CursorDirection::Next: - return IDBCursor::directionNext(); - - case IndexedDB::CursorDirection::NextNoDuplicate: - return IDBCursor::directionNextUnique(); - - case IndexedDB::CursorDirection::Prev: - return IDBCursor::directionPrev(); - - case IndexedDB::CursorDirection::PrevNoDuplicate: - return IDBCursor::directionPrevUnique(); + return m_outstandingRequestCount; +} - default: - ASSERT_NOT_REACHED(); - return IDBCursor::directionNext(); - } +void IDBCursor::decrementOutstandingRequestCount() +{ + ASSERT(m_outstandingRequestCount); + --m_outstandingRequestCount; } } // namespace WebCore |