diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp index bc9fa52df9dd..2a503112f53d 100644 --- a/dom/indexedDB/ActorsChild.cpp +++ b/dom/indexedDB/ActorsChild.cpp @@ -153,6 +153,12 @@ class MOZ_STACK_CLASS AutoSetCurrentTransaction final { ThreadLocal* mThreadLocal; public: + AutoSetCurrentTransaction(const AutoSetCurrentTransaction&) = delete; + AutoSetCurrentTransaction(AutoSetCurrentTransaction&&) = delete; + AutoSetCurrentTransaction& operator=(const AutoSetCurrentTransaction&) = + delete; + AutoSetCurrentTransaction& operator=(AutoSetCurrentTransaction&&) = delete; + explicit AutoSetCurrentTransaction(IDBTransaction* aTransaction) : mTransaction(aTransaction), mPreviousTransaction(nullptr), @@ -629,6 +635,8 @@ StructuredCloneReadInfo DeserializeStructuredCloneReadInfo( aDatabase, aSerialized.hasPreprocessInfo()}; } +// TODO: Remove duplication between DispatchErrorEvent and DispatchSucessEvent. + void DispatchErrorEvent(IDBRequest* aRequest, nsresult aErrorCode, IDBTransaction* aTransaction = nullptr, Event* aEvent = nullptr) { @@ -659,6 +667,10 @@ void DispatchErrorEvent(IDBRequest* aRequest, nsresult aErrorCode, asct.emplace(aTransaction); } + if (transaction && transaction->IsInactive()) { + transaction->TransitionToActive(); + } + if (transaction) { IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( "Firing %s event with error 0x%x", "%s (0x%x)", @@ -678,22 +690,25 @@ void DispatchErrorEvent(IDBRequest* aRequest, nsresult aErrorCode, return; } - MOZ_ASSERT(!transaction || transaction->CanAcceptRequests() || + MOZ_ASSERT(!transaction || transaction->IsActive() || transaction->IsAborted()); - // Do not abort the transaction here if this request is failed due to the - // abortion of its transaction to ensure that the correct error cause of - // the abort event be set in IDBTransaction::FireCompleteOrAbortEvents() - // later. - if (transaction && transaction->CanAcceptRequests() && - aErrorCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) { - WidgetEvent* const internalEvent = aEvent->WidgetEventPtr(); - MOZ_ASSERT(internalEvent); + if (transaction && transaction->IsActive()) { + transaction->TransitionToInactive(); - if (internalEvent->mFlags.mExceptionWasRaised) { - transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); - } else if (doDefault) { - transaction->Abort(request); + // Do not abort the transaction here if this request is failed due to the + // abortion of its transaction to ensure that the correct error cause of + // the abort event be set in IDBTransaction::FireCompleteOrAbortEvents() + // later. + if (aErrorCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) { + WidgetEvent* const internalEvent = aEvent->WidgetEventPtr(); + MOZ_ASSERT(internalEvent); + + if (internalEvent->mFlags.mExceptionWasRaised) { + transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); + } else if (doDefault) { + transaction->Abort(request); + } } } } @@ -728,7 +743,10 @@ void DispatchSuccessEvent(ResultHelper* aResultHelper, request->SetResultCallback(aResultHelper); MOZ_ASSERT(aEvent); - MOZ_ASSERT_IF(transaction, transaction->CanAcceptRequests()); + + if (transaction && transaction->IsInactive()) { + transaction->TransitionToActive(); + } if (transaction) { IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( @@ -742,7 +760,7 @@ void DispatchSuccessEvent(ResultHelper* aResultHelper, } MOZ_ASSERT_IF(transaction, - transaction->CanAcceptRequests() && !transaction->IsAborted()); + transaction->IsActive() && !transaction->IsAborted()); IgnoredErrorResult rv; request->DispatchEvent(*aEvent, rv); @@ -753,7 +771,9 @@ void DispatchSuccessEvent(ResultHelper* aResultHelper, WidgetEvent* const internalEvent = aEvent->WidgetEventPtr(); MOZ_ASSERT(internalEvent); - if (transaction && transaction->CanAcceptRequests()) { + if (transaction && transaction->IsActive()) { + transaction->TransitionToInactive(); + if (internalEvent->mFlags.mExceptionWasRaised) { transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); } else { diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index e24c810a9047..ca7b11b2a6a4 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -1514,7 +1514,8 @@ already_AddRefed IDBObjectStore::AddOrPut( nsTArray updateInfo; { - const auto autoStateRestore = mTransaction->TemporarilyProceedToInactive(); + const auto autoStateRestore = + mTransaction->TemporarilyTransitionToInactive(); GetAddInfo(aCx, aValueWrapper, aKey, cloneWriteInfo, key, updateInfo, aRv); } diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index 3a6883f9769e..e2ccd6f44ea0 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -343,7 +343,7 @@ void IDBTransaction::OnNewRequest() { void IDBTransaction::OnRequestFinished( const bool aRequestCompletedSuccessfully) { AssertIsOnOwningThread(); - MOZ_ASSERT(mReadyState == ReadyState::Active || + MOZ_ASSERT(mReadyState == ReadyState::Inactive || mReadyState == ReadyState::Finished); MOZ_ASSERT_IF(mReadyState == ReadyState::Finished, !NS_SUCCEEDED(mAbortCode)); MOZ_ASSERT(mPendingRequestCount); @@ -351,7 +351,7 @@ void IDBTransaction::OnRequestFinished( --mPendingRequestCount; if (!mPendingRequestCount) { - if (mReadyState == ReadyState::Active) { + if (mReadyState == ReadyState::Inactive) { mReadyState = ReadyState::Committing; } @@ -446,6 +446,18 @@ bool IDBTransaction::CanAcceptRequests() const { (!mStarted || mCreating || GetCurrent() == this); } +IDBTransaction::AutoRestoreState +IDBTransaction::TemporarilyTransitionToActive() { + return AutoRestoreState{*this}; +} + +IDBTransaction::AutoRestoreState +IDBTransaction::TemporarilyTransitionToInactive() { + return AutoRestoreState{*this}; +} + void IDBTransaction::GetCallerLocation(nsAString& aFilename, uint32_t* const aLineNo, uint32_t* const aColumn) const { @@ -584,7 +596,7 @@ void IDBTransaction::AbortInternal(const nsresult aAbortCode, MOZ_ASSERT(!IsCommittingOrFinished()); const bool isVersionChange = mMode == Mode::VersionChange; - const bool needToSendAbort = mReadyState == ReadyState::Active && !mStarted; + const bool needToSendAbort = mReadyState == ReadyState::Inactive && !mStarted; mAbortCode = aAbortCode; mReadyState = ReadyState::Finished; @@ -682,6 +694,7 @@ void IDBTransaction::Abort(const nsresult aErrorCode) { AbortInternal(aErrorCode, DOMException::Create(aErrorCode)); } +// Specified by https://w3c.github.io/IndexedDB/#dom-idbtransaction-abort. void IDBTransaction::Abort(ErrorResult& aRv) { AssertIsOnOwningThread(); @@ -690,6 +703,8 @@ void IDBTransaction::Abort(ErrorResult& aRv) { return; } + mReadyState = ReadyState::Inactive; + AbortInternal(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, nullptr); mAbortedByScript.Flip(); @@ -944,8 +959,12 @@ IDBTransaction::Run() { // We're back at the event loop, no longer newborn. mCreating = false; + MOZ_ASSERT_IF(mReadyState == ReadyState::Finished, IsAborted()); + // Maybe commit if there were no requests generated. - if (mReadyState == ReadyState::Active && !mStarted) { + if (!mStarted && mReadyState != ReadyState::Finished) { + MOZ_ASSERT(mReadyState == ReadyState::Inactive || + mReadyState == ReadyState::Active); mReadyState = ReadyState::Finished; SendCommit(); diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h index fe4057beea3b..4627e1c60b88 100644 --- a/dom/indexedDB/IDBTransaction.h +++ b/dom/indexedDB/IDBTransaction.h @@ -174,6 +174,18 @@ class IDBTransaction final : public DOMEventTargetHelper, public nsIRunnable { mReadyState == ReadyState::Finished; } + bool IsActive() const { + AssertIsOnOwningThread(); + + return mReadyState == ReadyState::Active; + } + + bool IsInactive() const { + AssertIsOnOwningThread(); + + return mReadyState == ReadyState::Inactive; + } + bool IsFinished() const { AssertIsOnOwningThread(); @@ -191,32 +203,47 @@ class IDBTransaction final : public DOMEventTargetHelper, public nsIRunnable { return NS_FAILED(mAbortCode); } - auto TemporarilyProceedToInactive() { - AssertIsOnOwningThread(); + template + class AutoRestoreState { + public: + explicit AutoRestoreState(IDBTransaction& aOwner) : mOwner { aOwner } +#ifdef DEBUG + , mSavedPendingRequestCount { mOwner.mPendingRequestCount } +#endif + { + mOwner.AssertIsOnOwningThread(); + MOZ_ASSERT(mOwner.mReadyState == OriginalState); + mOwner.mReadyState = TemporaryState; + } + + ~AutoRestoreState() { + mOwner.AssertIsOnOwningThread(); + MOZ_ASSERT(mOwner.mReadyState == TemporaryState); + MOZ_ASSERT(mOwner.mPendingRequestCount == mSavedPendingRequestCount); + + mOwner.mReadyState = OriginalState; + } + + private: + IDBTransaction& mOwner; +#ifdef DEBUG + const uint32_t mSavedPendingRequestCount; +#endif + }; + + AutoRestoreState + TemporarilyTransitionToActive(); + AutoRestoreState + TemporarilyTransitionToInactive(); + + void TransitionToActive() { + MOZ_ASSERT(mReadyState == ReadyState::Inactive); + mReadyState = ReadyState::Active; + } + + void TransitionToInactive() { MOZ_ASSERT(mReadyState == ReadyState::Active); mReadyState = ReadyState::Inactive; - - struct AutoRestoreState { - IDBTransaction& mOwner; -#ifdef DEBUG - uint32_t mSavedPendingRequestCount; -#endif - - ~AutoRestoreState() { - mOwner.AssertIsOnOwningThread(); - MOZ_ASSERT(mOwner.mReadyState == ReadyState::Inactive); - MOZ_ASSERT(mOwner.mPendingRequestCount == mSavedPendingRequestCount); - - mOwner.mReadyState = ReadyState::Active; - } - }; - - return AutoRestoreState{*this -#ifdef DEBUG - , - mPendingRequestCount -#endif - }; } nsresult AbortCode() const {