Bug 1515754 - Enter the reaction's realm in EnqueuePromiseReactionJob before creating the job. r=arai

This is consistent with what we do for cross-compartment wrappers and avoids
problems with running jobs against a dying global (Gecko drops such jobs).

I added a globalOfFirstJobInQueue() shell function so I could write a test that
checks both the compartment-per-global and single-compartment cases.

Differential Revision: https://phabricator.services.mozilla.com/D15176

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jan de Mooij 2018-12-22 21:42:21 +00:00
Родитель ecaf5bdea0
Коммит 1d8954ace9
3 изменённых файлов: 61 добавлений и 0 удалений

Просмотреть файл

@ -959,6 +959,13 @@ MOZ_MUST_USE static bool EnqueuePromiseReactionJob(
if (!IsProxy(reactionObj)) {
MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>());
reaction = &reactionObj->as<PromiseReactionRecord>();
if (cx->realm() != reaction->realm()) {
// If the compartment has multiple realms, create the job in the
// reaction's realm. This is consistent with the code in the else-branch
// and avoids problems with running jobs against a dying global (Gecko
// drops such jobs).
ar.emplace(cx, reaction);
}
} else {
JSObject* unwrappedReactionObj = UncheckedUnwrap(reactionObj);
if (JS_IsDeadWrapper(unwrappedReactionObj)) {

Просмотреть файл

@ -0,0 +1,31 @@
// Test that jobs added to the promise job queue have a global that's consistent
// with and without same-compartment realms.
var g1 = newGlobal();
var g2 = newGlobal({sameCompartmentAs: this});
// EnqueuePromiseReactionJob, handler is a primitive.
// Job's global is the reaction's global.
function test1(g) {
var resolve;
var p = new Promise(res => { resolve = res; });
g.Promise.prototype.then.call(p, 1);
resolve();
assertEq(globalOfFirstJobInQueue(), g);
drainJobQueue();
}
test1(g1);
test1(g2);
// EnqueuePromiseReactionJob, handler is an object.
// Job's global is the handler's global.
function test2(g) {
var resolve;
var p = new Promise(res => { resolve = res; });
p.then(new g.Function());
resolve();
assertEq(globalOfFirstJobInQueue(), g);
drainJobQueue();
}
test2(g1);
test2(g2);

Просмотреть файл

@ -1036,6 +1036,24 @@ static bool DrainJobQueue(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
static bool GlobalOfFirstJobInQueue(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (cx->jobQueue->empty()) {
JS_ReportErrorASCII(cx, "Job queue is empty");
return false;
}
RootedObject job(cx, cx->jobQueue->front());
RootedObject global(cx, &job->nonCCWGlobal());
if (!cx->compartment()->wrap(cx, &global)) {
return false;
}
args.rval().setObject(*global);
return true;
}
static void ForwardingPromiseRejectionTrackerCallback(
JSContext* cx, JS::HandleObject promise,
JS::PromiseRejectionHandlingState state, void* data) {
@ -8617,6 +8635,11 @@ JS_FN_HELP("parseBin", BinParse, 1, 0,
"enqueueJob(fn)",
" Enqueue 'fn' on the shell's job queue."),
JS_FN_HELP("globalOfFirstJobInQueue", GlobalOfFirstJobInQueue, 0, 0,
"globalOfFirstJobInQueue()",
" Returns the global of the first item in the job queue. Throws an exception\n"
" if the queue is empty.\n"),
JS_FN_HELP("drainJobQueue", DrainJobQueue, 0, 0,
"drainJobQueue()",
"Take jobs from the shell's job queue in FIFO order and run them until the\n"