fune/dom/promise/tests/file_promise_argument_tests.js
Boris Zbarsky a6fdc48869 Bug 1436276. Bindings should create their return promises in the current compartment even when called over Xrays. r=bholley
These are cases that are implementing the "convert an exception to a Promise"
steps of the Web IDL spec.  Typically the exception is thrown in the current
compartment; the Promise returned should simply match that.  Otherwise we can
end up with a situation in which the promise doesn't actaully have access to
its rejection value, which will cause problems if someone uses then() on the
promise: the return value of the then() call will get a sanitized exception
instead of the real one.

We _could_ try to match the actual compartment of the exception, in theory.
But it's not clear why this would be preferable to using the current
compartment, even if there were cases in which the exception _doesn't_ match
the current compartment.  Which there likely are not.

MozReview-Commit-ID: Ac2BHIHxfvY

--HG--
rename : dom/promise/tests/test_promise_argument.html => dom/promise/tests/test_promise_retval.html
rename : dom/promise/tests/test_promise_argument_xrays.html => dom/promise/tests/test_promise_retval_xrays.html
2018-02-10 01:34:10 -05:00

134 lines
5 KiB
JavaScript

/*
* This file is meant to provide common infrastructure for several consumers.
* The consumer is expected to define the following things:
*
* 1) An verifyPromiseGlobal function which does whatever test the consumer
* wants.
* 2) An isXrayArgumentTest global boolean, because some of these tests act
* differenly based on that boolean.
* 3) A function named getPromise. This function is given a global object and a
* single argument to use for getting the promise. The getPromise function
* is expected to trigger the canonical Promise.resolve for the given global
* with the given argument in some way that depends on the test, and return
* the result.
* 4) A subframe (frames[0]) which can be used as a second global for creating
* promises.
*/
var label = "parent";
function passBasicPromise() {
var p1 = Promise.resolve();
verifyPromiseGlobal(p1, window, "Promise.resolve return value 1");
var p2 = getPromise(window, p1);
is(p1, p2, "Basic promise should just pass on through");
return p2;
}
function passPrimitive(global) {
var p = getPromise(global, 5);
verifyPromiseGlobal(p, global, "Promise wrapping primitive");
return p.then(function(arg) {
is(arg, 5, "Should have the arg we passed in");
});
}
function passThenable(global) {
var called = false;
var thenable = {
then: function(f) {
called = true;
f(7);
}
};
var p = getPromise(global, thenable);
verifyPromiseGlobal(p, global, "Promise wrapping thenable");
return p.then(function(arg) {
ok(called, "Thenable should have been called");
is(arg, 7, "Should have the arg our thenable passed in");
});
}
function passWrongPromiseWithMatchingConstructor() {
var p1 = Promise.resolve();
verifyPromiseGlobal(p1, window, "Promise.resolve() return value 2");
p1.constructor = frames[0].Promise;
var p2 = getPromise(frames[0], p1);
// The behavior here will depend on whether we're touching frames[0] via Xrays
// or not. If we are not, the current compartment while getting our promise
// will be that of frames[0]. If we are, it will be our window's compartment.
if (isXrayArgumentTest) {
isnot(p1, p2, "Should have wrapped the Promise in a new promise, because its constructor is not matching the current-compartment Promise constructor");
verifyPromiseGlobal(p2, window, "Promise wrapping xrayed promise with therefore non-matching constructor");
} else {
is(p1, p2, "Should have left the Promise alone because its constructor matched");
}
return p2;
}
function passCorrectPromiseWithMismatchedConstructor() {
var p1 = Promise.resolve(9);
verifyPromiseGlobal(p1, window, "Promise.resolve() return value 3");
p1.constructor = frames[0].Promise;
var p2 = getPromise(window, p1);
isnot(p1, p2,
"Should have wrapped promise in a new promise, since its .constructor was wrong");
verifyPromiseGlobal(p2, window,
"Promise wrapping passed-in promise with mismatched constructor");
return p2.then(function(arg) {
is(arg, 9, "Should have propagated along our resolution value");
});
}
function passPromiseToOtherGlobal() {
var p1 = Promise.resolve();
verifyPromiseGlobal(p1, window, "Promise.resolve() return value 4");
var p2 = getPromise(frames[0], p1);
// The behavior here will depend on whether we're touching frames[0] via Xrays
// or not. If we are not, the current compartment while getting our promise
// will be that of frames[0]. If we are, it will be our window's compartment.
if (isXrayArgumentTest) {
is(p1, p2, "Should have left the Promise alone, because its constructor matches the current compartment's constructor");
} else {
isnot(p1, p2, "Should have wrapped promise in a promise from the other global");
verifyPromiseGlobal(p2, frames[0],
"Promise wrapping passed-in basic promise");
}
return p2;
}
function passPromiseSubclass() {
class PromiseSubclass extends Promise {
constructor(func) {
super(func);
}
}
var p1 = PromiseSubclass.resolve(11);
verifyPromiseGlobal(p1, window, "PromiseSubclass.resolve() return value");
var p2 = getPromise(window, p1);
isnot(p1, p2,
"Should have wrapped promise subclass in a new promise");
verifyPromiseGlobal(p2, window,
"Promise wrapping passed-in promise subclass");
return p2.then(function(arg) {
is(arg, 11, "Should have propagated along our resolution value from subclass");
});
}
function runPromiseArgumentTests(finishFunc) {
Promise.resolve()
.then(passBasicPromise)
.then(passPrimitive.bind(undefined, window))
.then(passPrimitive.bind(undefined, frames[0]))
.then(passThenable.bind(undefined, window))
.then(passThenable.bind(undefined, frames[0]))
.then(passWrongPromiseWithMatchingConstructor)
.then(passCorrectPromiseWithMismatchedConstructor)
.then(passPromiseToOtherGlobal)
.then(passPromiseSubclass)
.then(finishFunc)
.catch(function(e) {
ok(false, `Exception thrown: ${e}@${location.pathname}:${e.lineNumber}:${e.columnNumber}`);
finishFunc();
});
}