summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/indexeddb/IDBRequest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/indexeddb/IDBRequest.cpp')
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequest.cpp755
1 files changed, 380 insertions, 375 deletions
diff --git a/Source/WebCore/Modules/indexeddb/IDBRequest.cpp b/Source/WebCore/Modules/indexeddb/IDBRequest.cpp
index 1bc170647..8b84cd19c 100644
--- a/Source/WebCore/Modules/indexeddb/IDBRequest.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBRequest.cpp
@@ -1,29 +1,26 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015-2017 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.
- * 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.
+ * 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"
@@ -31,519 +28,527 @@
#if ENABLE(INDEXED_DATABASE)
-#include "EventException.h"
-#include "EventListener.h"
+#include "DOMError.h"
+#include "Event.h"
#include "EventNames.h"
#include "EventQueue.h"
-#include "ExceptionCodePlaceholder.h"
#include "IDBBindingUtilities.h"
-#include "IDBCursorBackend.h"
-#include "IDBCursorWithValue.h"
+#include "IDBConnectionProxy.h"
+#include "IDBCursor.h"
#include "IDBDatabase.h"
+#include "IDBDatabaseException.h"
#include "IDBEventDispatcher.h"
-#include "IDBTransaction.h"
+#include "IDBIndex.h"
+#include "IDBKeyData.h"
+#include "IDBObjectStore.h"
+#include "IDBResultData.h"
+#include "JSDOMConvert.h"
#include "Logging.h"
+#include "ScopeGuard.h"
#include "ScriptExecutionContext.h"
+#include "ThreadSafeDataBuffer.h"
+#include <heap/StrongInlines.h>
+#include <wtf/Variant.h>
+
+using namespace JSC;
namespace WebCore {
-PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
+Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction)
{
- RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, IDBDatabaseBackend::NormalTask, transaction)));
- request->suspendIfNeeded();
- // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
- if (transaction)
- transaction->registerRequest(request.get());
- return request.release();
+ return adoptRef(*new IDBRequest(context, objectStore, transaction));
}
-PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBDatabaseBackend::TaskType taskType, IDBTransaction* transaction)
+Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction)
{
- RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, taskType, transaction)));
- request->suspendIfNeeded();
- // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
- if (transaction)
- transaction->registerRequest(request.get());
- return request.release();
+ return adoptRef(*new IDBRequest(context, cursor, transaction));
}
-IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBDatabaseBackend::TaskType taskType, IDBTransaction* transaction)
- : ActiveDOMObject(context)
- , m_result(0)
- , m_errorCode(0)
- , m_contextStopped(false)
- , m_transaction(transaction)
- , m_readyState(PENDING)
- , m_requestAborted(false)
- , m_source(source)
- , m_taskType(taskType)
- , m_hasPendingActivity(true)
- , m_cursorType(IndexedDB::CursorType::KeyAndValue)
- , m_cursorDirection(IndexedDB::CursorDirection::Next)
- , m_cursorFinished(false)
- , m_pendingCursor(0)
- , m_didFireUpgradeNeededEvent(false)
- , m_preventPropagation(false)
- , m_requestState(context)
+Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction)
{
+ return adoptRef(*new IDBRequest(context, index, transaction));
}
-IDBRequest::~IDBRequest()
+Ref<IDBRequest> IDBRequest::createObjectStoreGet(ScriptExecutionContext& context, IDBObjectStore& objectStore, IndexedDB::ObjectStoreRecordType type, IDBTransaction& transaction)
{
- ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !scriptExecutionContext());
+ return adoptRef(*new IDBRequest(context, objectStore, type, transaction));
}
-PassRefPtr<IDBAny> IDBRequest::result(ExceptionCode& ec) const
+Ref<IDBRequest> IDBRequest::createIndexGet(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction)
{
- if (m_readyState != DONE) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- return m_result;
+ return adoptRef(*new IDBRequest(context, index, requestedRecordType, transaction));
}
-PassRefPtr<DOMError> IDBRequest::error(ExceptionCode& ec) const
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy)
+ : IDBActiveDOMObject(&context)
+ , m_resourceIdentifier(connectionProxy)
+ , m_connectionProxy(connectionProxy)
{
- if (m_readyState != DONE) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- return m_error;
+ suspendIfNeeded();
}
-unsigned short IDBRequest::errorCode(ExceptionCode& ec) const
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction)
+ : IDBActiveDOMObject(&context)
+ , m_transaction(&transaction)
+ , m_resourceIdentifier(transaction.connectionProxy())
+ , m_source(&objectStore)
+ , m_connectionProxy(transaction.database().connectionProxy())
{
- if (m_readyState != DONE) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- return m_errorCode;
+ suspendIfNeeded();
}
-PassRefPtr<IDBAny> IDBRequest::source() const
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction)
+ : IDBActiveDOMObject(&context)
+ , m_transaction(&transaction)
+ , m_resourceIdentifier(transaction.connectionProxy())
+ , m_pendingCursor(&cursor)
+ , m_connectionProxy(transaction.database().connectionProxy())
{
- return m_source;
+ suspendIfNeeded();
+
+ WTF::switchOn(cursor.source(),
+ [this] (const auto& value) { this->m_source = IDBRequest::Source { value }; }
+ );
+
+ cursor.setRequest(*this);
}
-PassRefPtr<IDBTransaction> IDBRequest::transaction() const
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction)
+ : IDBActiveDOMObject(&context)
+ , m_transaction(&transaction)
+ , m_resourceIdentifier(transaction.connectionProxy())
+ , m_source(&index)
+ , m_connectionProxy(transaction.database().connectionProxy())
{
- return m_transaction;
+ suspendIfNeeded();
}
-const String& IDBRequest::readyState() const
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBObjectStore& objectStore, IndexedDB::ObjectStoreRecordType type, IDBTransaction& transaction)
+ : IDBActiveDOMObject(&context)
+ , m_transaction(&transaction)
+ , m_resourceIdentifier(transaction.connectionProxy())
+ , m_source(&objectStore)
+ , m_requestedObjectStoreRecordType(type)
+ , m_connectionProxy(transaction.database().connectionProxy())
{
- ASSERT(m_readyState == PENDING || m_readyState == DONE);
- DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending", AtomicString::ConstructFromLiteral));
- DEFINE_STATIC_LOCAL(AtomicString, done, ("done", AtomicString::ConstructFromLiteral));
-
- if (m_readyState == PENDING)
- return pending;
+ suspendIfNeeded();
+}
- return done;
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction)
+ : IDBRequest(context, index, transaction)
+{
+ m_requestedIndexRecordType = requestedRecordType;
}
-void IDBRequest::markEarlyDeath()
+IDBRequest::~IDBRequest()
{
- ASSERT(m_readyState == PENDING);
- m_readyState = EarlyDeath;
- if (m_transaction)
- m_transaction->unregisterRequest(this);
+ ASSERT(currentThread() == originThreadID());
+
+ if (m_result) {
+ WTF::switchOn(m_result.value(),
+ [] (RefPtr<IDBCursor>& cursor) { cursor->clearRequest(); },
+ [] (const auto&) { }
+ );
+ }
}
-void IDBRequest::abort()
+ExceptionOr<std::optional<IDBRequest::Result>> IDBRequest::result() const
{
- ASSERT(!m_requestAborted);
- if (m_contextStopped || !scriptExecutionContext())
- return;
- ASSERT(m_readyState == PENDING || m_readyState == DONE);
- if (m_readyState == DONE)
- return;
+ if (!isDone())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to read the 'result' property from 'IDBRequest': The request has not finished.") };
- // Enqueued events may be the only reference to this object.
- RefPtr<IDBRequest> self(this);
+ return std::optional<IDBRequest::Result> { m_result };
+}
- EventQueue& eventQueue = scriptExecutionContext()->eventQueue();
- for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
- bool removed = eventQueue.cancelEvent(*m_enqueuedEvents[i]);
- ASSERT_UNUSED(removed, removed);
- }
- m_enqueuedEvents.clear();
+ExceptionOr<DOMError*> IDBRequest::error() const
+{
+ ASSERT(currentThread() == originThreadID());
+
+ if (!isDone())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to read the 'error' property from 'IDBRequest': The request has not finished.") };
- m_errorCode = 0;
- m_error.clear();
- m_errorMessage = String();
- m_result.clear();
- onError(IDBDatabaseError::create(IDBDatabaseException::AbortError));
- m_requestAborted = true;
+ return m_domError.get();
}
-void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, IndexedDB::CursorDirection direction)
+void IDBRequest::setSource(IDBCursor& cursor)
{
- ASSERT(m_readyState == PENDING);
- ASSERT(!m_pendingCursor);
- m_cursorType = cursorType;
- m_cursorDirection = direction;
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!m_cursorRequestNotifier);
+
+ m_source = Source { &cursor };
+ m_cursorRequestNotifier = std::make_unique<ScopeGuard>([this]() {
+ ASSERT(WTF::holds_alternative<RefPtr<IDBCursor>>(m_source.value()));
+ WTF::get<RefPtr<IDBCursor>>(m_source.value())->decrementOutstandingRequestCount();
+ });
}
-void IDBRequest::setPendingCursor(PassRefPtr<IDBCursor> cursor)
+void IDBRequest::setVersionChangeTransaction(IDBTransaction& transaction)
{
- ASSERT(m_readyState == DONE);
- ASSERT(scriptExecutionContext());
- ASSERT(m_transaction);
- ASSERT(!m_pendingCursor);
- ASSERT(cursor == getResultCursor());
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!m_transaction);
+ ASSERT(transaction.isVersionChange());
+ ASSERT(!transaction.isFinishedOrFinishing());
- m_pendingCursor = cursor;
- m_result.clear();
- m_readyState = PENDING;
- m_errorCode = 0;
- m_error.clear();
- m_errorMessage = String();
- m_transaction->registerRequest(this);
+ m_transaction = &transaction;
}
-PassRefPtr<IDBCursor> IDBRequest::getResultCursor()
+RefPtr<WebCore::IDBTransaction> IDBRequest::transaction() const
{
- if (!m_result)
+ ASSERT(currentThread() == originThreadID());
+ return m_shouldExposeTransactionToDOM ? m_transaction : nullptr;
+}
+
+uint64_t IDBRequest::sourceObjectStoreIdentifier() const
+{
+ ASSERT(currentThread() == originThreadID());
+
+ if (!m_source)
return 0;
- if (m_result->type() == IDBAny::IDBCursorType)
- return m_result->idbCursor();
- if (m_result->type() == IDBAny::IDBCursorWithValueType)
- return m_result->idbCursorWithValue();
- return 0;
+
+ return WTF::switchOn(m_source.value(),
+ [] (const RefPtr<IDBObjectStore>& objectStore) { return objectStore->info().identifier(); },
+ [] (const RefPtr<IDBIndex>& index) { return index->info().objectStoreIdentifier(); },
+ [] (const RefPtr<IDBCursor>&) { return 0; }
+ );
}
-void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, const Deprecated::ScriptValue& value)
+uint64_t IDBRequest::sourceIndexIdentifier() const
{
- ASSERT(m_readyState == PENDING);
- m_cursorKey = key;
- m_cursorPrimaryKey = primaryKey;
- m_cursorValue = value;
+ ASSERT(currentThread() == originThreadID());
- if (m_cursorType == IndexedDB::CursorType::KeyOnly) {
- m_result = IDBAny::create(cursor);
- return;
- }
+ if (!m_source)
+ return 0;
- m_result = IDBAny::create(IDBCursorWithValue::fromCursor(cursor));
+ return WTF::switchOn(m_source.value(),
+ [] (const RefPtr<IDBObjectStore>&) -> uint64_t { return 0; },
+ [] (const RefPtr<IDBIndex>& index) -> uint64_t { return index->info().identifier(); },
+ [] (const RefPtr<IDBCursor>&) -> uint64_t { return 0; }
+ );
}
-void IDBRequest::finishCursor()
+IndexedDB::ObjectStoreRecordType IDBRequest::requestedObjectStoreRecordType() const
{
- m_cursorFinished = true;
- if (m_readyState != PENDING)
- m_hasPendingActivity = false;
+ ASSERT(currentThread() == originThreadID());
+
+ return m_requestedObjectStoreRecordType;
}
-bool IDBRequest::shouldEnqueueEvent() const
+IndexedDB::IndexRecordType IDBRequest::requestedIndexRecordType() const
{
- if (m_contextStopped || !scriptExecutionContext())
- return false;
- ASSERT(m_readyState == PENDING || m_readyState == DONE);
- if (m_requestAborted)
- return false;
- ASSERT(m_readyState == PENDING);
- ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
- return true;
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(m_source);
+ ASSERT(WTF::holds_alternative<RefPtr<IDBIndex>>(m_source.value()));
+
+ return m_requestedIndexRecordType;
}
-void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error)
+EventTargetInterface IDBRequest::eventTargetInterface() const
{
- LOG(StorageAPI, "IDBRequest::onError()");
- if (!shouldEnqueueEvent())
- return;
+ ASSERT(currentThread() == originThreadID());
- m_errorCode = error->code();
- m_errorMessage = error->message();
- m_error = DOMError::create(IDBDatabaseException::getErrorName(error->idbCode()));
- m_pendingCursor.clear();
- enqueueEvent(Event::create(eventNames().errorEvent, true, true));
+ return IDBRequestEventTargetInterfaceType;
}
-static PassRefPtr<Event> createSuccessEvent()
+const char* IDBRequest::activeDOMObjectName() const
{
- return Event::create(eventNames().successEvent, false, false);
+ ASSERT(currentThread() == originThreadID());
+
+ return "IDBRequest";
}
-void IDBRequest::onSuccess(PassRefPtr<DOMStringList> domStringList)
+bool IDBRequest::canSuspendForDocumentSuspension() const
{
- LOG(StorageAPI, "IDBRequest::onSuccess(DOMStringList)");
- if (!shouldEnqueueEvent())
- return;
+ ASSERT(currentThread() == originThreadID());
+ return false;
+}
- m_result = IDBAny::create(domStringList);
- enqueueEvent(createSuccessEvent());
+bool IDBRequest::hasPendingActivity() const
+{
+ ASSERT(currentThread() == originThreadID() || mayBeGCThread());
+ return m_hasPendingActivity;
}
-void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackend> backend, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer)
+void IDBRequest::stop()
{
- LOG(StorageAPI, "IDBRequest::onSuccess(IDBCursor)");
- if (!shouldEnqueueEvent())
- return;
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!m_contextStopped);
- DOMRequestState::Scope scope(m_requestState);
- Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer);
- ASSERT(!m_pendingCursor);
- RefPtr<IDBCursor> cursor;
- switch (m_cursorType) {
- case IndexedDB::CursorType::KeyOnly:
- cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
- break;
- case IndexedDB::CursorType::KeyAndValue:
- cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
- break;
- default:
- ASSERT_NOT_REACHED();
- }
- setResultCursor(cursor, key, primaryKey, value);
+ cancelForStop();
+
+ removeAllEventListeners();
- enqueueEvent(createSuccessEvent());
+ m_contextStopped = true;
}
-void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
+void IDBRequest::cancelForStop()
{
- LOG(StorageAPI, "IDBRequest::onSuccess(IDBKey)");
- if (!shouldEnqueueEvent())
- return;
-
- if (idbKey && idbKey->isValid()) {
- DOMRequestState::Scope scope(m_requestState);
- m_result = IDBAny::create(idbKeyToScriptValue(requestState(), idbKey));
- } else
- m_result = IDBAny::createInvalid();
- enqueueEvent(createSuccessEvent());
+ // The base IDBRequest class has nothing additional to do here.
}
-void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer)
+void IDBRequest::enqueueEvent(Ref<Event>&& event)
{
- LOG(StorageAPI, "IDBRequest::onSuccess(SharedBuffer)");
- if (!shouldEnqueueEvent())
+ ASSERT(currentThread() == originThreadID());
+ if (!scriptExecutionContext() || m_contextStopped)
return;
- DOMRequestState::Scope scope(m_requestState);
- Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer);
- onSuccessInternal(value);
+ event->setTarget(this);
+ scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event));
}
-#ifndef NDEBUG
-static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source)
+bool IDBRequest::dispatchEvent(Event& event)
{
- if (source->type() == IDBAny::IDBObjectStoreType)
- return source->idbObjectStore();
- if (source->type() == IDBAny::IDBIndexType)
- return source->idbIndex()->objectStore();
+ LOG(IndexedDB, "IDBRequest::dispatchEvent - %s (%p)", event.type().string().utf8().data(), this);
+
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(m_hasPendingActivity);
+ ASSERT(!m_contextStopped);
+
+ if (event.type() != eventNames().blockedEvent)
+ m_readyState = ReadyState::Done;
+
+ Vector<RefPtr<EventTarget>> targets;
+ targets.append(this);
+
+ if (&event == m_openDatabaseSuccessEvent)
+ m_openDatabaseSuccessEvent = nullptr;
+ else if (m_transaction && !m_transaction->isFinished()) {
+ targets.append(m_transaction);
+ targets.append(m_transaction->db());
+ }
+
+ m_hasPendingActivity = false;
+
+ m_cursorRequestNotifier = nullptr;
+
+ bool dontPreventDefault;
+ {
+ TransactionActivator activator(m_transaction.get());
+ dontPreventDefault = IDBEventDispatcher::dispatch(event, targets);
+ }
+
+ // IDBEventDispatcher::dispatch() might have set the pending activity flag back to true, suggesting the request will be reused.
+ // We might also re-use the request if this event was the upgradeneeded event for an IDBOpenDBRequest.
+ if (!m_hasPendingActivity)
+ m_hasPendingActivity = isOpenDBRequest() && (event.type() == eventNames().upgradeneededEvent || event.type() == eventNames().blockedEvent);
+
+ // The request should only remain in the transaction's request list if it represents a pending cursor operation, or this is an open request that was blocked.
+ if (m_transaction && !m_pendingCursor && event.type() != eventNames().blockedEvent)
+ m_transaction->removeRequest(*this);
- ASSERT_NOT_REACHED();
- return 0;
+ if (dontPreventDefault && event.type() == eventNames().errorEvent && m_transaction && !m_transaction->isFinishedOrFinishing()) {
+ ASSERT(m_domError);
+ m_transaction->abortDueToFailedRequest(*m_domError);
+ }
+
+ if (m_transaction)
+ m_transaction->finishedDispatchEventForRequest(*this);
+
+ return dontPreventDefault;
}
-#endif
-void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath)
+void IDBRequest::uncaughtExceptionInEventHandler()
{
- LOG(StorageAPI, "IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
- if (!shouldEnqueueEvent())
- return;
+ LOG(IndexedDB, "IDBRequest::uncaughtExceptionInEventHandler");
-#ifndef NDEBUG
- ASSERT(keyPath == effectiveObjectStore(m_source)->keyPath());
-#endif
- DOMRequestState::Scope scope(m_requestState);
- Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer);
+ ASSERT(currentThread() == originThreadID());
- RefPtr<IDBKey> primaryKey = prpPrimaryKey;
-#ifndef NDEBUG
- RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(requestState(), value, keyPath);
- ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get()));
-#endif
- bool injected = injectIDBKeyIntoScriptValue(requestState(), primaryKey, value, keyPath);
- ASSERT_UNUSED(injected, injected);
- onSuccessInternal(value);
+ if (m_transaction && m_idbError.code() != IDBDatabaseException::AbortError)
+ m_transaction->abortDueToFailedRequest(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError), ASCIILiteral("IDBTransaction will abort due to uncaught exception in an event handler")));
}
-void IDBRequest::onSuccess(int64_t value)
+void IDBRequest::setResult(const IDBKeyData& keyData)
{
- LOG(StorageAPI, "IDBRequest::onSuccess(int64_t)");
- if (!shouldEnqueueEvent())
+ ASSERT(currentThread() == originThreadID());
+
+ auto* context = scriptExecutionContext();
+ if (!context)
return;
- return onSuccessInternal(SerializedScriptValue::numberValue(value));
-}
-void IDBRequest::onSuccess()
-{
- LOG(StorageAPI, "IDBRequest::onSuccess()");
- if (!shouldEnqueueEvent())
+ auto* state = context->execState();
+ if (!state)
return;
- return onSuccessInternal(SerializedScriptValue::undefinedValue());
+
+ // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object
+ // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject.
+ VM& vm = context->vm();
+ JSLockHolder lock(vm);
+ m_result = Result { JSC::Strong<JSC::Unknown> { vm, toJS<IDLIDBKeyData>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), keyData) } };
}
-void IDBRequest::onSuccessInternal(PassRefPtr<SerializedScriptValue> value)
+void IDBRequest::setResult(const Vector<IDBKeyData>& keyDatas)
{
- ASSERT(!m_contextStopped);
- DOMRequestState::Scope scope(m_requestState);
- return onSuccessInternal(deserializeIDBValue(requestState(), value));
+ ASSERT(currentThread() == originThreadID());
+
+ auto* context = scriptExecutionContext();
+ if (!context)
+ return;
+
+ auto* state = context->execState();
+ if (!state)
+ return;
+
+ // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object
+ // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject.
+ VM& vm = context->vm();
+ JSLockHolder lock(vm);
+ m_result = Result { JSC::Strong<JSC::Unknown> { vm, toJS<IDLSequence<IDLIDBKeyData>>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), keyDatas) } };
}
-void IDBRequest::onSuccessInternal(const Deprecated::ScriptValue& value)
+void IDBRequest::setResult(const Vector<IDBValue>& values)
{
- m_result = IDBAny::create(value);
- if (m_pendingCursor) {
- m_pendingCursor->close();
- m_pendingCursor.clear();
- }
- enqueueEvent(createSuccessEvent());
+ ASSERT(currentThread() == originThreadID());
+
+ auto* context = scriptExecutionContext();
+ if (!context)
+ return;
+
+ auto* state = context->execState();
+ if (!state)
+ return;
+
+ // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object
+ // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject.
+ VM& vm = context->vm();
+ JSLockHolder lock(vm);
+ m_result = Result { JSC::Strong<JSC::Unknown> { vm, toJS<IDLSequence<IDLIDBValue>>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), values) } };
}
-void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer)
+void IDBRequest::setResult(uint64_t number)
{
- LOG(StorageAPI, "IDBRequest::onSuccess(key, primaryKey, value)");
- if (!shouldEnqueueEvent())
+ ASSERT(currentThread() == originThreadID());
+
+ auto* context = scriptExecutionContext();
+ if (!context)
return;
- DOMRequestState::Scope scope(m_requestState);
- Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer);
- ASSERT(m_pendingCursor);
- setResultCursor(m_pendingCursor.release(), key, primaryKey, value);
- enqueueEvent(createSuccessEvent());
+ m_result = Result { JSC::Strong<JSC::Unknown> { context->vm(), toJS<IDLUnrestrictedDouble>(number) } };
}
-bool IDBRequest::hasPendingActivity() const
+void IDBRequest::setResultToStructuredClone(const IDBValue& value)
{
- // FIXME: In an ideal world, we should return true as long as anyone has a or can
- // get a handle to us and we have event listeners. This is order to handle
- // user generated events properly.
- return m_hasPendingActivity && !m_contextStopped;
+ ASSERT(currentThread() == originThreadID());
+
+ LOG(IndexedDB, "IDBRequest::setResultToStructuredClone");
+
+ auto* context = scriptExecutionContext();
+ if (!context)
+ return;
+
+ auto* state = context->execState();
+ if (!state)
+ return;
+
+ // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object
+ // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject.
+ VM& vm = context->vm();
+ JSLockHolder lock(vm);
+ m_result = Result { JSC::Strong<JSC::Unknown> { vm, toJS<IDLIDBValue>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), value) } };
}
-void IDBRequest::stop()
+void IDBRequest::setResultToUndefined()
{
- if (m_contextStopped)
+ ASSERT(currentThread() == originThreadID());
+
+ auto* context = scriptExecutionContext();
+ if (!context)
return;
- m_contextStopped = true;
- m_requestState.clear();
- if (m_readyState == PENDING)
- markEarlyDeath();
+ m_result = Result { JSC::Strong<JSC::Unknown> { context->vm(), JSC::jsUndefined() } };
}
-EventTargetInterface IDBRequest::eventTargetInterface() const
+IDBCursor* IDBRequest::resultCursor()
{
- return IDBRequestEventTargetInterfaceType;
+ ASSERT(currentThread() == originThreadID());
+
+ if (!m_result)
+ return nullptr;
+
+ return WTF::switchOn(m_result.value(),
+ [] (const RefPtr<IDBCursor>& cursor) -> IDBCursor* { return cursor.get(); },
+ [] (const auto&) -> IDBCursor* { return nullptr; }
+ );
}
-bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
+void IDBRequest::willIterateCursor(IDBCursor& cursor)
{
- LOG(StorageAPI, "IDBRequest::dispatchEvent");
- ASSERT(m_readyState == PENDING);
- ASSERT(!m_contextStopped);
- ASSERT(m_hasPendingActivity);
- ASSERT(m_enqueuedEvents.size());
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(isDone());
ASSERT(scriptExecutionContext());
- ASSERT(event->target() == this);
- ASSERT_WITH_MESSAGE(m_readyState < DONE, "When dispatching event %s, m_readyState < DONE(%d), was %d", event->type().string().utf8().data(), DONE, m_readyState);
-
- DOMRequestState::Scope scope(m_requestState);
+ ASSERT(m_transaction);
+ ASSERT(!m_pendingCursor);
+ ASSERT(&cursor == resultCursor());
+ ASSERT(!m_cursorRequestNotifier);
- if (event->type() != eventNames().blockedEvent)
- m_readyState = DONE;
+ m_pendingCursor = &cursor;
+ m_hasPendingActivity = true;
+ m_result = std::nullopt;
+ m_readyState = ReadyState::Pending;
+ m_domError = nullptr;
+ m_idbError = { };
- for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
- if (m_enqueuedEvents[i].get() == event.get())
- m_enqueuedEvents.remove(i);
- }
+ m_cursorRequestNotifier = std::make_unique<ScopeGuard>([this]() {
+ m_pendingCursor->decrementOutstandingRequestCount();
+ });
+}
- Vector<RefPtr<EventTarget>> targets;
- targets.append(this);
- if (m_transaction && !m_preventPropagation) {
- targets.append(m_transaction);
- // If there ever are events that are associated with a database but
- // that do not have a transaction, then this will not work and we need
- // this object to actually hold a reference to the database (to ensure
- // it stays alive).
- targets.append(m_transaction->db());
- }
+void IDBRequest::didOpenOrIterateCursor(const IDBResultData& resultData)
+{
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(m_pendingCursor);
- // Cursor properties should not updated until the success event is being dispatched.
- RefPtr<IDBCursor> cursorToNotify;
- if (event->type() == eventNames().successEvent) {
- cursorToNotify = getResultCursor();
- if (cursorToNotify) {
- cursorToNotify->setValueReady(requestState(), m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue);
- m_cursorValue.clear();
- }
- }
+ m_result = std::nullopt;
- if (event->type() == eventNames().upgradeneededEvent) {
- ASSERT(!m_didFireUpgradeNeededEvent);
- m_didFireUpgradeNeededEvent = true;
+ if (resultData.type() == IDBResultType::IterateCursorSuccess || resultData.type() == IDBResultType::OpenCursorSuccess) {
+ m_pendingCursor->setGetResult(*this, resultData.getResult());
+ if (resultData.getResult().isDefined())
+ m_result = Result { m_pendingCursor };
}
- // FIXME: When we allow custom event dispatching, this will probably need to change.
- ASSERT_WITH_MESSAGE(event->type() == eventNames().successEvent || event->type() == eventNames().errorEvent || event->type() == eventNames().blockedEvent || event->type() == eventNames().upgradeneededEvent, "event type was %s", event->type().string().utf8().data());
- const bool setTransactionActive = m_transaction && (event->type() == eventNames().successEvent || event->type() == eventNames().upgradeneededEvent || (event->type() == eventNames().errorEvent && m_errorCode != IDBDatabaseException::AbortError));
-
- if (setTransactionActive)
- m_transaction->setActive(true);
+ m_cursorRequestNotifier = nullptr;
+ m_pendingCursor = nullptr;
- bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
+ completeRequestAndDispatchEvent(resultData);
+}
- if (m_transaction) {
- if (m_readyState == DONE)
- m_transaction->unregisterRequest(this);
+void IDBRequest::completeRequestAndDispatchEvent(const IDBResultData& resultData)
+{
+ ASSERT(currentThread() == originThreadID());
- // Possibly abort the transaction. This must occur after unregistering (so this request
- // doesn't receive a second error) and before deactivating (which might trigger commit).
- if (event->type() == eventNames().errorEvent && dontPreventDefault && !m_requestAborted) {
- m_transaction->setError(m_error, m_errorMessage);
- m_transaction->abort(IGNORE_EXCEPTION);
- }
+ m_readyState = ReadyState::Done;
- // If this was the last request in the transaction's list, it may commit here.
- if (setTransactionActive)
- m_transaction->setActive(false);
- }
+ m_idbError = resultData.error();
+ if (!m_idbError.isNull())
+ onError();
+ else
+ onSuccess();
+}
- if (cursorToNotify)
- cursorToNotify->postSuccessHandlerCallback();
+void IDBRequest::onError()
+{
+ LOG(IndexedDB, "IDBRequest::onError");
- if (m_readyState == DONE && (!cursorToNotify || m_cursorFinished) && event->type() != eventNames().upgradeneededEvent)
- m_hasPendingActivity = false;
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!m_idbError.isNull());
- return dontPreventDefault;
+ m_domError = DOMError::create(m_idbError.name(), m_idbError.message());
+ enqueueEvent(Event::create(eventNames().errorEvent, true, true));
}
-void IDBRequest::uncaughtExceptionInEventHandler()
+void IDBRequest::onSuccess()
{
- if (m_transaction && !m_requestAborted) {
- m_transaction->setError(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError)), "Uncaught exception in event handler.");
- m_transaction->abort(IGNORE_EXCEPTION);
- }
-}
+ LOG(IndexedDB, "IDBRequest::onSuccess");
+ ASSERT(currentThread() == originThreadID());
-void IDBRequest::transactionDidFinishAndDispatch()
-{
- ASSERT(m_transaction);
- ASSERT(m_transaction->isVersionChange());
- ASSERT(m_readyState == DONE);
- ASSERT(scriptExecutionContext());
- m_transaction.clear();
- m_readyState = PENDING;
+ enqueueEvent(Event::create(eventNames().successEvent, false, false));
}
-void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
+void IDBRequest::setResult(Ref<IDBDatabase>&& database)
{
- ASSERT(m_readyState == PENDING || m_readyState == DONE);
-
- if (m_contextStopped || !scriptExecutionContext())
- return;
-
- ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().string().utf8().data(), m_readyState);
-
- event->setTarget(this);
+ ASSERT(currentThread() == originThreadID());
- if (scriptExecutionContext()->eventQueue().enqueueEvent(event.get()))
- m_enqueuedEvents.append(event);
+ m_result = Result { RefPtr<IDBDatabase> { WTFMove(database) } };
}
} // namespace WebCore
-#endif
+#endif // ENABLE(INDEXED_DATABASE)