2 * Copyright (C) 2015, 2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "IDBTransaction.h"
29 #if ENABLE(INDEXED_DATABASE)
32 #include "DOMStringList.h"
33 #include "DOMWindow.h"
35 #include "EventNames.h"
36 #include "EventQueue.h"
37 #include "IDBCursorWithValue.h"
38 #include "IDBDatabase.h"
39 #include "IDBDatabaseException.h"
41 #include "IDBEventDispatcher.h"
42 #include "IDBGetRecordData.h"
44 #include "IDBIterateCursorData.h"
45 #include "IDBKeyData.h"
46 #include "IDBKeyRangeData.h"
47 #include "IDBObjectStore.h"
48 #include "IDBOpenDBRequest.h"
49 #include "IDBRequest.h"
50 #include "IDBResultData.h"
52 #include "JSDOMWindowBase.h"
54 #include "ScriptExecutionContext.h"
55 #include "ScriptState.h"
56 #include "TransactionOperation.h"
57 #include <wtf/NeverDestroyed.h>
63 Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info)
65 return adoptRef(*new IDBTransaction(database, info, nullptr));
68 Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest& request)
70 return adoptRef(*new IDBTransaction(database, info, &request));
73 IDBTransaction::IDBTransaction(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest* request)
74 : IDBActiveDOMObject(database.scriptExecutionContext())
75 , m_database(database)
77 , m_operationTimer(*this, &IDBTransaction::operationTimerFired)
78 , m_openDBRequest(request)
81 LOG(IndexedDB, "IDBTransaction::IDBTransaction - %s", m_info.loggingString().utf8().data());
82 ASSERT(currentThread() == m_database->originThreadID());
84 if (m_info.mode() == IDBTransactionMode::Versionchange) {
85 ASSERT(m_openDBRequest);
86 m_openDBRequest->setVersionChangeTransaction(*this);
87 m_startedOnServer = true;
91 auto* context = scriptExecutionContext();
94 RefPtr<IDBTransaction> self;
95 JSC::VM& vm = context->vm();
96 vm.whenIdle([self, this]() {
106 IDBTransaction::~IDBTransaction()
108 ASSERT(currentThread() == m_database->originThreadID());
111 IDBClient::IDBConnectionProxy& IDBTransaction::connectionProxy()
113 return m_database->connectionProxy();
116 Ref<DOMStringList> IDBTransaction::objectStoreNames() const
118 ASSERT(currentThread() == m_database->originThreadID());
120 const Vector<String> names = isVersionChange() ? m_database->info().objectStoreNames() : m_info.objectStores();
122 Ref<DOMStringList> objectStoreNames = DOMStringList::create();
123 for (auto& name : names)
124 objectStoreNames->append(name);
126 objectStoreNames->sort();
127 return objectStoreNames;
130 IDBDatabase* IDBTransaction::db()
132 ASSERT(currentThread() == m_database->originThreadID());
133 return m_database.ptr();
136 DOMError* IDBTransaction::error() const
138 ASSERT(currentThread() == m_database->originThreadID());
139 return m_domError.get();
142 ExceptionOr<Ref<IDBObjectStore>> IDBTransaction::objectStore(const String& objectStoreName)
144 LOG(IndexedDB, "IDBTransaction::objectStore");
145 ASSERT(currentThread() == m_database->originThreadID());
147 if (!scriptExecutionContext())
148 return Exception { IDBDatabaseException::InvalidStateError };
150 if (isFinishedOrFinishing())
151 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The transaction finished.") };
153 Locker<Lock> locker(m_referencedObjectStoreLock);
155 auto iterator = m_referencedObjectStores.find(objectStoreName);
156 if (iterator != m_referencedObjectStores.end())
157 return Ref<IDBObjectStore> { *iterator->value };
160 for (auto& objectStore : m_info.objectStores()) {
161 if (objectStore == objectStoreName) {
167 auto* info = m_database->info().infoForExistingObjectStore(objectStoreName);
169 return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found.") };
171 // Version change transactions are scoped to every object store in the database.
172 if (!info || (!found && !isVersionChange()))
173 return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found.") };
175 auto objectStore = std::make_unique<IDBObjectStore>(*scriptExecutionContext(), *info, *this);
176 auto* rawObjectStore = objectStore.get();
177 m_referencedObjectStores.set(objectStoreName, WTFMove(objectStore));
179 return Ref<IDBObjectStore>(*rawObjectStore);
183 void IDBTransaction::abortDueToFailedRequest(DOMError& error)
185 LOG(IndexedDB, "IDBTransaction::abortDueToFailedRequest");
186 ASSERT(currentThread() == m_database->originThreadID());
188 if (isFinishedOrFinishing())
195 void IDBTransaction::transitionedToFinishing(IndexedDB::TransactionState state)
197 ASSERT(currentThread() == m_database->originThreadID());
199 ASSERT(!isFinishedOrFinishing());
201 ASSERT(isFinishedOrFinishing());
204 ExceptionOr<void> IDBTransaction::abort()
206 LOG(IndexedDB, "IDBTransaction::abort");
207 ASSERT(currentThread() == m_database->originThreadID());
209 if (isFinishedOrFinishing())
210 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'abort' on 'IDBTransaction': The transaction is inactive or finished.") };
217 void IDBTransaction::internalAbort()
219 LOG(IndexedDB, "IDBTransaction::internalAbort");
220 ASSERT(currentThread() == m_database->originThreadID());
221 ASSERT(!isFinishedOrFinishing());
223 m_database->willAbortTransaction(*this);
225 if (isVersionChange()) {
226 Locker<Lock> locker(m_referencedObjectStoreLock);
228 auto& info = m_database->info();
229 for (auto& iterator : m_deletedObjectStores) {
230 if (info.infoForExistingObjectStore(iterator.key)) {
231 auto name = iterator.value->info().name();
232 m_referencedObjectStores.set(name, WTFMove(iterator.value));
236 for (auto& objectStore : m_referencedObjectStores.values())
237 objectStore->rollbackForVersionChangeAbort();
240 transitionedToFinishing(IndexedDB::TransactionState::Aborting);
242 m_abortQueue.swap(m_transactionOperationQueue);
244 scheduleOperation(IDBClient::createTransactionOperation(*this, nullptr, &IDBTransaction::abortOnServerAndCancelRequests));
247 void IDBTransaction::abortOnServerAndCancelRequests(IDBClient::TransactionOperation& operation)
249 LOG(IndexedDB, "IDBTransaction::abortOnServerAndCancelRequests");
250 ASSERT(currentThread() == m_database->originThreadID());
251 ASSERT(m_transactionOperationQueue.isEmpty());
253 m_database->connectionProxy().abortTransaction(*this);
255 ASSERT(m_transactionOperationMap.contains(operation.identifier()));
256 m_transactionOperationMap.remove(operation.identifier());
258 IDBError error(IDBDatabaseException::AbortError);
259 for (auto& operation : m_abortQueue)
260 operation->completed(IDBResultData::error(operation->identifier(), error));
262 // Since we're aborting, it should be impossible to have queued any further operations.
263 ASSERT(m_transactionOperationQueue.isEmpty());
266 const char* IDBTransaction::activeDOMObjectName() const
268 ASSERT(currentThread() == m_database->originThreadID());
269 return "IDBTransaction";
272 bool IDBTransaction::canSuspendForDocumentSuspension() const
274 ASSERT(currentThread() == m_database->originThreadID());
278 bool IDBTransaction::hasPendingActivity() const
280 ASSERT(currentThread() == m_database->originThreadID() || mayBeGCThread());
281 return !m_contextStopped && m_state != IndexedDB::TransactionState::Finished;
284 void IDBTransaction::stop()
286 LOG(IndexedDB, "IDBTransaction::stop - %s", m_info.loggingString().utf8().data());
287 ASSERT(currentThread() == m_database->originThreadID());
289 // IDBDatabase::stop() calls IDBTransaction::stop() for each of its active transactions.
290 // Since the order of calling ActiveDOMObject::stop() is random, we might already have been stopped.
291 if (m_contextStopped)
294 removeAllEventListeners();
296 m_contextStopped = true;
298 if (isFinishedOrFinishing())
304 bool IDBTransaction::isActive() const
306 ASSERT(currentThread() == m_database->originThreadID());
307 return m_state == IndexedDB::TransactionState::Active;
310 bool IDBTransaction::isFinishedOrFinishing() const
312 ASSERT(currentThread() == m_database->originThreadID());
314 return m_state == IndexedDB::TransactionState::Committing
315 || m_state == IndexedDB::TransactionState::Aborting
316 || m_state == IndexedDB::TransactionState::Finished;
319 void IDBTransaction::addRequest(IDBRequest& request)
321 ASSERT(currentThread() == m_database->originThreadID());
322 m_openRequests.add(&request);
325 void IDBTransaction::removeRequest(IDBRequest& request)
327 ASSERT(currentThread() == m_database->originThreadID());
328 ASSERT(m_openRequests.contains(&request));
329 m_openRequests.remove(&request);
332 void IDBTransaction::scheduleOperation(RefPtr<IDBClient::TransactionOperation>&& operation)
334 ASSERT(!m_transactionOperationMap.contains(operation->identifier()));
335 ASSERT(currentThread() == m_database->originThreadID());
337 m_transactionOperationQueue.append(operation);
338 m_transactionOperationMap.set(operation->identifier(), WTFMove(operation));
340 scheduleOperationTimer();
343 void IDBTransaction::scheduleOperationTimer()
345 ASSERT(currentThread() == m_database->originThreadID());
347 if (!m_operationTimer.isActive())
348 m_operationTimer.startOneShot(0);
351 void IDBTransaction::operationTimerFired()
353 LOG(IndexedDB, "IDBTransaction::operationTimerFired (%p)", this);
354 ASSERT(currentThread() == m_database->originThreadID());
356 if (!m_startedOnServer)
359 if (!m_transactionOperationQueue.isEmpty()) {
360 auto operation = m_transactionOperationQueue.takeFirst();
361 operation->perform();
366 if (!m_transactionOperationMap.isEmpty() || !m_openRequests.isEmpty())
369 if (!isFinishedOrFinishing())
373 void IDBTransaction::commit()
375 LOG(IndexedDB, "IDBTransaction::commit");
376 ASSERT(currentThread() == m_database->originThreadID());
377 ASSERT(!isFinishedOrFinishing());
379 transitionedToFinishing(IndexedDB::TransactionState::Committing);
380 m_database->willCommitTransaction(*this);
382 scheduleOperation(IDBClient::createTransactionOperation(*this, nullptr, &IDBTransaction::commitOnServer));
385 void IDBTransaction::commitOnServer(IDBClient::TransactionOperation& operation)
387 LOG(IndexedDB, "IDBTransaction::commitOnServer");
388 ASSERT(currentThread() == m_database->originThreadID());
390 m_database->connectionProxy().commitTransaction(*this);
392 ASSERT(m_transactionOperationMap.contains(operation.identifier()));
393 m_transactionOperationMap.remove(operation.identifier());
396 void IDBTransaction::finishAbortOrCommit()
398 ASSERT(m_state != IndexedDB::TransactionState::Finished);
399 ASSERT(currentThread() == m_database->originThreadID());
401 m_state = IndexedDB::TransactionState::Finished;
404 void IDBTransaction::didStart(const IDBError& error)
406 LOG(IndexedDB, "IDBTransaction::didStart");
407 ASSERT(currentThread() == m_database->originThreadID());
409 m_database->didStartTransaction(*this);
411 m_startedOnServer = true;
413 // It's possible the transaction failed to start on the server.
414 // That equates to an abort.
415 if (!error.isNull()) {
420 scheduleOperationTimer();
423 void IDBTransaction::notifyDidAbort(const IDBError& error)
425 ASSERT(currentThread() == m_database->originThreadID());
427 m_database->didAbortTransaction(*this);
431 if (isVersionChange()) {
432 ASSERT(m_openDBRequest);
433 m_openDBRequest->fireErrorAfterVersionChangeCompletion();
437 void IDBTransaction::didAbort(const IDBError& error)
439 LOG(IndexedDB, "IDBTransaction::didAbort");
440 ASSERT(currentThread() == m_database->originThreadID());
442 if (m_state == IndexedDB::TransactionState::Finished)
445 notifyDidAbort(error);
447 finishAbortOrCommit();
450 void IDBTransaction::didCommit(const IDBError& error)
452 LOG(IndexedDB, "IDBTransaction::didCommit");
453 ASSERT(currentThread() == m_database->originThreadID());
454 ASSERT(m_state == IndexedDB::TransactionState::Committing);
456 if (error.isNull()) {
457 m_database->didCommitTransaction(*this);
460 m_database->willAbortTransaction(*this);
461 notifyDidAbort(error);
464 finishAbortOrCommit();
467 void IDBTransaction::fireOnComplete()
469 LOG(IndexedDB, "IDBTransaction::fireOnComplete");
470 ASSERT(currentThread() == m_database->originThreadID());
471 enqueueEvent(Event::create(eventNames().completeEvent, false, false));
474 void IDBTransaction::fireOnAbort()
476 LOG(IndexedDB, "IDBTransaction::fireOnAbort");
477 ASSERT(currentThread() == m_database->originThreadID());
478 enqueueEvent(Event::create(eventNames().abortEvent, true, false));
481 void IDBTransaction::enqueueEvent(Ref<Event>&& event)
483 ASSERT(m_state != IndexedDB::TransactionState::Finished);
484 ASSERT(currentThread() == m_database->originThreadID());
486 if (!scriptExecutionContext() || m_contextStopped)
489 event->setTarget(this);
490 scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event));
493 bool IDBTransaction::dispatchEvent(Event& event)
495 LOG(IndexedDB, "IDBTransaction::dispatchEvent");
497 ASSERT(currentThread() == m_database->originThreadID());
498 ASSERT(scriptExecutionContext());
499 ASSERT(!m_contextStopped);
500 ASSERT(event.target() == this);
501 ASSERT(event.type() == eventNames().completeEvent || event.type() == eventNames().abortEvent);
503 Vector<RefPtr<EventTarget>> targets;
504 targets.append(this);
505 targets.append(db());
507 bool result = IDBEventDispatcher::dispatch(event, targets);
509 if (isVersionChange()) {
510 ASSERT(m_openDBRequest);
511 m_openDBRequest->versionChangeTransactionDidFinish();
513 if (event.type() == eventNames().completeEvent) {
514 if (m_database->isClosingOrClosed())
515 m_openDBRequest->fireErrorAfterVersionChangeCompletion();
517 m_openDBRequest->fireSuccessAfterVersionChangeCommit();
520 m_openDBRequest = nullptr;
526 Ref<IDBObjectStore> IDBTransaction::createObjectStore(const IDBObjectStoreInfo& info)
528 LOG(IndexedDB, "IDBTransaction::createObjectStore");
529 ASSERT(isVersionChange());
530 ASSERT(scriptExecutionContext());
531 ASSERT(currentThread() == m_database->originThreadID());
533 Locker<Lock> locker(m_referencedObjectStoreLock);
535 auto objectStore = std::make_unique<IDBObjectStore>(*scriptExecutionContext(), info, *this);
536 auto* rawObjectStore = objectStore.get();
537 m_referencedObjectStores.set(info.name(), WTFMove(objectStore));
539 scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didCreateObjectStoreOnServer, &IDBTransaction::createObjectStoreOnServer, info));
541 return *rawObjectStore;
544 void IDBTransaction::createObjectStoreOnServer(IDBClient::TransactionOperation& operation, const IDBObjectStoreInfo& info)
546 LOG(IndexedDB, "IDBTransaction::createObjectStoreOnServer");
547 ASSERT(currentThread() == m_database->originThreadID());
548 ASSERT(isVersionChange());
550 m_database->connectionProxy().createObjectStore(operation, info);
553 void IDBTransaction::didCreateObjectStoreOnServer(const IDBResultData& resultData)
555 LOG(IndexedDB, "IDBTransaction::didCreateObjectStoreOnServer");
556 ASSERT(currentThread() == m_database->originThreadID());
557 ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::CreateObjectStoreSuccess || resultData.type() == IDBResultType::Error);
560 void IDBTransaction::renameObjectStore(IDBObjectStore& objectStore, const String& newName)
562 LOG(IndexedDB, "IDBTransaction::renameObjectStore");
564 Locker<Lock> locker(m_referencedObjectStoreLock);
566 ASSERT(isVersionChange());
567 ASSERT(scriptExecutionContext());
568 ASSERT(currentThread() == m_database->originThreadID());
570 ASSERT(m_referencedObjectStores.contains(objectStore.info().name()));
571 ASSERT(!m_referencedObjectStores.contains(newName));
572 ASSERT(m_referencedObjectStores.get(objectStore.info().name()) == &objectStore);
574 uint64_t objectStoreIdentifier = objectStore.info().identifier();
575 scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didRenameObjectStoreOnServer, &IDBTransaction::renameObjectStoreOnServer, objectStoreIdentifier, newName));
577 m_referencedObjectStores.set(newName, m_referencedObjectStores.take(objectStore.info().name()));
580 void IDBTransaction::renameObjectStoreOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& newName)
582 LOG(IndexedDB, "IDBTransaction::renameObjectStoreOnServer");
583 ASSERT(currentThread() == m_database->originThreadID());
584 ASSERT(isVersionChange());
586 m_database->connectionProxy().renameObjectStore(operation, objectStoreIdentifier, newName);
589 void IDBTransaction::didRenameObjectStoreOnServer(const IDBResultData& resultData)
591 LOG(IndexedDB, "IDBTransaction::didRenameObjectStoreOnServer");
592 ASSERT(currentThread() == m_database->originThreadID());
593 ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::RenameObjectStoreSuccess || resultData.type() == IDBResultType::Error);
596 std::unique_ptr<IDBIndex> IDBTransaction::createIndex(IDBObjectStore& objectStore, const IDBIndexInfo& info)
598 LOG(IndexedDB, "IDBTransaction::createIndex");
599 ASSERT(isVersionChange());
600 ASSERT(currentThread() == m_database->originThreadID());
602 if (!scriptExecutionContext())
605 scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didCreateIndexOnServer, &IDBTransaction::createIndexOnServer, info));
607 return std::make_unique<IDBIndex>(*scriptExecutionContext(), info, objectStore);
610 void IDBTransaction::createIndexOnServer(IDBClient::TransactionOperation& operation, const IDBIndexInfo& info)
612 LOG(IndexedDB, "IDBTransaction::createIndexOnServer");
613 ASSERT(currentThread() == m_database->originThreadID());
614 ASSERT(isVersionChange());
616 m_database->connectionProxy().createIndex(operation, info);
619 void IDBTransaction::didCreateIndexOnServer(const IDBResultData& resultData)
621 LOG(IndexedDB, "IDBTransaction::didCreateIndexOnServer");
622 ASSERT(currentThread() == m_database->originThreadID());
624 if (resultData.type() == IDBResultType::CreateIndexSuccess)
627 ASSERT(resultData.type() == IDBResultType::Error);
629 // This operation might have failed because the transaction is already aborting.
630 if (m_state == IndexedDB::TransactionState::Aborting)
633 // Otherwise, failure to create an index forced abortion of the transaction.
634 abortDueToFailedRequest(DOMError::create(IDBDatabaseException::getErrorName(resultData.error().code()), resultData.error().message()));
637 void IDBTransaction::renameIndex(IDBIndex& index, const String& newName)
639 LOG(IndexedDB, "IDBTransaction::renameIndex");
640 Locker<Lock> locker(m_referencedObjectStoreLock);
642 ASSERT(isVersionChange());
643 ASSERT(scriptExecutionContext());
644 ASSERT(currentThread() == m_database->originThreadID());
646 ASSERT(m_referencedObjectStores.contains(index.objectStore().info().name()));
647 ASSERT(m_referencedObjectStores.get(index.objectStore().info().name()) == &index.objectStore());
649 index.objectStore().renameReferencedIndex(index, newName);
651 uint64_t objectStoreIdentifier = index.objectStore().info().identifier();
652 uint64_t indexIdentifier = index.info().identifier();
653 scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didRenameIndexOnServer, &IDBTransaction::renameIndexOnServer, objectStoreIdentifier, indexIdentifier, newName));
656 void IDBTransaction::renameIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const uint64_t& indexIdentifier, const String& newName)
658 LOG(IndexedDB, "IDBTransaction::renameIndexOnServer");
659 ASSERT(currentThread() == m_database->originThreadID());
660 ASSERT(isVersionChange());
662 m_database->connectionProxy().renameIndex(operation, objectStoreIdentifier, indexIdentifier, newName);
665 void IDBTransaction::didRenameIndexOnServer(const IDBResultData& resultData)
667 LOG(IndexedDB, "IDBTransaction::didRenameIndexOnServer");
668 ASSERT(currentThread() == m_database->originThreadID());
669 ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::RenameIndexSuccess || resultData.type() == IDBResultType::Error);
672 Ref<IDBRequest> IDBTransaction::requestOpenCursor(ExecState& state, IDBObjectStore& objectStore, const IDBCursorInfo& info)
674 LOG(IndexedDB, "IDBTransaction::requestOpenCursor");
675 ASSERT(currentThread() == m_database->originThreadID());
677 if (info.cursorType() == IndexedDB::CursorType::KeyOnly)
678 return doRequestOpenCursor(state, IDBCursor::create(*this, objectStore, info));
680 return doRequestOpenCursor(state, IDBCursorWithValue::create(*this, objectStore, info));
683 Ref<IDBRequest> IDBTransaction::requestOpenCursor(ExecState& state, IDBIndex& index, const IDBCursorInfo& info)
685 LOG(IndexedDB, "IDBTransaction::requestOpenCursor");
686 ASSERT(currentThread() == m_database->originThreadID());
688 if (info.cursorType() == IndexedDB::CursorType::KeyOnly)
689 return doRequestOpenCursor(state, IDBCursor::create(*this, index, info));
691 return doRequestOpenCursor(state, IDBCursorWithValue::create(*this, index, info));
694 Ref<IDBRequest> IDBTransaction::doRequestOpenCursor(ExecState& state, Ref<IDBCursor>&& cursor)
697 ASSERT(currentThread() == m_database->originThreadID());
699 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
701 auto request = IDBRequest::create(*scriptExecutionContext(), cursor.get(), *this);
702 addRequest(request.get());
704 scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didOpenCursorOnServer, &IDBTransaction::openCursorOnServer, cursor->info()));
709 void IDBTransaction::openCursorOnServer(IDBClient::TransactionOperation& operation, const IDBCursorInfo& info)
711 LOG(IndexedDB, "IDBTransaction::openCursorOnServer");
712 ASSERT(currentThread() == m_database->originThreadID());
714 m_database->connectionProxy().openCursor(operation, info);
717 void IDBTransaction::didOpenCursorOnServer(IDBRequest& request, const IDBResultData& resultData)
719 LOG(IndexedDB, "IDBTransaction::didOpenCursorOnServer");
720 ASSERT(currentThread() == m_database->originThreadID());
722 request.didOpenOrIterateCursor(resultData);
725 void IDBTransaction::iterateCursor(IDBCursor& cursor, const IDBIterateCursorData& data)
727 LOG(IndexedDB, "IDBTransaction::iterateCursor");
729 ASSERT(cursor.request());
730 ASSERT(currentThread() == m_database->originThreadID());
732 addRequest(*cursor.request());
734 scheduleOperation(IDBClient::createTransactionOperation(*this, *cursor.request(), &IDBTransaction::didIterateCursorOnServer, &IDBTransaction::iterateCursorOnServer, data));
737 // FIXME: changes here
738 void IDBTransaction::iterateCursorOnServer(IDBClient::TransactionOperation& operation, const IDBIterateCursorData& data)
740 LOG(IndexedDB, "IDBTransaction::iterateCursorOnServer");
741 ASSERT(currentThread() == m_database->originThreadID());
743 m_database->connectionProxy().iterateCursor(operation, data);
746 void IDBTransaction::didIterateCursorOnServer(IDBRequest& request, const IDBResultData& resultData)
748 LOG(IndexedDB, "IDBTransaction::didIterateCursorOnServer");
749 ASSERT(currentThread() == m_database->originThreadID());
751 request.didOpenOrIterateCursor(resultData);
754 Ref<IDBRequest> IDBTransaction::requestGetAllObjectStoreRecords(JSC::ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& keyRangeData, IndexedDB::GetAllType getAllType, Optional<uint32_t> count)
756 LOG(IndexedDB, "IDBTransaction::requestGetAllObjectStoreRecords");
758 ASSERT(currentThread() == m_database->originThreadID());
760 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
762 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
763 addRequest(request.get());
765 IDBGetAllRecordsData getAllRecordsData { keyRangeData, getAllType, count, objectStore.info().identifier(), 0 };
767 scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetAllRecordsOnServer, &IDBTransaction::getAllRecordsOnServer, getAllRecordsData));
772 Ref<IDBRequest> IDBTransaction::requestGetAllIndexRecords(JSC::ExecState& state, IDBIndex& index, const IDBKeyRangeData& keyRangeData, IndexedDB::GetAllType getAllType, Optional<uint32_t> count)
774 LOG(IndexedDB, "IDBTransaction::requestGetAllIndexRecords");
776 ASSERT(currentThread() == m_database->originThreadID());
778 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
780 auto request = IDBRequest::create(*scriptExecutionContext(), index, *this);
781 addRequest(request.get());
783 IDBGetAllRecordsData getAllRecordsData { keyRangeData, getAllType, count, index.objectStore().info().identifier(), index.info().identifier() };
785 scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetAllRecordsOnServer, &IDBTransaction::getAllRecordsOnServer, getAllRecordsData));
790 void IDBTransaction::getAllRecordsOnServer(IDBClient::TransactionOperation& operation, const IDBGetAllRecordsData& getAllRecordsData)
792 LOG(IndexedDB, "IDBTransaction::getAllRecordsOnServer");
793 ASSERT(currentThread() == m_database->originThreadID());
795 m_database->connectionProxy().getAllRecords(operation, getAllRecordsData);
798 void IDBTransaction::didGetAllRecordsOnServer(IDBRequest& request, const IDBResultData& resultData)
800 LOG(IndexedDB, "IDBTransaction::didGetAllRecordsOnServer");
801 ASSERT(currentThread() == m_database->originThreadID());
803 if (resultData.type() == IDBResultType::Error) {
804 request.requestCompleted(resultData);
808 ASSERT(resultData.type() == IDBResultType::GetAllRecordsSuccess);
810 auto& getAllResult = resultData.getAllResult();
811 switch (getAllResult.type()) {
812 case IndexedDB::GetAllType::Keys:
813 request.setResult(getAllResult.keys());
815 case IndexedDB::GetAllType::Values:
816 request.setResult(getAllResult.values());
820 request.requestCompleted(resultData);
823 Ref<IDBRequest> IDBTransaction::requestGetRecord(ExecState& state, IDBObjectStore& objectStore, const IDBGetRecordData& getRecordData)
825 LOG(IndexedDB, "IDBTransaction::requestGetRecord");
827 ASSERT(!getRecordData.keyRangeData.isNull);
828 ASSERT(currentThread() == m_database->originThreadID());
830 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
832 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
833 addRequest(request.get());
835 scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetRecordOnServer, &IDBTransaction::getRecordOnServer, getRecordData));
840 Ref<IDBRequest> IDBTransaction::requestGetValue(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range)
842 LOG(IndexedDB, "IDBTransaction::requestGetValue");
843 ASSERT(currentThread() == m_database->originThreadID());
845 return requestIndexRecord(state, index, IndexedDB::IndexRecordType::Value, range);
848 Ref<IDBRequest> IDBTransaction::requestGetKey(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range)
850 LOG(IndexedDB, "IDBTransaction::requestGetValue");
851 ASSERT(currentThread() == m_database->originThreadID());
853 return requestIndexRecord(state, index, IndexedDB::IndexRecordType::Key, range);
856 Ref<IDBRequest> IDBTransaction::requestIndexRecord(ExecState& state, IDBIndex& index, IndexedDB::IndexRecordType type, const IDBKeyRangeData& range)
858 LOG(IndexedDB, "IDBTransaction::requestGetValue");
860 ASSERT(!range.isNull);
861 ASSERT(currentThread() == m_database->originThreadID());
863 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
865 auto request = IDBRequest::createIndexGet(*scriptExecutionContext(), index, type, *this);
866 addRequest(request.get());
868 IDBGetRecordData getRecordData = { range };
869 scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetRecordOnServer, &IDBTransaction::getRecordOnServer, getRecordData));
874 void IDBTransaction::getRecordOnServer(IDBClient::TransactionOperation& operation, const IDBGetRecordData& getRecordData)
876 LOG(IndexedDB, "IDBTransaction::getRecordOnServer");
877 ASSERT(currentThread() == m_database->originThreadID());
879 m_database->connectionProxy().getRecord(operation, getRecordData);
882 void IDBTransaction::didGetRecordOnServer(IDBRequest& request, const IDBResultData& resultData)
884 LOG(IndexedDB, "IDBTransaction::didGetRecordOnServer");
885 ASSERT(currentThread() == m_database->originThreadID());
887 if (resultData.type() == IDBResultType::Error) {
888 request.requestCompleted(resultData);
892 ASSERT(resultData.type() == IDBResultType::GetRecordSuccess);
894 const IDBGetResult& result = resultData.getResult();
896 if (request.sourceIndexIdentifier() && request.requestedIndexRecordType() == IndexedDB::IndexRecordType::Key) {
897 if (!result.keyData().isNull())
898 request.setResult(result.keyData());
900 request.setResultToUndefined();
902 if (resultData.getResult().value().data().data())
903 request.setResultToStructuredClone(resultData.getResult().value());
905 request.setResultToUndefined();
908 request.requestCompleted(resultData);
911 Ref<IDBRequest> IDBTransaction::requestCount(ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& range)
913 LOG(IndexedDB, "IDBTransaction::requestCount (IDBObjectStore)");
915 ASSERT(!range.isNull);
916 ASSERT(currentThread() == m_database->originThreadID());
918 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
920 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
921 addRequest(request.get());
923 scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetCountOnServer, &IDBTransaction::getCountOnServer, range));
928 Ref<IDBRequest> IDBTransaction::requestCount(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range)
930 LOG(IndexedDB, "IDBTransaction::requestCount (IDBIndex)");
932 ASSERT(!range.isNull);
933 ASSERT(currentThread() == m_database->originThreadID());
935 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
937 auto request = IDBRequest::create(*scriptExecutionContext(), index, *this);
938 addRequest(request.get());
940 scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetCountOnServer, &IDBTransaction::getCountOnServer, range));
945 void IDBTransaction::getCountOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange)
947 LOG(IndexedDB, "IDBTransaction::getCountOnServer");
948 ASSERT(currentThread() == m_database->originThreadID());
950 m_database->connectionProxy().getCount(operation, keyRange);
953 void IDBTransaction::didGetCountOnServer(IDBRequest& request, const IDBResultData& resultData)
955 LOG(IndexedDB, "IDBTransaction::didGetCountOnServer");
956 ASSERT(currentThread() == m_database->originThreadID());
958 request.setResult(resultData.resultInteger());
959 request.requestCompleted(resultData);
962 Ref<IDBRequest> IDBTransaction::requestDeleteRecord(ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& range)
964 LOG(IndexedDB, "IDBTransaction::requestDeleteRecord");
966 ASSERT(!range.isNull);
967 ASSERT(currentThread() == m_database->originThreadID());
969 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
971 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
972 addRequest(request.get());
974 scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didDeleteRecordOnServer, &IDBTransaction::deleteRecordOnServer, range));
978 void IDBTransaction::deleteRecordOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange)
980 LOG(IndexedDB, "IDBTransaction::deleteRecordOnServer");
981 ASSERT(currentThread() == m_database->originThreadID());
983 m_database->connectionProxy().deleteRecord(operation, keyRange);
986 void IDBTransaction::didDeleteRecordOnServer(IDBRequest& request, const IDBResultData& resultData)
988 LOG(IndexedDB, "IDBTransaction::didDeleteRecordOnServer");
989 ASSERT(currentThread() == m_database->originThreadID());
991 request.setResultToUndefined();
992 request.requestCompleted(resultData);
995 Ref<IDBRequest> IDBTransaction::requestClearObjectStore(ExecState& state, IDBObjectStore& objectStore)
997 LOG(IndexedDB, "IDBTransaction::requestClearObjectStore");
999 ASSERT(currentThread() == m_database->originThreadID());
1001 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
1003 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
1004 addRequest(request.get());
1006 uint64_t objectStoreIdentifier = objectStore.info().identifier();
1007 scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didClearObjectStoreOnServer, &IDBTransaction::clearObjectStoreOnServer, objectStoreIdentifier));
1012 void IDBTransaction::clearObjectStoreOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier)
1014 LOG(IndexedDB, "IDBTransaction::clearObjectStoreOnServer");
1015 ASSERT(currentThread() == m_database->originThreadID());
1017 m_database->connectionProxy().clearObjectStore(operation, objectStoreIdentifier);
1020 void IDBTransaction::didClearObjectStoreOnServer(IDBRequest& request, const IDBResultData& resultData)
1022 LOG(IndexedDB, "IDBTransaction::didClearObjectStoreOnServer");
1023 ASSERT(currentThread() == m_database->originThreadID());
1025 request.setResultToUndefined();
1026 request.requestCompleted(resultData);
1029 Ref<IDBRequest> IDBTransaction::requestPutOrAdd(ExecState& state, IDBObjectStore& objectStore, IDBKey* key, SerializedScriptValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
1031 LOG(IndexedDB, "IDBTransaction::requestPutOrAdd");
1033 ASSERT(!isReadOnly());
1034 ASSERT(objectStore.info().autoIncrement() || key);
1035 ASSERT(currentThread() == m_database->originThreadID());
1037 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
1039 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
1040 addRequest(request.get());
1042 scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didPutOrAddOnServer, &IDBTransaction::putOrAddOnServer, key, &value, overwriteMode));
1047 void IDBTransaction::putOrAddOnServer(IDBClient::TransactionOperation& operation, RefPtr<IDBKey> key, RefPtr<SerializedScriptValue> value, const IndexedDB::ObjectStoreOverwriteMode& overwriteMode)
1049 LOG(IndexedDB, "IDBTransaction::putOrAddOnServer");
1050 ASSERT(currentThread() == originThreadID());
1051 ASSERT(!isReadOnly());
1054 if (!value->hasBlobURLs()) {
1055 m_database->connectionProxy().putOrAdd(operation, key.get(), *value, overwriteMode);
1059 // Due to current limitations on our ability to post tasks back to a worker thread,
1060 // workers currently write blobs to disk synchronously.
1061 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=157958 - Make this asynchronous after refactoring allows it.
1062 if (!isMainThread()) {
1063 auto idbValue = value->writeBlobsToDiskForIndexedDBSynchronously();
1064 if (idbValue.data().data())
1065 m_database->connectionProxy().putOrAdd(operation, key.get(), idbValue, overwriteMode);
1067 // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk.
1068 // In that case, we cannot successfully store this record, so we callback with an error.
1069 RefPtr<IDBClient::TransactionOperation> protectedOperation(&operation);
1070 auto result = IDBResultData::error(operation.identifier(), { IDBDatabaseException::UnknownError, ASCIILiteral("Error preparing Blob/File data to be stored in object store") });
1071 scriptExecutionContext()->postTask([protectedOperation = WTFMove(protectedOperation), result = WTFMove(result)](ScriptExecutionContext&) {
1072 protectedOperation->completed(result);
1078 value->writeBlobsToDiskForIndexedDB([protectedThis = makeRef(*this), this, protectedOperation = Ref<IDBClient::TransactionOperation>(operation), keyData = IDBKeyData(key.get()).isolatedCopy(), overwriteMode](const IDBValue& idbValue) mutable {
1079 ASSERT(currentThread() == originThreadID());
1080 ASSERT(isMainThread());
1081 if (idbValue.data().data()) {
1082 m_database->connectionProxy().putOrAdd(protectedOperation.get(), WTFMove(keyData), idbValue, overwriteMode);
1086 // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk.
1087 // In that case, we cannot successfully store this record, so we callback with an error.
1088 auto result = IDBResultData::error(protectedOperation->identifier(), { IDBDatabaseException::UnknownError, ASCIILiteral("Error preparing Blob/File data to be stored in object store") });
1089 callOnMainThread([protectedThis = WTFMove(protectedThis), protectedOperation = WTFMove(protectedOperation), result = WTFMove(result)]() mutable {
1090 protectedOperation->completed(result);
1095 void IDBTransaction::didPutOrAddOnServer(IDBRequest& request, const IDBResultData& resultData)
1097 LOG(IndexedDB, "IDBTransaction::didPutOrAddOnServer");
1098 ASSERT(currentThread() == m_database->originThreadID());
1100 if (auto* result = resultData.resultKey())
1101 request.setResult(*result);
1103 request.setResultToUndefined();
1104 request.requestCompleted(resultData);
1107 void IDBTransaction::deleteObjectStore(const String& objectStoreName)
1109 LOG(IndexedDB, "IDBTransaction::deleteObjectStore");
1110 ASSERT(currentThread() == m_database->originThreadID());
1111 ASSERT(isVersionChange());
1113 Locker<Lock> locker(m_referencedObjectStoreLock);
1115 if (auto objectStore = m_referencedObjectStores.take(objectStoreName)) {
1116 objectStore->markAsDeleted();
1117 auto identifier = objectStore->info().identifier();
1118 m_deletedObjectStores.set(identifier, WTFMove(objectStore));
1121 scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didDeleteObjectStoreOnServer, &IDBTransaction::deleteObjectStoreOnServer, objectStoreName));
1124 void IDBTransaction::deleteObjectStoreOnServer(IDBClient::TransactionOperation& operation, const String& objectStoreName)
1126 LOG(IndexedDB, "IDBTransaction::deleteObjectStoreOnServer");
1127 ASSERT(isVersionChange());
1128 ASSERT(currentThread() == m_database->originThreadID());
1130 m_database->connectionProxy().deleteObjectStore(operation, objectStoreName);
1133 void IDBTransaction::didDeleteObjectStoreOnServer(const IDBResultData& resultData)
1135 LOG(IndexedDB, "IDBTransaction::didDeleteObjectStoreOnServer");
1136 ASSERT(currentThread() == m_database->originThreadID());
1137 ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteObjectStoreSuccess || resultData.type() == IDBResultType::Error);
1140 void IDBTransaction::deleteIndex(uint64_t objectStoreIdentifier, const String& indexName)
1142 LOG(IndexedDB, "IDBTransaction::deleteIndex");
1143 ASSERT(currentThread() == m_database->originThreadID());
1144 ASSERT(isVersionChange());
1146 scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didDeleteIndexOnServer, &IDBTransaction::deleteIndexOnServer, objectStoreIdentifier, indexName));
1149 void IDBTransaction::deleteIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& indexName)
1151 LOG(IndexedDB, "IDBTransaction::deleteIndexOnServer");
1152 ASSERT(isVersionChange());
1153 ASSERT(currentThread() == m_database->originThreadID());
1155 m_database->connectionProxy().deleteIndex(operation, objectStoreIdentifier, indexName);
1158 void IDBTransaction::didDeleteIndexOnServer(const IDBResultData& resultData)
1160 LOG(IndexedDB, "IDBTransaction::didDeleteIndexOnServer");
1161 ASSERT(currentThread() == m_database->originThreadID());
1162 ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteIndexSuccess || resultData.type() == IDBResultType::Error);
1165 void IDBTransaction::operationDidComplete(IDBClient::TransactionOperation& operation)
1167 ASSERT(m_transactionOperationMap.get(operation.identifier()) == &operation);
1168 ASSERT(currentThread() == m_database->originThreadID());
1169 ASSERT(currentThread() == operation.originThreadID());
1171 m_transactionOperationMap.remove(operation.identifier());
1173 scheduleOperationTimer();
1176 void IDBTransaction::establishOnServer()
1178 LOG(IndexedDB, "IDBTransaction::establishOnServer");
1179 ASSERT(currentThread() == m_database->originThreadID());
1181 m_database->connectionProxy().establishTransaction(*this);
1184 void IDBTransaction::activate()
1186 ASSERT(currentThread() == m_database->originThreadID());
1188 if (isFinishedOrFinishing())
1191 m_state = IndexedDB::TransactionState::Active;
1194 void IDBTransaction::deactivate()
1196 ASSERT(currentThread() == m_database->originThreadID());
1198 if (m_state == IndexedDB::TransactionState::Active)
1199 m_state = IndexedDB::TransactionState::Inactive;
1201 scheduleOperationTimer();
1204 void IDBTransaction::connectionClosedFromServer(const IDBError& error)
1206 LOG(IndexedDB, "IDBTransaction::connectionClosedFromServer - %s", error.message().utf8().data());
1208 m_state = IndexedDB::TransactionState::Aborting;
1210 Vector<RefPtr<IDBClient::TransactionOperation>> operations;
1211 copyValuesToVector(m_transactionOperationMap, operations);
1213 for (auto& operation : operations)
1214 operation->completed(IDBResultData::error(operation->identifier(), error));
1216 connectionProxy().forgetActiveOperations(operations);
1218 m_transactionOperationQueue.clear();
1219 m_abortQueue.clear();
1220 m_transactionOperationMap.clear();
1223 m_domError = error.toDOMError();
1227 void IDBTransaction::visitReferencedObjectStores(JSC::SlotVisitor& visitor) const
1229 Locker<Lock> locker(m_referencedObjectStoreLock);
1230 for (auto& objectStore : m_referencedObjectStores.values())
1231 visitor.addOpaqueRoot(objectStore.get());
1232 for (auto& objectStore : m_deletedObjectStores.values())
1233 visitor.addOpaqueRoot(objectStore.get());
1236 } // namespace WebCore
1238 #endif // ENABLE(INDEXED_DATABASE)