diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 6d092a53d0..029748c40a 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -5444,6 +5444,14 @@ nsContentUtils::AddPendingIDBTransaction(already_AddRefed aTransact CycleCollectedJSContext::Get()->AddPendingIDBTransaction(Move(aTransaction)); } +/* static */ +bool +nsContentUtils::IsInStableOrMetaStableState() +{ + MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!"); + return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState(); +} + /* * Helper function for nsContentUtils::ProcessViewportInfo. * diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 9c22fa491e..bce0085c61 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1779,6 +1779,11 @@ public: */ static void AddPendingIDBTransaction(already_AddRefed aTransaction); + /** + * Returns true if we are doing StableState/MetastableState. + */ + static bool IsInStableOrMetaStableState(); + /* Process viewport META data. This gives us information for the scale * and zoom of a page on mobile devices. We stick the information in * the document header and use it later on after rendering. diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index 001b458cf3..794b2be7ed 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -693,6 +693,12 @@ EventDispatcher::Dispatch(nsISupports* aTarget, NS_ENSURE_TRUE(aEvent->mMessage || !aDOMEvent || aTargets, NS_ERROR_DOM_INVALID_STATE_ERR); + // 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); + #ifdef MOZ_TASK_TRACER { if (aDOMEvent) { diff --git a/dom/media/webspeech/synth/nsSpeechTask.cpp b/dom/media/webspeech/synth/nsSpeechTask.cpp index 66ab0139e9..02f2fa5260 100644 --- a/dom/media/webspeech/synth/nsSpeechTask.cpp +++ b/dom/media/webspeech/synth/nsSpeechTask.cpp @@ -59,15 +59,30 @@ public: switch (event) { case MediaStreamGraphEvent::EVENT_FINISHED: { + RefPtr self = this; if (!mStarted) { mStarted = true; - nsCOMPtr startRunnable = - NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted); + nsCOMPtr startRunnable = NS_NewRunnableFunction( + [self] { + // "start" event will be fired in DoNotifyStarted() which is + // not allowed in stable state, so we do it asynchronously in + // next run. + NS_DispatchToMainThread(NewRunnableMethod( + self, + &SynthStreamListener::DoNotifyStarted)); + }); aGraph->DispatchToMainThreadAfterStreamStateUpdate(startRunnable.forget()); } - nsCOMPtr endRunnable = - NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished); + nsCOMPtr endRunnable = NS_NewRunnableFunction( + [self] { + // "end" event will be fired in DoNotifyFinished() which is + // not allowed in stable state, so we do it asynchronously in + // next run. + NS_DispatchToMainThread(NewRunnableMethod( + self, + &SynthStreamListener::DoNotifyFinished)); + }); aGraph->DispatchToMainThreadAfterStreamStateUpdate(endRunnable.forget()); } break; @@ -85,8 +100,16 @@ public: { if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) { mStarted = true; - nsCOMPtr event = - NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted); + RefPtr self = this; + nsCOMPtr event = NS_NewRunnableFunction( + [self] { + // "start" event will be fired in DoNotifyStarted() which is + // not allowed in stable state, so we do it asynchronously in + // next run. + NS_DispatchToMainThread(NewRunnableMethod( + self, + &SynthStreamListener::DoNotifyStarted)); + }); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); } } diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h index 4e387bae1a..58f6d97e19 100644 --- a/xpcom/base/CycleCollectedJSContext.h +++ b/xpcom/base/CycleCollectedJSContext.h @@ -416,6 +416,11 @@ public: void PerformDebuggerMicroTaskCheckpoint(); + bool IsInStableOrMetaStableState() + { + return mDoingStableStates; + } + // Storage for watching rejected promises waiting for some client to // consume their rejection.