From a2c28b4268855a0357396bbb1890b5036717b221 Mon Sep 17 00:00:00 2001 From: Martok Date: Tue, 16 Jan 2024 11:04:58 +0100 Subject: [PATCH] Issue #2452 - Handle re-entrant Microtask checkpoints from Events dispatched by StableState callbacks --- dom/events/EventDispatcher.cpp | 7 ++++--- xpcom/base/CycleCollectedJSContext.cpp | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index 794b2be7ed..df3080dd47 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -695,9 +695,10 @@ EventDispatcher::Dispatch(nsISupports* aTarget, // Events shall not be fired while we are in stable state to prevent anything // visible from the scripts. - MOZ_ASSERT(!nsContentUtils::IsInStableOrMetaStableState()); - NS_ENSURE_TRUE(!nsContentUtils::IsInStableOrMetaStableState(), - NS_ERROR_DOM_INVALID_STATE_ERR); + // See comment in CycleCollectedJSContext::AfterProcessMicrotasks for why we allow it anyway. + // MOZ_ASSERT(!nsContentUtils::IsInStableOrMetaStableState()); + // NS_ENSURE_TRUE(!nsContentUtils::IsInStableOrMetaStableState(), + // NS_ERROR_DOM_INVALID_STATE_ERR); #ifdef MOZ_TASK_TRACER { diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp index e9c7e27469..997769055f 100644 --- a/xpcom/base/CycleCollectedJSContext.cpp +++ b/xpcom/base/CycleCollectedJSContext.cpp @@ -1479,7 +1479,29 @@ CycleCollectedJSContext::AfterProcessMicrotasks() } // Cleanup Indexed Database transactions: // https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint - CleanupIDBTransactions(RecursionDepth()); + + // We should only ever get here from PerformMicroTaskCheckPoint after a task or other + // checkpoint-able state, never from within ProcessStableStateQueue (mDoingStableStates==false). + // However, some buggy XUL addons may dispatch JS Events from runnables in the StableState queue, + // which then perform a checkpoint at their end, ending up here while ProcessStableStateQueue is + // on the stack. + // Specifically catch that here and add the call to CleanupIDBTransactions to the outer queue, to + // be performed when we know we're in a "regular" stable state again. + if (!mDoingStableStates) { + CleanupIDBTransactions(RecursionDepth()); + } else { + // Don't need a RefPtr to this here as we know this->ProcessStableStateQueue is on the stack + nsCOMPtr cleanupRunnable = NS_NewRunnableFunction( + [this, rec = RecursionDepth()] { + MOZ_ASSERT(mDoingStableStates); + // As this is called from ProcessStableStateQueue, mDoingStableStates == true. + // Switch the flag while CleanupIDBTransactions executes. + mDoingStableStates = false; + CleanupIDBTransactions(rec); + mDoingStableStates = true; + }); + RunInStableState(cleanupRunnable.forget()); + }; } uint32_t