diff --git a/js/public/Promise.h b/js/public/Promise.h index 75bc7fd8370d..d652f5790713 100644 --- a/js/public/Promise.h +++ b/js/public/Promise.h @@ -36,19 +36,36 @@ class JS_PUBLIC_API JobQueue { virtual ~JobQueue() = default; /** - * Ask the embedding for the incumbent global. + * Ask the embedding for the host defined data. * - * SpiderMonkey doesn't itself have a notion of incumbent globals as defined + * This is the step 5 in + * https://html.spec.whatwg.org/multipage/webappapis.html#hostmakejobcallback + * + * SpiderMonkey doesn't itself have a notion of host defined data as defined * by the HTML spec, so we need the embedding to provide this. See * dom/script/ScriptSettings.h for details. + * + * If the embedding has the host defined data, this method should return the + * host defined data via the `data` out parameter and return `true`. + * The object in the `data` out parameter can belong to any compartment. + * If the embedding doesn't need the host defined data, this method should + * set the `data` out parameter to `nullptr` and return `true`. + * If any error happens while generating the host defined data, this method + * should set a pending exception to `cx` and return `false`. */ - virtual JSObject* getIncumbentGlobal(JSContext* cx) = 0; + virtual bool getHostDefinedData(JSContext* cx, + JS::MutableHandle data) const = 0; /** * Enqueue a reaction job `job` for `promise`, which was allocated at - * `allocationSite`. Provide `incumbentGlobal` as the incumbent global for + * `allocationSite`. Provide `hostDefineData` as the host defined data for * the reaction job's execution. * + * The `hostDefinedData` value comes from `getHostDefinedData` method. + * The object is unwrapped, and it can belong to a different compartment + * than the current compartment. It can be `nullptr` if `getHostDefinedData` + * returns `nullptr`. + * * `promise` can be null if the promise is optimized out. * `promise` is guaranteed not to be optimized out if the promise has * non-default user-interaction flag. @@ -56,7 +73,7 @@ class JS_PUBLIC_API JobQueue { virtual bool enqueuePromiseJob(JSContext* cx, JS::HandleObject promise, JS::HandleObject job, JS::HandleObject allocationSite, - JS::HandleObject incumbentGlobal) = 0; + JS::HandleObject hostDefinedData) = 0; /** * Run all jobs in the queue. Running one job may enqueue others; continue to diff --git a/js/src/builtin/FinalizationRegistryObject.cpp b/js/src/builtin/FinalizationRegistryObject.cpp index 070becc0a744..cdb6e7c229b0 100644 --- a/js/src/builtin/FinalizationRegistryObject.cpp +++ b/js/src/builtin/FinalizationRegistryObject.cpp @@ -688,8 +688,8 @@ FinalizationQueueObject* FinalizationQueueObject::create( // you don't know how far to unwrap it to get the original object // back. Instead store a CCW to a plain object in the same compartment as the // global (this uses Object.prototype). - RootedObject incumbentObject(cx); - if (!GetObjectFromIncumbentGlobal(cx, &incumbentObject) || !incumbentObject) { + Rooted hostDefinedData(cx); + if (!GetObjectFromHostDefinedData(cx, &hostDefinedData)) { return nullptr; } @@ -700,7 +700,8 @@ FinalizationQueueObject* FinalizationQueueObject::create( } queue->initReservedSlot(CleanupCallbackSlot, ObjectValue(*cleanupCallback)); - queue->initReservedSlot(IncumbentObjectSlot, ObjectValue(*incumbentObject)); + queue->initReservedSlot(HostDefinedDataSlot, + JS::ObjectOrNullValue(hostDefinedData)); InitReservedSlot(queue, RecordsToBeCleanedUpSlot, recordsToBeCleanedUp.release(), MemoryUse::FinalizationRegistryRecordVector); @@ -754,12 +755,12 @@ inline JSObject* FinalizationQueueObject::cleanupCallback() const { return &value.toObject(); } -JSObject* FinalizationQueueObject::incumbentObject() const { - Value value = getReservedSlot(IncumbentObjectSlot); +JSObject* FinalizationQueueObject::getHostDefinedData() const { + Value value = getReservedSlot(HostDefinedDataSlot); if (value.isUndefined()) { return nullptr; } - return &value.toObject(); + return value.toObjectOrNull(); } FinalizationRecordVector* FinalizationQueueObject::recordsToBeCleanedUp() diff --git a/js/src/builtin/FinalizationRegistryObject.h b/js/src/builtin/FinalizationRegistryObject.h index 288ebea424cf..e442c2d1ae3c 100644 --- a/js/src/builtin/FinalizationRegistryObject.h +++ b/js/src/builtin/FinalizationRegistryObject.h @@ -226,7 +226,7 @@ class FinalizationRegistryObject : public NativeObject { class FinalizationQueueObject : public NativeObject { enum { CleanupCallbackSlot = 0, - IncumbentObjectSlot, + HostDefinedDataSlot, RecordsToBeCleanedUpSlot, IsQueuedForCleanupSlot, DoCleanupFunctionSlot, @@ -242,7 +242,7 @@ class FinalizationQueueObject : public NativeObject { static const JSClass class_; JSObject* cleanupCallback() const; - JSObject* incumbentObject() const; + JSObject* getHostDefinedData() const; FinalizationRecordVector* recordsToBeCleanedUp() const; bool isQueuedForCleanup() const; JSFunction* doCleanupFunction() const; diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 09cc587ac4dd..1a143f38cdad 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -700,8 +700,9 @@ enum ReactionRecordSlots { ReactionRecordSlot_Resolve, ReactionRecordSlot_Reject, - // The incumbent global for this reaction record. Can be null. - ReactionRecordSlot_IncumbentGlobalObject, + // The host defined data for this reaction record. Can be null. + // See step 5 in https://html.spec.whatwg.org/#hostmakejobcallback + ReactionRecordSlot_HostDefinedData, // Bitmask of the REACTION_FLAG values. ReactionRecordSlot_Flags, @@ -901,10 +902,10 @@ class PromiseReactionRecord : public NativeObject { return getFixedSlot(handlerArgSlot()); } - JSObject* getAndClearIncumbentGlobalObject() { + JSObject* getAndClearHostDefinedData() { JSObject* obj = - getFixedSlot(ReactionRecordSlot_IncumbentGlobalObject).toObjectOrNull(); - setFixedSlot(ReactionRecordSlot_IncumbentGlobalObject, UndefinedValue()); + getFixedSlot(ReactionRecordSlot_HostDefinedData).toObjectOrNull(); + setFixedSlot(ReactionRecordSlot_HostDefinedData, UndefinedValue()); return obj; } @@ -1545,26 +1546,21 @@ static bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp); } } - // Using objectFromIncumbentGlobal, we can derive the incumbent global by - // unwrapping and then getting the global. This is very convoluted, but - // much better than having to store the original global as a private value - // because we couldn't wrap it to store it as a normal JS value. - Rooted global(cx); - if (JSObject* objectFromIncumbentGlobal = - reaction->getAndClearIncumbentGlobalObject()) { - objectFromIncumbentGlobal = CheckedUnwrapStatic(objectFromIncumbentGlobal); - MOZ_ASSERT(objectFromIncumbentGlobal); - global = &objectFromIncumbentGlobal->nonCCWGlobal(); + JS::Rooted hostDefinedData(cx); + if (JSObject* hostDefined = reaction->getAndClearHostDefinedData()) { + hostDefined = CheckedUnwrapStatic(hostDefined); + MOZ_ASSERT(hostDefined); + // If the hostDefined object becomes a dead wrapper here, the target + // global has already gone, and the job queue won't run the promise job + // anyway. + if (JS_IsDeadWrapper(hostDefined)) { + return true; + } + hostDefinedData = hostDefined; } // HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). - // - // Note: the global we pass here might be from a different compartment - // than job and promise. While it's somewhat unusual to pass objects - // from multiple compartments, in this case we specifically need the - // global to be unwrapped because wrapping and unwrapping aren't - // necessarily symmetric for globals. - return cx->runtime()->enqueuePromiseJob(cx, job, promise, global); + return cx->runtime()->enqueuePromiseJob(cx, job, promise, hostDefinedData); } [[nodiscard]] static bool TriggerPromiseReactions(JSContext* cx, @@ -2500,11 +2496,13 @@ static bool PromiseResolveBuiltinThenableJob(JSContext* cx, unsigned argc, // compartment. RootedObject promise(cx, &promiseToResolve.toObject()); - Rooted incumbentGlobal(cx, - cx->runtime()->getIncumbentGlobal(cx)); + Rooted hostDefinedData(cx); + if (!cx->runtime()->getHostDefinedData(cx, &hostDefinedData)) { + return false; + } // Step X. HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). - return cx->runtime()->enqueuePromiseJob(cx, job, promise, incumbentGlobal); + return cx->runtime()->enqueuePromiseJob(cx, job, promise, hostDefinedData); } /** @@ -2546,12 +2544,14 @@ static bool PromiseResolveBuiltinThenableJob(JSContext* cx, unsigned argc, job->setExtendedSlot(ThenableJobSlot_Promise, ObjectValue(*promiseToResolve)); job->setExtendedSlot(ThenableJobSlot_Thenable, ObjectValue(*thenable)); - Rooted incumbentGlobal(cx, - cx->runtime()->getIncumbentGlobal(cx)); + Rooted hostDefinedData(cx); + if (!cx->runtime()->getHostDefinedData(cx, &hostDefinedData)) { + return false; + } // HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). return cx->runtime()->enqueuePromiseJob(cx, job, promiseToResolve, - incumbentGlobal); + hostDefinedData); } [[nodiscard]] static bool AddDummyPromiseReactionForDebugger( @@ -5141,9 +5141,9 @@ static PromiseReactionRecord* NewReactionRecord( // Handlers must either both be present or both be absent. MOZ_ASSERT(onFulfilled.isNull() == onRejected.isNull()); - RootedObject incumbentGlobalObject(cx); + RootedObject hostDefinedData(cx); if (incumbentGlobalObjectOption == IncumbentGlobalObject::Yes) { - if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobalObject)) { + if (!GetObjectFromHostDefinedData(cx, &hostDefinedData)) { return nullptr; } } @@ -5159,7 +5159,7 @@ static PromiseReactionRecord* NewReactionRecord( cx->check(onRejected); cx->check(resultCapability.resolve()); cx->check(resultCapability.reject()); - cx->check(incumbentGlobalObject); + cx->check(hostDefinedData); // Step 7. Let fulfillReaction be the PromiseReaction // { [[Capability]]: resultCapability, [[Type]]: Fulfill, @@ -5181,8 +5181,8 @@ static PromiseReactionRecord* NewReactionRecord( ObjectOrNullValue(resultCapability.resolve())); reaction->setFixedSlot(ReactionRecordSlot_Reject, ObjectOrNullValue(resultCapability.reject())); - reaction->setFixedSlot(ReactionRecordSlot_IncumbentGlobalObject, - ObjectOrNullValue(incumbentGlobalObject)); + reaction->setFixedSlot(ReactionRecordSlot_HostDefinedData, + ObjectOrNullValue(hostDefinedData)); return reaction; } @@ -6715,9 +6715,8 @@ void PromiseReactionRecord::dumpOwnFields(js::JSONPrinter& json) const { } { - js::GenericPrinter& out = json.beginStringProperty("incumbentGlobal"); - getFixedSlot(ReactionRecordSlot_IncumbentGlobalObject) - .dumpStringContent(out); + js::GenericPrinter& out = json.beginStringProperty("hostDefinedData"); + getFixedSlot(ReactionRecordSlot_HostDefinedData).dumpStringContent(out); json.endStringProperty(); } diff --git a/js/src/gc/FinalizationObservers.cpp b/js/src/gc/FinalizationObservers.cpp index 3a7a11464523..950523dc448a 100644 --- a/js/src/gc/FinalizationObservers.cpp +++ b/js/src/gc/FinalizationObservers.cpp @@ -300,14 +300,21 @@ void GCRuntime::queueFinalizationRegistryForCleanup( return; } - // Derive the incumbent global by unwrapping the incumbent global object and - // then getting its global. - JSObject* object = UncheckedUnwrapWithoutExpose(queue->incumbentObject()); - MOZ_ASSERT(object); - GlobalObject* incumbentGlobal = &object->nonCCWGlobal(); + JSObject* unwrappedHostDefineData = nullptr; + + if (JSObject* wrapped = queue->getHostDefinedData()) { + unwrappedHostDefineData = UncheckedUnwrapWithoutExpose(wrapped); + MOZ_ASSERT(unwrappedHostDefineData); + // If the hostDefined object becomes a dead wrapper here, the target global + // has already gone, and the finalization callback won't do anything to it + // anyway. + if (JS_IsDeadWrapper(unwrappedHostDefineData)) { + return; + } + } callHostCleanupFinalizationRegistryCallback(queue->doCleanupFunction(), - incumbentGlobal); + unwrappedHostDefineData); // The queue object may be gray, and that's OK. AutoTouchingGrayThings atgt; diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index dcdea3d2edf4..d6deff9ab133 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -1646,11 +1646,11 @@ void GCRuntime::setHostCleanupFinalizationRegistryCallback( } void GCRuntime::callHostCleanupFinalizationRegistryCallback( - JSFunction* doCleanup, GlobalObject* incumbentGlobal) { + JSFunction* doCleanup, JSObject* hostDefinedData) { JS::AutoSuppressGCAnalysis nogc; const auto& callback = hostCleanupFinalizationRegistryCallback.ref(); if (callback.op) { - callback.op(doCleanup, incumbentGlobal, callback.data); + callback.op(doCleanup, hostDefinedData, callback.data); } } diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 9770c08cb41a..f9ea67b1e742 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -476,8 +476,8 @@ class GCRuntime { void removeFinalizeCallback(JSFinalizeCallback callback); void setHostCleanupFinalizationRegistryCallback( JSHostCleanupFinalizationRegistryCallback callback, void* data); - void callHostCleanupFinalizationRegistryCallback( - JSFunction* doCleanup, GlobalObject* incumbentGlobal); + void callHostCleanupFinalizationRegistryCallback(JSFunction* doCleanup, + JSObject* hostDefinedData); [[nodiscard]] bool addWeakPointerZonesCallback( JSWeakPointerZonesCallback callback, void* data); void removeWeakPointerZonesCallback(JSWeakPointerZonesCallback callback); diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp index 117eac7b17a3..d23062e1b474 100644 --- a/js/src/vm/JSContext.cpp +++ b/js/src/vm/JSContext.cpp @@ -814,18 +814,17 @@ JS_PUBLIC_API void js::RunJobs(JSContext* cx) { JS::ClearKeptObjects(cx); } -JSObject* InternalJobQueue::getIncumbentGlobal(JSContext* cx) { - if (!cx->compartment()) { - return nullptr; - } - return cx->global(); +bool InternalJobQueue::getHostDefinedData( + JSContext* cx, JS::MutableHandle data) const { + data.set(nullptr); + return true; } bool InternalJobQueue::enqueuePromiseJob(JSContext* cx, JS::HandleObject promise, JS::HandleObject job, JS::HandleObject allocationSite, - JS::HandleObject incumbentGlobal) { + JS::HandleObject hostDefinedData) { MOZ_ASSERT(job); if (!queue.pushBack(job)) { ReportOutOfMemory(cx); diff --git a/js/src/vm/JSContext.h b/js/src/vm/JSContext.h index cb8d4d47db47..cd6f9824e7eb 100644 --- a/js/src/vm/JSContext.h +++ b/js/src/vm/JSContext.h @@ -88,10 +88,12 @@ class InternalJobQueue : public JS::JobQueue { ~InternalJobQueue() = default; // JS::JobQueue methods. - JSObject* getIncumbentGlobal(JSContext* cx) override; + bool getHostDefinedData(JSContext* cx, + JS::MutableHandle data) const override; + bool enqueuePromiseJob(JSContext* cx, JS::HandleObject promise, JS::HandleObject job, JS::HandleObject allocationSite, - JS::HandleObject incumbentGlobal) override; + JS::HandleObject hostDefinedData) override; void runJobs(JSContext* cx) override; bool empty() const override; bool isDrainingStopped() const override { return interrupted_; } diff --git a/js/src/vm/JSObject.cpp b/js/src/vm/JSObject.cpp index 6e4856ff7602..5befd1b2c23a 100644 --- a/js/src/vm/JSObject.cpp +++ b/js/src/vm/JSObject.cpp @@ -1504,15 +1504,11 @@ NativeObject* js::InitClass(JSContext* cx, HandleObject obj, * it - e.g. EnqueuePromiseReactionJob - can then unwrap the object and get * its global without fear of unwrapping too far. */ -bool js::GetObjectFromIncumbentGlobal(JSContext* cx, MutableHandleObject obj) { - Rooted globalObj(cx, cx->runtime()->getIncumbentGlobal(cx)); - if (!globalObj) { - obj.set(nullptr); - return true; +bool js::GetObjectFromHostDefinedData(JSContext* cx, MutableHandleObject obj) { + if (!cx->runtime()->getHostDefinedData(cx, obj)) { + return false; } - obj.set(&globalObj->getObjectPrototype()); - // The object might be from a different compartment, so wrap it. if (obj && !cx->compartment()->wrap(cx, obj)) { return false; diff --git a/js/src/vm/JSObject.h b/js/src/vm/JSObject.h index 5d2047e3b50e..fb6bb9867cb5 100644 --- a/js/src/vm/JSObject.h +++ b/js/src/vm/JSObject.h @@ -1075,7 +1075,7 @@ extern bool TestIntegrityLevel(JSContext* cx, HandleObject obj, JSContext* cx, HandleObject obj, JSProtoKey ctorKey, bool (*isDefaultSpecies)(JSContext*, JSFunction*)); -extern bool GetObjectFromIncumbentGlobal(JSContext* cx, +extern bool GetObjectFromHostDefinedData(JSContext* cx, MutableHandleObject obj); #ifdef DEBUG diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index e89fab97cb24..ddf98b66ba62 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -573,22 +573,16 @@ SharedScriptDataTableHolder& JSRuntime::scriptDataTableHolder() { return scriptDataTableHolder_; } -GlobalObject* JSRuntime::getIncumbentGlobal(JSContext* cx) { +bool JSRuntime::getHostDefinedData(JSContext* cx, + JS::MutableHandle data) const { MOZ_ASSERT(cx->jobQueue); - JSObject* obj = cx->jobQueue->getIncumbentGlobal(cx); - if (!obj) { - return nullptr; - } - - MOZ_ASSERT(obj->is(), - "getIncumbentGlobalCallback must return a global!"); - return &obj->as(); + return cx->jobQueue->getHostDefinedData(cx, data); } bool JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job, HandleObject promise, - Handle incumbentGlobal) { + HandleObject hostDefinedData) { MOZ_ASSERT(cx->jobQueue, "Must select a JobQueue implementation using JS::JobQueue " "or js::UseInternalJobQueues before using Promises"); @@ -611,7 +605,7 @@ bool JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job, } } return cx->jobQueue->enqueuePromiseJob(cx, promise, job, allocationSite, - incumbentGlobal); + hostDefinedData); } void JSRuntime::addUnhandledRejectedPromise(JSContext* cx, diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index d416726ddd13..5cfd26cd465d 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -441,10 +441,12 @@ struct JSRuntime { js::UnprotectedData consumeStreamCallback; js::UnprotectedData reportStreamErrorCallback; - js::GlobalObject* getIncumbentGlobal(JSContext* cx); + bool getHostDefinedData(JSContext* cx, + JS::MutableHandle data) const; + bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job, js::HandleObject promise, - js::Handle incumbentGlobal); + js::HandleObject hostDefinedData); void addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise); void removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise); diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp index 5def01d88c47..46dba16745ba 100644 --- a/xpcom/base/CycleCollectedJSContext.cpp +++ b/xpcom/base/CycleCollectedJSContext.cpp @@ -228,24 +228,78 @@ class PromiseJobRunnable final : public MicroTaskRunnable { bool mPropagateUserInputEventHandling; }; -JSObject* CycleCollectedJSContext::getIncumbentGlobal(JSContext* aCx) { +// Finalizer for instances of FinalizeHostDefinedData. +// +// HostDefinedData only contains incumbent global, no need to +// clean that up. +// TODO(sefeng): Bug 1929356 will add [[SchedulingState]] to HostDefinedData. +void FinalizeHostDefinedData(JS::GCContext* gcx, JSObject* objSelf) {} + +static const JSClassOps sHostDefinedData = { + nullptr /* addProperty */, nullptr /* delProperty */, + nullptr /* enumerate */, nullptr /* newEnumerate */, + nullptr /* resolve */, nullptr /* mayResolve */, + FinalizeHostDefinedData /* finalize */ +}; + +enum { INCUMBENT_SETTING_SLOT, HOSTDEFINED_DATA_SLOTS }; + +// Implements `HostDefined` in https://html.spec.whatwg.org/#hostmakejobcallback +static const JSClass sHostDefinedDataClass = { + "HostDefinedData", + JSCLASS_HAS_RESERVED_SLOTS(HOSTDEFINED_DATA_SLOTS) | + JSCLASS_BACKGROUND_FINALIZE, + &sHostDefinedData}; + +bool CycleCollectedJSContext::getHostDefinedData( + JSContext* aCx, JS::MutableHandle aData) const { nsIGlobalObject* global = mozilla::dom::GetIncumbentGlobal(); - if (global) { - return global->GetGlobalJSObject(); + if (!global) { + aData.set(nullptr); + return true; } - return nullptr; + + JS::Rooted incumbentGlobal(aCx, global->GetGlobalJSObject()); + + if (!incumbentGlobal) { + aData.set(nullptr); + return true; + } + + JSAutoRealm ar(aCx, incumbentGlobal); + + JS::Rooted objResult(aCx, + JS_NewObject(aCx, &sHostDefinedDataClass)); + if (!objResult) { + aData.set(nullptr); + return false; + } + + JS_SetReservedSlot(objResult, INCUMBENT_SETTING_SLOT, + JS::ObjectValue(*incumbentGlobal)); + aData.set(objResult); + + return true; } bool CycleCollectedJSContext::enqueuePromiseJob( JSContext* aCx, JS::HandleObject aPromise, JS::HandleObject aJob, - JS::HandleObject aAllocationSite, JS::HandleObject aIncumbentGlobal) { + JS::HandleObject aAllocationSite, JS::HandleObject hostDefinedData) { MOZ_ASSERT(aCx == Context()); MOZ_ASSERT(Get() == this); nsIGlobalObject* global = nullptr; - if (aIncumbentGlobal) { - global = xpc::NativeGlobal(aIncumbentGlobal); + + if (hostDefinedData) { + MOZ_RELEASE_ASSERT(JS::GetClass(hostDefinedData.get()) == + &sHostDefinedDataClass); + JS::Value incumbentGlobal = + JS::GetReservedSlot(hostDefinedData.get(), INCUMBENT_SETTING_SLOT); + // hostDefinedData is only created when incumbent global exists. + MOZ_ASSERT(incumbentGlobal.isObject()); + global = xpc::NativeGlobal(&incumbentGlobal.toObject()); } + JS::RootedObject jobGlobal(aCx, JS::CurrentGlobalOrNull(aCx)); RefPtr runnable = new PromiseJobRunnable( aPromise, aJob, jobGlobal, aAllocationSite, global); @@ -892,18 +946,29 @@ void FinalizationRegistryCleanup::Init() { /* static */ void FinalizationRegistryCleanup::QueueCallback(JSFunction* aDoCleanup, - JSObject* aIncumbentGlobal, + JSObject* aHostDefinedData, void* aData) { FinalizationRegistryCleanup* cleanup = static_cast(aData); - cleanup->QueueCallback(aDoCleanup, aIncumbentGlobal); + cleanup->QueueCallback(aDoCleanup, aHostDefinedData); } void FinalizationRegistryCleanup::QueueCallback(JSFunction* aDoCleanup, - JSObject* aIncumbentGlobal) { + JSObject* aHostDefinedData) { bool firstCallback = mCallbacks.empty(); - MOZ_ALWAYS_TRUE(mCallbacks.append(Callback{aDoCleanup, aIncumbentGlobal})); + JSObject* incumbentGlobal = nullptr; + + // Extract incumbentGlobal from aHostDefinedData. + if (aHostDefinedData) { + MOZ_RELEASE_ASSERT(JS::GetClass(aHostDefinedData) == + &sHostDefinedDataClass); + JS::Value global = + JS::GetReservedSlot(aHostDefinedData, INCUMBENT_SETTING_SLOT); + incumbentGlobal = &global.toObject(); + } + + MOZ_ALWAYS_TRUE(mCallbacks.append(Callback{aDoCleanup, incumbentGlobal})); if (firstCallback) { RefPtr cleanup = new CleanupRunnable(this); diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h index 43ecbd85cb60..038f3c0d4e98 100644 --- a/xpcom/base/CycleCollectedJSContext.h +++ b/xpcom/base/CycleCollectedJSContext.h @@ -108,11 +108,11 @@ class FinalizationRegistryCleanup { explicit FinalizationRegistryCleanup(CycleCollectedJSContext* aContext); void Init(); void Destroy(); - void QueueCallback(JSFunction* aDoCleanup, JSObject* aIncumbentGlobal); + void QueueCallback(JSFunction* aDoCleanup, JSObject* aHostDefinedData); MOZ_CAN_RUN_SCRIPT void DoCleanup(); private: - static void QueueCallback(JSFunction* aDoCleanup, JSObject* aIncumbentGlobal, + static void QueueCallback(JSFunction* aDoCleanup, JSObject* aHostDefinedData, void* aData); class CleanupRunnable; @@ -298,11 +298,13 @@ class CycleCollectedJSContext : dom::PerThreadAtomCache, private JS::JobQueue { // Others protect the debuggee microtask queue from the debugger's // interruptions; see the comments on JS::AutoDebuggerJobQueueInterruption for // details. - JSObject* getIncumbentGlobal(JSContext* cx) override; + bool getHostDefinedData(JSContext* cx, + JS::MutableHandle aData) const override; + bool enqueuePromiseJob(JSContext* cx, JS::Handle promise, JS::Handle job, JS::Handle allocationSite, - JS::Handle incumbentGlobal) override; + JS::Handle hostDefinedData) override; // MOZ_CAN_RUN_SCRIPT_BOUNDARY for now so we don't have to change SpiderMonkey // headers. The caller presumably knows this can run script (like everything // in SpiderMonkey!) and will deal.