From 2077cdb41e3ef814cbbef482774d8bbec464fb1c Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Wed, 22 Jan 2020 21:07:19 -0500 Subject: Bug 1406922 - Make CycleCollectedJSContext to handle microtasks and make MutationObserver to use them Tag UXP Issue #1344 --- xpcom/base/CycleCollectedJSContext.cpp | 67 ++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) (limited to 'xpcom/base/CycleCollectedJSContext.cpp') 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 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 restore(mMicroTaskRecursionDepth); + MOZ_ASSERT(currentDepth > 0); + mMicroTaskRecursionDepth = currentDepth; + + AutoSlowOperation aso; + + std::queue> suppressed; + while (!mPendingMicroTaskRunnables.empty()) { + RefPtr 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 aRunnable) +{ + mPendingMicroTaskRunnables.push(aRunnable); } void -- cgit v1.2.3