summaryrefslogtreecommitdiff
path: root/xpcom
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom')
-rw-r--r--xpcom/base/CycleCollectedJSContext.cpp67
-rw-r--r--xpcom/base/CycleCollectedJSContext.h22
2 files changed, 83 insertions, 6 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
diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h
index 2197eae92..4cd1479ed 100644
--- a/xpcom/base/CycleCollectedJSContext.h
+++ b/xpcom/base/CycleCollectedJSContext.h
@@ -33,6 +33,7 @@ struct Class;
} // namespace js
namespace mozilla {
+class AutoSlowOperation;
class JSGCThingParticipant: public nsCycleCollectionParticipant
{
@@ -134,6 +135,17 @@ struct CycleCollectorResults
uint32_t mNumSlices;
};
+class MicroTaskRunnable
+{
+public:
+ MicroTaskRunnable() {}
+ NS_INLINE_DECL_REFCOUNTING(MicroTaskRunnable)
+ virtual void Run(AutoSlowOperation& aAso) = 0;
+ virtual bool Suppressed() { return false; }
+protected:
+ virtual ~MicroTaskRunnable() {}
+};
+
class CycleCollectedJSContext
{
friend class JSGCThingParticipant;
@@ -412,7 +424,7 @@ public:
void LeaveMicroTask()
{
if (--mMicroTaskLevel == 0) {
- PerformMainThreadMicroTaskCheckpoint();
+ PerformMicroTaskCheckPoint();
}
}
@@ -431,7 +443,9 @@ public:
mMicroTaskLevel = aLevel;
}
- void PerformMainThreadMicroTaskCheckpoint();
+ void PerformMicroTaskCheckPoint();
+
+ void DispatchMicroTaskRunnable(already_AddRefed<MicroTaskRunnable> aRunnable);
// Storage for watching rejected promises waiting for some client to
// consume their rejection.
@@ -484,6 +498,10 @@ private:
bool mDisableMicroTaskCheckpoint;
uint32_t mMicroTaskLevel;
+ std::queue<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables;
+
+ uint32_t mMicroTaskRecursionDepth;
+
OOMState mOutOfMemoryState;
OOMState mLargeAllocationFailureState;