Bug 1928412 - Implemenet the HostDefined object that will be carried around for Javascript callbacks r=arai

This patch implements the `HostDefined` object in
step 5 of https://html.spec.whatwg.org/#hostmakejobcallback. Currently,
`incumbent global` is the only possible member for this object.

Differential Revision: https://phabricator.services.mozilla.com/D224959
This commit is contained in:
Sean Feng
2024-11-25 14:10:41 +00:00
parent 997fb832b3
commit 24e03c93dd
15 changed files with 187 additions and 103 deletions

View File

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

View File

@@ -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<JSObject*> 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()

View File

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

View File

@@ -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<GlobalObject*> global(cx);
if (JSObject* objectFromIncumbentGlobal =
reaction->getAndClearIncumbentGlobalObject()) {
objectFromIncumbentGlobal = CheckedUnwrapStatic(objectFromIncumbentGlobal);
MOZ_ASSERT(objectFromIncumbentGlobal);
global = &objectFromIncumbentGlobal->nonCCWGlobal();
JS::Rooted<JSObject*> 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<GlobalObject*> incumbentGlobal(cx,
cx->runtime()->getIncumbentGlobal(cx));
Rooted<JSObject*> 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<GlobalObject*> incumbentGlobal(cx,
cx->runtime()->getIncumbentGlobal(cx));
Rooted<JSObject*> 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();
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<JSObject*> 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_; }

View File

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

View File

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

View File

@@ -573,22 +573,16 @@ SharedScriptDataTableHolder& JSRuntime::scriptDataTableHolder() {
return scriptDataTableHolder_;
}
GlobalObject* JSRuntime::getIncumbentGlobal(JSContext* cx) {
bool JSRuntime::getHostDefinedData(JSContext* cx,
JS::MutableHandle<JSObject*> data) const {
MOZ_ASSERT(cx->jobQueue);
JSObject* obj = cx->jobQueue->getIncumbentGlobal(cx);
if (!obj) {
return nullptr;
}
MOZ_ASSERT(obj->is<GlobalObject>(),
"getIncumbentGlobalCallback must return a global!");
return &obj->as<GlobalObject>();
return cx->jobQueue->getHostDefinedData(cx, data);
}
bool JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job,
HandleObject promise,
Handle<GlobalObject*> 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,

View File

@@ -441,10 +441,12 @@ struct JSRuntime {
js::UnprotectedData<JS::ConsumeStreamCallback> consumeStreamCallback;
js::UnprotectedData<JS::ReportStreamErrorCallback> reportStreamErrorCallback;
js::GlobalObject* getIncumbentGlobal(JSContext* cx);
bool getHostDefinedData(JSContext* cx,
JS::MutableHandle<JSObject*> data) const;
bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job,
js::HandleObject promise,
js::Handle<js::GlobalObject*> incumbentGlobal);
js::HandleObject hostDefinedData);
void addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
void removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);

View File

@@ -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<JSObject*> aData) const {
nsIGlobalObject* global = mozilla::dom::GetIncumbentGlobal();
if (global) {
return global->GetGlobalJSObject();
if (!global) {
aData.set(nullptr);
return true;
}
return nullptr;
JS::Rooted<JSObject*> incumbentGlobal(aCx, global->GetGlobalJSObject());
if (!incumbentGlobal) {
aData.set(nullptr);
return true;
}
JSAutoRealm ar(aCx, incumbentGlobal);
JS::Rooted<JSObject*> 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<PromiseJobRunnable> 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<FinalizationRegistryCleanup*>(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<CleanupRunnable> cleanup = new CleanupRunnable(this);

View File

@@ -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<JSObject*> aData) const override;
bool enqueuePromiseJob(JSContext* cx, JS::Handle<JSObject*> promise,
JS::Handle<JSObject*> job,
JS::Handle<JSObject*> allocationSite,
JS::Handle<JSObject*> incumbentGlobal) override;
JS::Handle<JSObject*> 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.