mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-27 02:47:31 +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();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -1390,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();
|
||||
}
|
||||
|
|
@ -1415,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;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "mozilla/dom/CustomElementRegistry.h"
|
||||
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/dom/CustomElementRegistryBinding.h"
|
||||
#include "mozilla/dom/HTMLElementBinding.h"
|
||||
#include "mozilla/dom/WebComponentsBinding.h"
|
||||
|
|
@ -1153,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
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "js/GCHashTable.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/FunctionBinding.h"
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -908,6 +908,7 @@ GK_ATOM(onreadsuccess, "onreadsuccess")
|
|||
GK_ATOM(onready, "onready")
|
||||
GK_ATOM(onreadystatechange, "onreadystatechange")
|
||||
GK_ATOM(onreceived, "onreceived")
|
||||
GK_ATOM(onrejectionhandled, "onrejectionhandled")
|
||||
GK_ATOM(onremoteheld, "onremoteheld")
|
||||
GK_ATOM(onremoteresumed, "onremoteresumed")
|
||||
GK_ATOM(onrequestprogress, "onrequestprogress")
|
||||
|
|
@ -951,6 +952,7 @@ GK_ATOM(ontransitionend, "ontransitionend")
|
|||
GK_ATOM(ontransitionrun, "ontransitionrun")
|
||||
GK_ATOM(ontransitionstart, "ontransitionstart")
|
||||
GK_ATOM(onunderflow, "onunderflow")
|
||||
GK_ATOM(onunhandledrejection, "onunhandledrejection")
|
||||
GK_ATOM(onunload, "onunload")
|
||||
GK_ATOM(onupdatefound, "onupdatefound")
|
||||
GK_ATOM(onupdateready, "onupdateready")
|
||||
|
|
|
|||
|
|
@ -13011,12 +13011,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());
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@
|
|||
#include "mozilla/Alignment.h"
|
||||
#include "mozilla/Array.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/DeferredFinalize.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/CallbackObject.h"
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ def idlTypeNeedsCycleCollection(type):
|
|||
type.isObject() or
|
||||
type.isSpiderMonkeyInterface()):
|
||||
return False
|
||||
elif type.isCallback() or type.isGeckoInterface():
|
||||
elif type.isCallback() or type.isPromise() or type.isGeckoInterface():
|
||||
return True
|
||||
elif type.isUnion():
|
||||
return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
|
||||
|
|
|
|||
|
|
@ -1065,12 +1065,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;
|
||||
|
|
@ -1080,12 +1076,6 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
|
|||
} else {
|
||||
result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent);
|
||||
}
|
||||
if (mIsMainThreadELM) {
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->LeaveMicroTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -578,14 +578,14 @@ WINDOW_EVENT(popstate,
|
|||
ePopState,
|
||||
EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
|
||||
eBasicEventClass)
|
||||
// Not supported yet
|
||||
// WINDOW_EVENT(redo)
|
||||
WINDOW_EVENT(rejectionhandled, eRejectionHandled,
|
||||
EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass)
|
||||
WINDOW_EVENT(storage,
|
||||
eStorage,
|
||||
EventNameType_HTMLBodyOrFramesetOnly,
|
||||
eBasicEventClass)
|
||||
// Not supported yet
|
||||
// WINDOW_EVENT(undo)
|
||||
WINDOW_EVENT(unhandledrejection, eUnhandledRejection,
|
||||
EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass)
|
||||
WINDOW_EVENT(unload,
|
||||
eUnload,
|
||||
(EventNameType_XUL | EventNameType_SVGSVG |
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@
|
|||
#include "nsIObserverService.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "IndexedDatabase.h"
|
||||
#include "IndexedDatabaseInlines.h"
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
|
@ -870,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "mozilla/ErrorNames.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!");
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "nsPluginStreamListenerPeer.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsPluginInstanceOwner.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -217,8 +217,16 @@ PromiseDebugging::RemoveUncaughtRejectionObserver(GlobalObject&,
|
|||
/* static */ void
|
||||
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.
|
||||
if (CycleCollectedJSContext::Get()->mUncaughtRejections.append(aPromise)) {
|
||||
if (uncaught.append(aPromise)) {
|
||||
FlushRejections::DispatchNeeded();
|
||||
}
|
||||
}
|
||||
|
|
@ -226,15 +234,20 @@ PromiseDebugging::AddUncaughtRejection(JS::HandleObject aPromise)
|
|||
/* void */ void
|
||||
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
|
||||
// reported it as unhandled. In that case, just remove it from the list
|
||||
// and don't add it to the list of consumed rejections.
|
||||
auto& uncaughtRejections = CycleCollectedJSContext::Get()->mUncaughtRejections;
|
||||
for (size_t i = 0; i < uncaughtRejections.length(); i++) {
|
||||
if (uncaughtRejections[i] == aPromise) {
|
||||
uint64_t promiseID = JS::GetPromiseID(aPromise);
|
||||
outstanding.Remove(promiseID);
|
||||
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.
|
||||
// Instead, we filter out nullptrs when iterating over the vector later.
|
||||
uncaughtRejections[i].set(nullptr);
|
||||
uncaught[i].set(nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -251,6 +264,7 @@ PromiseDebugging::FlushUncaughtRejectionsInternal()
|
|||
|
||||
auto& uncaught = storage->mUncaughtRejections;
|
||||
auto& consumed = storage->mConsumedRejections;
|
||||
auto& outstanding = storage->mOutstandingRejections;
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
|
|
@ -267,6 +281,10 @@ PromiseDebugging::FlushUncaughtRejectionsInternal()
|
|||
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) {
|
||||
RefPtr<UncaughtRejectionObserver> obs =
|
||||
static_cast<UncaughtRejectionObserver*>(observers[j].get());
|
||||
|
|
@ -274,8 +292,12 @@ PromiseDebugging::FlushUncaughtRejectionsInternal()
|
|||
IgnoredErrorResult err;
|
||||
obs->OnLeftUncaught(promise, err);
|
||||
}
|
||||
JSAutoCompartment ac(cx, promise);
|
||||
Promise::ReportRejectedPromise(cx, promise);
|
||||
// report error to console, unless marked by unhandledrejection event
|
||||
bool reportError = !JS::GetPromiseIsReported(promise);
|
||||
if (reportError) {
|
||||
JSAutoCompartment ac(cx, promise);
|
||||
Promise::ReportRejectedPromise(cx, promise);
|
||||
}
|
||||
}
|
||||
storage->mUncaughtRejections.clear();
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
#include "ImportManager.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/ConsoleReportCollector.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
|
||||
#include "mozilla/Attributes.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)
|
||||
: 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,9 @@ interface WindowEventHandlers {
|
|||
attribute EventHandler onpagehide;
|
||||
attribute EventHandler onpageshow;
|
||||
attribute EventHandler onpopstate;
|
||||
attribute EventHandler onrejectionhandled;
|
||||
attribute EventHandler onstorage;
|
||||
attribute EventHandler onunhandledrejection;
|
||||
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 ononline;
|
||||
attribute EventHandler onrejectionhandled;
|
||||
attribute EventHandler onunhandledrejection;
|
||||
// also has additional members in a partial interface
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -691,6 +691,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
|
|||
'PopStateEvent.webidl',
|
||||
'PopupBlockedEvent.webidl',
|
||||
'ProgressEvent.webidl',
|
||||
'PromiseRejectionEvent.webidl',
|
||||
'RecordErrorEvent.webidl',
|
||||
'ScrollViewChangeEvent.webidl',
|
||||
'ServiceWorkerMessageEvent.webidl',
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ContentEvents.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/LoadContext.h"
|
||||
|
|
@ -4815,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();
|
||||
|
|
@ -5802,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();
|
||||
}
|
||||
|
||||
|
|
@ -5812,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);
|
||||
|
|
@ -5828,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)) {
|
||||
|
|
@ -6256,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.
|
||||
|
|
@ -6292,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!");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -151,6 +151,8 @@ public:
|
|||
|
||||
IMPL_EVENT_HANDLER(online)
|
||||
IMPL_EVENT_HANDLER(offline)
|
||||
IMPL_EVENT_HANDLER(rejectionhandled)
|
||||
IMPL_EVENT_HANDLER(unhandledrejection)
|
||||
|
||||
void
|
||||
Dump(const Optional<nsAString>& aString) const;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue