mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 20:07:30 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
d4a80aa2c4
42 changed files with 538 additions and 364 deletions
|
|
@ -1383,6 +1383,30 @@ Animation::GetRenderedDocument() const
|
||||||
return mEffect->AsKeyframeEffect()->GetRenderedDocument();
|
return mEffect->AsKeyframeEffect()->GetRenderedDocument();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AsyncFinishNotification : public MicroTaskRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AsyncFinishNotification(Animation* aAnimation)
|
||||||
|
: MicroTaskRunnable()
|
||||||
|
, mAnimation(aAnimation)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void Run(AutoSlowOperation& aAso) override
|
||||||
|
{
|
||||||
|
mAnimation->DoFinishNotificationImmediately(this);
|
||||||
|
mAnimation = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool Suppressed() override
|
||||||
|
{
|
||||||
|
nsIGlobalObject* global = mAnimation->GetOwnerGlobal();
|
||||||
|
return global && global->IsInSyncOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefPtr<Animation> mAnimation;
|
||||||
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
|
Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
|
||||||
{
|
{
|
||||||
|
|
@ -1390,9 +1414,8 @@ Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
|
||||||
|
|
||||||
if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
|
if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
|
||||||
DoFinishNotificationImmediately();
|
DoFinishNotificationImmediately();
|
||||||
} else if (!mFinishNotificationTask.IsPending()) {
|
} else if (!mFinishNotificationTask) {
|
||||||
RefPtr<nsRunnableMethod<Animation>> runnable =
|
RefPtr<MicroTaskRunnable> runnable = new AsyncFinishNotification(this);
|
||||||
NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately);
|
|
||||||
context->DispatchToMicroTask(do_AddRef(runnable));
|
context->DispatchToMicroTask(do_AddRef(runnable));
|
||||||
mFinishNotificationTask = runnable.forget();
|
mFinishNotificationTask = runnable.forget();
|
||||||
}
|
}
|
||||||
|
|
@ -1415,9 +1438,13 @@ Animation::MaybeResolveFinishedPromise()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Animation::DoFinishNotificationImmediately()
|
Animation::DoFinishNotificationImmediately(MicroTaskRunnable* aAsync)
|
||||||
{
|
{
|
||||||
mFinishNotificationTask.Revoke();
|
if (aAsync && aAsync != mFinishNotificationTask) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mFinishNotificationTask = nullptr;
|
||||||
|
|
||||||
if (PlayState() != AnimationPlayState::Finished) {
|
if (PlayState() != AnimationPlayState::Finished) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "nsWrapperCache.h"
|
#include "nsWrapperCache.h"
|
||||||
#include "nsCycleCollectionParticipant.h"
|
#include "nsCycleCollectionParticipant.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
#include "mozilla/DOMEventTargetHelper.h"
|
#include "mozilla/DOMEventTargetHelper.h"
|
||||||
#include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel
|
#include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel
|
||||||
#include "mozilla/LinkedList.h"
|
#include "mozilla/LinkedList.h"
|
||||||
|
|
@ -42,6 +43,7 @@ class AnimValuesStyleRule;
|
||||||
|
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
|
class AsyncFinishNotification;
|
||||||
class CSSAnimation;
|
class CSSAnimation;
|
||||||
class CSSTransition;
|
class CSSTransition;
|
||||||
|
|
||||||
|
|
@ -378,7 +380,8 @@ protected:
|
||||||
void ResetFinishedPromise();
|
void ResetFinishedPromise();
|
||||||
void MaybeResolveFinishedPromise();
|
void MaybeResolveFinishedPromise();
|
||||||
void DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag);
|
void DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag);
|
||||||
void DoFinishNotificationImmediately();
|
friend class AsyncFinishNotification;
|
||||||
|
void DoFinishNotificationImmediately(MicroTaskRunnable* aAsync = nullptr);
|
||||||
void DispatchPlaybackEvent(const nsAString& aName);
|
void DispatchPlaybackEvent(const nsAString& aName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -446,7 +449,7 @@ protected:
|
||||||
// getAnimations() list.
|
// getAnimations() list.
|
||||||
bool mIsRelevant;
|
bool mIsRelevant;
|
||||||
|
|
||||||
nsRevocableEventPtr<nsRunnableMethod<Animation>> mFinishNotificationTask;
|
RefPtr<MicroTaskRunnable> mFinishNotificationTask;
|
||||||
// True if mFinished is resolved or would be resolved if mFinished has
|
// True if mFinished is resolved or would be resolved if mFinished has
|
||||||
// yet to be created. This is not set when mFinished is rejected since
|
// yet to be created. This is not set when mFinished is rejected since
|
||||||
// in that case mFinished is immediately reset to represent a new current
|
// in that case mFinished is immediately reset to represent a new current
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,13 @@ DocumentTimeline::WillRefresh(mozilla::TimeStamp aTime)
|
||||||
bool needsTicks = false;
|
bool needsTicks = false;
|
||||||
nsTArray<Animation*> animationsToRemove(mAnimations.Count());
|
nsTArray<Animation*> animationsToRemove(mAnimations.Count());
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events,
|
||||||
|
// step2.
|
||||||
|
// Note that this should be done before nsAutoAnimationMutationBatch. If
|
||||||
|
// PerformMicroTaskCheckpoint was called before nsAutoAnimationMutationBatch
|
||||||
|
// is destroyed, some mutation records might not be delivered in this
|
||||||
|
// checkpoint.
|
||||||
|
nsAutoMicroTask mt;
|
||||||
nsAutoAnimationMutationBatch mb(mDocument);
|
nsAutoAnimationMutationBatch mb(mDocument);
|
||||||
|
|
||||||
for (Animation* animation = mAnimationOrder.getFirst(); animation;
|
for (Animation* animation = mAnimationOrder.getFirst(); animation;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
#include "mozilla/dom/CustomElementRegistry.h"
|
#include "mozilla/dom/CustomElementRegistry.h"
|
||||||
|
|
||||||
#include "mozilla/CycleCollectedJSContext.h"
|
|
||||||
#include "mozilla/dom/CustomElementRegistryBinding.h"
|
#include "mozilla/dom/CustomElementRegistryBinding.h"
|
||||||
#include "mozilla/dom/HTMLElementBinding.h"
|
#include "mozilla/dom/HTMLElementBinding.h"
|
||||||
#include "mozilla/dom/WebComponentsBinding.h"
|
#include "mozilla/dom/WebComponentsBinding.h"
|
||||||
|
|
@ -1153,7 +1152,7 @@ CustomElementReactionsStack::Enqueue(Element* aElement,
|
||||||
|
|
||||||
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
||||||
RefPtr<BackupQueueMicroTask> bqmt = new BackupQueueMicroTask(this);
|
RefPtr<BackupQueueMicroTask> bqmt = new BackupQueueMicroTask(this);
|
||||||
context->DispatchMicroTaskRunnable(bqmt.forget());
|
context->DispatchToMicroTask(bqmt.forget());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "js/GCHashTable.h"
|
#include "js/GCHashTable.h"
|
||||||
#include "js/TypeDecls.h"
|
#include "js/TypeDecls.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
#include "mozilla/ErrorResult.h"
|
#include "mozilla/ErrorResult.h"
|
||||||
#include "mozilla/dom/BindingDeclarations.h"
|
#include "mozilla/dom/BindingDeclarations.h"
|
||||||
#include "mozilla/dom/FunctionBinding.h"
|
#include "mozilla/dom/FunctionBinding.h"
|
||||||
|
|
|
||||||
|
|
@ -5418,10 +5418,10 @@ nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable)
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
void
|
void
|
||||||
nsContentUtils::RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable)
|
nsContentUtils::AddPendingIDBTransaction(already_AddRefed<nsIRunnable> aTransaction)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
|
MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
|
||||||
CycleCollectedJSContext::Get()->RunInMetastableState(Move(aRunnable));
|
CycleCollectedJSContext::Get()->AddPendingIDBTransaction(Move(aTransaction));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -6472,9 +6472,16 @@ nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent)
|
||||||
contentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
|
contentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
|
||||||
|
|
||||||
// If there are 2 viewers for the current docshell, that
|
// If there are 2 viewers for the current docshell, that
|
||||||
// means the current document is a zombie document.
|
// means the current document may be a zombie document.
|
||||||
// Only navigate into the subdocument if it's not a zombie.
|
// While load and pageshow events are dispatched, zombie viewer is the old,
|
||||||
return !zombieViewer;
|
// to be hidden document.
|
||||||
|
if (zombieViewer) {
|
||||||
|
bool inOnLoad = false;
|
||||||
|
docShell->GetIsExecutingOnLoadHandler(&inOnLoad);
|
||||||
|
return inOnLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
||||||
|
|
@ -1766,17 +1766,12 @@ public:
|
||||||
*/
|
*/
|
||||||
static void RunInStableState(already_AddRefed<nsIRunnable> aRunnable);
|
static void RunInStableState(already_AddRefed<nsIRunnable> aRunnable);
|
||||||
|
|
||||||
/* Add a "synchronous section", in the form of an nsIRunnable run once the
|
/* Add a pending IDBTransaction to be cleaned up at the end of performing a
|
||||||
* event loop has reached a "metastable state". |aRunnable| must not cause any
|
* microtask checkpoint.
|
||||||
* queued events to be processed (i.e. must not spin the event loop).
|
* See the step of "Cleanup Indexed Database Transactions" in
|
||||||
* We've reached a metastable state when the currently executing task or
|
* https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
|
||||||
* microtask has finished. This is not specced at this time.
|
|
||||||
* In practice this runs aRunnable once the currently executing task or
|
|
||||||
* microtask finishes. If called multiple times per microtask, all the
|
|
||||||
* runnables will be executed, in the order in which RunInMetastableState()
|
|
||||||
* was called
|
|
||||||
*/
|
*/
|
||||||
static void RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable);
|
static void AddPendingIDBTransaction(already_AddRefed<nsIRunnable> aTransaction);
|
||||||
|
|
||||||
/* Process viewport META data. This gives us information for the scale
|
/* Process viewport META data. This gives us information for the scale
|
||||||
* and zoom of a page on mobile devices. We stick the information in
|
* and zoom of a page on mobile devices. We stick the information in
|
||||||
|
|
|
||||||
|
|
@ -623,7 +623,7 @@ nsDOMMutationObserver::QueueMutationObserverMicroTask()
|
||||||
|
|
||||||
RefPtr<MutationObserverMicroTask> momt =
|
RefPtr<MutationObserverMicroTask> momt =
|
||||||
new MutationObserverMicroTask();
|
new MutationObserverMicroTask();
|
||||||
ccjs->DispatchMicroTaskRunnable(momt.forget());
|
ccjs->DispatchToMicroTask(momt.forget());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -644,9 +644,8 @@ nsDOMMutationObserver::RescheduleForRun()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<MutationObserverMicroTask> momt =
|
RefPtr<MutationObserverMicroTask> momt = new MutationObserverMicroTask();
|
||||||
new MutationObserverMicroTask();
|
ccjs->DispatchToMicroTask(momt.forget());
|
||||||
ccjs->DispatchMicroTaskRunnable(momt.forget());
|
|
||||||
sScheduledMutationObservers = new AutoTArray<RefPtr<nsDOMMutationObserver>, 4>;
|
sScheduledMutationObservers = new AutoTArray<RefPtr<nsDOMMutationObserver>, 4>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -908,6 +908,7 @@ GK_ATOM(onreadsuccess, "onreadsuccess")
|
||||||
GK_ATOM(onready, "onready")
|
GK_ATOM(onready, "onready")
|
||||||
GK_ATOM(onreadystatechange, "onreadystatechange")
|
GK_ATOM(onreadystatechange, "onreadystatechange")
|
||||||
GK_ATOM(onreceived, "onreceived")
|
GK_ATOM(onreceived, "onreceived")
|
||||||
|
GK_ATOM(onrejectionhandled, "onrejectionhandled")
|
||||||
GK_ATOM(onremoteheld, "onremoteheld")
|
GK_ATOM(onremoteheld, "onremoteheld")
|
||||||
GK_ATOM(onremoteresumed, "onremoteresumed")
|
GK_ATOM(onremoteresumed, "onremoteresumed")
|
||||||
GK_ATOM(onrequestprogress, "onrequestprogress")
|
GK_ATOM(onrequestprogress, "onrequestprogress")
|
||||||
|
|
@ -951,6 +952,7 @@ GK_ATOM(ontransitionend, "ontransitionend")
|
||||||
GK_ATOM(ontransitionrun, "ontransitionrun")
|
GK_ATOM(ontransitionrun, "ontransitionrun")
|
||||||
GK_ATOM(ontransitionstart, "ontransitionstart")
|
GK_ATOM(ontransitionstart, "ontransitionstart")
|
||||||
GK_ATOM(onunderflow, "onunderflow")
|
GK_ATOM(onunderflow, "onunderflow")
|
||||||
|
GK_ATOM(onunhandledrejection, "onunhandledrejection")
|
||||||
GK_ATOM(onunload, "onunload")
|
GK_ATOM(onunload, "onunload")
|
||||||
GK_ATOM(onupdatefound, "onupdatefound")
|
GK_ATOM(onupdatefound, "onupdatefound")
|
||||||
GK_ATOM(onupdateready, "onupdateready")
|
GK_ATOM(onupdateready, "onupdateready")
|
||||||
|
|
|
||||||
|
|
@ -13011,12 +13011,6 @@ nsGlobalWindow::RunTimeoutHandler(Timeout* aTimeout,
|
||||||
// point anyway, and the script context should have already reported
|
// point anyway, and the script context should have already reported
|
||||||
// the script error in the usual way - so we just drop it.
|
// the script error in the usual way - so we just drop it.
|
||||||
|
|
||||||
// Since we might be processing more timeouts, go ahead and flush the promise
|
|
||||||
// queue now before we do that. We need to do that while we're still in our
|
|
||||||
// "running JS is safe" state (e.g. mRunningTimeout is set, timeout->mRunning
|
|
||||||
// is false).
|
|
||||||
Promise::PerformMicroTaskCheckpoint();
|
|
||||||
|
|
||||||
if (trackNestingLevel) {
|
if (trackNestingLevel) {
|
||||||
sNestingLevel = nestingLevel;
|
sNestingLevel = nestingLevel;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,6 @@ void nsIGlobalObject::QueueMicrotask(VoidFunction& aCallback) {
|
||||||
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
||||||
if (context) {
|
if (context) {
|
||||||
RefPtr<MicroTaskRunnable> mt = new QueuedMicrotask(this, aCallback);
|
RefPtr<MicroTaskRunnable> mt = new QueuedMicrotask(this, aCallback);
|
||||||
context->DispatchMicroTaskRunnable(mt.forget());
|
context->DispatchToMicroTask(mt.forget());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
#include "mozilla/Alignment.h"
|
#include "mozilla/Alignment.h"
|
||||||
#include "mozilla/Array.h"
|
#include "mozilla/Array.h"
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/CycleCollectedJSContext.h"
|
|
||||||
#include "mozilla/DeferredFinalize.h"
|
#include "mozilla/DeferredFinalize.h"
|
||||||
#include "mozilla/dom/BindingDeclarations.h"
|
#include "mozilla/dom/BindingDeclarations.h"
|
||||||
#include "mozilla/dom/CallbackObject.h"
|
#include "mozilla/dom/CallbackObject.h"
|
||||||
|
|
|
||||||
|
|
@ -78,12 +78,10 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
|
||||||
, mExceptionHandling(aExceptionHandling)
|
, mExceptionHandling(aExceptionHandling)
|
||||||
, mIsMainThread(NS_IsMainThread())
|
, mIsMainThread(NS_IsMainThread())
|
||||||
{
|
{
|
||||||
if (mIsMainThread) {
|
|
||||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||||
if (ccjs) {
|
if (ccjs) {
|
||||||
ccjs->EnterMicroTask();
|
ccjs->EnterMicroTask();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the caller's subject principal (if necessary) early, before we
|
// Compute the caller's subject principal (if necessary) early, before we
|
||||||
// do anything that might perturb the relevant state.
|
// do anything that might perturb the relevant state.
|
||||||
|
|
@ -290,13 +288,11 @@ CallbackObject::CallSetup::~CallSetup()
|
||||||
|
|
||||||
// It is important that this is the last thing we do, after leaving the
|
// It is important that this is the last thing we do, after leaving the
|
||||||
// compartment and undoing all our entry/incumbent script changes
|
// compartment and undoing all our entry/incumbent script changes
|
||||||
if (mIsMainThread) {
|
|
||||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||||
if (ccjs) {
|
if (ccjs) {
|
||||||
ccjs->LeaveMicroTask();
|
ccjs->LeaveMicroTask();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<nsISupports>
|
already_AddRefed<nsISupports>
|
||||||
CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
|
CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ def idlTypeNeedsCycleCollection(type):
|
||||||
type.isObject() or
|
type.isObject() or
|
||||||
type.isSpiderMonkeyInterface()):
|
type.isSpiderMonkeyInterface()):
|
||||||
return False
|
return False
|
||||||
elif type.isCallback() or type.isGeckoInterface():
|
elif type.isCallback() or type.isPromise() or type.isGeckoInterface():
|
||||||
return True
|
return True
|
||||||
elif type.isUnion():
|
elif type.isUnion():
|
||||||
return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
|
return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
|
||||||
|
|
|
||||||
|
|
@ -1065,12 +1065,8 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NS_SUCCEEDED(result)) {
|
if (NS_SUCCEEDED(result)) {
|
||||||
if (mIsMainThreadELM) {
|
nsAutoMicroTask mt;
|
||||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
|
||||||
if (ccjs) {
|
|
||||||
ccjs->EnterMicroTask();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// nsIDOMEvent::currentTarget is set in EventDispatcher.
|
// nsIDOMEvent::currentTarget is set in EventDispatcher.
|
||||||
if (listenerHolder.HasWebIDLCallback()) {
|
if (listenerHolder.HasWebIDLCallback()) {
|
||||||
ErrorResult rv;
|
ErrorResult rv;
|
||||||
|
|
@ -1080,12 +1076,6 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
|
||||||
} else {
|
} else {
|
||||||
result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent);
|
result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent);
|
||||||
}
|
}
|
||||||
if (mIsMainThreadELM) {
|
|
||||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
|
||||||
if (ccjs) {
|
|
||||||
ccjs->LeaveMicroTask();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -578,14 +578,14 @@ WINDOW_EVENT(popstate,
|
||||||
ePopState,
|
ePopState,
|
||||||
EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
|
EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
|
||||||
eBasicEventClass)
|
eBasicEventClass)
|
||||||
// Not supported yet
|
WINDOW_EVENT(rejectionhandled, eRejectionHandled,
|
||||||
// WINDOW_EVENT(redo)
|
EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass)
|
||||||
WINDOW_EVENT(storage,
|
WINDOW_EVENT(storage,
|
||||||
eStorage,
|
eStorage,
|
||||||
EventNameType_HTMLBodyOrFramesetOnly,
|
EventNameType_HTMLBodyOrFramesetOnly,
|
||||||
eBasicEventClass)
|
eBasicEventClass)
|
||||||
// Not supported yet
|
WINDOW_EVENT(unhandledrejection, eUnhandledRejection,
|
||||||
// WINDOW_EVENT(undo)
|
EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass)
|
||||||
WINDOW_EVENT(unload,
|
WINDOW_EVENT(unload,
|
||||||
eUnload,
|
eUnload,
|
||||||
(EventNameType_XUL | EventNameType_SVGSVG |
|
(EventNameType_XUL | EventNameType_SVGSVG |
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,11 @@
|
||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
#include "nsPIDOMWindow.h"
|
#include "nsPIDOMWindow.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
#include "mozilla/ClearOnShutdown.h"
|
||||||
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/Services.h"
|
#include "mozilla/Services.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
#include "mozilla/Preferences.h"
|
|
||||||
#include "mozilla/ClearOnShutdown.h"
|
|
||||||
#include "mozilla/WeakPtr.h"
|
#include "mozilla/WeakPtr.h"
|
||||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
#include "IndexedDatabase.h"
|
#include "IndexedDatabase.h"
|
||||||
#include "IndexedDatabaseInlines.h"
|
#include "IndexedDatabaseInlines.h"
|
||||||
#include "mozilla/BasicEvents.h"
|
#include "mozilla/BasicEvents.h"
|
||||||
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/TypeTraits.h"
|
#include "mozilla/TypeTraits.h"
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
|
|
@ -870,22 +871,26 @@ DispatchSuccessEvent(ResultHelper* aResultHelper,
|
||||||
IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
|
IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT_IF(transaction,
|
||||||
|
transaction->IsOpen() && !transaction->IsAborted());
|
||||||
|
|
||||||
bool dummy;
|
bool dummy;
|
||||||
nsresult rv = request->DispatchEvent(aEvent, &dummy);
|
nsresult rv = request->DispatchEvent(aEvent, &dummy);
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT_IF(transaction,
|
|
||||||
transaction->IsOpen() || transaction->IsAborted());
|
|
||||||
|
|
||||||
WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
|
WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
|
||||||
MOZ_ASSERT(internalEvent);
|
MOZ_ASSERT(internalEvent);
|
||||||
|
|
||||||
if (transaction &&
|
if (transaction &&
|
||||||
transaction->IsOpen() &&
|
transaction->IsOpen()) {
|
||||||
internalEvent->mFlags.mExceptionWasRaised) {
|
if (internalEvent->mFlags.mExceptionWasRaised) {
|
||||||
transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
|
transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
|
||||||
|
} else {
|
||||||
|
// To handle upgrade transaction.
|
||||||
|
transaction->Run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "mozilla/AutoRestore.h"
|
#include "mozilla/AutoRestore.h"
|
||||||
#include "mozilla/Casting.h"
|
#include "mozilla/Casting.h"
|
||||||
#include "mozilla/CheckedInt.h"
|
#include "mozilla/CheckedInt.h"
|
||||||
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
#include "mozilla/EndianUtils.h"
|
#include "mozilla/EndianUtils.h"
|
||||||
#include "mozilla/ErrorNames.h"
|
#include "mozilla/ErrorNames.h"
|
||||||
#include "mozilla/LazyIdleThread.h"
|
#include "mozilla/LazyIdleThread.h"
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ IDBFileHandle::Create(IDBMutableFile* aMutableFile,
|
||||||
MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
|
MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
|
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
|
||||||
nsContentUtils::RunInMetastableState(runnable.forget());
|
nsContentUtils::AddPendingIDBTransaction(runnable.forget());
|
||||||
|
|
||||||
fileHandle->SetCreating();
|
fileHandle->SetCreating();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -190,13 +190,9 @@ IDBTransaction::CreateVersionChange(
|
||||||
|
|
||||||
transaction->SetScriptOwner(aDatabase->GetScriptOwner());
|
transaction->SetScriptOwner(aDatabase->GetScriptOwner());
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
|
|
||||||
nsContentUtils::RunInMetastableState(runnable.forget());
|
|
||||||
|
|
||||||
transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
|
transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
|
||||||
transaction->mNextObjectStoreId = aNextObjectStoreId;
|
transaction->mNextObjectStoreId = aNextObjectStoreId;
|
||||||
transaction->mNextIndexId = aNextIndexId;
|
transaction->mNextIndexId = aNextIndexId;
|
||||||
transaction->mCreating = true;
|
|
||||||
|
|
||||||
aDatabase->RegisterTransaction(transaction);
|
aDatabase->RegisterTransaction(transaction);
|
||||||
transaction->mRegistered = true;
|
transaction->mRegistered = true;
|
||||||
|
|
@ -226,7 +222,7 @@ IDBTransaction::Create(JSContext* aCx, IDBDatabase* aDatabase,
|
||||||
transaction->SetScriptOwner(aDatabase->GetScriptOwner());
|
transaction->SetScriptOwner(aDatabase->GetScriptOwner());
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
|
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
|
||||||
nsContentUtils::RunInMetastableState(runnable.forget());
|
nsContentUtils::AddPendingIDBTransaction(runnable.forget());
|
||||||
|
|
||||||
transaction->mCreating = true;
|
transaction->mCreating = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include "nsPluginStreamListenerPeer.h"
|
#include "nsPluginStreamListenerPeer.h"
|
||||||
#include "nsIServiceManager.h"
|
#include "nsIServiceManager.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "nsPluginInstanceOwner.h"
|
#include "nsPluginInstanceOwner.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -508,110 +508,6 @@ Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise)
|
||||||
NS_DispatchToMainThread(new AsyncErrorReporter(xpcReport));
|
NS_DispatchToMainThread(new AsyncErrorReporter(xpcReport));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
Promise::PerformMicroTaskCheckpoint()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
|
||||||
|
|
||||||
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
|
||||||
|
|
||||||
// On the main thread, we always use the main promise micro task queue.
|
|
||||||
std::queue<nsCOMPtr<nsIRunnable>>& microtaskQueue =
|
|
||||||
context->GetPromiseMicroTaskQueue();
|
|
||||||
|
|
||||||
if (microtaskQueue.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoSlowOperation aso;
|
|
||||||
|
|
||||||
do {
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = microtaskQueue.front().forget();
|
|
||||||
MOZ_ASSERT(runnable);
|
|
||||||
|
|
||||||
// This function can re-enter, so we remove the element before calling.
|
|
||||||
microtaskQueue.pop();
|
|
||||||
nsresult rv = runnable->Run();
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
aso.CheckForInterrupt();
|
|
||||||
context->AfterProcessMicrotask();
|
|
||||||
} while (!microtaskQueue.empty());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Promise::PerformWorkerMicroTaskCheckpoint()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
|
|
||||||
|
|
||||||
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
|
||||||
if (!context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
// For a normal microtask checkpoint, we try to use the debugger microtask
|
|
||||||
// queue first. If the debugger queue is empty, we use the normal microtask
|
|
||||||
// queue instead.
|
|
||||||
std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
|
|
||||||
&context->GetDebuggerPromiseMicroTaskQueue();
|
|
||||||
|
|
||||||
if (microtaskQueue->empty()) {
|
|
||||||
microtaskQueue = &context->GetPromiseMicroTaskQueue();
|
|
||||||
if (microtaskQueue->empty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front().forget();
|
|
||||||
MOZ_ASSERT(runnable);
|
|
||||||
|
|
||||||
// This function can re-enter, so we remove the element before calling.
|
|
||||||
microtaskQueue->pop();
|
|
||||||
nsresult rv = runnable->Run();
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
context->AfterProcessMicrotask();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Promise::PerformWorkerDebuggerMicroTaskCheckpoint()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
|
|
||||||
|
|
||||||
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
|
||||||
if (!context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
// For a debugger microtask checkpoint, we always use the debugger microtask
|
|
||||||
// queue.
|
|
||||||
std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
|
|
||||||
&context->GetDebuggerPromiseMicroTaskQueue();
|
|
||||||
|
|
||||||
if (microtaskQueue->empty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front().forget();
|
|
||||||
MOZ_ASSERT(runnable);
|
|
||||||
|
|
||||||
// This function can re-enter, so we remove the element before calling.
|
|
||||||
microtaskQueue->pop();
|
|
||||||
nsresult rv = runnable->Run();
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
context->AfterProcessMicrotask();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
Promise::GlobalJSObject() const
|
Promise::GlobalJSObject() const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -104,14 +104,6 @@ public:
|
||||||
// specializations in the .cpp for
|
// specializations in the .cpp for
|
||||||
// the T values we support.
|
// the T values we support.
|
||||||
|
|
||||||
// Called by DOM to let us execute our callbacks. May be called recursively.
|
|
||||||
// Returns true if at least one microtask was processed.
|
|
||||||
static bool PerformMicroTaskCheckpoint();
|
|
||||||
|
|
||||||
static void PerformWorkerMicroTaskCheckpoint();
|
|
||||||
|
|
||||||
static void PerformWorkerDebuggerMicroTaskCheckpoint();
|
|
||||||
|
|
||||||
// WebIDL
|
// WebIDL
|
||||||
|
|
||||||
nsIGlobalObject* GetParentObject() const
|
nsIGlobalObject* GetParentObject() const
|
||||||
|
|
|
||||||
|
|
@ -217,8 +217,16 @@ PromiseDebugging::RemoveUncaughtRejectionObserver(GlobalObject&,
|
||||||
/* static */ void
|
/* static */ void
|
||||||
PromiseDebugging::AddUncaughtRejection(JS::HandleObject aPromise)
|
PromiseDebugging::AddUncaughtRejection(JS::HandleObject aPromise)
|
||||||
{
|
{
|
||||||
|
CycleCollectedJSContext* storage = CycleCollectedJSContext::Get();
|
||||||
|
auto& uncaught = storage->mUncaughtRejections;
|
||||||
|
auto& outstanding = storage->mOutstandingRejections;
|
||||||
|
|
||||||
|
// See 8.1.4.7 Unhandled promise rejections, Step 5.1.4.
|
||||||
|
RefPtr<Promise> promise = Promise::CreateFromExisting(xpc::NativeGlobal(aPromise), aPromise);
|
||||||
|
uint64_t promiseID = JS::GetPromiseID(aPromise);
|
||||||
|
outstanding.Put(promiseID, promise);
|
||||||
// This might OOM, but won't set a pending exception, so we'll just ignore it.
|
// This might OOM, but won't set a pending exception, so we'll just ignore it.
|
||||||
if (CycleCollectedJSContext::Get()->mUncaughtRejections.append(aPromise)) {
|
if (uncaught.append(aPromise)) {
|
||||||
FlushRejections::DispatchNeeded();
|
FlushRejections::DispatchNeeded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -226,15 +234,20 @@ PromiseDebugging::AddUncaughtRejection(JS::HandleObject aPromise)
|
||||||
/* void */ void
|
/* void */ void
|
||||||
PromiseDebugging::AddConsumedRejection(JS::HandleObject aPromise)
|
PromiseDebugging::AddConsumedRejection(JS::HandleObject aPromise)
|
||||||
{
|
{
|
||||||
|
CycleCollectedJSContext* storage = CycleCollectedJSContext::Get();
|
||||||
|
auto& uncaught = storage->mUncaughtRejections;
|
||||||
|
auto& outstanding = storage->mOutstandingRejections;
|
||||||
|
|
||||||
// If the promise is in our list of uncaught rejections, we haven't yet
|
// If the promise is in our list of uncaught rejections, we haven't yet
|
||||||
// reported it as unhandled. In that case, just remove it from the list
|
// reported it as unhandled. In that case, just remove it from the list
|
||||||
// and don't add it to the list of consumed rejections.
|
// and don't add it to the list of consumed rejections.
|
||||||
auto& uncaughtRejections = CycleCollectedJSContext::Get()->mUncaughtRejections;
|
uint64_t promiseID = JS::GetPromiseID(aPromise);
|
||||||
for (size_t i = 0; i < uncaughtRejections.length(); i++) {
|
outstanding.Remove(promiseID);
|
||||||
if (uncaughtRejections[i] == aPromise) {
|
for (size_t i = 0; i < uncaught.length(); i++) {
|
||||||
|
if (uncaught[i] == aPromise) {
|
||||||
// To avoid large amounts of memmoves, we don't shrink the vector here.
|
// To avoid large amounts of memmoves, we don't shrink the vector here.
|
||||||
// Instead, we filter out nullptrs when iterating over the vector later.
|
// Instead, we filter out nullptrs when iterating over the vector later.
|
||||||
uncaughtRejections[i].set(nullptr);
|
uncaught[i].set(nullptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -251,6 +264,7 @@ PromiseDebugging::FlushUncaughtRejectionsInternal()
|
||||||
|
|
||||||
auto& uncaught = storage->mUncaughtRejections;
|
auto& uncaught = storage->mUncaughtRejections;
|
||||||
auto& consumed = storage->mConsumedRejections;
|
auto& consumed = storage->mConsumedRejections;
|
||||||
|
auto& outstanding = storage->mOutstandingRejections;
|
||||||
|
|
||||||
AutoJSAPI jsapi;
|
AutoJSAPI jsapi;
|
||||||
jsapi.Init();
|
jsapi.Init();
|
||||||
|
|
@ -267,6 +281,10 @@ PromiseDebugging::FlushUncaughtRejectionsInternal()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up outstanding rejected promises weak set
|
||||||
|
uint64_t promiseID = JS::GetPromiseID(promise);
|
||||||
|
outstanding.Remove(promiseID);
|
||||||
|
|
||||||
for (size_t j = 0; j < observers.Length(); ++j) {
|
for (size_t j = 0; j < observers.Length(); ++j) {
|
||||||
RefPtr<UncaughtRejectionObserver> obs =
|
RefPtr<UncaughtRejectionObserver> obs =
|
||||||
static_cast<UncaughtRejectionObserver*>(observers[j].get());
|
static_cast<UncaughtRejectionObserver*>(observers[j].get());
|
||||||
|
|
@ -274,9 +292,13 @@ PromiseDebugging::FlushUncaughtRejectionsInternal()
|
||||||
IgnoredErrorResult err;
|
IgnoredErrorResult err;
|
||||||
obs->OnLeftUncaught(promise, err);
|
obs->OnLeftUncaught(promise, err);
|
||||||
}
|
}
|
||||||
|
// report error to console, unless marked by unhandledrejection event
|
||||||
|
bool reportError = !JS::GetPromiseIsReported(promise);
|
||||||
|
if (reportError) {
|
||||||
JSAutoCompartment ac(cx, promise);
|
JSAutoCompartment ac(cx, promise);
|
||||||
Promise::ReportRejectedPromise(cx, promise);
|
Promise::ReportRejectedPromise(cx, promise);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
storage->mUncaughtRejections.clear();
|
storage->mUncaughtRejections.clear();
|
||||||
|
|
||||||
// Notify observers of consumed Promise.
|
// Notify observers of consumed Promise.
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@
|
||||||
#include "ImportManager.h"
|
#include "ImportManager.h"
|
||||||
#include "mozilla/dom/EncodingUtils.h"
|
#include "mozilla/dom/EncodingUtils.h"
|
||||||
#include "mozilla/ConsoleReportCollector.h"
|
#include "mozilla/ConsoleReportCollector.h"
|
||||||
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
|
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
|
|
|
||||||
|
|
@ -828,8 +828,6 @@ AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMP
|
||||||
AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
|
AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
|
||||||
: AutoJSAPI()
|
: AutoJSAPI()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
|
|
@ -838,9 +836,12 @@ AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMP
|
||||||
void
|
void
|
||||||
AutoSlowOperation::CheckForInterrupt()
|
AutoSlowOperation::CheckForInterrupt()
|
||||||
{
|
{
|
||||||
|
// For now we support only main thread!
|
||||||
|
if (mIsMainThread) {
|
||||||
// JS_CheckForInterrupt expects us to be in a compartment.
|
// JS_CheckForInterrupt expects us to be in a compartment.
|
||||||
JSAutoCompartment ac(cx(), xpc::UnprivilegedJunkScope());
|
JSAutoCompartment ac(cx(), xpc::UnprivilegedJunkScope());
|
||||||
JS_CheckForInterrupt(cx());
|
JS_CheckForInterrupt(cx());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,6 @@ protected:
|
||||||
// AutoJSAPI, so Init must NOT be called on subclasses that use this.
|
// AutoJSAPI, so Init must NOT be called on subclasses that use this.
|
||||||
AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, Type aType);
|
AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, Type aType);
|
||||||
|
|
||||||
private:
|
|
||||||
mozilla::Maybe<JSAutoRequest> mAutoRequest;
|
mozilla::Maybe<JSAutoRequest> mAutoRequest;
|
||||||
mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
|
mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
|
||||||
JSContext *mCx;
|
JSContext *mCx;
|
||||||
|
|
@ -307,6 +306,7 @@ private:
|
||||||
bool mIsMainThread;
|
bool mIsMainThread;
|
||||||
Maybe<JS::WarningReporter> mOldWarningReporter;
|
Maybe<JS::WarningReporter> mOldWarningReporter;
|
||||||
|
|
||||||
|
private:
|
||||||
void InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
|
void InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
|
||||||
JSContext* aCx, bool aIsMainThread);
|
JSContext* aCx, bool aIsMainThread);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,9 @@ interface WindowEventHandlers {
|
||||||
attribute EventHandler onpagehide;
|
attribute EventHandler onpagehide;
|
||||||
attribute EventHandler onpageshow;
|
attribute EventHandler onpageshow;
|
||||||
attribute EventHandler onpopstate;
|
attribute EventHandler onpopstate;
|
||||||
|
attribute EventHandler onrejectionhandled;
|
||||||
attribute EventHandler onstorage;
|
attribute EventHandler onstorage;
|
||||||
|
attribute EventHandler onunhandledrejection;
|
||||||
attribute EventHandler onunload;
|
attribute EventHandler onunload;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
22
dom/webidl/PromiseRejectionEvent.webidl
Normal file
22
dom/webidl/PromiseRejectionEvent.webidl
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* The origin of this IDL file is
|
||||||
|
* https://html.spec.whatwg.org/multipage/webappapis.html#the-promiserejectionevent-interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
[Constructor(DOMString type, PromiseRejectionEventInit eventInitDict),
|
||||||
|
Exposed=(Window,Worker)]
|
||||||
|
interface PromiseRejectionEvent : Event
|
||||||
|
{
|
||||||
|
[BinaryName="rejectedPromise"]
|
||||||
|
readonly attribute Promise<any> promise;
|
||||||
|
readonly attribute any reason;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary PromiseRejectionEventInit : EventInit {
|
||||||
|
required Promise<any> promise;
|
||||||
|
any reason;
|
||||||
|
};
|
||||||
|
|
@ -25,6 +25,8 @@ interface WorkerGlobalScope : EventTarget {
|
||||||
|
|
||||||
attribute EventHandler onoffline;
|
attribute EventHandler onoffline;
|
||||||
attribute EventHandler ononline;
|
attribute EventHandler ononline;
|
||||||
|
attribute EventHandler onrejectionhandled;
|
||||||
|
attribute EventHandler onunhandledrejection;
|
||||||
// also has additional members in a partial interface
|
// also has additional members in a partial interface
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -691,6 +691,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
|
||||||
'PopStateEvent.webidl',
|
'PopStateEvent.webidl',
|
||||||
'PopupBlockedEvent.webidl',
|
'PopupBlockedEvent.webidl',
|
||||||
'ProgressEvent.webidl',
|
'ProgressEvent.webidl',
|
||||||
|
'PromiseRejectionEvent.webidl',
|
||||||
'RecordErrorEvent.webidl',
|
'RecordErrorEvent.webidl',
|
||||||
'ScrollViewChangeEvent.webidl',
|
'ScrollViewChangeEvent.webidl',
|
||||||
'ServiceWorkerMessageEvent.webidl',
|
'ServiceWorkerMessageEvent.webidl',
|
||||||
|
|
|
||||||
|
|
@ -1007,6 +1007,10 @@ public:
|
||||||
: mWorkerPrivate(aWorkerPrivate)
|
: mWorkerPrivate(aWorkerPrivate)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aWorkerPrivate);
|
MOZ_ASSERT(aWorkerPrivate);
|
||||||
|
// Magical number 2. Workers have the base recursion depth 1, and normal
|
||||||
|
// runnables run at level 2, and we don't want to process microtasks
|
||||||
|
// at any other level.
|
||||||
|
SetTargetedMicroTaskRecursionDepth(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
~WorkerJSContext()
|
~WorkerJSContext()
|
||||||
|
|
@ -1092,26 +1096,14 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void AfterProcessTask(uint32_t aRecursionDepth) override
|
virtual void DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable) override
|
||||||
{
|
{
|
||||||
// Only perform the Promise microtask checkpoint on the outermost event
|
RefPtr<MicroTaskRunnable> runnable(aRunnable);
|
||||||
// loop. Don't run it, for example, during sync XHR or importScripts.
|
|
||||||
if (aRecursionDepth == 2) {
|
|
||||||
CycleCollectedJSContext::AfterProcessTask(aRecursionDepth);
|
|
||||||
} else if (aRecursionDepth > 2) {
|
|
||||||
AutoDisableMicroTaskCheckpoint disableMicroTaskCheckpoint;
|
|
||||||
CycleCollectedJSContext::AfterProcessTask(aRecursionDepth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable) override
|
|
||||||
{
|
|
||||||
RefPtr<nsIRunnable> runnable(aRunnable);
|
|
||||||
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
MOZ_ASSERT(!NS_IsMainThread());
|
||||||
MOZ_ASSERT(runnable);
|
MOZ_ASSERT(runnable);
|
||||||
|
|
||||||
std::queue<nsCOMPtr<nsIRunnable>>* microTaskQueue = nullptr;
|
std::queue<RefPtr<MicroTaskRunnable>>* microTaskQueue = nullptr;
|
||||||
|
|
||||||
JSContext* cx = GetCurrentThreadJSContext();
|
JSContext* cx = GetCurrentThreadJSContext();
|
||||||
NS_ASSERTION(cx, "This should never be null!");
|
NS_ASSERTION(cx, "This should never be null!");
|
||||||
|
|
@ -1120,15 +1112,15 @@ public:
|
||||||
NS_ASSERTION(global, "This should never be null!");
|
NS_ASSERTION(global, "This should never be null!");
|
||||||
|
|
||||||
// On worker threads, if the current global is the worker global, we use the
|
// On worker threads, if the current global is the worker global, we use the
|
||||||
// main promise micro task queue. Otherwise, the current global must be
|
// main micro task queue. Otherwise, the current global must be
|
||||||
// either the debugger global or a debugger sandbox, and we use the debugger
|
// either the debugger global or a debugger sandbox, and we use the debugger
|
||||||
// promise micro task queue instead.
|
// micro task queue instead.
|
||||||
if (IsWorkerGlobal(global)) {
|
if (IsWorkerGlobal(global)) {
|
||||||
microTaskQueue = &mPromiseMicroTaskQueue;
|
microTaskQueue = &GetMicroTaskQueue();
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(IsDebuggerGlobal(global) || IsDebuggerSandbox(global));
|
MOZ_ASSERT(IsDebuggerGlobal(global) || IsDebuggerSandbox(global));
|
||||||
|
|
||||||
microTaskQueue = &mDebuggerPromiseMicroTaskQueue;
|
microTaskQueue = &GetDebuggerMicroTaskQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
microTaskQueue->push(runnable.forget());
|
microTaskQueue->push(runnable.forget());
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/ContentEvents.h"
|
#include "mozilla/ContentEvents.h"
|
||||||
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
#include "mozilla/EventDispatcher.h"
|
#include "mozilla/EventDispatcher.h"
|
||||||
#include "mozilla/Likely.h"
|
#include "mozilla/Likely.h"
|
||||||
#include "mozilla/LoadContext.h"
|
#include "mozilla/LoadContext.h"
|
||||||
|
|
@ -4815,8 +4816,10 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
||||||
static_cast<nsIRunnable*>(runnable)->Run();
|
static_cast<nsIRunnable*>(runnable)->Run();
|
||||||
runnable->Release();
|
runnable->Release();
|
||||||
|
|
||||||
// Flush the promise queue.
|
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||||
Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
|
if (ccjs) {
|
||||||
|
ccjs->PerformDebuggerMicroTaskCheckpoint();
|
||||||
|
}
|
||||||
|
|
||||||
if (debuggerRunnablesPending) {
|
if (debuggerRunnablesPending) {
|
||||||
WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
|
WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
|
||||||
|
|
@ -5802,8 +5805,12 @@ WorkerPrivate::EnterDebuggerEventLoop()
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(mMutex);
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
|
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
||||||
|
std::queue<RefPtr<MicroTaskRunnable>>& debuggerMtQueue =
|
||||||
|
context->GetDebuggerMicroTaskQueue();
|
||||||
while (mControlQueue.IsEmpty() &&
|
while (mControlQueue.IsEmpty() &&
|
||||||
!(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty())) {
|
!(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
|
||||||
|
debuggerMtQueue.empty()) {
|
||||||
WaitForWorkerEvents();
|
WaitForWorkerEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5812,6 +5819,11 @@ WorkerPrivate::EnterDebuggerEventLoop()
|
||||||
// XXXkhuey should we abort JS on the stack here if we got Abort above?
|
// XXXkhuey should we abort JS on the stack here if we got Abort above?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
||||||
|
if (context) {
|
||||||
|
context->PerformDebuggerMicroTaskCheckpoint();
|
||||||
|
}
|
||||||
|
|
||||||
if (debuggerRunnablesPending) {
|
if (debuggerRunnablesPending) {
|
||||||
// Start the periodic GC timer if it is not already running.
|
// Start the periodic GC timer if it is not already running.
|
||||||
SetGCTimerMode(PeriodicTimer);
|
SetGCTimerMode(PeriodicTimer);
|
||||||
|
|
@ -5828,8 +5840,10 @@ WorkerPrivate::EnterDebuggerEventLoop()
|
||||||
static_cast<nsIRunnable*>(runnable)->Run();
|
static_cast<nsIRunnable*>(runnable)->Run();
|
||||||
runnable->Release();
|
runnable->Release();
|
||||||
|
|
||||||
// Flush the promise queue.
|
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||||
Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
|
if (ccjs) {
|
||||||
|
ccjs->PerformDebuggerMicroTaskCheckpoint();
|
||||||
|
}
|
||||||
|
|
||||||
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
||||||
if (JS::CurrentGlobalOrNull(cx)) {
|
if (JS::CurrentGlobalOrNull(cx)) {
|
||||||
|
|
@ -6256,8 +6270,8 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
|
||||||
|
|
||||||
RefPtr<Function> callback = info->mHandler->GetCallback();
|
RefPtr<Function> callback = info->mHandler->GetCallback();
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
// scope for the AutoEntryScript, so it comes off the stack before we do
|
nsAutoMicroTask mt;
|
||||||
// Promise::PerformMicroTaskCheckpoint.
|
|
||||||
AutoEntryScript aes(global, reason, false);
|
AutoEntryScript aes(global, reason, false);
|
||||||
|
|
||||||
// Evaluate the timeout expression.
|
// Evaluate the timeout expression.
|
||||||
|
|
@ -6292,10 +6306,6 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
|
||||||
rv.SuppressException();
|
rv.SuppressException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since we might be processing more timeouts, go ahead and flush
|
|
||||||
// the promise queue now before we do that.
|
|
||||||
Promise::PerformWorkerMicroTaskCheckpoint();
|
|
||||||
|
|
||||||
NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
|
NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,8 @@ public:
|
||||||
|
|
||||||
IMPL_EVENT_HANDLER(online)
|
IMPL_EVENT_HANDLER(online)
|
||||||
IMPL_EVENT_HANDLER(offline)
|
IMPL_EVENT_HANDLER(offline)
|
||||||
|
IMPL_EVENT_HANDLER(rejectionhandled)
|
||||||
|
IMPL_EVENT_HANDLER(unhandledrejection)
|
||||||
|
|
||||||
void
|
void
|
||||||
Dump(const Optional<nsAString>& aString) const;
|
Dump(const Optional<nsAString>& aString) const;
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,10 @@ class PromiseObject : public NativeObject
|
||||||
int32_t flags = getFixedSlot(PromiseSlot_Flags).toInt32();
|
int32_t flags = getFixedSlot(PromiseSlot_Flags).toInt32();
|
||||||
setFixedSlot(PromiseSlot_Flags, Int32Value(flags | PROMISE_FLAG_REPORTED));
|
setFixedSlot(PromiseSlot_Flags, Int32Value(flags | PROMISE_FLAG_REPORTED));
|
||||||
}
|
}
|
||||||
|
bool isReported() {
|
||||||
|
MOZ_ASSERT(isUnhandled());
|
||||||
|
return flags() & PROMISE_FLAG_REPORTED;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -4949,12 +4949,31 @@ JS::GetPromiseResult(JS::HandleObject promiseObj)
|
||||||
return promise->state() == JS::PromiseState::Fulfilled ? promise->value() : promise->reason();
|
return promise->state() == JS::PromiseState::Fulfilled ? promise->value() : promise->reason();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS_PUBLIC_API(bool)
|
||||||
|
JS::GetPromiseIsHandled(JS::HandleObject promise)
|
||||||
|
{
|
||||||
|
PromiseObject* promiseObj = &promise->as<PromiseObject>();
|
||||||
|
return !promiseObj->isUnhandled();
|
||||||
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(JSObject*)
|
JS_PUBLIC_API(JSObject*)
|
||||||
JS::GetPromiseAllocationSite(JS::HandleObject promise)
|
JS::GetPromiseAllocationSite(JS::HandleObject promise)
|
||||||
{
|
{
|
||||||
return promise->as<PromiseObject>().allocationSite();
|
return promise->as<PromiseObject>().allocationSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS_PUBLIC_API(bool)
|
||||||
|
JS::GetPromiseIsReported(JS::HandleObject promise)
|
||||||
|
{
|
||||||
|
return promise->as<PromiseObject>().isReported();
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_PUBLIC_API(void)
|
||||||
|
JS::MarkPromiseRejectionReported(JS::HandleObject promise)
|
||||||
|
{
|
||||||
|
return promise->as<PromiseObject>().markAsReported();
|
||||||
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(JSObject*)
|
JS_PUBLIC_API(JSObject*)
|
||||||
JS::GetPromiseResolutionSite(JS::HandleObject promise)
|
JS::GetPromiseResolutionSite(JS::HandleObject promise)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4611,6 +4611,27 @@ GetPromiseID(JS::HandleObject promise);
|
||||||
extern JS_PUBLIC_API(JS::Value)
|
extern JS_PUBLIC_API(JS::Value)
|
||||||
GetPromiseResult(JS::HandleObject promise);
|
GetPromiseResult(JS::HandleObject promise);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given promise's rejection is already handled or not.
|
||||||
|
*
|
||||||
|
* The caller must check the given promise is rejected before checking it's
|
||||||
|
* handled or not.
|
||||||
|
*/
|
||||||
|
extern JS_PUBLIC_API(bool)
|
||||||
|
GetPromiseIsHandled(JS::HandleObject promise);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given promise's rejection is reported or not.
|
||||||
|
*/
|
||||||
|
extern JS_PUBLIC_API(bool)
|
||||||
|
GetPromiseIsReported(JS::HandleObject promise);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark the given promise's rejection as already reported.
|
||||||
|
*/
|
||||||
|
extern JS_PUBLIC_API(void)
|
||||||
|
MarkPromiseRejectionReported(JS::HandleObject promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a js::SavedFrame linked list of the stack that lead to the given
|
* Returns a js::SavedFrame linked list of the stack that lead to the given
|
||||||
* Promise's allocation.
|
* Promise's allocation.
|
||||||
|
|
|
||||||
|
|
@ -3485,21 +3485,6 @@ XPCJSContext::BeforeProcessTask(bool aMightBlock)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
// If ProcessNextEvent was called during a Promise "then" 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) {
|
|
||||||
if (Promise::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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the slow script timer.
|
// Start the slow script timer.
|
||||||
mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
|
mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
|
||||||
mSlowScriptSecondHalf = false;
|
mSlowScriptSecondHalf = false;
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,9 @@ NS_EVENT_MESSAGE(eImageAbort)
|
||||||
NS_EVENT_MESSAGE(eLoadError)
|
NS_EVENT_MESSAGE(eLoadError)
|
||||||
NS_EVENT_MESSAGE(eLoadEnd)
|
NS_EVENT_MESSAGE(eLoadEnd)
|
||||||
NS_EVENT_MESSAGE(ePopState)
|
NS_EVENT_MESSAGE(ePopState)
|
||||||
|
NS_EVENT_MESSAGE(eRejectionHandled)
|
||||||
NS_EVENT_MESSAGE(eStorage)
|
NS_EVENT_MESSAGE(eStorage)
|
||||||
|
NS_EVENT_MESSAGE(eUnhandledRejection)
|
||||||
NS_EVENT_MESSAGE(eBeforeUnload)
|
NS_EVENT_MESSAGE(eBeforeUnload)
|
||||||
NS_EVENT_MESSAGE(eReadyStateChange)
|
NS_EVENT_MESSAGE(eReadyStateChange)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
#include "mozilla/CycleCollectedJSContext.h"
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
|
#include "mozilla/AsyncEventDispatcher.h"
|
||||||
#include "mozilla/AutoRestore.h"
|
#include "mozilla/AutoRestore.h"
|
||||||
#include "mozilla/Move.h"
|
#include "mozilla/Move.h"
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
|
|
@ -69,6 +70,8 @@
|
||||||
#include "mozilla/dom/Promise.h"
|
#include "mozilla/dom/Promise.h"
|
||||||
#include "mozilla/dom/PromiseBinding.h"
|
#include "mozilla/dom/PromiseBinding.h"
|
||||||
#include "mozilla/dom/PromiseDebugging.h"
|
#include "mozilla/dom/PromiseDebugging.h"
|
||||||
|
#include "mozilla/dom/PromiseRejectionEvent.h"
|
||||||
|
#include "mozilla/dom/PromiseRejectionEventBinding.h"
|
||||||
#include "mozilla/dom/ScriptSettings.h"
|
#include "mozilla/dom/ScriptSettings.h"
|
||||||
#include "jsprf.h"
|
#include "jsprf.h"
|
||||||
#include "js/Debug.h"
|
#include "js/Debug.h"
|
||||||
|
|
@ -437,7 +440,7 @@ CycleCollectedJSContext::CycleCollectedJSContext()
|
||||||
, mPrevGCNurseryCollectionCallback(nullptr)
|
, mPrevGCNurseryCollectionCallback(nullptr)
|
||||||
, mJSHolders(256)
|
, mJSHolders(256)
|
||||||
, mDoingStableStates(false)
|
, mDoingStableStates(false)
|
||||||
, mDisableMicroTaskCheckpoint(false)
|
, mTargetedMicroTaskRecursionDepth(0)
|
||||||
, mMicroTaskLevel(0)
|
, mMicroTaskLevel(0)
|
||||||
, mMicroTaskRecursionDepth(0)
|
, mMicroTaskRecursionDepth(0)
|
||||||
, mOutOfMemoryState(OOMState::OK)
|
, mOutOfMemoryState(OOMState::OK)
|
||||||
|
|
@ -458,8 +461,8 @@ CycleCollectedJSContext::~CycleCollectedJSContext()
|
||||||
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
|
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
|
||||||
|
|
||||||
// Last chance to process any events.
|
// Last chance to process any events.
|
||||||
ProcessMetastableStateQueue(mBaseRecursionDepth);
|
CleanupIDBTransactions(mBaseRecursionDepth);
|
||||||
MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
|
MOZ_ASSERT(mPendingIDBTransactions.IsEmpty());
|
||||||
|
|
||||||
ProcessStableStateQueue();
|
ProcessStableStateQueue();
|
||||||
MOZ_ASSERT(mStableStateEvents.IsEmpty());
|
MOZ_ASSERT(mStableStateEvents.IsEmpty());
|
||||||
|
|
@ -467,8 +470,8 @@ CycleCollectedJSContext::~CycleCollectedJSContext()
|
||||||
// Clear mPendingException first, since it might be cycle collected.
|
// Clear mPendingException first, since it might be cycle collected.
|
||||||
mPendingException = nullptr;
|
mPendingException = nullptr;
|
||||||
|
|
||||||
MOZ_ASSERT(mDebuggerPromiseMicroTaskQueue.empty());
|
MOZ_ASSERT(mDebuggerMicroTaskQueue.empty());
|
||||||
MOZ_ASSERT(mPromiseMicroTaskQueue.empty());
|
MOZ_ASSERT(mPendingMicroTaskRunnables.empty());
|
||||||
|
|
||||||
mUncaughtRejections.reset();
|
mUncaughtRejections.reset();
|
||||||
mConsumedRejections.reset();
|
mConsumedRejections.reset();
|
||||||
|
|
@ -921,7 +924,7 @@ CycleCollectedJSContext::LargeAllocationFailureCallback(void* aData)
|
||||||
self->OnLargeAllocationFailure();
|
self->OnLargeAllocationFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
class PromiseJobRunnable final : public Runnable
|
class PromiseJobRunnable final : public MicroTaskRunnable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PromiseJobRunnable(JS::HandleObject aCallback, JS::HandleObject aAllocationSite,
|
PromiseJobRunnable(JS::HandleObject aCallback, JS::HandleObject aAllocationSite,
|
||||||
|
|
@ -935,14 +938,20 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NS_IMETHOD
|
virtual void Run(AutoSlowOperation& aAso) override
|
||||||
Run() override
|
|
||||||
{
|
{
|
||||||
nsIGlobalObject* global = xpc::NativeGlobal(mCallback->CallbackPreserveColor());
|
nsIGlobalObject* global = xpc::NativeGlobal(mCallback->CallbackPreserveColor());
|
||||||
if (global && !global->IsDying()) {
|
if (global && !global->IsDying()) {
|
||||||
mCallback->Call("promise callback");
|
mCallback->Call("promise callback");
|
||||||
|
aAso.CheckForInterrupt();
|
||||||
}
|
}
|
||||||
return NS_OK;
|
}
|
||||||
|
|
||||||
|
virtual bool Suppressed() override
|
||||||
|
{
|
||||||
|
nsIGlobalObject* global =
|
||||||
|
xpc::NativeGlobal(mCallback->CallbackPreserveColor());
|
||||||
|
return global && global->IsInSyncOperation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -976,7 +985,7 @@ CycleCollectedJSContext::EnqueuePromiseJobCallback(JSContext* aCx,
|
||||||
if (aIncumbentGlobal) {
|
if (aIncumbentGlobal) {
|
||||||
global = xpc::NativeGlobal(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());
|
self->DispatchToMicroTask(runnable.forget());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -988,15 +997,65 @@ CycleCollectedJSContext::PromiseRejectionTrackerCallback(JSContext* aCx,
|
||||||
PromiseRejectionHandlingState state,
|
PromiseRejectionHandlingState state,
|
||||||
void* aData)
|
void* aData)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
|
||||||
CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
|
CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
|
||||||
#endif // DEBUG
|
|
||||||
MOZ_ASSERT(aCx == self->Context());
|
MOZ_ASSERT(aCx == self->Context());
|
||||||
MOZ_ASSERT(Get() == self);
|
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) {
|
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 {
|
} 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);
|
PromiseDebugging::AddConsumedRejection(aPromise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1175,18 +1234,18 @@ CycleCollectedJSContext::SetPendingException(nsIException* aException)
|
||||||
mPendingException = aException;
|
mPendingException = aException;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::queue<nsCOMPtr<nsIRunnable>>&
|
std::queue<RefPtr<MicroTaskRunnable>>&
|
||||||
CycleCollectedJSContext::GetPromiseMicroTaskQueue()
|
CycleCollectedJSContext::GetMicroTaskQueue()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mJSContext);
|
MOZ_ASSERT(mJSContext);
|
||||||
return mPromiseMicroTaskQueue;
|
return mPendingMicroTaskRunnables;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::queue<nsCOMPtr<nsIRunnable>>&
|
std::queue<RefPtr<MicroTaskRunnable>>&
|
||||||
CycleCollectedJSContext::GetDebuggerPromiseMicroTaskQueue()
|
CycleCollectedJSContext::GetDebuggerMicroTaskQueue()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mJSContext);
|
MOZ_ASSERT(mJSContext);
|
||||||
return mDebuggerPromiseMicroTaskQueue;
|
return mDebuggerMicroTaskQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCycleCollectionParticipant*
|
nsCycleCollectionParticipant*
|
||||||
|
|
@ -1345,24 +1404,24 @@ CycleCollectedJSContext::ProcessStableStateQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CycleCollectedJSContext::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
|
CycleCollectedJSContext::CleanupIDBTransactions(uint32_t aRecursionDepth)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mJSContext);
|
MOZ_ASSERT(mJSContext);
|
||||||
MOZ_RELEASE_ASSERT(!mDoingStableStates);
|
MOZ_RELEASE_ASSERT(!mDoingStableStates);
|
||||||
mDoingStableStates = true;
|
mDoingStableStates = true;
|
||||||
|
|
||||||
nsTArray<RunInMetastableStateData> localQueue = Move(mMetastableStateEvents);
|
nsTArray<PendingIDBTransactionData> localQueue = Move(mPendingIDBTransactions);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < localQueue.Length(); ++i)
|
for (uint32_t i = 0; i < localQueue.Length(); ++i)
|
||||||
{
|
{
|
||||||
RunInMetastableStateData& data = localQueue[i];
|
PendingIDBTransactionData& data = localQueue[i];
|
||||||
if (data.mRecursionDepth != aRecursionDepth) {
|
if (data.mRecursionDepth != aRecursionDepth) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIRunnable> runnable = data.mRunnable.forget();
|
nsCOMPtr<nsIRunnable> transaction = data.mTransaction.forget();
|
||||||
runnable->Run();
|
transaction->Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
localQueue.RemoveElementAt(i--);
|
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,
|
// If the queue has events in it now, they were added from something we called,
|
||||||
// so they belong at the end of the queue.
|
// so they belong at the end of the queue.
|
||||||
localQueue.AppendElements(mMetastableStateEvents);
|
localQueue.AppendElements(mPendingIDBTransactions);
|
||||||
localQueue.SwapElements(mMetastableStateEvents);
|
localQueue.SwapElements(mPendingIDBTransactions);
|
||||||
mDoingStableStates = false;
|
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
|
void
|
||||||
CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
|
CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
|
||||||
{
|
{
|
||||||
|
|
@ -1382,39 +1457,27 @@ CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
|
||||||
|
|
||||||
// See HTML 6.1.4.2 Processing model
|
// 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.
|
// Step 4.1: Execute microtasks.
|
||||||
if (!mDisableMicroTaskCheckpoint) {
|
|
||||||
PerformMicroTaskCheckPoint();
|
PerformMicroTaskCheckPoint();
|
||||||
if (NS_IsMainThread()) {
|
|
||||||
Promise::PerformMicroTaskCheckpoint();
|
|
||||||
} else {
|
|
||||||
Promise::PerformWorkerMicroTaskCheckpoint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4.2 Execute any events that were waiting for a stable state.
|
// Step 4.2 Execute any events that were waiting for a stable state.
|
||||||
ProcessStableStateQueue();
|
ProcessStableStateQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CycleCollectedJSContext::AfterProcessMicrotask()
|
CycleCollectedJSContext::AfterProcessMicrotasks()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mJSContext);
|
MOZ_ASSERT(mJSContext);
|
||||||
AfterProcessMicrotask(RecursionDepth());
|
// 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:
|
||||||
void
|
// https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
|
||||||
CycleCollectedJSContext::AfterProcessMicrotask(uint32_t aRecursionDepth)
|
CleanupIDBTransactions(RecursionDepth());
|
||||||
{
|
|
||||||
MOZ_ASSERT(mJSContext);
|
|
||||||
|
|
||||||
// Between microtasks, execute any events that were waiting for a microtask
|
|
||||||
// to complete.
|
|
||||||
ProcessMetastableStateQueue(aRecursionDepth);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
|
|
@ -1431,12 +1494,12 @@ CycleCollectedJSContext::RunInStableState(already_AddRefed<nsIRunnable>&& aRunna
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CycleCollectedJSContext::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable)
|
CycleCollectedJSContext::AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mJSContext);
|
MOZ_ASSERT(mJSContext);
|
||||||
|
|
||||||
RunInMetastableStateData data;
|
PendingIDBTransactionData data;
|
||||||
data.mRunnable = aRunnable;
|
data.mTransaction = aTransaction;
|
||||||
|
|
||||||
MOZ_ASSERT(mOwningThread);
|
MOZ_ASSERT(mOwningThread);
|
||||||
data.mRecursionDepth = RecursionDepth();
|
data.mRecursionDepth = RecursionDepth();
|
||||||
|
|
@ -1453,7 +1516,7 @@ CycleCollectedJSContext::RunInMetastableState(already_AddRefed<nsIRunnable>&& aR
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mMetastableStateEvents.AppendElement(Move(data));
|
mPendingIDBTransactions.AppendElement(Move(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSContext* aCx,
|
IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSContext* aCx,
|
||||||
|
|
@ -1659,14 +1722,14 @@ CycleCollectedJSContext::PrepareWaitingZonesForGC()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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(NS_IsMainThread());
|
||||||
MOZ_ASSERT(runnable);
|
MOZ_ASSERT(runnable);
|
||||||
|
|
||||||
mPromiseMicroTaskQueue.push(runnable.forget());
|
mPendingMicroTaskRunnables.push(runnable.forget());
|
||||||
}
|
}
|
||||||
|
|
||||||
class AsyncMutationHandler final : public mozilla::Runnable
|
class AsyncMutationHandler final : public mozilla::Runnable
|
||||||
|
|
@ -1682,41 +1745,61 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
bool
|
||||||
CycleCollectedJSContext::PerformMicroTaskCheckPoint()
|
CycleCollectedJSContext::PerformMicroTaskCheckPoint()
|
||||||
{
|
{
|
||||||
if (mPendingMicroTaskRunnables.empty()) {
|
if (mPendingMicroTaskRunnables.empty() && mDebuggerMicroTaskQueue.empty()) {
|
||||||
|
AfterProcessMicrotasks();
|
||||||
// Nothing to do, return early.
|
// Nothing to do, return early.
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t currentDepth = RecursionDepth();
|
uint32_t currentDepth = RecursionDepth();
|
||||||
if (mMicroTaskRecursionDepth >= currentDepth) {
|
if (mMicroTaskRecursionDepth >= currentDepth) {
|
||||||
// We are already executing microtasks for the current recursion depth.
|
// 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()) {
|
if (NS_IsMainThread() && !nsContentUtils::IsSafeToRunScript()) {
|
||||||
// Special case for main thread where DOM mutations may happen when
|
// Special case for main thread where DOM mutations may happen when
|
||||||
// it is not safe to run scripts.
|
// it is not safe to run scripts.
|
||||||
nsContentUtils::AddScriptRunner(new AsyncMutationHandler());
|
nsContentUtils::AddScriptRunner(new AsyncMutationHandler());
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mozilla::AutoRestore<uint32_t> restore(mMicroTaskRecursionDepth);
|
mozilla::AutoRestore<uint32_t> restore(mMicroTaskRecursionDepth);
|
||||||
MOZ_ASSERT(currentDepth > 0);
|
MOZ_ASSERT(currentDepth > 0);
|
||||||
mMicroTaskRecursionDepth = currentDepth;
|
mMicroTaskRecursionDepth = currentDepth;
|
||||||
|
|
||||||
|
bool didProcess = false;
|
||||||
AutoSlowOperation aso;
|
AutoSlowOperation aso;
|
||||||
|
|
||||||
std::queue<RefPtr<MicroTaskRunnable>> suppressed;
|
std::queue<RefPtr<MicroTaskRunnable>> suppressed;
|
||||||
while (!mPendingMicroTaskRunnables.empty()) {
|
for (;;) {
|
||||||
RefPtr<MicroTaskRunnable> runnable =
|
RefPtr<MicroTaskRunnable> runnable;
|
||||||
mPendingMicroTaskRunnables.front().forget();
|
if (!mDebuggerMicroTaskQueue.empty()) {
|
||||||
|
runnable = mDebuggerMicroTaskQueue.front().forget();
|
||||||
|
mDebuggerMicroTaskQueue.pop();
|
||||||
|
} else if (!mPendingMicroTaskRunnables.empty()) {
|
||||||
|
runnable = mPendingMicroTaskRunnables.front().forget();
|
||||||
mPendingMicroTaskRunnables.pop();
|
mPendingMicroTaskRunnables.pop();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (runnable->Suppressed()) {
|
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);
|
suppressed.push(runnable);
|
||||||
} else {
|
} else {
|
||||||
|
didProcess = true;
|
||||||
runnable->Run(aso);
|
runnable->Run(aso);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1726,13 +1809,105 @@ CycleCollectedJSContext::PerformMicroTaskCheckPoint()
|
||||||
// for some time, but no longer than spinning the event loop nestedly
|
// for some time, but no longer than spinning the event loop nestedly
|
||||||
// (sync XHR, alert, etc.)
|
// (sync XHR, alert, etc.)
|
||||||
mPendingMicroTaskRunnables.swap(suppressed);
|
mPendingMicroTaskRunnables.swap(suppressed);
|
||||||
|
|
||||||
|
AfterProcessMicrotasks();
|
||||||
|
|
||||||
|
return didProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CycleCollectedJSContext::DispatchMicroTaskRunnable(
|
CycleCollectedJSContext::PerformDebuggerMicroTaskCheckpoint()
|
||||||
already_AddRefed<MicroTaskRunnable> aRunnable)
|
|
||||||
{
|
{
|
||||||
mPendingMicroTaskRunnables.push(aRunnable);
|
// 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()
|
||||||
|
{
|
||||||
|
// 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
|
void
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include "mozilla/mozalloc.h"
|
#include "mozilla/mozalloc.h"
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
#include "mozilla/SegmentedVector.h"
|
#include "mozilla/SegmentedVector.h"
|
||||||
|
#include "mozilla/dom/Promise.h"
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jsfriendapi.h"
|
#include "jsfriendapi.h"
|
||||||
|
|
||||||
|
|
@ -170,9 +171,6 @@ protected:
|
||||||
virtual void CustomOutOfMemoryCallback() {}
|
virtual void CustomOutOfMemoryCallback() {}
|
||||||
virtual void CustomLargeAllocationFailureCallback() {}
|
virtual void CustomLargeAllocationFailureCallback() {}
|
||||||
|
|
||||||
std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue;
|
|
||||||
std::queue<nsCOMPtr<nsIRunnable>> mDebuggerPromiseMicroTaskQueue;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void
|
void
|
||||||
DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
|
DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
|
||||||
|
|
@ -243,11 +241,11 @@ private:
|
||||||
virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
|
virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
|
||||||
void TraceNativeGrayRoots(JSTracer* aTracer);
|
void TraceNativeGrayRoots(JSTracer* aTracer);
|
||||||
|
|
||||||
void AfterProcessMicrotask(uint32_t aRecursionDepth);
|
void AfterProcessMicrotasks();
|
||||||
public:
|
public:
|
||||||
void ProcessStableStateQueue();
|
void ProcessStableStateQueue();
|
||||||
private:
|
private:
|
||||||
void ProcessMetastableStateQueue(uint32_t aRecursionDepth);
|
void CleanupIDBTransactions(uint32_t aRecursionDepth);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum DeferredFinalizeType {
|
enum DeferredFinalizeType {
|
||||||
|
|
@ -306,8 +304,8 @@ public:
|
||||||
already_AddRefed<nsIException> GetPendingException() const;
|
already_AddRefed<nsIException> GetPendingException() const;
|
||||||
void SetPendingException(nsIException* aException);
|
void SetPendingException(nsIException* aException);
|
||||||
|
|
||||||
std::queue<nsCOMPtr<nsIRunnable>>& GetPromiseMicroTaskQueue();
|
std::queue<RefPtr<MicroTaskRunnable>>& GetMicroTaskQueue();
|
||||||
std::queue<nsCOMPtr<nsIRunnable>>& GetDebuggerPromiseMicroTaskQueue();
|
std::queue<RefPtr<MicroTaskRunnable>>& GetDebuggerMicroTaskQueue();
|
||||||
|
|
||||||
nsCycleCollectionParticipant* GCThingParticipant();
|
nsCycleCollectionParticipant* GCThingParticipant();
|
||||||
nsCycleCollectionParticipant* ZoneParticipant();
|
nsCycleCollectionParticipant* ZoneParticipant();
|
||||||
|
|
@ -346,53 +344,25 @@ public:
|
||||||
return JS::RootingContext::get(mJSContext);
|
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:
|
protected:
|
||||||
JSContext* MaybeContext() const { return mJSContext; }
|
JSContext* MaybeContext() const { return mJSContext; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// nsThread entrypoints
|
// nsThread entrypoints
|
||||||
virtual void BeforeProcessTask(bool aMightBlock) { };
|
virtual void BeforeProcessTask(bool aMightBlock);
|
||||||
virtual void AfterProcessTask(uint32_t aRecursionDepth);
|
virtual void AfterProcessTask(uint32_t aRecursionDepth);
|
||||||
|
|
||||||
// microtask processor entry point
|
|
||||||
void AfterProcessMicrotask();
|
|
||||||
|
|
||||||
uint32_t RecursionDepth();
|
uint32_t RecursionDepth();
|
||||||
|
|
||||||
// Run in stable state (call through nsContentUtils)
|
// Run in stable state (call through nsContentUtils)
|
||||||
void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
|
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 AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction);
|
||||||
void RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable);
|
|
||||||
|
|
||||||
// Get the current thread's CycleCollectedJSContext. Returns null if there
|
// Get the current thread's CycleCollectedJSContext. Returns null if there
|
||||||
// isn't one.
|
// isn't one.
|
||||||
|
|
@ -411,7 +381,7 @@ public:
|
||||||
void PrepareWaitingZonesForGC();
|
void PrepareWaitingZonesForGC();
|
||||||
|
|
||||||
// Queue an async microtask to the current main or worker thread.
|
// 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.
|
// Call EnterMicroTask when you're entering JS execution.
|
||||||
// Usually the best way to do this is to use nsAutoMicroTask.
|
// Usually the best way to do this is to use nsAutoMicroTask.
|
||||||
|
|
@ -442,9 +412,9 @@ public:
|
||||||
mMicroTaskLevel = aLevel;
|
mMicroTaskLevel = aLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerformMicroTaskCheckPoint();
|
bool PerformMicroTaskCheckPoint();
|
||||||
|
|
||||||
void DispatchMicroTaskRunnable(already_AddRefed<MicroTaskRunnable> aRunnable);
|
void PerformDebuggerMicroTaskCheckpoint();
|
||||||
|
|
||||||
// Storage for watching rejected promises waiting for some client to
|
// Storage for watching rejected promises waiting for some client to
|
||||||
// consume their rejection.
|
// consume their rejection.
|
||||||
|
|
@ -460,6 +430,12 @@ public:
|
||||||
// in the last turn of the event loop.
|
// in the last turn of the event loop.
|
||||||
JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mConsumedRejections;
|
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;
|
nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */ >> mUncaughtRejectionObservers;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -483,24 +459,50 @@ private:
|
||||||
nsCOMPtr<nsIException> mPendingException;
|
nsCOMPtr<nsIException> mPendingException;
|
||||||
nsThread* mOwningThread; // Manual refcounting to avoid include hell.
|
nsThread* mOwningThread; // Manual refcounting to avoid include hell.
|
||||||
|
|
||||||
struct RunInMetastableStateData
|
struct PendingIDBTransactionData
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIRunnable> mRunnable;
|
nsCOMPtr<nsIRunnable> mTransaction;
|
||||||
uint32_t mRecursionDepth;
|
uint32_t mRecursionDepth;
|
||||||
};
|
};
|
||||||
|
|
||||||
nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
|
nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
|
||||||
nsTArray<RunInMetastableStateData> mMetastableStateEvents;
|
nsTArray<PendingIDBTransactionData> mPendingIDBTransactions;
|
||||||
uint32_t mBaseRecursionDepth;
|
uint32_t mBaseRecursionDepth;
|
||||||
bool mDoingStableStates;
|
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;
|
uint32_t mMicroTaskLevel;
|
||||||
|
|
||||||
std::queue<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables;
|
std::queue<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables;
|
||||||
|
std::queue<RefPtr<MicroTaskRunnable>> mDebuggerMicroTaskQueue;
|
||||||
|
|
||||||
uint32_t mMicroTaskRecursionDepth;
|
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 mOutOfMemoryState;
|
||||||
OOMState mLargeAllocationFailureState;
|
OOMState mLargeAllocationFailureState;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue