import from UXP: Bug 1406922 - Make CycleCollectedJSContext to handle microtasks and make MutationObserver to use them (24966709)

This commit is contained in:
2022-04-08 15:13:33 +08:00
parent 3d3913696d
commit c43ceb226c
5 changed files with 165 additions and 49 deletions
+64 -5
View File
@@ -443,6 +443,7 @@ CycleCollectedJSContext::CycleCollectedJSContext()
, mDoingStableStates(false)
, mDisableMicroTaskCheckpoint(false)
, mMicroTaskLevel(0)
, mMicroTaskRecursionDepth(0)
, mOutOfMemoryState(OOMState::OK)
, mLargeAllocationFailureState(OOMState::OK)
{
@@ -1405,8 +1406,8 @@ CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
// Step 4.1: Execute microtasks.
if (!mDisableMicroTaskCheckpoint) {
PerformMicroTaskCheckPoint();
if (NS_IsMainThread()) {
PerformMainThreadMicroTaskCheckpoint();
Promise::PerformMicroTaskCheckpoint();
} else {
Promise::PerformWorkerMicroTaskCheckpoint();
@@ -1689,12 +1690,70 @@ CycleCollectedJSContext::DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunn
mPromiseMicroTaskQueue.push(runnable.forget());
}
void
CycleCollectedJSContext::PerformMainThreadMicroTaskCheckpoint()
class AsyncMutationHandler final : public mozilla::Runnable
{
MOZ_ASSERT(NS_IsMainThread());
public:
NS_IMETHOD Run() override
{
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
if (ccjs) {
ccjs->PerformMicroTaskCheckPoint();
}
return NS_OK;
}
};
nsDOMMutationObserver::HandleMutations();
void
CycleCollectedJSContext::PerformMicroTaskCheckPoint()
{
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;
}
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