void IDBDatabase::AbortTransactions(bool aShouldWarn) { AssertIsOnOwningThread(); class MOZ_STACK_CLASS Helper final { typedef nsAutoTArray<RefPtr<IDBTransaction>, 20> StrongTransactionArray; typedef nsAutoTArray<IDBTransaction*, 20> WeakTransactionArray; public: static void AbortTransactions(IDBDatabase* aDatabase, const bool aShouldWarn) { MOZ_ASSERT(aDatabase); aDatabase->AssertIsOnOwningThread(); nsTHashtable<nsPtrHashKey<IDBTransaction>>& transactionTable = aDatabase->mTransactions; if (!transactionTable.Count()) { return; } StrongTransactionArray transactionsToAbort; transactionsToAbort.SetCapacity(transactionTable.Count()); for (auto iter = transactionTable.Iter(); !iter.Done(); iter.Next()) { IDBTransaction* transaction = iter.Get()->GetKey(); MOZ_ASSERT(transaction); transaction->AssertIsOnOwningThread(); // Transactions that are already done can simply be ignored. Otherwise // there is a race here and it's possible that the transaction has not // been successfully committed yet so we will warn the user. if (!transaction->IsDone()) { transactionsToAbort.AppendElement(transaction); } } MOZ_ASSERT(transactionsToAbort.Length() <= transactionTable.Count()); if (transactionsToAbort.IsEmpty()) { return; } // We want to abort transactions as soon as possible so we iterate the // transactions once and abort them all first, collecting the transactions // that need to have a warning issued along the way. Those that need a // warning will be a subset of those that are aborted, so we don't need // additional strong references here. WeakTransactionArray transactionsThatNeedWarning; for (RefPtr<IDBTransaction>& transaction : transactionsToAbort) { MOZ_ASSERT(transaction); MOZ_ASSERT(!transaction->IsDone()); if (aShouldWarn) { switch (transaction->GetMode()) { // We ignore transactions that could not have written any data. case IDBTransaction::READ_ONLY: break; // We warn for any transactions that could have written data. case IDBTransaction::READ_WRITE: case IDBTransaction::READ_WRITE_FLUSH: case IDBTransaction::VERSION_CHANGE: transactionsThatNeedWarning.AppendElement(transaction); break; default: MOZ_CRASH("Unknown mode!"); } } transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } static const char kWarningMessage[] = "IndexedDBTransactionAbortNavigation"; for (IDBTransaction* transaction : transactionsThatNeedWarning) { MOZ_ASSERT(transaction); nsString filename; uint32_t lineNo, column; transaction->GetCallerLocation(filename, &lineNo, &column); aDatabase->LogWarning(kWarningMessage, filename, lineNo, column); } }