mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-24 01:17:34 +09:00
Issue #2240 - Align Microtasks and promises scheduling with spec
Microtasks, resolved Promises and Observers are handled after the sync task that caused them, in the order they were generated. Also simplifies reentrancy handling. Based-on: m-c 1193394
This commit is contained in:
parent
73b14eae31
commit
f059bb0a59
23 changed files with 260 additions and 347 deletions
|
|
@ -11,7 +11,6 @@
|
|||
#include "mozilla/AnimationTarget.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h" // For AsyncEventDispatcher
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/Maybe.h" // For Maybe
|
||||
#include "nsAnimationManager.h" // For CSSAnimation
|
||||
#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
|
||||
|
|
@ -1384,6 +1383,30 @@ Animation::GetRenderedDocument() const
|
|||
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
|
||||
Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
|
||||
{
|
||||
|
|
@ -1391,9 +1414,8 @@ Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
|
|||
|
||||
if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
|
||||
DoFinishNotificationImmediately();
|
||||
} else if (!mFinishNotificationTask.IsPending()) {
|
||||
RefPtr<nsRunnableMethod<Animation>> runnable =
|
||||
NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately);
|
||||
} else if (!mFinishNotificationTask) {
|
||||
RefPtr<MicroTaskRunnable> runnable = new AsyncFinishNotification(this);
|
||||
context->DispatchToMicroTask(do_AddRef(runnable));
|
||||
mFinishNotificationTask = runnable.forget();
|
||||
}
|
||||
|
|
@ -1416,9 +1438,13 @@ Animation::MaybeResolveFinishedPromise()
|
|||
}
|
||||
|
||||
void
|
||||
Animation::DoFinishNotificationImmediately()
|
||||
Animation::DoFinishNotificationImmediately(MicroTaskRunnable* aAsync)
|
||||
{
|
||||
mFinishNotificationTask.Revoke();
|
||||
if (aAsync && aAsync != mFinishNotificationTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
mFinishNotificationTask = nullptr;
|
||||
|
||||
if (PlayState() != AnimationPlayState::Finished) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "nsWrapperCache.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel
|
||||
#include "mozilla/LinkedList.h"
|
||||
|
|
@ -42,6 +43,7 @@ class AnimValuesStyleRule;
|
|||
|
||||
namespace dom {
|
||||
|
||||
class AsyncFinishNotification;
|
||||
class CSSAnimation;
|
||||
class CSSTransition;
|
||||
|
||||
|
|
@ -378,7 +380,8 @@ protected:
|
|||
void ResetFinishedPromise();
|
||||
void MaybeResolveFinishedPromise();
|
||||
void DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag);
|
||||
void DoFinishNotificationImmediately();
|
||||
friend class AsyncFinishNotification;
|
||||
void DoFinishNotificationImmediately(MicroTaskRunnable* aAsync = nullptr);
|
||||
void DispatchPlaybackEvent(const nsAString& aName);
|
||||
|
||||
/**
|
||||
|
|
@ -446,7 +449,7 @@ protected:
|
|||
// getAnimations() list.
|
||||
bool mIsRelevant;
|
||||
|
||||
nsRevocableEventPtr<nsRunnableMethod<Animation>> mFinishNotificationTask;
|
||||
RefPtr<MicroTaskRunnable> mFinishNotificationTask;
|
||||
// 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
|
||||
// in that case mFinished is immediately reset to represent a new current
|
||||
|
|
|
|||
|
|
@ -155,6 +155,13 @@ DocumentTimeline::WillRefresh(mozilla::TimeStamp aTime)
|
|||
bool needsTicks = false;
|
||||
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);
|
||||
|
||||
for (Animation* animation = mAnimationOrder.getFirst(); animation;
|
||||
|
|
|
|||
|
|
@ -1152,7 +1152,7 @@ CustomElementReactionsStack::Enqueue(Element* aElement,
|
|||
|
||||
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
||||
RefPtr<BackupQueueMicroTask> bqmt = new BackupQueueMicroTask(this);
|
||||
context->DispatchMicroTaskRunnable(bqmt.forget());
|
||||
context->DispatchToMicroTask(bqmt.forget());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -5418,10 +5418,10 @@ nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable)
|
|||
|
||||
/* static */
|
||||
void
|
||||
nsContentUtils::RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable)
|
||||
nsContentUtils::AddPendingIDBTransaction(already_AddRefed<nsIRunnable> aTransaction)
|
||||
{
|
||||
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));
|
||||
|
||||
// If there are 2 viewers for the current docshell, that
|
||||
// means the current document is a zombie document.
|
||||
// Only navigate into the subdocument if it's not a zombie.
|
||||
return !zombieViewer;
|
||||
// means the current document may be a zombie document.
|
||||
// While load and pageshow events are dispatched, zombie viewer is the old,
|
||||
// to be hidden document.
|
||||
if (zombieViewer) {
|
||||
bool inOnLoad = false;
|
||||
docShell->GetIsExecutingOnLoadHandler(&inOnLoad);
|
||||
return inOnLoad;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -1766,17 +1766,12 @@ public:
|
|||
*/
|
||||
static void RunInStableState(already_AddRefed<nsIRunnable> aRunnable);
|
||||
|
||||
/* Add a "synchronous section", in the form of an nsIRunnable run once the
|
||||
* event loop has reached a "metastable state". |aRunnable| must not cause any
|
||||
* queued events to be processed (i.e. must not spin the event loop).
|
||||
* We've reached a metastable state when the currently executing task or
|
||||
* 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
|
||||
/* Add a pending IDBTransaction to be cleaned up at the end of performing a
|
||||
* microtask checkpoint.
|
||||
* See the step of "Cleanup Indexed Database Transactions" in
|
||||
* https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
|
||||
*/
|
||||
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
|
||||
* and zoom of a page on mobile devices. We stick the information in
|
||||
|
|
|
|||
|
|
@ -623,7 +623,7 @@ nsDOMMutationObserver::QueueMutationObserverMicroTask()
|
|||
|
||||
RefPtr<MutationObserverMicroTask> momt =
|
||||
new MutationObserverMicroTask();
|
||||
ccjs->DispatchMicroTaskRunnable(momt.forget());
|
||||
ccjs->DispatchToMicroTask(momt.forget());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -644,9 +644,8 @@ nsDOMMutationObserver::RescheduleForRun()
|
|||
return;
|
||||
}
|
||||
|
||||
RefPtr<MutationObserverMicroTask> momt =
|
||||
new MutationObserverMicroTask();
|
||||
ccjs->DispatchMicroTaskRunnable(momt.forget());
|
||||
RefPtr<MutationObserverMicroTask> momt = new MutationObserverMicroTask();
|
||||
ccjs->DispatchToMicroTask(momt.forget());
|
||||
sScheduledMutationObservers = new AutoTArray<RefPtr<nsDOMMutationObserver>, 4>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12999,12 +12999,6 @@ nsGlobalWindow::RunTimeoutHandler(Timeout* aTimeout,
|
|||
// point anyway, and the script context should have already reported
|
||||
// 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) {
|
||||
sNestingLevel = nestingLevel;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,6 +145,6 @@ void nsIGlobalObject::QueueMicrotask(VoidFunction& aCallback) {
|
|||
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
||||
if (context) {
|
||||
RefPtr<MicroTaskRunnable> mt = new QueuedMicrotask(this, aCallback);
|
||||
context->DispatchMicroTaskRunnable(mt.forget());
|
||||
context->DispatchToMicroTask(mt.forget());
|
||||
}
|
||||
}
|
||||
|
|
@ -78,11 +78,9 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
|
|||
, mExceptionHandling(aExceptionHandling)
|
||||
, mIsMainThread(NS_IsMainThread())
|
||||
{
|
||||
if (mIsMainThread) {
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->EnterMicroTask();
|
||||
}
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->EnterMicroTask();
|
||||
}
|
||||
|
||||
// Compute the caller's subject principal (if necessary) early, before we
|
||||
|
|
@ -290,11 +288,9 @@ CallbackObject::CallSetup::~CallSetup()
|
|||
|
||||
// It is important that this is the last thing we do, after leaving the
|
||||
// compartment and undoing all our entry/incumbent script changes
|
||||
if (mIsMainThread) {
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->LeaveMicroTask();
|
||||
}
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->LeaveMicroTask();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1064,12 +1064,8 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
|
|||
}
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
if (mIsMainThreadELM) {
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->EnterMicroTask();
|
||||
}
|
||||
}
|
||||
nsAutoMicroTask mt;
|
||||
|
||||
// nsIDOMEvent::currentTarget is set in EventDispatcher.
|
||||
if (listenerHolder.HasWebIDLCallback()) {
|
||||
ErrorResult rv;
|
||||
|
|
@ -1079,12 +1075,6 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
|
|||
} else {
|
||||
result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent);
|
||||
}
|
||||
if (mIsMainThreadELM) {
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->LeaveMicroTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -871,22 +871,26 @@ DispatchSuccessEvent(ResultHelper* aResultHelper,
|
|||
IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(transaction,
|
||||
transaction->IsOpen() && !transaction->IsAborted());
|
||||
|
||||
bool dummy;
|
||||
nsresult rv = request->DispatchEvent(aEvent, &dummy);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(transaction,
|
||||
transaction->IsOpen() || transaction->IsAborted());
|
||||
|
||||
WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
|
||||
MOZ_ASSERT(internalEvent);
|
||||
|
||||
if (transaction &&
|
||||
transaction->IsOpen() &&
|
||||
internalEvent->mFlags.mExceptionWasRaised) {
|
||||
transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
|
||||
transaction->IsOpen()) {
|
||||
if (internalEvent->mFlags.mExceptionWasRaised) {
|
||||
transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
|
||||
} else {
|
||||
// To handle upgrade transaction.
|
||||
transaction->Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ IDBFileHandle::Create(IDBMutableFile* aMutableFile,
|
|||
MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
|
||||
nsContentUtils::RunInMetastableState(runnable.forget());
|
||||
nsContentUtils::AddPendingIDBTransaction(runnable.forget());
|
||||
|
||||
fileHandle->SetCreating();
|
||||
|
||||
|
|
|
|||
|
|
@ -190,13 +190,9 @@ IDBTransaction::CreateVersionChange(
|
|||
|
||||
transaction->SetScriptOwner(aDatabase->GetScriptOwner());
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
|
||||
nsContentUtils::RunInMetastableState(runnable.forget());
|
||||
|
||||
transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
|
||||
transaction->mNextObjectStoreId = aNextObjectStoreId;
|
||||
transaction->mNextIndexId = aNextIndexId;
|
||||
transaction->mCreating = true;
|
||||
|
||||
aDatabase->RegisterTransaction(transaction);
|
||||
transaction->mRegistered = true;
|
||||
|
|
@ -226,7 +222,7 @@ IDBTransaction::Create(JSContext* aCx, IDBDatabase* aDatabase,
|
|||
transaction->SetScriptOwner(aDatabase->GetScriptOwner());
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
|
||||
nsContentUtils::RunInMetastableState(runnable.forget());
|
||||
nsContentUtils::AddPendingIDBTransaction(runnable.forget());
|
||||
|
||||
transaction->mCreating = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -508,110 +508,6 @@ Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise)
|
|||
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*
|
||||
Promise::GlobalJSObject() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -104,14 +104,6 @@ public:
|
|||
// specializations in the .cpp for
|
||||
// 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
|
||||
|
||||
nsIGlobalObject* GetParentObject() const
|
||||
|
|
|
|||
|
|
@ -828,8 +828,6 @@ AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMP
|
|||
AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
|
||||
: AutoJSAPI()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
|
||||
Init();
|
||||
|
|
@ -838,9 +836,12 @@ AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMP
|
|||
void
|
||||
AutoSlowOperation::CheckForInterrupt()
|
||||
{
|
||||
// JS_CheckForInterrupt expects us to be in a compartment.
|
||||
JSAutoCompartment ac(cx(), xpc::UnprivilegedJunkScope());
|
||||
JS_CheckForInterrupt(cx());
|
||||
// For now we support only main thread!
|
||||
if (mIsMainThread) {
|
||||
// JS_CheckForInterrupt expects us to be in a compartment.
|
||||
JSAutoCompartment ac(cx(), xpc::UnprivilegedJunkScope());
|
||||
JS_CheckForInterrupt(cx());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
|||
|
|
@ -298,7 +298,6 @@ protected:
|
|||
// AutoJSAPI, so Init must NOT be called on subclasses that use this.
|
||||
AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, Type aType);
|
||||
|
||||
private:
|
||||
mozilla::Maybe<JSAutoRequest> mAutoRequest;
|
||||
mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
|
||||
JSContext *mCx;
|
||||
|
|
@ -307,6 +306,7 @@ private:
|
|||
bool mIsMainThread;
|
||||
Maybe<JS::WarningReporter> mOldWarningReporter;
|
||||
|
||||
private:
|
||||
void InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
|
||||
JSContext* aCx, bool aIsMainThread);
|
||||
|
||||
|
|
|
|||
|
|
@ -1007,6 +1007,10 @@ public:
|
|||
: mWorkerPrivate(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()
|
||||
|
|
@ -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
|
||||
// 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);
|
||||
RefPtr<MicroTaskRunnable> runnable(aRunnable);
|
||||
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(runnable);
|
||||
|
||||
std::queue<nsCOMPtr<nsIRunnable>>* microTaskQueue = nullptr;
|
||||
std::queue<RefPtr<MicroTaskRunnable>>* microTaskQueue = nullptr;
|
||||
|
||||
JSContext* cx = GetCurrentThreadJSContext();
|
||||
NS_ASSERTION(cx, "This should never be null!");
|
||||
|
|
@ -1120,15 +1112,15 @@ public:
|
|||
NS_ASSERTION(global, "This should never be null!");
|
||||
|
||||
// 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
|
||||
// promise micro task queue instead.
|
||||
// micro task queue instead.
|
||||
if (IsWorkerGlobal(global)) {
|
||||
microTaskQueue = &mPromiseMicroTaskQueue;
|
||||
microTaskQueue = &GetMicroTaskQueue();
|
||||
} else {
|
||||
MOZ_ASSERT(IsDebuggerGlobal(global) || IsDebuggerSandbox(global));
|
||||
|
||||
microTaskQueue = &mDebuggerPromiseMicroTaskQueue;
|
||||
microTaskQueue = &GetDebuggerMicroTaskQueue();
|
||||
}
|
||||
|
||||
microTaskQueue->push(runnable.forget());
|
||||
|
|
|
|||
|
|
@ -4816,8 +4816,10 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
|||
static_cast<nsIRunnable*>(runnable)->Run();
|
||||
runnable->Release();
|
||||
|
||||
// Flush the promise queue.
|
||||
Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->PerformDebuggerMicroTaskCheckpoint();
|
||||
}
|
||||
|
||||
if (debuggerRunnablesPending) {
|
||||
WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
|
||||
|
|
@ -5803,8 +5805,12 @@ WorkerPrivate::EnterDebuggerEventLoop()
|
|||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
||||
std::queue<RefPtr<MicroTaskRunnable>>& debuggerMtQueue =
|
||||
context->GetDebuggerMicroTaskQueue();
|
||||
while (mControlQueue.IsEmpty() &&
|
||||
!(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty())) {
|
||||
!(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
|
||||
debuggerMtQueue.empty()) {
|
||||
WaitForWorkerEvents();
|
||||
}
|
||||
|
||||
|
|
@ -5813,6 +5819,11 @@ WorkerPrivate::EnterDebuggerEventLoop()
|
|||
// XXXkhuey should we abort JS on the stack here if we got Abort above?
|
||||
}
|
||||
|
||||
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
||||
if (context) {
|
||||
context->PerformDebuggerMicroTaskCheckpoint();
|
||||
}
|
||||
|
||||
if (debuggerRunnablesPending) {
|
||||
// Start the periodic GC timer if it is not already running.
|
||||
SetGCTimerMode(PeriodicTimer);
|
||||
|
|
@ -5829,8 +5840,10 @@ WorkerPrivate::EnterDebuggerEventLoop()
|
|||
static_cast<nsIRunnable*>(runnable)->Run();
|
||||
runnable->Release();
|
||||
|
||||
// Flush the promise queue.
|
||||
Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->PerformDebuggerMicroTaskCheckpoint();
|
||||
}
|
||||
|
||||
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
||||
if (JS::CurrentGlobalOrNull(cx)) {
|
||||
|
|
@ -6257,8 +6270,8 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
|
|||
|
||||
RefPtr<Function> callback = info->mHandler->GetCallback();
|
||||
if (!callback) {
|
||||
// scope for the AutoEntryScript, so it comes off the stack before we do
|
||||
// Promise::PerformMicroTaskCheckpoint.
|
||||
nsAutoMicroTask mt;
|
||||
|
||||
AutoEntryScript aes(global, reason, false);
|
||||
|
||||
// Evaluate the timeout expression.
|
||||
|
|
@ -6293,10 +6306,6 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
|
|||
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!");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue