Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2024-01-09 12:01:14 +08:00
commit d4a80aa2c4
42 changed files with 538 additions and 364 deletions

View file

@ -56,6 +56,7 @@
#include "mozilla/CycleCollectedJSContext.h"
#include <algorithm>
#include "mozilla/ArrayUtils.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Move.h"
#include "mozilla/MemoryReporting.h"
@ -69,6 +70,8 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseBinding.h"
#include "mozilla/dom/PromiseDebugging.h"
#include "mozilla/dom/PromiseRejectionEvent.h"
#include "mozilla/dom/PromiseRejectionEventBinding.h"
#include "mozilla/dom/ScriptSettings.h"
#include "jsprf.h"
#include "js/Debug.h"
@ -437,7 +440,7 @@ CycleCollectedJSContext::CycleCollectedJSContext()
, mPrevGCNurseryCollectionCallback(nullptr)
, mJSHolders(256)
, mDoingStableStates(false)
, mDisableMicroTaskCheckpoint(false)
, mTargetedMicroTaskRecursionDepth(0)
, mMicroTaskLevel(0)
, mMicroTaskRecursionDepth(0)
, mOutOfMemoryState(OOMState::OK)
@ -458,8 +461,8 @@ CycleCollectedJSContext::~CycleCollectedJSContext()
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
// Last chance to process any events.
ProcessMetastableStateQueue(mBaseRecursionDepth);
MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
CleanupIDBTransactions(mBaseRecursionDepth);
MOZ_ASSERT(mPendingIDBTransactions.IsEmpty());
ProcessStableStateQueue();
MOZ_ASSERT(mStableStateEvents.IsEmpty());
@ -467,8 +470,8 @@ CycleCollectedJSContext::~CycleCollectedJSContext()
// Clear mPendingException first, since it might be cycle collected.
mPendingException = nullptr;
MOZ_ASSERT(mDebuggerPromiseMicroTaskQueue.empty());
MOZ_ASSERT(mPromiseMicroTaskQueue.empty());
MOZ_ASSERT(mDebuggerMicroTaskQueue.empty());
MOZ_ASSERT(mPendingMicroTaskRunnables.empty());
mUncaughtRejections.reset();
mConsumedRejections.reset();
@ -921,7 +924,7 @@ CycleCollectedJSContext::LargeAllocationFailureCallback(void* aData)
self->OnLargeAllocationFailure();
}
class PromiseJobRunnable final : public Runnable
class PromiseJobRunnable final : public MicroTaskRunnable
{
public:
PromiseJobRunnable(JS::HandleObject aCallback, JS::HandleObject aAllocationSite,
@ -935,14 +938,20 @@ public:
}
protected:
NS_IMETHOD
Run() override
virtual void Run(AutoSlowOperation& aAso) override
{
nsIGlobalObject* global = xpc::NativeGlobal(mCallback->CallbackPreserveColor());
if (global && !global->IsDying()) {
mCallback->Call("promise callback");
aAso.CheckForInterrupt();
}
return NS_OK;
}
virtual bool Suppressed() override
{
nsIGlobalObject* global =
xpc::NativeGlobal(mCallback->CallbackPreserveColor());
return global && global->IsInSyncOperation();
}
private:
@ -976,7 +985,7 @@ CycleCollectedJSContext::EnqueuePromiseJobCallback(JSContext* aCx,
if (aIncumbentGlobal) {
global = xpc::NativeGlobal(aIncumbentGlobal);
}
nsCOMPtr<nsIRunnable> runnable = new PromiseJobRunnable(aJob, aAllocationSite, global);
RefPtr<MicroTaskRunnable> runnable = new PromiseJobRunnable(aJob, aAllocationSite, global);
self->DispatchToMicroTask(runnable.forget());
return true;
}
@ -988,15 +997,65 @@ CycleCollectedJSContext::PromiseRejectionTrackerCallback(JSContext* aCx,
PromiseRejectionHandlingState state,
void* aData)
{
#ifdef DEBUG
CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
#endif // DEBUG
MOZ_ASSERT(aCx == self->Context());
MOZ_ASSERT(Get() == self);
// TODO: Bug 1549351 - Promise rejection event should not be sent for
// cross-origin scripts
// See HTML 8.1.6.3 HostPromiseRejectionTracker
PromiseArray& aboutToBeNotified = self->mAboutToBeNotifiedRejectedPromises;
PromiseHashtable& outstanding = self->mOutstandingRejections;
uint64_t promiseID = JS::GetPromiseID(aPromise);
if (state == PromiseRejectionHandlingState::Unhandled) {
PromiseDebugging::AddUncaughtRejection(aPromise);
// Step 5.
// Unhandled Promises first go to the about-to-be-notified queue, processed
// after Microtasks as NotifyUnhandledRejections
RefPtr<Promise> promise = Promise::CreateFromExisting(xpc::NativeGlobal(aPromise), aPromise);
aboutToBeNotified.AppendElement(promise);
} else {
// Unhandled-then-handled Promises (ie.e Promises where the unhandledrejection event
// handler attached a handler) may fire the rejectionhandled event.
// Step 6.1.
for (size_t i = 0; i < aboutToBeNotified.Length(); i++) {
if (aboutToBeNotified[i] &&
aboutToBeNotified[i]->PromiseObj() == aPromise) {
// To avoid large amounts of memmoves, we don't shrink the vector
// here. Instead, we filter out nullptrs when iterating over the
// vector later.
aboutToBeNotified[i] = nullptr;
MOZ_ASSERT(!outstanding.Get(promiseID, nullptr));
return;
}
}
// Step 6.2. and 6.3.
RefPtr<Promise> promise;
if (outstanding.Remove(promiseID, getter_AddRefs(promise))) {
// Step 6.4.
nsIGlobalObject* global = xpc::NativeGlobal(aPromise);
if (nsCOMPtr<EventTarget> owner = do_QueryInterface(global)) {
// Step 6.5.
// The spec says to do this in a global task, but we're just queuing an event,
// which will execute in the next event loop either way.
PromiseRejectionEventInit init;
init.mPromise = Promise::CreateFromExisting(global, aPromise);
init.mReason = JS::GetPromiseResult(aPromise);
RefPtr<PromiseRejectionEvent> event = PromiseRejectionEvent::Constructor(owner,
NS_LITERAL_STRING("rejectionhandled"), init);
RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(owner, event);
asyncDispatcher->PostDOMEvent();
}
}
// Finally, notifiy debug observers
PromiseDebugging::AddConsumedRejection(aPromise);
}
}
@ -1175,18 +1234,18 @@ CycleCollectedJSContext::SetPendingException(nsIException* aException)
mPendingException = aException;
}
std::queue<nsCOMPtr<nsIRunnable>>&
CycleCollectedJSContext::GetPromiseMicroTaskQueue()
std::queue<RefPtr<MicroTaskRunnable>>&
CycleCollectedJSContext::GetMicroTaskQueue()
{
MOZ_ASSERT(mJSContext);
return mPromiseMicroTaskQueue;
return mPendingMicroTaskRunnables;
}
std::queue<nsCOMPtr<nsIRunnable>>&
CycleCollectedJSContext::GetDebuggerPromiseMicroTaskQueue()
std::queue<RefPtr<MicroTaskRunnable>>&
CycleCollectedJSContext::GetDebuggerMicroTaskQueue()
{
MOZ_ASSERT(mJSContext);
return mDebuggerPromiseMicroTaskQueue;
return mDebuggerMicroTaskQueue;
}
nsCycleCollectionParticipant*
@ -1345,24 +1404,24 @@ CycleCollectedJSContext::ProcessStableStateQueue()
}
void
CycleCollectedJSContext::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
CycleCollectedJSContext::CleanupIDBTransactions(uint32_t aRecursionDepth)
{
MOZ_ASSERT(mJSContext);
MOZ_RELEASE_ASSERT(!mDoingStableStates);
mDoingStableStates = true;
nsTArray<RunInMetastableStateData> localQueue = Move(mMetastableStateEvents);
nsTArray<PendingIDBTransactionData> localQueue = Move(mPendingIDBTransactions);
for (uint32_t i = 0; i < localQueue.Length(); ++i)
{
RunInMetastableStateData& data = localQueue[i];
PendingIDBTransactionData& data = localQueue[i];
if (data.mRecursionDepth != aRecursionDepth) {
continue;
}
{
nsCOMPtr<nsIRunnable> runnable = data.mRunnable.forget();
runnable->Run();
nsCOMPtr<nsIRunnable> transaction = data.mTransaction.forget();
transaction->Run();
}
localQueue.RemoveElementAt(i--);
@ -1370,11 +1429,27 @@ CycleCollectedJSContext::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
// If the queue has events in it now, they were added from something we called,
// so they belong at the end of the queue.
localQueue.AppendElements(mMetastableStateEvents);
localQueue.SwapElements(mMetastableStateEvents);
localQueue.AppendElements(mPendingIDBTransactions);
localQueue.SwapElements(mPendingIDBTransactions);
mDoingStableStates = false;
}
void
CycleCollectedJSContext::BeforeProcessTask(bool aMightBlock)
{
// If ProcessNextEvent was called during a microtask callback, we
// must process any pending microtasks before blocking in the event loop,
// otherwise we may deadlock until an event enters the queue later.
if (aMightBlock && PerformMicroTaskCheckPoint()) {
// If any microtask was processed, we post a dummy event in order to
// force the ProcessNextEvent call not to block. This is required
// to support nested event loops implemented using a pattern like
// "while (condition) thread.processNextEvent(true)", in case the
// condition is triggered here by a Promise "then" callback.
NS_DispatchToMainThread(new Runnable());
}
}
void
CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
{
@ -1382,39 +1457,27 @@ CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
// See HTML 6.1.4.2 Processing model
// Execute any events that were waiting for a microtask to complete.
// This is not (yet) in the spec.
ProcessMetastableStateQueue(aRecursionDepth);
// Step 4.1: Execute microtasks.
if (!mDisableMicroTaskCheckpoint) {
PerformMicroTaskCheckPoint();
if (NS_IsMainThread()) {
Promise::PerformMicroTaskCheckpoint();
} else {
Promise::PerformWorkerMicroTaskCheckpoint();
}
}
PerformMicroTaskCheckPoint();
// Step 4.2 Execute any events that were waiting for a stable state.
ProcessStableStateQueue();
}
void
CycleCollectedJSContext::AfterProcessMicrotask()
CycleCollectedJSContext::AfterProcessMicrotasks()
{
MOZ_ASSERT(mJSContext);
AfterProcessMicrotask(RecursionDepth());
}
void
CycleCollectedJSContext::AfterProcessMicrotask(uint32_t aRecursionDepth)
{
MOZ_ASSERT(mJSContext);
// Between microtasks, execute any events that were waiting for a microtask
// to complete.
ProcessMetastableStateQueue(aRecursionDepth);
// Notify unhandled promise rejections:
// https://html.spec.whatwg.org/multipage/webappapis.html#notify-about-rejected-promises
if (mAboutToBeNotifiedRejectedPromises.Length()) {
RefPtr<NotifyUnhandledRejections> runnable = new NotifyUnhandledRejections(
this, std::move(mAboutToBeNotifiedRejectedPromises));
NS_DispatchToCurrentThread(runnable);
}
// Cleanup Indexed Database transactions:
// https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
CleanupIDBTransactions(RecursionDepth());
}
uint32_t
@ -1431,12 +1494,12 @@ CycleCollectedJSContext::RunInStableState(already_AddRefed<nsIRunnable>&& aRunna
}
void
CycleCollectedJSContext::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable)
CycleCollectedJSContext::AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction)
{
MOZ_ASSERT(mJSContext);
RunInMetastableStateData data;
data.mRunnable = aRunnable;
PendingIDBTransactionData data;
data.mTransaction = aTransaction;
MOZ_ASSERT(mOwningThread);
data.mRecursionDepth = RecursionDepth();
@ -1453,7 +1516,7 @@ CycleCollectedJSContext::RunInMetastableState(already_AddRefed<nsIRunnable>&& aR
}
#endif
mMetastableStateEvents.AppendElement(Move(data));
mPendingIDBTransactions.AppendElement(Move(data));
}
IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSContext* aCx,
@ -1659,14 +1722,14 @@ CycleCollectedJSContext::PrepareWaitingZonesForGC()
}
void
CycleCollectedJSContext::DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable)
CycleCollectedJSContext::DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable)
{
RefPtr<nsIRunnable> runnable(aRunnable);
RefPtr<MicroTaskRunnable> runnable(aRunnable);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(runnable);
mPromiseMicroTaskQueue.push(runnable.forget());
mPendingMicroTaskRunnables.push(runnable.forget());
}
class AsyncMutationHandler final : public mozilla::Runnable
@ -1682,41 +1745,61 @@ public:
}
};
void
bool
CycleCollectedJSContext::PerformMicroTaskCheckPoint()
{
if (mPendingMicroTaskRunnables.empty()) {
if (mPendingMicroTaskRunnables.empty() && mDebuggerMicroTaskQueue.empty()) {
AfterProcessMicrotasks();
// Nothing to do, return early.
return;
return false;
}
uint32_t currentDepth = RecursionDepth();
if (mMicroTaskRecursionDepth >= currentDepth) {
// We are already executing microtasks for the current recursion depth.
return;
return false;
}
if (mTargetedMicroTaskRecursionDepth != 0 &&
mTargetedMicroTaskRecursionDepth != currentDepth) {
return false;
}
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;
return false;
}
mozilla::AutoRestore<uint32_t> restore(mMicroTaskRecursionDepth);
MOZ_ASSERT(currentDepth > 0);
mMicroTaskRecursionDepth = currentDepth;
bool didProcess = false;
AutoSlowOperation aso;
std::queue<RefPtr<MicroTaskRunnable>> suppressed;
while (!mPendingMicroTaskRunnables.empty()) {
RefPtr<MicroTaskRunnable> runnable =
mPendingMicroTaskRunnables.front().forget();
mPendingMicroTaskRunnables.pop();
for (;;) {
RefPtr<MicroTaskRunnable> runnable;
if (!mDebuggerMicroTaskQueue.empty()) {
runnable = mDebuggerMicroTaskQueue.front().forget();
mDebuggerMicroTaskQueue.pop();
} else if (!mPendingMicroTaskRunnables.empty()) {
runnable = mPendingMicroTaskRunnables.front().forget();
mPendingMicroTaskRunnables.pop();
} else {
break;
}
if (runnable->Suppressed()) {
// Microtasks in worker shall never be suppressed.
// Otherwise, mPendingMicroTaskRunnables will be replaced later with
// all suppressed tasks in mDebuggerMicroTaskQueue unexpectedly.
MOZ_ASSERT(NS_IsMainThread());
suppressed.push(runnable);
} else {
didProcess = true;
runnable->Run(aso);
}
}
@ -1726,13 +1809,105 @@ CycleCollectedJSContext::PerformMicroTaskCheckPoint()
// for some time, but no longer than spinning the event loop nestedly
// (sync XHR, alert, etc.)
mPendingMicroTaskRunnables.swap(suppressed);
AfterProcessMicrotasks();
return didProcess;
}
void
CycleCollectedJSContext::DispatchMicroTaskRunnable(
already_AddRefed<MicroTaskRunnable> aRunnable)
CycleCollectedJSContext::PerformDebuggerMicroTaskCheckpoint()
{
// Don't do normal microtask handling checks here, since whoever is calling
// this method is supposed to know what they are doing.
AutoSlowOperation aso;
for (;;) {
// For a debugger microtask checkpoint, we always use the debugger microtask
// queue.
std::queue<RefPtr<MicroTaskRunnable>>* microtaskQueue =
&GetDebuggerMicroTaskQueue();
if (microtaskQueue->empty()) {
break;
}
RefPtr<MicroTaskRunnable> runnable = microtaskQueue->front().forget();
MOZ_ASSERT(runnable);
// This function can re-enter, so we remove the element before calling.
microtaskQueue->pop();
runnable->Run(aso);
}
AfterProcessMicrotasks();
}
NS_IMETHODIMP
CycleCollectedJSContext::NotifyUnhandledRejections::Run()
{
mPendingMicroTaskRunnables.push(aRunnable);
// See HTML 8.1.4.7 Unhandled promise rejections
// with 'mUnhandledRejections' == copy of 'about-to-be-notified rejected'
for (size_t i = 0; i < mUnhandledRejections.Length(); ++i) {
// Step 5.1.
RefPtr<Promise>& promise = mUnhandledRejections[i];
if (!promise)
continue;
JS::RootedObject promiseObj(mCx->RootingCx(), promise->PromiseObj());
MOZ_ASSERT(JS::IsPromiseObject(promiseObj));
// Step 5.1.1.
if (JS::GetPromiseIsHandled(promiseObj))
continue;
uint64_t promiseID = JS::GetPromiseID(promiseObj);
// Step 5.1.2.
bool raiseError = true;
if (nsCOMPtr<EventTarget> target =
do_QueryInterface(promise->GetParentObject())) {
PromiseRejectionEventInit init;
init.mPromise = promise;
init.mReason = JS::GetPromiseResult(promiseObj);
init.mCancelable = true;
RefPtr<PromiseRejectionEvent> event =
PromiseRejectionEvent::Constructor(
target, NS_LITERAL_STRING("unhandledrejection"), init);
// Step 5.1.4. (reordered)
// In contrast to the letter of the spec, this must be done before
// the event handler is called or the PromiseRejectionTrackerCallback
// will not see the correct state.
PromiseDebugging::AddUncaughtRejection(promiseObj);
target->DispatchEvent(event, &raiseError);
}
// Step 5.1.3. (implied)
// If the Promise became handled, PromiseRejectionTrackerCallback will have marked it.
// If not, keep it in the "uncaught" list for PromiseDebugging, and set the flag if
// preventDefault indicated we don't want it reported to console.
if (!JS::GetPromiseIsHandled(promiseObj) && !raiseError) {
JS::MarkPromiseRejectionReported(promiseObj);
}
}
return NS_OK;
}
nsresult
CycleCollectedJSContext::NotifyUnhandledRejections::Cancel()
{
for (size_t i = 0; i < mUnhandledRejections.Length(); ++i) {
RefPtr<Promise>& promise = mUnhandledRejections[i];
if (!promise)
continue;
JS::RootedObject promiseObj(mCx->RootingCx(), promise->PromiseObj());
mCx->mOutstandingRejections.Remove(JS::GetPromiseID(promiseObj));
}
return NS_OK;
}
void

View file

@ -12,6 +12,7 @@
#include "mozilla/mozalloc.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/SegmentedVector.h"
#include "mozilla/dom/Promise.h"
#include "jsapi.h"
#include "jsfriendapi.h"
@ -170,9 +171,6 @@ protected:
virtual void CustomOutOfMemoryCallback() {}
virtual void CustomLargeAllocationFailureCallback() {}
std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue;
std::queue<nsCOMPtr<nsIRunnable>> mDebuggerPromiseMicroTaskQueue;
private:
void
DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
@ -243,11 +241,11 @@ private:
virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
void TraceNativeGrayRoots(JSTracer* aTracer);
void AfterProcessMicrotask(uint32_t aRecursionDepth);
void AfterProcessMicrotasks();
public:
void ProcessStableStateQueue();
private:
void ProcessMetastableStateQueue(uint32_t aRecursionDepth);
void CleanupIDBTransactions(uint32_t aRecursionDepth);
public:
enum DeferredFinalizeType {
@ -306,8 +304,8 @@ public:
already_AddRefed<nsIException> GetPendingException() const;
void SetPendingException(nsIException* aException);
std::queue<nsCOMPtr<nsIRunnable>>& GetPromiseMicroTaskQueue();
std::queue<nsCOMPtr<nsIRunnable>>& GetDebuggerPromiseMicroTaskQueue();
std::queue<RefPtr<MicroTaskRunnable>>& GetMicroTaskQueue();
std::queue<RefPtr<MicroTaskRunnable>>& GetDebuggerMicroTaskQueue();
nsCycleCollectionParticipant* GCThingParticipant();
nsCycleCollectionParticipant* ZoneParticipant();
@ -346,53 +344,25 @@ public:
return JS::RootingContext::get(mJSContext);
}
bool MicroTaskCheckpointDisabled() const
void SetTargetedMicroTaskRecursionDepth(uint32_t aDepth)
{
return mDisableMicroTaskCheckpoint;
mTargetedMicroTaskRecursionDepth = aDepth;
}
void DisableMicroTaskCheckpoint(bool aDisable)
{
mDisableMicroTaskCheckpoint = aDisable;
}
class MOZ_RAII AutoDisableMicroTaskCheckpoint
{
public:
AutoDisableMicroTaskCheckpoint()
: mCCJSCX(CycleCollectedJSContext::Get())
{
mOldValue = mCCJSCX->MicroTaskCheckpointDisabled();
mCCJSCX->DisableMicroTaskCheckpoint(true);
}
~AutoDisableMicroTaskCheckpoint()
{
mCCJSCX->DisableMicroTaskCheckpoint(mOldValue);
}
CycleCollectedJSContext* mCCJSCX;
bool mOldValue;
};
protected:
JSContext* MaybeContext() const { return mJSContext; }
public:
// nsThread entrypoints
virtual void BeforeProcessTask(bool aMightBlock) { };
virtual void BeforeProcessTask(bool aMightBlock);
virtual void AfterProcessTask(uint32_t aRecursionDepth);
// microtask processor entry point
void AfterProcessMicrotask();
uint32_t RecursionDepth();
// Run in stable state (call through nsContentUtils)
void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
// This isn't in the spec at all yet, but this gets the behavior we want for IDB.
// Runs after the current microtask completes.
void RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable);
void AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction);
// Get the current thread's CycleCollectedJSContext. Returns null if there
// isn't one.
@ -411,7 +381,7 @@ public:
void PrepareWaitingZonesForGC();
// Queue an async microtask to the current main or worker thread.
virtual void DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable);
virtual void DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable);
// Call EnterMicroTask when you're entering JS execution.
// Usually the best way to do this is to use nsAutoMicroTask.
@ -442,9 +412,9 @@ public:
mMicroTaskLevel = aLevel;
}
void PerformMicroTaskCheckPoint();
bool PerformMicroTaskCheckPoint();
void DispatchMicroTaskRunnable(already_AddRefed<MicroTaskRunnable> aRunnable);
void PerformDebuggerMicroTaskCheckpoint();
// Storage for watching rejected promises waiting for some client to
// consume their rejection.
@ -460,6 +430,12 @@ public:
// in the last turn of the event loop.
JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mConsumedRejections;
// This is for the "outstanding rejected promises weak set" in the spec,
// https://html.spec.whatwg.org/multipage/webappapis.html#outstanding-rejected-promises-weak-set
typedef nsRefPtrHashtable<nsUint64HashKey, dom::Promise> PromiseHashtable;
PromiseHashtable mOutstandingRejections;
nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */ >> mUncaughtRejectionObservers;
private:
@ -483,24 +459,50 @@ private:
nsCOMPtr<nsIException> mPendingException;
nsThread* mOwningThread; // Manual refcounting to avoid include hell.
struct RunInMetastableStateData
struct PendingIDBTransactionData
{
nsCOMPtr<nsIRunnable> mRunnable;
nsCOMPtr<nsIRunnable> mTransaction;
uint32_t mRecursionDepth;
};
nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
nsTArray<RunInMetastableStateData> mMetastableStateEvents;
nsTArray<PendingIDBTransactionData> mPendingIDBTransactions;
uint32_t mBaseRecursionDepth;
bool mDoingStableStates;
bool mDisableMicroTaskCheckpoint;
// If set to none 0, microtasks will be processed only when recursion depth
// is the set value.
uint32_t mTargetedMicroTaskRecursionDepth;
uint32_t mMicroTaskLevel;
std::queue<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables;
std::queue<RefPtr<MicroTaskRunnable>> mDebuggerMicroTaskQueue;
uint32_t mMicroTaskRecursionDepth;
// This implements about-to-be-notified rejected promises list in the spec.
// https://html.spec.whatwg.org/multipage/webappapis.html#about-to-be-notified-rejected-promises-list
typedef nsTArray<RefPtr<dom::Promise>> PromiseArray;
PromiseArray mAboutToBeNotifiedRejectedPromises;
class NotifyUnhandledRejections final : public CancelableRunnable {
public:
NotifyUnhandledRejections(CycleCollectedJSContext* aCx,
PromiseArray&& aPromises)
: CancelableRunnable(),
mCx(aCx),
mUnhandledRejections(std::move(aPromises)) {}
NS_IMETHOD Run() final;
nsresult Cancel() final;
private:
CycleCollectedJSContext* mCx;
PromiseArray mUnhandledRejections;
};
OOMState mOutOfMemoryState;
OOMState mLargeAllocationFailureState;