/* * 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. * * 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" #include "IDBTransactionImpl.h" #if ENABLE(INDEXED_DATABASE) #include "DOMError.h" #include "EventQueue.h" #include "IDBCursorWithValueImpl.h" #include "IDBDatabaseException.h" #include "IDBDatabaseImpl.h" #include "IDBError.h" #include "IDBEventDispatcher.h" #include "IDBKeyData.h" #include "IDBKeyRangeData.h" #include "IDBObjectStore.h" #include "IDBOpenDBRequestImpl.h" #include "IDBRequestImpl.h" #include "IDBResultData.h" #include "JSDOMWindowBase.h" #include "Logging.h" #include "ScriptExecutionContext.h" #include "TransactionOperation.h" namespace WebCore { namespace IDBClient { Ref IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info) { return adoptRef(*new IDBTransaction(database, info, nullptr)); } Ref IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest& request) { return adoptRef(*new IDBTransaction(database, info, &request)); } IDBTransaction::IDBTransaction(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest* request) : WebCore::IDBTransaction(database.scriptExecutionContext()) , m_database(database) , m_info(info) , m_operationTimer(*this, &IDBTransaction::operationTimerFired) , m_openDBRequest(request) { LOG(IndexedDB, "IDBTransaction::IDBTransaction - %s", m_info.loggingString().utf8().data()); relaxAdoptionRequirement(); if (m_info.mode() == IndexedDB::TransactionMode::VersionChange) { ASSERT(m_openDBRequest); m_openDBRequest->setVersionChangeTransaction(*this); m_startedOnServer = true; } else { activate(); RefPtr self; JSC::VM& vm = JSDOMWindowBase::commonVM(); vm.whenIdle([self, this]() { deactivate(); }); establishOnServer(); } suspendIfNeeded(); } IDBTransaction::~IDBTransaction() { } const String& IDBTransaction::mode() const { switch (m_info.mode()) { case IndexedDB::TransactionMode::ReadOnly: return IDBTransaction::modeReadOnly(); case IndexedDB::TransactionMode::ReadWrite: return IDBTransaction::modeReadWrite(); case IndexedDB::TransactionMode::VersionChange: return IDBTransaction::modeVersionChange(); } RELEASE_ASSERT_NOT_REACHED(); } WebCore::IDBDatabase* IDBTransaction::db() { return &m_database.get(); } IDBConnectionToServer& IDBTransaction::serverConnection() { return m_database->serverConnection(); } RefPtr IDBTransaction::error() const { return m_domError; } RefPtr IDBTransaction::objectStore(const String& objectStoreName, ExceptionCodeWithMessage& ec) { LOG(IndexedDB, "IDBTransaction::objectStore"); if (!scriptExecutionContext()) return nullptr; if (isFinishedOrFinishing()) { ec.code = IDBDatabaseException::InvalidStateError; ec.message = ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The transaction finished."); return nullptr; } auto iterator = m_referencedObjectStores.find(objectStoreName); if (iterator != m_referencedObjectStores.end()) return iterator->value; bool found = false; for (auto& objectStore : m_info.objectStores()) { if (objectStore == objectStoreName) { found = true; break; } } auto* info = m_database->info().infoForExistingObjectStore(objectStoreName); if (!info) { ec.code = IDBDatabaseException::NotFoundError; ec.message = ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found."); return nullptr; } // Version change transactions are scoped to every object store in the database. if (!info || (!found && !isVersionChange())) { ec.code = IDBDatabaseException::NotFoundError; ec.message = ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found."); return nullptr; } auto objectStore = IDBObjectStore::create(scriptExecutionContext(), *info, *this); m_referencedObjectStores.set(objectStoreName, &objectStore.get()); return adoptRef(&objectStore.leakRef()); } void IDBTransaction::abortDueToFailedRequest(DOMError& error) { LOG(IndexedDB, "IDBTransaction::abortDueToFailedRequest"); if (isFinishedOrFinishing()) return; m_domError = &error; ExceptionCodeWithMessage ec; abort(ec); } void IDBTransaction::transitionedToFinishing(IndexedDB::TransactionState state) { ASSERT(!isFinishedOrFinishing()); m_state = state; ASSERT(isFinishedOrFinishing()); m_referencedObjectStores.clear(); } void IDBTransaction::abort(ExceptionCodeWithMessage& ec) { LOG(IndexedDB, "IDBTransaction::abort"); if (isFinishedOrFinishing()) { ec.code = IDBDatabaseException::InvalidStateError; ec.message = ASCIILiteral("Failed to execute 'abort' on 'IDBTransaction': The transaction is inactive or finished."); return; } m_database->willAbortTransaction(*this); if (isVersionChange()) { for (auto& objectStore : m_referencedObjectStores.values()) objectStore->rollbackInfoForVersionChangeAbort(); } transitionedToFinishing(IndexedDB::TransactionState::Aborting); m_abortQueue.swap(m_transactionOperationQueue); auto operation = createTransactionOperation(*this, nullptr, &IDBTransaction::abortOnServerAndCancelRequests); scheduleOperation(WTFMove(operation)); } void IDBTransaction::abortOnServerAndCancelRequests(TransactionOperation& operation) { LOG(IndexedDB, "IDBTransaction::abortOnServerAndCancelRequests"); ASSERT(m_transactionOperationQueue.isEmpty()); serverConnection().abortTransaction(*this); ASSERT(m_transactionOperationMap.contains(operation.identifier())); m_transactionOperationMap.remove(operation.identifier()); IDBError error(IDBDatabaseException::AbortError); for (auto& operation : m_abortQueue) operation->completed(IDBResultData::error(operation->identifier(), error)); // Since we're aborting, it should be impossible to have queued any further operations. ASSERT(m_transactionOperationQueue.isEmpty()); } const char* IDBTransaction::activeDOMObjectName() const { return "IDBTransaction"; } bool IDBTransaction::canSuspendForDocumentSuspension() const { return false; } bool IDBTransaction::hasPendingActivity() const { return !m_contextStopped && m_state != IndexedDB::TransactionState::Finished; } void IDBTransaction::stop() { LOG(IndexedDB, "IDBTransaction::stop"); // IDBDatabase::stop() calls IDBTransaction::stop() for each of its active transactions. // Since the order of calling ActiveDOMObject::stop() is random, we might already have been stopped. if (m_contextStopped) return; m_contextStopped = true; if (isFinishedOrFinishing()) return; ExceptionCodeWithMessage ec; abort(ec); } bool IDBTransaction::isActive() const { return m_state == IndexedDB::TransactionState::Active; } bool IDBTransaction::isFinishedOrFinishing() const { return m_state == IndexedDB::TransactionState::Committing || m_state == IndexedDB::TransactionState::Aborting || m_state == IndexedDB::TransactionState::Finished; } void IDBTransaction::addRequest(IDBRequest& request) { m_openRequests.add(&request); } void IDBTransaction::removeRequest(IDBRequest& request) { ASSERT(m_openRequests.contains(&request)); m_openRequests.remove(&request); } void IDBTransaction::scheduleOperation(RefPtr&& operation) { ASSERT(!m_transactionOperationMap.contains(operation->identifier())); m_transactionOperationQueue.append(operation); m_transactionOperationMap.set(operation->identifier(), WTFMove(operation)); scheduleOperationTimer(); } void IDBTransaction::scheduleOperationTimer() { if (!m_operationTimer.isActive()) m_operationTimer.startOneShot(0); } void IDBTransaction::operationTimerFired() { LOG(IndexedDB, "IDBTransaction::operationTimerFired (%p)", this); if (!m_startedOnServer) return; if (!m_transactionOperationQueue.isEmpty()) { auto operation = m_transactionOperationQueue.takeFirst(); operation->perform(); return; } if (!m_transactionOperationMap.isEmpty() || !m_openRequests.isEmpty()) return; if (!isFinishedOrFinishing()) commit(); } void IDBTransaction::commit() { LOG(IndexedDB, "IDBTransaction::commit"); ASSERT(!isFinishedOrFinishing()); transitionedToFinishing(IndexedDB::TransactionState::Committing); m_database->willCommitTransaction(*this); auto operation = createTransactionOperation(*this, nullptr, &IDBTransaction::commitOnServer); scheduleOperation(WTFMove(operation)); } void IDBTransaction::commitOnServer(TransactionOperation& operation) { LOG(IndexedDB, "IDBTransaction::commitOnServer"); serverConnection().commitTransaction(*this); ASSERT(m_transactionOperationMap.contains(operation.identifier())); m_transactionOperationMap.remove(operation.identifier()); } void IDBTransaction::finishAbortOrCommit() { ASSERT(m_state != IndexedDB::TransactionState::Finished); m_state = IndexedDB::TransactionState::Finished; } void IDBTransaction::didStart(const IDBError& error) { LOG(IndexedDB, "IDBTransaction::didStart"); m_database->didStartTransaction(*this); m_startedOnServer = true; // It's possible the transaction failed to start on the server. // That equates to an abort. if (!error.isNull()) { didAbort(error); return; } scheduleOperationTimer(); } void IDBTransaction::notifyDidAbort(const IDBError& error) { m_database->didAbortTransaction(*this); m_idbError = error; fireOnAbort(); if (isVersionChange()) { ASSERT(m_openDBRequest); m_openDBRequest->fireErrorAfterVersionChangeCompletion(); } } void IDBTransaction::didAbort(const IDBError& error) { LOG(IndexedDB, "IDBTransaction::didAbort"); if (m_state == IndexedDB::TransactionState::Finished) return; notifyDidAbort(error); finishAbortOrCommit(); } void IDBTransaction::didCommit(const IDBError& error) { LOG(IndexedDB, "IDBTransaction::didCommit"); ASSERT(m_state == IndexedDB::TransactionState::Committing); if (error.isNull()) { m_database->didCommitTransaction(*this); fireOnComplete(); } else { m_database->willAbortTransaction(*this); notifyDidAbort(error); } finishAbortOrCommit(); } void IDBTransaction::fireOnComplete() { LOG(IndexedDB, "IDBTransaction::fireOnComplete"); enqueueEvent(Event::create(eventNames().completeEvent, false, false)); } void IDBTransaction::fireOnAbort() { LOG(IndexedDB, "IDBTransaction::fireOnAbort"); enqueueEvent(Event::create(eventNames().abortEvent, true, false)); } void IDBTransaction::enqueueEvent(Ref&& event) { ASSERT(m_state != IndexedDB::TransactionState::Finished); if (!scriptExecutionContext() || m_contextStopped) return; event->setTarget(this); scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event)); } bool IDBTransaction::dispatchEvent(Event& event) { LOG(IndexedDB, "IDBTransaction::dispatchEvent"); ASSERT(scriptExecutionContext()); ASSERT(!m_contextStopped); ASSERT(event.target() == this); ASSERT(event.type() == eventNames().completeEvent || event.type() == eventNames().abortEvent); Vector> targets; targets.append(this); targets.append(db()); bool result = IDBEventDispatcher::dispatch(event, targets); if (isVersionChange()) { ASSERT(m_openDBRequest); m_openDBRequest->versionChangeTransactionDidFinish(); if (event.type() == eventNames().completeEvent) { if (m_database->isClosingOrClosed()) m_openDBRequest->fireErrorAfterVersionChangeCompletion(); else m_openDBRequest->fireSuccessAfterVersionChangeCommit(); } m_openDBRequest = nullptr; } return result; } Ref IDBTransaction::createObjectStore(const IDBObjectStoreInfo& info) { LOG(IndexedDB, "IDBTransaction::createObjectStore"); ASSERT(isVersionChange()); ASSERT(scriptExecutionContext()); Ref objectStore = IDBObjectStore::create(scriptExecutionContext(), info, *this); m_referencedObjectStores.set(info.name(), &objectStore.get()); auto operation = createTransactionOperation(*this, &IDBTransaction::didCreateObjectStoreOnServer, &IDBTransaction::createObjectStoreOnServer, info); scheduleOperation(WTFMove(operation)); return objectStore; } void IDBTransaction::createObjectStoreOnServer(TransactionOperation& operation, const IDBObjectStoreInfo& info) { LOG(IndexedDB, "IDBTransaction::createObjectStoreOnServer"); ASSERT(isVersionChange()); m_database->serverConnection().createObjectStore(operation, info); } void IDBTransaction::didCreateObjectStoreOnServer(const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didCreateObjectStoreOnServer"); ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::CreateObjectStoreSuccess || resultData.type() == IDBResultType::Error); } std::unique_ptr IDBTransaction::createIndex(IDBObjectStore& objectStore, const IDBIndexInfo& info) { LOG(IndexedDB, "IDBTransaction::createIndex"); ASSERT(isVersionChange()); if (!scriptExecutionContext()) return nullptr; auto operation = createTransactionOperation(*this, &IDBTransaction::didCreateIndexOnServer, &IDBTransaction::createIndexOnServer, info); scheduleOperation(WTFMove(operation)); return std::make_unique(scriptExecutionContext(), info, objectStore); } void IDBTransaction::createIndexOnServer(TransactionOperation& operation, const IDBIndexInfo& info) { LOG(IndexedDB, "IDBTransaction::createIndexOnServer"); ASSERT(isVersionChange()); m_database->serverConnection().createIndex(operation, info); } void IDBTransaction::didCreateIndexOnServer(const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didCreateIndexOnServer"); if (resultData.type() == IDBResultType::CreateIndexSuccess) return; ASSERT(resultData.type() == IDBResultType::Error); // This operation might have failed because the transaction is already aborting. if (m_state == IndexedDB::TransactionState::Aborting) return; // Otherwise, failure to create an index forced abortion of the transaction. abortDueToFailedRequest(DOMError::create(IDBDatabaseException::getErrorName(resultData.error().code()))); } Ref IDBTransaction::requestOpenCursor(ScriptExecutionContext& context, IDBObjectStore& objectStore, const IDBCursorInfo& info) { LOG(IndexedDB, "IDBTransaction::requestOpenCursor"); return doRequestOpenCursor(context, IDBCursorWithValue::create(*this, objectStore, info)); } Ref IDBTransaction::requestOpenCursor(ScriptExecutionContext& context, IDBIndex& index, const IDBCursorInfo& info) { LOG(IndexedDB, "IDBTransaction::requestOpenCursor"); if (info.cursorType() == IndexedDB::CursorType::KeyOnly) return doRequestOpenCursor(context, IDBCursor::create(*this, index, info)); return doRequestOpenCursor(context, IDBCursorWithValue::create(*this, index, info)); } Ref IDBTransaction::doRequestOpenCursor(ScriptExecutionContext& context, Ref&& cursor) { ASSERT(isActive()); Ref request = IDBRequest::create(context, cursor.get(), *this); addRequest(request.get()); auto operation = createTransactionOperation(*this, request.get(), &IDBTransaction::didOpenCursorOnServer, &IDBTransaction::openCursorOnServer, cursor->info()); scheduleOperation(WTFMove(operation)); return request; } void IDBTransaction::openCursorOnServer(TransactionOperation& operation, const IDBCursorInfo& info) { LOG(IndexedDB, "IDBTransaction::openCursorOnServer"); m_database->serverConnection().openCursor(operation, info); } void IDBTransaction::didOpenCursorOnServer(IDBRequest& request, const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didOpenCursorOnServer"); request.didOpenOrIterateCursor(resultData); } void IDBTransaction::iterateCursor(IDBCursor& cursor, const IDBKeyData& key, unsigned long count) { LOG(IndexedDB, "IDBTransaction::iterateCursor"); ASSERT(isActive()); ASSERT(cursor.request()); addRequest(*cursor.request()); auto operation = createTransactionOperation(*this, *cursor.request(), &IDBTransaction::didIterateCursorOnServer, &IDBTransaction::iterateCursorOnServer, key, count); scheduleOperation(WTFMove(operation)); } void IDBTransaction::iterateCursorOnServer(TransactionOperation& operation, const IDBKeyData& key, const unsigned long& count) { LOG(IndexedDB, "IDBTransaction::iterateCursorOnServer"); serverConnection().iterateCursor(operation, key, count); } void IDBTransaction::didIterateCursorOnServer(IDBRequest& request, const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didIterateCursorOnServer"); request.didOpenOrIterateCursor(resultData); } Ref IDBTransaction::requestGetRecord(ScriptExecutionContext& context, IDBObjectStore& objectStore, const IDBKeyRangeData& keyRangeData) { LOG(IndexedDB, "IDBTransaction::requestGetRecord"); ASSERT(isActive()); ASSERT(!keyRangeData.isNull); Ref request = IDBRequest::create(context, objectStore, *this); addRequest(request.get()); auto operation = createTransactionOperation(*this, request.get(), &IDBTransaction::didGetRecordOnServer, &IDBTransaction::getRecordOnServer, keyRangeData); scheduleOperation(WTFMove(operation)); return request; } Ref IDBTransaction::requestGetValue(ScriptExecutionContext& context, IDBIndex& index, const IDBKeyRangeData& range) { LOG(IndexedDB, "IDBTransaction::requestGetValue"); return requestIndexRecord(context, index, IndexedDB::IndexRecordType::Value, range); } Ref IDBTransaction::requestGetKey(ScriptExecutionContext& context, IDBIndex& index, const IDBKeyRangeData& range) { LOG(IndexedDB, "IDBTransaction::requestGetValue"); return requestIndexRecord(context, index, IndexedDB::IndexRecordType::Key, range); } Ref IDBTransaction::requestIndexRecord(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType type, const IDBKeyRangeData&range) { LOG(IndexedDB, "IDBTransaction::requestGetValue"); ASSERT(isActive()); ASSERT(!range.isNull); Ref request = IDBRequest::createGet(context, index, type, *this); addRequest(request.get()); auto operation = createTransactionOperation(*this, request.get(), &IDBTransaction::didGetRecordOnServer, &IDBTransaction::getRecordOnServer, range); scheduleOperation(WTFMove(operation)); return request; } void IDBTransaction::getRecordOnServer(TransactionOperation& operation, const IDBKeyRangeData& keyRange) { LOG(IndexedDB, "IDBTransaction::getRecordOnServer"); serverConnection().getRecord(operation, keyRange); } void IDBTransaction::didGetRecordOnServer(IDBRequest& request, const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didGetRecordOnServer"); if (resultData.type() == IDBResultType::Error) { request.requestCompleted(resultData); return; } ASSERT(resultData.type() == IDBResultType::GetRecordSuccess); const IDBGetResult& result = resultData.getResult(); if (request.sourceIndexIdentifier() && request.requestedIndexRecordType() == IndexedDB::IndexRecordType::Key) { if (!result.keyData().isNull()) request.setResult(&result.keyData()); else request.setResultToUndefined(); } else { if (resultData.getResult().valueBuffer().data()) request.setResultToStructuredClone(resultData.getResult().valueBuffer()); else request.setResultToUndefined(); } request.requestCompleted(resultData); } Ref IDBTransaction::requestCount(ScriptExecutionContext& context, IDBObjectStore& objectStore, const IDBKeyRangeData& range) { LOG(IndexedDB, "IDBTransaction::requestCount (IDBObjectStore)"); ASSERT(isActive()); ASSERT(!range.isNull); Ref request = IDBRequest::create(context, objectStore, *this); addRequest(request.get()); scheduleOperation(createTransactionOperation(*this, request.get(), &IDBTransaction::didGetCountOnServer, &IDBTransaction::getCountOnServer, range)); return request; } Ref IDBTransaction::requestCount(ScriptExecutionContext& context, IDBIndex& index, const IDBKeyRangeData& range) { LOG(IndexedDB, "IDBTransaction::requestCount (IDBIndex)"); ASSERT(isActive()); ASSERT(!range.isNull); Ref request = IDBRequest::createCount(context, index, *this); addRequest(request.get()); scheduleOperation(createTransactionOperation(*this, request.get(), &IDBTransaction::didGetCountOnServer, &IDBTransaction::getCountOnServer, range)); return request; } void IDBTransaction::getCountOnServer(TransactionOperation& operation, const IDBKeyRangeData& keyRange) { LOG(IndexedDB, "IDBTransaction::getCountOnServer"); serverConnection().getCount(operation, keyRange); } void IDBTransaction::didGetCountOnServer(IDBRequest& request, const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didGetCountOnServer"); request.setResult(resultData.resultInteger()); request.requestCompleted(resultData); } Ref IDBTransaction::requestDeleteRecord(ScriptExecutionContext& context, IDBObjectStore& objectStore, const IDBKeyRangeData& range) { LOG(IndexedDB, "IDBTransaction::requestDeleteRecord"); ASSERT(isActive()); ASSERT(!range.isNull); Ref request = IDBRequest::create(context, objectStore, *this); addRequest(request.get()); scheduleOperation(createTransactionOperation(*this, request.get(), &IDBTransaction::didDeleteRecordOnServer, &IDBTransaction::deleteRecordOnServer, range)); return request; } void IDBTransaction::deleteRecordOnServer(TransactionOperation& operation, const IDBKeyRangeData& keyRange) { LOG(IndexedDB, "IDBTransaction::deleteRecordOnServer"); serverConnection().deleteRecord(operation, keyRange); } void IDBTransaction::didDeleteRecordOnServer(IDBRequest& request, const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didDeleteRecordOnServer"); request.setResultToUndefined(); request.requestCompleted(resultData); } Ref IDBTransaction::requestClearObjectStore(ScriptExecutionContext& context, IDBObjectStore& objectStore) { LOG(IndexedDB, "IDBTransaction::requestClearObjectStore"); ASSERT(isActive()); Ref request = IDBRequest::create(context, objectStore, *this); addRequest(request.get()); uint64_t objectStoreIdentifier = objectStore.info().identifier(); auto operation = createTransactionOperation(*this, request.get(), &IDBTransaction::didClearObjectStoreOnServer, &IDBTransaction::clearObjectStoreOnServer, objectStoreIdentifier); scheduleOperation(WTFMove(operation)); return request; } void IDBTransaction::clearObjectStoreOnServer(TransactionOperation& operation, const uint64_t& objectStoreIdentifier) { LOG(IndexedDB, "IDBTransaction::clearObjectStoreOnServer"); serverConnection().clearObjectStore(operation, objectStoreIdentifier); } void IDBTransaction::didClearObjectStoreOnServer(IDBRequest& request, const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didClearObjectStoreOnServer"); request.setResultToUndefined(); request.requestCompleted(resultData); } Ref IDBTransaction::requestPutOrAdd(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBKey* key, SerializedScriptValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode) { LOG(IndexedDB, "IDBTransaction::requestPutOrAdd"); ASSERT(isActive()); ASSERT(!isReadOnly()); ASSERT(objectStore.info().autoIncrement() || key); Ref request = IDBRequest::create(context, objectStore, *this); addRequest(request.get()); auto operation = createTransactionOperation(*this, request.get(), &IDBTransaction::didPutOrAddOnServer, &IDBTransaction::putOrAddOnServer, key, &value, overwriteMode); scheduleOperation(WTFMove(operation)); return request; } void IDBTransaction::putOrAddOnServer(TransactionOperation& operation, RefPtr key, RefPtr value, const IndexedDB::ObjectStoreOverwriteMode& overwriteMode) { LOG(IndexedDB, "IDBTransaction::putOrAddOnServer"); ASSERT(!isReadOnly()); serverConnection().putOrAdd(operation, key, value, overwriteMode); } void IDBTransaction::didPutOrAddOnServer(IDBRequest& request, const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didPutOrAddOnServer"); request.setResult(resultData.resultKey()); request.requestCompleted(resultData); } void IDBTransaction::deleteObjectStore(const String& objectStoreName) { LOG(IndexedDB, "IDBTransaction::deleteObjectStore"); ASSERT(isVersionChange()); if (auto objectStore = m_referencedObjectStores.take(objectStoreName)) objectStore->markAsDeleted(); auto operation = createTransactionOperation(*this, &IDBTransaction::didDeleteObjectStoreOnServer, &IDBTransaction::deleteObjectStoreOnServer, objectStoreName); scheduleOperation(WTFMove(operation)); } void IDBTransaction::deleteObjectStoreOnServer(TransactionOperation& operation, const String& objectStoreName) { LOG(IndexedDB, "IDBTransaction::deleteObjectStoreOnServer"); ASSERT(isVersionChange()); serverConnection().deleteObjectStore(operation, objectStoreName); } void IDBTransaction::didDeleteObjectStoreOnServer(const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didDeleteObjectStoreOnServer"); ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteObjectStoreSuccess || resultData.type() == IDBResultType::Error); } void IDBTransaction::deleteIndex(uint64_t objectStoreIdentifier, const String& indexName) { LOG(IndexedDB, "IDBTransaction::deleteIndex"); ASSERT(isVersionChange()); auto operation = createTransactionOperation(*this, &IDBTransaction::didDeleteIndexOnServer, &IDBTransaction::deleteIndexOnServer, objectStoreIdentifier, indexName); scheduleOperation(WTFMove(operation)); } void IDBTransaction::deleteIndexOnServer(TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& indexName) { LOG(IndexedDB, "IDBTransaction::deleteIndexOnServer"); ASSERT(isVersionChange()); serverConnection().deleteIndex(operation, objectStoreIdentifier, indexName); } void IDBTransaction::didDeleteIndexOnServer(const IDBResultData& resultData) { LOG(IndexedDB, "IDBTransaction::didDeleteIndexOnServer"); ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteIndexSuccess || resultData.type() == IDBResultType::Error); } void IDBTransaction::operationDidComplete(TransactionOperation& operation) { ASSERT(m_transactionOperationMap.get(operation.identifier()) == &operation); m_transactionOperationMap.remove(operation.identifier()); scheduleOperationTimer(); } void IDBTransaction::establishOnServer() { LOG(IndexedDB, "IDBTransaction::establishOnServer"); serverConnection().establishTransaction(*this); } void IDBTransaction::activate() { if (isFinishedOrFinishing()) return; m_state = IndexedDB::TransactionState::Active; } void IDBTransaction::deactivate() { if (m_state == IndexedDB::TransactionState::Active) m_state = IndexedDB::TransactionState::Inactive; scheduleOperationTimer(); } } // namespace IDBClient } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE)