Bug 1170760 part 11. Add subclassing support to Promise::Reject. r=baku,efaust

This commit is contained in:
Boris Zbarsky 2015-11-25 15:48:09 -05:00
parent 93faa5b1b0
commit 0bbce93c83
8 changed files with 79 additions and 19 deletions

View File

@ -273,7 +273,7 @@ var RemoteTabViewer = {
}.bind(this);
return CloudSync().tabs.getRemoteTabs()
.then(updateTabList, Promise.reject);
.then(updateTabList, Promise.reject.bind(Promise));
},
adjustContextMenu: function (event) {

View File

@ -1092,22 +1092,56 @@ Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
return promise.forget();
}
/* static */ already_AddRefed<Promise>
/* static */ void
Promise::Reject(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
JS::Handle<JS::Value> aValue,
JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv)
{
// Implementation of
// http://www.ecma-international.org/ecma-262/6.0/#sec-promise.reject
JSContext* cx = aGlobal.Context();
nsCOMPtr<nsIGlobalObject> global =
do_QueryInterface(aGlobal.GetAsSupports());
if (!global) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
return;
}
RefPtr<Promise> p = Reject(global, aGlobal.Context(), aValue, aRv);
if (p) {
p->mRejectionStack = p->mAllocationStack;
// Steps 1 and 2.
if (!aThisv.isObject()) {
aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
return;
}
return p.forget();
// Step 3.
PromiseCapability capability(cx);
NewPromiseCapability(cx, global, aThisv, false, capability, aRv);
// Step 4.
if (aRv.Failed()) {
return;
}
// Step 5.
Promise* p = capability.mNativePromise;
if (p) {
p->MaybeRejectInternal(cx, aValue);
p->mRejectionStack = p->mAllocationStack;
} else {
JS::Rooted<JS::Value> value(cx, aValue);
JS::Rooted<JS::Value> ignored(cx);
if (!JS::Call(cx, JS::UndefinedHandleValue /* thisVal */,
capability.mReject, JS::HandleValueArray(value),
&ignored)) {
// Step 6.
aRv.NoteJSContextException();
return;
}
}
// Step 7.
aRetval.set(capability.PromiseValue());
}
/* static */ already_AddRefed<Promise>

View File

@ -172,9 +172,10 @@ public:
Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
JS::Handle<JS::Value> aValue, ErrorResult& aRv);
static already_AddRefed<Promise>
static void
Reject(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
JS::Handle<JS::Value> aValue, ErrorResult& aRv);
JS::Handle<JS::Value> aValue,
JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv);
static already_AddRefed<Promise>
Reject(nsIGlobalObject* aGlobal, JSContext* aCx,

View File

@ -17,7 +17,7 @@
<script type="application/javascript"><!--
function runTest() {
[{}, {}, {}, {}, {}].reduce(Promise.reject);
[{}, {}, {}, {}, {}].reduce(Promise.reject.bind(Promise));
ok(true, "No leaks with reject?");
[{}, {}, {}, {}, {}].reduce(Promise.resolve.bind(Promise));

View File

@ -205,6 +205,19 @@ function testResolve3() {
).then(nextTest);
}
function testReject1() {
var p = win.Promise.reject(5);
ok(p instanceof win.Promise, "Promise.reject should return a promise");
p.then(
function(arg) {
ok(false, "Promise should be rejected");
},
function(e) {
is(e, 5, "Should get correct Promise.reject value");
}
).then(nextTest);
}
var tests = [
testLoadComplete,
testHaveXray,
@ -220,6 +233,7 @@ var tests = [
testResolve1,
testResolve2,
testResolve3,
testReject1,
];
function nextTest() {

View File

@ -25,8 +25,8 @@ interface _Promise {
// return value of PromiseSubclass.resolve/reject to be a Promise object.
[NewObject, Throws]
static any resolve(optional any value);
[NewObject]
static Promise<void> reject(optional any value);
[NewObject, Throws]
static any reject(optional any value);
// The [TreatNonCallableAsNull] annotation is required since then() should do
// nothing instead of throwing errors when non-callable arguments are passed.

View File

@ -294,7 +294,7 @@ var RootFolder = function (rootId, rootName) {
result.parent = guidResult;
return Promise.resolve(result);
},
Promise.reject
Promise.reject.bind(Promise)
);
promises.push(promise);
});
@ -310,7 +310,7 @@ var RootFolder = function (rootId, rootName) {
result.annos = annos;
return Promise.resolve(result);
},
Promise.reject
Promise.reject.bind(Promise)
);
promises.push(promise);
});
@ -352,7 +352,7 @@ var RootFolder = function (rootId, rootName) {
result.parent = guidResult;
return Promise.resolve(result);
},
Promise.reject
Promise.reject.bind(Promise)
);
promises.push(promise);
});
@ -559,7 +559,7 @@ var RootFolder = function (rootId, rootName) {
continue;
}
let promise = exists(item).then(handleSortedItem, Promise.reject);
let promise = exists(item).then(handleSortedItem, Promise.reject.bind(Promise));
promises.push(promise);
}
@ -588,7 +588,7 @@ var RootFolder = function (rootId, rootName) {
function () {
return _createItem(root);
},
Promise.reject
Promise.reject.bind(Promise)
);
let items = [].concat(root._children);
@ -599,7 +599,7 @@ var RootFolder = function (rootId, rootName) {
function () {
return _createItem(item);
},
Promise.reject
Promise.reject.bind(Promise)
);
}
promises.push(promise);

View File

@ -206,4 +206,15 @@ promise_test(function testPromiseResolve() {
});
}, "Promise.resolve behavior");
promise_test(function testPromiseReject() {
clearLog();
var p = LoggingPromise.reject(5);
var log = takeLog();
assert_array_equals(log, ["Reject 1", "Constructor 1"]);
return p.catch(function(arg) {
assert_equals(arg, 5);
});
}, "Promise.reject behavior");
</script>