Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

function createSandbox() {
const uri = Services.io.newURI("https://example.com");
const principal = Services.scriptSecurityManager.createContentPrincipal(
uri,
{}
);
return new Cu.Sandbox(principal, {});
}
add_task(async function testReactionJob() {
const sandbox = createSandbox();
sandbox.eval(`
var testPromise = Promise.resolve(10);
`);
// Calling `Promise.prototype.then` from sandbox performs GetFunctionRealm
// on wrapped `resolve` in sandbox realm, and it fails to unwrap the security
// wrapper. The reaction job should be created with sandbox realm.
const p = new Promise(resolve => {
sandbox.resolve = resolve;
sandbox.eval(`
testPromise.then(resolve);
`);
});
const result = await p;
equal(result, 10);
});
add_task(async function testReactionJobNuked() {
const sandbox = createSandbox();
sandbox.eval(`
var testPromise = Promise.resolve(10);
`);
// Calling `Promise.prototype.then` from sandbox performs GetFunctionRealm
// on wrapped `resolve` in sandbox realm, and it fails to unwrap the security
// wrapper. The reaction job should be created with sandbox realm.
const p1 = new Promise(resolve => {
sandbox.resolve = resolve;
sandbox.eval(`
testPromise.then(resolve);
`);
// Given the reaction job is created with the sandbox realm, nuking the
// sandbox prevents the job gets executed.
Cu.nukeSandbox(sandbox);
});
const p2 = Promise.resolve(11);
// Given the p1 doesn't get resolved, p2 should win.
const result = await Promise.race([p1, p2]);
equal(result, 11);
});
add_task(async function testReactionJobWithXray() {
const sandbox = createSandbox();
sandbox.eval(`
var testPromise = Promise.resolve(10);
`);
// Calling `Promise.prototype.then` from privileged realm via Xray uses
// privileged `Promise.prototype.then` function, and GetFunctionRealm
// performed there successfully gets top-level realm. The reaction job
// should be created with top-level realm.
const result = await new Promise(resolve => {
sandbox.testPromise.then(resolve);
// Given the reaction job is created with the top-level realm, nuking the
// sandbox doesn't affect the reaction job.
Cu.nukeSandbox(sandbox);
});
equal(result, 10);
});
add_task(async function testBoundReactionJob() {
const sandbox = createSandbox();
sandbox.eval(`
var resolve = undefined;
var callbackPromise = new Promise(r => { resolve = r; });
var callback = function (v) { resolve(v + 1); };
`);
// Create a bound function where its realm is privileged realm, and
// its target is from sandbox realm.
sandbox.bound_callback = Function.prototype.bind.call(
sandbox.callback,
sandbox
);
// Calling `Promise.prototype.then` from sandbox performs GetFunctionRealm
// and it fails. The reaction job should be created with sandbox realm.
sandbox.eval(`
Promise.resolve(10).then(bound_callback);
`);
const result = await sandbox.callbackPromise;
equal(result, 11);
});
add_task(async function testThenableJob() {
const sandbox = createSandbox();
const p = new Promise(resolve => {
// Create a bound function where its realm is privileged realm, and
// its target is from sandbox realm.
sandbox.then = function () {
resolve(10);
};
});
// Creating a promise thenable job in the following `Promise.resolve` performs
// GetFunctionRealm on the bound thenable.then and fails. The reaction job
// should be created with sandbox realm.
sandbox.eval(`
var thenable = {
then: then,
};
Promise.resolve(thenable);
`);
const result = await p;
equal(result, 10);
});
add_task(async function testThenableJobNuked() {
const sandbox = createSandbox();
let called = false;
sandbox.then = function () {
called = true;
};
// Creating a promise thenable job in the following `Promise.resolve` performs
// GetFunctionRealm on the bound thenable.then and fails. The reaction job
// should be created with sandbox realm.
sandbox.eval(`
var thenable = {
then: then,
};
Promise.resolve(thenable);
`);
Cu.nukeSandbox(sandbox);
// Drain the job queue, to make sure we hit dead object error inside the
// thenable job.
await Promise.resolve(10);
equal(
Services.console.getMessageArray().find(x => {
return x.toString().includes("can't access dead object");
}) !== undefined,
true
);
equal(called, false);
});
add_task(async function testThenableJobAccessError() {
const sandbox = createSandbox();
let accessed = false;
sandbox.thenable = {
get then() {
accessed = true;
return undefined;
},
};
// The following operation silently fails when accessing `then` property.
sandbox.eval(`
var x = typeof thenable.then;
Promise.resolve(thenable);
`);
equal(accessed, false);
});
add_task(async function testBoundThenableJob() {
const sandbox = createSandbox();
sandbox.eval(`
var resolve = undefined;
var callbackPromise = new Promise(r => { resolve = r; });
var callback = function (v) { resolve(v + 1); };
var then = function(onFulfilled, onRejected) {
onFulfilled(10);
};
`);
// Create a bound function where its realm is privileged realm, and
// its target is from sandbox realm.
sandbox.bound_then = Function.prototype.bind.call(sandbox.then, sandbox);
// Creating a promise thenable job in the following `Promise.resolve` performs
// GetFunctionRealm on the bound thenable.then and fails. The reaction job
// should be created with sandbox realm.
sandbox.eval(`
var thenable = {
then: bound_then,
};
Promise.resolve(thenable).then(callback);
`);
const result = await sandbox.callbackPromise;
equal(result, 11);
});