void IDBTransaction::transitionedToFinishing(IndexedDB::TransactionState state)
{
    ASSERT(!isFinishedOrFinishing());
    m_state = state;
    ASSERT(isFinishedOrFinishing());
    m_referencedObjectStores.clear();
}
void IDBTransaction::activate()
{
    if (isFinishedOrFinishing())
        return;

    m_state = IndexedDB::TransactionState::Active;
}
void IDBTransaction::abortDueToFailedRequest(DOMError& error)
{
    LOG(IndexedDB, "IDBTransaction::abortDueToFailedRequest");
    if (isFinishedOrFinishing())
        return;

    m_domError = &error;
    ExceptionCodeWithMessage ec;
    abort(ec);
}
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::abort(ExceptionCode& ec)
{
    LOG(IndexedDB, "IDBTransaction::abort");

    if (isFinishedOrFinishing()) {
        ec = INVALID_STATE_ERR;
        return;
    }

    m_state = IndexedDB::TransactionState::Aborting;
    m_database->willAbortTransaction(*this);

    auto operation = createTransactionOperation(*this, nullptr, &IDBTransaction::abortOnServer);
    scheduleOperation(WTF::move(operation));
}
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);
}
RefPtr<WebCore::IDBObjectStore> IDBTransaction::objectStore(const String& objectStoreName, ExceptionCode& ec)
{
    LOG(IndexedDB, "IDBTransaction::objectStore");

    if (objectStoreName.isEmpty()) {
        ec = NOT_FOUND_ERR;
        return nullptr;
    }

    if (isFinishedOrFinishing()) {
        ec = INVALID_STATE_ERR;
        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 = NOT_FOUND_ERR;
        return nullptr;
    }

    // Version change transactions are scoped to every object store in the database.
    if (!found && !isVersionChange()) {
        ec = NOT_FOUND_ERR;
        return nullptr;
    }

    auto objectStore = IDBObjectStore::create(*info, *this);
    m_referencedObjectStores.set(objectStoreName, &objectStore.get());

    return adoptRef(&objectStore.leakRef());
}
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();
}
RefPtr<WebCore::IDBObjectStore> IDBTransaction::objectStore(const String& objectStoreName, ExceptionCodeWithMessage& ec)
{
    LOG(IndexedDB, "IDBTransaction::objectStore");

    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(*info, *this);
    m_referencedObjectStores.set(objectStoreName, &objectStore.get());

    return adoptRef(&objectStore.leakRef());
}
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_state = IndexedDB::TransactionState::Aborting;
    m_database->willAbortTransaction(*this);

    if (isVersionChange()) {
        for (auto& objectStore : m_referencedObjectStores.values())
            objectStore->rollbackInfoForVersionChangeAbort();
    }
    
    m_abortQueue.swap(m_transactionOperationQueue);

    auto operation = createTransactionOperation(*this, nullptr, &IDBTransaction::abortOnServerAndCancelRequests);
    scheduleOperation(WTFMove(operation));
}