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

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

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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>;
}

View file

@ -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")

View file

@ -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;
}

View file

@ -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());
}
}

View file

@ -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"

View file

@ -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();
}
}

View file

@ -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)

View file

@ -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;

View file

@ -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 |

View file

@ -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"

View file

@ -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();
}
}
}

View file

@ -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"

View file

@ -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();

View file

@ -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;

View file

@ -22,6 +22,7 @@
#include "nsPluginStreamListenerPeer.h"
#include "nsIServiceManager.h"
#include "nsThreadUtils.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/Preferences.h"
#include "nsPluginInstanceOwner.h"

View file

@ -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
{

View file

@ -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

View file

@ -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();

View file

@ -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"

View file

@ -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

View file

@ -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);

View file

@ -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;
};

View 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;
};

View file

@ -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
};

View file

@ -691,6 +691,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
'PopStateEvent.webidl',
'PopupBlockedEvent.webidl',
'ProgressEvent.webidl',
'PromiseRejectionEvent.webidl',
'RecordErrorEvent.webidl',
'ScrollViewChangeEvent.webidl',
'ServiceWorkerMessageEvent.webidl',

View file

@ -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());

View file

@ -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!");
}

View file

@ -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;