diff options
Diffstat (limited to 'xpcom/base/CycleCollectedJSContext.cpp')
-rw-r--r-- | xpcom/base/CycleCollectedJSContext.cpp | 67 |
1 files changed, 63 insertions, 4 deletions
diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp index 0a85ae6ac..033ca562a 100644 --- a/xpcom/base/CycleCollectedJSContext.cpp +++ b/xpcom/base/CycleCollectedJSContext.cpp @@ -440,6 +440,7 @@ CycleCollectedJSContext::CycleCollectedJSContext() , mDoingStableStates(false) , mDisableMicroTaskCheckpoint(false) , mMicroTaskLevel(0) + , mMicroTaskRecursionDepth(0) , mOutOfMemoryState(OOMState::OK) , mLargeAllocationFailureState(OOMState::OK) { @@ -1380,8 +1381,8 @@ CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth) // Step 4.1: Execute microtasks. if (!mDisableMicroTaskCheckpoint) { + PerformMicroTaskCheckPoint(); if (NS_IsMainThread()) { - PerformMainThreadMicroTaskCheckpoint(); Promise::PerformMicroTaskCheckpoint(); } else { Promise::PerformWorkerMicroTaskCheckpoint(); @@ -1661,12 +1662,70 @@ CycleCollectedJSContext::DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunn mPromiseMicroTaskQueue.push(runnable.forget()); } +class AsyncMutationHandler final : public mozilla::Runnable +{ +public: + NS_IMETHOD Run() override + { + CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); + if (ccjs) { + ccjs->PerformMicroTaskCheckPoint(); + } + return NS_OK; + } +}; + void -CycleCollectedJSContext::PerformMainThreadMicroTaskCheckpoint() +CycleCollectedJSContext::PerformMicroTaskCheckPoint() { - MOZ_ASSERT(NS_IsMainThread()); + if (mPendingMicroTaskRunnables.empty()) { + // Nothing to do, return early. + return; + } + + uint32_t currentDepth = RecursionDepth(); + if (mMicroTaskRecursionDepth >= currentDepth) { + // We are already executing microtasks for the current recursion depth. + return; + } - nsDOMMutationObserver::HandleMutations(); + if (NS_IsMainThread() && !nsContentUtils::IsSafeToRunScript()) { + // Special case for main thread where DOM mutations may happen when + // it is not safe to run scripts. + nsContentUtils::AddScriptRunner(new AsyncMutationHandler()); + return; + } + + mozilla::AutoRestore<uint32_t> restore(mMicroTaskRecursionDepth); + MOZ_ASSERT(currentDepth > 0); + mMicroTaskRecursionDepth = currentDepth; + + AutoSlowOperation aso; + + std::queue<RefPtr<MicroTaskRunnable>> suppressed; + while (!mPendingMicroTaskRunnables.empty()) { + RefPtr<MicroTaskRunnable> runnable = + mPendingMicroTaskRunnables.front().forget(); + mPendingMicroTaskRunnables.pop(); + if (runnable->Suppressed()) { + suppressed.push(runnable); + } else { + runnable->Run(aso); + } + } + + // Put back the suppressed microtasks so that they will be run later. + // Note, it is possible that we end up keeping these suppressed tasks around + // for some time, but no longer than spinning the event loop nestedly + // (sync XHR, alert, etc.) + mPendingMicroTaskRunnables.swap(suppressed); +} + +void +CycleCollectedJSContext::DispatchMicroTaskRunnable( + already_AddRefed<MicroTaskRunnable> aRunnable) +{ + mPendingMicroTaskRunnables.push(aRunnable); } void |