summaryrefslogtreecommitdiffstats
path: root/dom
diff options
context:
space:
mode:
authorNew Tobin Paradigm <email@mattatobin.com>2019-12-18 23:07:31 -0500
committerGitHub <noreply@github.com>2019-12-18 23:07:31 -0500
commit3332f584c829aaac455d688c6c073937442c4c7f (patch)
tree741e4325721d1d8d9970e075320262097d3cbeb1 /dom
parent8220776c9b2e8291f0414f98ac20fbda30e00d2e (diff)
parentc7ae364a887e9fb77196596d6e4345b455ab209f (diff)
downloadUXP-3332f584c829aaac455d688c6c073937442c4c7f.tar
UXP-3332f584c829aaac455d688c6c073937442c4c7f.tar.gz
UXP-3332f584c829aaac455d688c6c073937442c4c7f.tar.lz
UXP-3332f584c829aaac455d688c6c073937442c4c7f.tar.xz
UXP-3332f584c829aaac455d688c6c073937442c4c7f.zip
Merge pull request #1330 from MoonchildProductions/DOM-promise-removal
DOM `Promise` removal
Diffstat (limited to 'dom')
-rw-r--r--dom/bindings/BindingUtils.cpp37
-rw-r--r--dom/bindings/Bindings.conf4
-rw-r--r--dom/bindings/Codegen.py488
-rw-r--r--dom/bindings/Configuration.py7
-rw-r--r--dom/bindings/Errors.msg5
-rw-r--r--dom/bindings/ToJSValue.cpp6
-rw-r--r--dom/bindings/ToJSValue.h2
-rw-r--r--dom/bindings/TypedArray.h2
-rw-r--r--dom/bindings/parser/WebIDL.py96
-rw-r--r--dom/bindings/parser/tests/test_distinguishability.py1
-rw-r--r--dom/bindings/parser/tests/test_promise.py4
-rw-r--r--dom/promise/Promise.cpp2312
-rw-r--r--dom/promise/Promise.h319
-rw-r--r--dom/promise/PromiseCallback.cpp571
-rw-r--r--dom/promise/PromiseCallback.h203
-rw-r--r--dom/promise/PromiseDebugging.cpp221
-rw-r--r--dom/promise/PromiseDebugging.h20
-rw-r--r--dom/promise/moz.build3
-rw-r--r--dom/webidl/Promise.webidl61
-rw-r--r--dom/webidl/PromiseDebugging.webidl44
-rw-r--r--dom/webidl/TestInterfaceJS.webidl2
-rw-r--r--dom/webidl/moz.build4
22 files changed, 293 insertions, 4119 deletions
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp
index 6e0db29b1..b244d4d2a 100644
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2976,42 +2976,6 @@ ConvertExceptionToPromise(JSContext* cx,
JSObject* promiseScope,
JS::MutableHandle<JS::Value> rval)
{
-#ifndef SPIDERMONKEY_PROMISE
- GlobalObject global(cx, promiseScope);
- if (global.Failed()) {
- return false;
- }
-
- JS::Rooted<JS::Value> exn(cx);
- if (!JS_GetPendingException(cx, &exn)) {
- // This is very important: if there is no pending exception here but we're
- // ending up in this code, that means the callee threw an uncatchable
- // exception. Just propagate that out as-is.
- return false;
- }
-
- JS_ClearPendingException(cx);
-
- nsCOMPtr<nsIGlobalObject> globalObj =
- do_QueryInterface(global.GetAsSupports());
- if (!globalObj) {
- ErrorResult rv;
- rv.Throw(NS_ERROR_UNEXPECTED);
- return !rv.MaybeSetPendingException(cx);
- }
-
- ErrorResult rv;
- RefPtr<Promise> promise = Promise::Reject(globalObj, cx, exn, rv);
- if (rv.MaybeSetPendingException(cx)) {
- // We just give up. We put the exception from the ErrorResult on
- // the JSContext just to make sure to not leak memory on the
- // ErrorResult, but now just put the original exception back.
- JS_SetPendingException(cx, exn);
- return false;
- }
-
- return GetOrCreateDOMReflector(cx, promise, rval);
-#else // SPIDERMONKEY_PROMISE
{
JSAutoCompartment ac(cx, promiseScope);
@@ -3037,7 +3001,6 @@ ConvertExceptionToPromise(JSContext* cx,
// Now make sure we rewrap promise back into the compartment we want
return JS_WrapValue(cx, rval);
-#endif // SPIDERMONKEY_PROMISE
}
/* static */
diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf
index 6f9733c5f..b00af2085 100644
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -694,10 +694,6 @@ DOMInterfaces = {
'headerFile': 'nsGeolocation.h'
},
-'Promise': {
- 'implicitJSContext': [ 'then', 'catch' ],
-},
-
'PromiseDebugging': {
'concrete': False,
},
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py
index 924241aa0..6b23e8225 100644
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1135,6 +1135,12 @@ class CGHeaders(CGWrapper):
declareIncludes.add("mozilla/dom/Date.h")
else:
bindingHeaders.add("mozilla/dom/Date.h")
+ elif unrolled.isPromise():
+ # See comment in the isInterface() case for why we add
+ # Promise.h to headerSet, not bindingHeaders.
+ headerSet.add("mozilla/dom/Promise.h")
+ # We need ToJSValue to do the Promise to JS conversion.
+ bindingHeaders.add("mozilla/dom/ToJSValue.h")
elif unrolled.isInterface():
if unrolled.isSpiderMonkeyInterface():
bindingHeaders.add("jsfriendapi.h")
@@ -1352,7 +1358,11 @@ def UnionTypes(unionTypes, config):
headers.add("mozilla/dom/Nullable.h")
isSequence = f.isSequence()
f = f.unroll()
- if f.isInterface():
+ if f.isPromise():
+ headers.add("mozilla/dom/Promise.h")
+ # We need ToJSValue to do the Promise to JS conversion.
+ headers.add("mozilla/dom/ToJSValue.h")
+ elif f.isInterface():
if f.isSpiderMonkeyInterface():
headers.add("jsfriendapi.h")
headers.add("mozilla/dom/TypedArray.h")
@@ -1434,7 +1444,11 @@ def UnionConversions(unionTypes, config):
def addHeadersForType(f):
f = f.unroll()
- if f.isInterface():
+ if f.isPromise():
+ headers.add("mozilla/dom/Promise.h")
+ # We need ToJSValue to do the Promise to JS conversion.
+ headers.add("mozilla/dom/ToJSValue.h")
+ elif f.isInterface():
if f.isSpiderMonkeyInterface():
headers.add("jsfriendapi.h")
headers.add("mozilla/dom/TypedArray.h")
@@ -3052,23 +3066,6 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
else:
unforgeableHolderSetup = None
- if self.descriptor.name == "Promise":
- speciesSetup = CGGeneric(fill(
- """
- #ifndef SPIDERMONKEY_PROMISE
- JS::Rooted<JSObject*> promiseConstructor(aCx, *interfaceCache);
- JS::Rooted<jsid> species(aCx,
- SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species)));
- if (!JS_DefinePropertyById(aCx, promiseConstructor, species, JS::UndefinedHandleValue,
- JSPROP_SHARED, Promise::PromiseSpecies, nullptr)) {
- $*{failureCode}
- }
- #endif // SPIDERMONKEY_PROMISE
- """,
- failureCode=failureCode))
- else:
- speciesSetup = None
-
if (self.descriptor.interface.isOnGlobalProtoChain() and
needInterfacePrototypeObject):
makeProtoPrototypeImmutable = CGGeneric(fill(
@@ -3094,7 +3091,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
return CGList(
[getParentProto, getConstructorProto, initIds,
prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup,
- speciesSetup, makeProtoPrototypeImmutable],
+ makeProtoPrototypeImmutable],
"\n").define()
@@ -5244,6 +5241,139 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
holderArgs=holderArgs,
dealWithOptional=isOptional and (not nullable or isOwningUnion))
+ if type.isPromise():
+ assert not type.nullable()
+ assert defaultValue is None
+
+ # We always have to hold a strong ref to Promise here, because
+ # Promise::resolve returns an addrefed thing.
+ argIsPointer = isCallbackReturnValue
+ if argIsPointer:
+ declType = CGGeneric("RefPtr<Promise>")
+ else:
+ declType = CGGeneric("OwningNonNull<Promise>")
+
+ # Per spec, what we're supposed to do is take the original
+ # Promise.resolve and call it with the original Promise as this
+ # value to make a Promise out of whatever value we actually have
+ # here. The question is which global we should use. There are
+ # several cases to consider:
+ #
+ # 1) Normal call to API with a Promise argument. This is a case the
+ # spec covers, and we should be using the current Realm's
+ # Promise. That means the current compartment.
+ # 2) Call to API with a Promise argument over Xrays. In practice,
+ # this sort of thing seems to be used for giving an API
+ # implementation a way to wait for conclusion of an asyc
+ # operation, _not_ to expose the Promise to content code. So we
+ # probably want to allow callers to use such an API in a
+ # "natural" way, by passing chrome-side promises; indeed, that
+ # may be all that the caller has to represent their async
+ # operation. That means we really need to do the
+ # Promise.resolve() in the caller (chrome) compartment: if we do
+ # it in the content compartment, we will try to call .then() on
+ # the chrome promise while in the content compartment, which will
+ # throw and we'll just get a rejected Promise. Note that this is
+ # also the reason why a caller who has a chrome Promise
+ # representing an async operation can't itself convert it to a
+ # content-side Promise (at least not without some serious
+ # gyrations).
+ # 3) Promise return value from a callback or callback interface.
+ # Per spec, this should use the Realm of the callback object. In
+ # our case, that's the compartment of the underlying callback,
+ # not the current compartment (which may be the compartment of
+ # some cross-compartment wrapper around said callback).
+ # 4) Return value from a JS-implemented interface. In this case we
+ # have a problem. Our current compartment is the compartment of
+ # the JS implementation. But if the JS implementation returned
+ # a page-side Promise (which is a totally sane thing to do, and
+ # in fact the right thing to do given that this return value is
+ # going right to content script) then we don't want to
+ # Promise.resolve with our current compartment Promise, because
+ # that will wrap it up in a chrome-side Promise, which is
+ # decidedly _not_ what's desired here. So in that case we
+ # should really unwrap the return value and use the global of
+ # the result. CheckedUnwrap should be good enough for that; if
+ # it fails, then we're failing unwrap while in a
+ # system-privileged compartment, so presumably we have a dead
+ # object wrapper. Just error out. Do NOT fall back to using
+ # the current compartment instead: that will return a
+ # system-privileged rejected (because getting .then inside
+ # resolve() failed) Promise to the caller, which they won't be
+ # able to touch. That's not helpful. If we error out, on the
+ # other hand, they will get a content-side rejected promise.
+ # Same thing if the value returned is not even an object.
+ if isCallbackReturnValue == "JSImpl":
+ # Case 4 above. Note that globalObj defaults to the current
+ # compartment global. Note that we don't use $*{exceptionCode}
+ # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED)
+ # which we don't really want here.
+ assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n"
+ getPromiseGlobal = fill(
+ """
+ if (!$${val}.isObject()) {
+ aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
+ return nullptr;
+ }
+ JSObject* unwrappedVal = js::CheckedUnwrap(&$${val}.toObject());
+ if (!unwrappedVal) {
+ // A slight lie, but not much of one, for a dead object wrapper.
+ aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
+ return nullptr;
+ }
+ globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal);
+ """,
+ sourceDescription=sourceDescription)
+ elif isCallbackReturnValue == "Callback":
+ getPromiseGlobal = dedent(
+ """
+ // We basically want our entry global here. Play it safe
+ // and use GetEntryGlobal() to get it, with whatever
+ // principal-clamping it ends up doing.
+ globalObj = GetEntryGlobal()->GetGlobalJSObject();
+ """)
+ else:
+ getPromiseGlobal = ""
+
+ templateBody = fill(
+ """
+ { // Scope for our GlobalObject, FastErrorResult, JSAutoCompartment,
+ // etc.
+
+ JS::Rooted<JSObject*> globalObj(cx, JS::CurrentGlobalOrNull(cx));
+ $*{getPromiseGlobal}
+ JSAutoCompartment ac(cx, globalObj);
+ GlobalObject promiseGlobal(cx, globalObj);
+ if (promiseGlobal.Failed()) {
+ $*{exceptionCode}
+ }
+
+ JS::Rooted<JS::Value> valueToResolve(cx, $${val});
+ if (!JS_WrapValue(cx, &valueToResolve)) {
+ $*{exceptionCode}
+ }
+ binding_detail::FastErrorResult promiseRv;
+ nsCOMPtr<nsIGlobalObject> global =
+ do_QueryInterface(promiseGlobal.GetAsSupports());
+ if (!global) {
+ promiseRv.Throw(NS_ERROR_UNEXPECTED);
+ promiseRv.MaybeSetPendingException(cx);
+ $*{exceptionCode}
+ }
+ $${declName} = Promise::Resolve(global, cx, valueToResolve,
+ promiseRv);
+ if (promiseRv.MaybeSetPendingException(cx)) {
+ $*{exceptionCode}
+ }
+ }
+ """,
+ getPromiseGlobal=getPromiseGlobal,
+ exceptionCode=exceptionCode)
+
+ return JSToNativeConversionInfo(templateBody,
+ declType=declType,
+ dealWithOptional=isOptional)
+
if type.isGeckoInterface():
assert not isEnforceRange and not isClamp
@@ -5282,12 +5412,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
# Also, callback return values always end up addrefing anyway, so there
# is no point trying to avoid it here and it makes other things simpler
# since we can assume the return value is a strong ref.
- #
- # Finally, promises need to hold a strong ref because that's what
- # Promise.resolve returns.
assert not descriptor.interface.isCallback()
- isPromise = descriptor.interface.identifier.name == "Promise"
- forceOwningType = isMember or isCallbackReturnValue or isPromise
+ forceOwningType = isMember or isCallbackReturnValue
typeName = descriptor.nativeType
typePtr = typeName + "*"
@@ -5315,143 +5441,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if forceOwningType:
templateBody += 'static_assert(IsRefcounted<%s>::value, "We can only store refcounted classes.");' % typeName
- if isPromise:
- # Per spec, what we're supposed to do is take the original
- # Promise.resolve and call it with the original Promise as this
- # value to make a Promise out of whatever value we actually have
- # here. The question is which global we should use. There are
- # several cases to consider:
- #
- # 1) Normal call to API with a Promise argument. This is a case the
- # spec covers, and we should be using the current Realm's
- # Promise. That means the current compartment.
- # 2) Call to API with a Promise argument over Xrays. In practice,
- # this sort of thing seems to be used for giving an API
- # implementation a way to wait for conclusion of an asyc
- # operation, _not_ to expose the Promise to content code. So we
- # probably want to allow callers to use such an API in a
- # "natural" way, by passing chrome-side promises; indeed, that
- # may be all that the caller has to represent their async
- # operation. That means we really need to do the
- # Promise.resolve() in the caller (chrome) compartment: if we do
- # it in the content compartment, we will try to call .then() on
- # the chrome promise while in the content compartment, which will
- # throw and we'll just get a rejected Promise. Note that this is
- # also the reason why a caller who has a chrome Promise
- # representing an async operation can't itself convert it to a
- # content-side Promise (at least not without some serious
- # gyrations).
- # 3) Promise return value from a callback or callback interface.
- # This is in theory a case the spec covers but in practice it
- # really doesn't define behavior here because it doesn't define
- # what Realm we're in after the callback returns, which is when
- # the argument conversion happens. We will use the current
- # compartment, which is the compartment of the callable (which
- # may itself be a cross-compartment wrapper itself), which makes
- # as much sense as anything else. In practice, such an API would
- # once again be providing a Promise to signal completion of an
- # operation, which would then not be exposed to anyone other than
- # our own implementation code.
- # 4) Return value from a JS-implemented interface. In this case we
- # have a problem. Our current compartment is the compartment of
- # the JS implementation. But if the JS implementation returned
- # a page-side Promise (which is a totally sane thing to do, and
- # in fact the right thing to do given that this return value is
- # going right to content script) then we don't want to
- # Promise.resolve with our current compartment Promise, because
- # that will wrap it up in a chrome-side Promise, which is
- # decidedly _not_ what's desired here. So in that case we
- # should really unwrap the return value and use the global of
- # the result. CheckedUnwrap should be good enough for that; if
- # it fails, then we're failing unwrap while in a
- # system-privileged compartment, so presumably we have a dead
- # object wrapper. Just error out. Do NOT fall back to using
- # the current compartment instead: that will return a
- # system-privileged rejected (because getting .then inside
- # resolve() failed) Promise to the caller, which they won't be
- # able to touch. That's not helpful. If we error out, on the
- # other hand, they will get a content-side rejected promise.
- # Same thing if the value returned is not even an object.
- if isCallbackReturnValue == "JSImpl":
- # Case 4 above. Note that globalObj defaults to the current
- # compartment global. Note that we don't use $*{exceptionCode}
- # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED)
- # which we don't really want here.
- assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n"
- getPromiseGlobal = fill(
- """
- if (!$${val}.isObject()) {
- aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
- return nullptr;
- }
- JSObject* unwrappedVal = js::CheckedUnwrap(&$${val}.toObject());
- if (!unwrappedVal) {
- // A slight lie, but not much of one, for a dead object wrapper.
- aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
- return nullptr;
- }
- globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal);
- """,
- sourceDescription=sourceDescription)
- else:
- getPromiseGlobal = ""
-
- templateBody = fill(
- """
- { // Scope for our GlobalObject, FastErrorResult, JSAutoCompartment,
- // etc.
-
- JS::Rooted<JSObject*> globalObj(cx, JS::CurrentGlobalOrNull(cx));
- $*{getPromiseGlobal}
- JSAutoCompartment ac(cx, globalObj);
- GlobalObject promiseGlobal(cx, globalObj);
- if (promiseGlobal.Failed()) {
- $*{exceptionCode}
- }
-
- JS::Rooted<JS::Value> valueToResolve(cx, $${val});
- if (!JS_WrapValue(cx, &valueToResolve)) {
- $*{exceptionCode}
- }
- binding_detail::FastErrorResult promiseRv;
- #ifdef SPIDERMONKEY_PROMISE
- nsCOMPtr<nsIGlobalObject> global =
- do_QueryInterface(promiseGlobal.GetAsSupports());
- if (!global) {
- promiseRv.Throw(NS_ERROR_UNEXPECTED);
- promiseRv.MaybeSetPendingException(cx);
- $*{exceptionCode}
- }
- $${declName} = Promise::Resolve(global, cx, valueToResolve,
- promiseRv);
- if (promiseRv.MaybeSetPendingException(cx)) {
- $*{exceptionCode}
- }
- #else
- JS::Handle<JSObject*> promiseCtor =
- PromiseBinding::GetConstructorObjectHandle(cx);
- if (!promiseCtor) {
- $*{exceptionCode}
- }
- JS::Rooted<JS::Value> resolveThisv(cx, JS::ObjectValue(*promiseCtor));
- JS::Rooted<JS::Value> resolveResult(cx);
- Promise::Resolve(promiseGlobal, resolveThisv, valueToResolve,
- &resolveResult, promiseRv);
- if (promiseRv.MaybeSetPendingException(cx)) {
- $*{exceptionCode}
- }
- nsresult unwrapRv = UNWRAP_OBJECT(Promise, &resolveResult.toObject(), $${declName});
- if (NS_FAILED(unwrapRv)) { // Quite odd
- promiseRv.Throw(unwrapRv);
- promiseRv.MaybeSetPendingException(cx);
- $*{exceptionCode}
- }
- #endif // SPIDERMONKEY_PROMISE
- }
- """,
- getPromiseGlobal=getPromiseGlobal,
- exceptionCode=exceptionCode)
- elif not descriptor.interface.isConsequential() and not descriptor.interface.isExternal():
+ if (not descriptor.interface.isConsequential() and
+ not descriptor.interface.isExternal()):
if failureCode is not None:
templateBody += str(CastableObjectUnwrapper(
descriptor,
@@ -5491,24 +5482,12 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
# And store our value in ${declName}
templateBody += "${declName} = ${holderName};\n"
- if isPromise:
- if type.nullable():
- codeToSetNull = "${declName} = nullptr;\n"
- templateBody = CGIfElseWrapper(
- "${val}.isNullOrUndefined()",
- CGGeneric(codeToSetNull),
- CGGeneric(templateBody)).define()
- if isinstance(defaultValue, IDLNullValue):
- templateBody = handleDefault(templateBody, codeToSetNull)
- else:
- assert defaultValue is None
- else:
- # Just pass failureCode, not onFailureBadType, here, so we'll report
- # the thing as not an object as opposed to not implementing whatever
- # our interface is.
- templateBody = wrapObjectTemplate(templateBody, type,
- "${declName} = nullptr;\n",
- failureCode)
+ # Just pass failureCode, not onFailureBadType, here, so we'll report
+ # the thing as not an object as opposed to not implementing whatever
+ # our interface is.
+ templateBody = wrapObjectTemplate(templateBody, type,
+ "${declName} = nullptr;\n",
+ failureCode)
declType = CGGeneric(declType)
if holderType is not None:
@@ -6575,6 +6554,19 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
return (code, False)
+ if type.isPromise():
+ assert not type.nullable()
+ # The use of ToJSValue here is a bit annoying because the Promise
+ # version is not inlined, but we can't put an inline version in either
+ # ToJSValue.h or BindingUtils.h, because Promise.h includes ToJSValue.h
+ # and that includes BindingUtils.h, so we'd get an include loop if
+ # either of those headers included Promise.h. Trying to write the
+ # conversion by hand here is annoying because we'd have to handle
+ # the various RefPtr, rawptr, NonNull, etc. cases, which ToJSValue will
+ # already handle for us, so we just use the function call.
+ return (wrapAndSetPtr("ToJSValue(cx, %s, ${jsvalHandle})" % result),
+ False)
+
if type.isGeckoInterface() and not type.isCallbackInterface():
descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
if type.nullable():
@@ -6589,14 +6581,6 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
wrapMethod = "GetOrCreateDOMReflector"
wrapArgs = "cx, %s, ${jsvalHandle}" % result
else:
- # Hack: the "Promise" interface is OK to return from
- # non-newobject things even when it's not wrappercached; that
- # happens when using SpiderMonkey promises, and the WrapObject()
- # method will just return the existing reflector, which is just
- # not stored in a wrappercache.
- if (not returnsNewObject and
- descriptor.interface.identifier.name != "Promise"):
- raise MethodNotNewObjectError(descriptor.interface.identifier.name)
wrapMethod = "WrapNewBindingNonWrapperCachedObject"
wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result
if isConstructorRetval:
@@ -6912,14 +6896,17 @@ def getRetvalDeclarationForType(returnType, descriptorProvider,
if returnType.nullable():
result = CGTemplatedType("Nullable", result)
return result, None, None, None, None
- if returnType.isGeckoInterface():
- result = CGGeneric(descriptorProvider.getDescriptor(
- returnType.unroll().inner.identifier.name).nativeType)
- conversion = None
+ if returnType.isGeckoInterface() or returnType.isPromise():
+ if returnType.isGeckoInterface():
+ typeName = descriptorProvider.getDescriptor(
+ returnType.unroll().inner.identifier.name).nativeType
+ else:
+ typeName = "Promise"
if isMember:
- result = CGGeneric("StrongPtrForMember<%s>::Type" % result.define())
+ conversion = None
+ result = CGGeneric("StrongPtrForMember<%s>::Type" % typeName)
else:
- conversion = CGGeneric("StrongOrRawPtr<%s>" % result.define())
+ conversion = CGGeneric("StrongOrRawPtr<%s>" % typeName)
result = CGGeneric("auto")
return result, None, None, None, conversion
if returnType.isCallback():
@@ -7084,7 +7071,9 @@ class CGCallGenerator(CGThing):
if needsConst(a):
arg = CGWrapper(arg, pre="Constify(", post=")")
# And convert NonNull<T> to T&
- if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or
+ if (((a.type.isGeckoInterface() or a.type.isCallback() or
+ a.type.isPromise()) and
+ not a.type.nullable()) or
a.type.isDOMString()):
arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
args.append(arg)
@@ -7208,6 +7197,9 @@ class CGCallGenerator(CGThing):
def getUnionMemberName(type):
+ # Promises can't be in unions, because they're not distinguishable
+ # from anything else.
+ assert not type.isPromise()
if type.isGeckoInterface():
return type.inner.identifier.name
if type.isEnum():
@@ -7337,8 +7329,9 @@ def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None
if (type.isString() or type.isPrimitive() or type.isEnum() or
- type.isGeckoInterface() or type.isCallback() or type.isDate()):
- # All of these don't need wrapping
+ type.isGeckoInterface() or type.isCallback() or type.isDate() or
+ type.isPromise()):
+ # All of these don't need wrapping.
return None
raise TypeError("Unknown type; we don't know how to wrap it in constructor "
@@ -7476,58 +7469,12 @@ class CGPerSignatureCall(CGThing):
if needsCx:
argsPre.append("cx")
- # Hack for making Promise.prototype.then work well over Xrays.
- if (not idlNode.isStatic() and
- descriptor.name == "Promise" and
- idlNode.isMethod() and
- idlNode.identifier.name == "then"):
- cgThings.append(CGGeneric(dedent(
- """
- JS::Rooted<JSObject*> calleeGlobal(cx, xpc::XrayAwareCalleeGlobal(&args.callee()));
- """)))
- argsPre.append("calleeGlobal")
-
needsUnwrap = False
argsPost = []
if isConstructor:
- if descriptor.name == "Promise":
- # Hack for Promise for now: pass in our desired proto so the
- # implementation can create the reflector with the right proto.
- argsPost.append("desiredProto")
- # Also, we do not want to enter the content compartment when the
- # Promise constructor is called via Xrays, because we want to
- # create our callback functions that we will hand to our caller
- # in the Xray compartment. The reason we want to do that is the
- # following situation, over Xrays:
- #
- # contentWindow.Promise.race([Promise.resolve(5)])
- #
- # Ideally this would work. Internally, race() does a
- # contentWindow.Promise.resolve() on everything in the array.
- # Per spec, to support subclassing,
- # contentWindow.Promise.resolve has to do:
- #
- # var resolve, reject;
- # var p = new contentWindow.Promise(function(a, b) {
- # resolve = a;
- # reject = b;
- # });
- # resolve(arg);
- # return p;
- #
- # where "arg" is, in this case, the chrome-side return value of
- # Promise.resolve(5). But if the "resolve" function in that
- # case were created in the content compartment, then calling it
- # would wrap "arg" in an opaque wrapper, and that function tries
- # to get .then off the argument, which would throw. So we need
- # to create the "resolve" function in the chrome compartment,
- # and hence want to be running the entire Promise constructor
- # (which creates that function) in the chrome compartment in
- # this case. So don't set needsUnwrap here.
- else:
- needsUnwrap = True
- needsUnwrappedVar = False
- unwrappedVar = "obj"
+ needsUnwrap = True
+ needsUnwrappedVar = False
+ unwrappedVar = "obj"
elif descriptor.interface.isJSImplemented():
if not idlNode.isStatic():
needsUnwrap = True
@@ -7540,11 +7487,6 @@ class CGPerSignatureCall(CGThing):
needsUnwrappedVar = True
argsPre.append("unwrappedObj ? *unwrappedObj : obj")
- if idlNode.isStatic() and not isConstructor and descriptor.name == "Promise":
- # Hack for Promise for now: pass in the "this" value to
- # Promise static methods.
- argsPre.append("args.thisv()")
-
if needsUnwrap and needsUnwrappedVar:
# We cannot assign into obj because it's a Handle, not a
# MutableHandle, so we need a separate Rooted.
@@ -7687,7 +7629,8 @@ class CGPerSignatureCall(CGThing):
returnsNewObject = memberReturnsNewObject(self.idlNode)
if (returnsNewObject and
- self.returnType.isGeckoInterface()):
+ (self.returnType.isGeckoInterface() or
+ self.returnType.isPromise())):
wrapCode += dedent(
"""
static_assert(!IsPointer<decltype(result)>::value,
@@ -9493,6 +9436,8 @@ class CGMemberJITInfo(CGThing):
return "JSVAL_TYPE_OBJECT"
if t.isRecord():
return "JSVAL_TYPE_OBJECT"
+ if t.isPromise():
+ return "JSVAL_TYPE_OBJECT"
if t.isGeckoInterface():
return "JSVAL_TYPE_OBJECT"
if t.isString():
@@ -9566,6 +9511,8 @@ class CGMemberJITInfo(CGThing):
return "JSJitInfo::ArgType(JSJitInfo::Null | %s)" % CGMemberJITInfo.getJSArgType(t.inner)
if t.isSequence():
return "JSJitInfo::Object"
+ if t.isPromise():
+ return "JSJitInfo::Object"
if t.isGeckoInterface():
return "JSJitInfo::Object"
if t.isString():
@@ -9752,6 +9699,10 @@ def getUnionAccessorSignatureType(type, descriptorProvider):
# Flat member types have already unwrapped nullables.
assert not type.nullable()
+ # Promise types can never appear in unions, because Promise is not
+ # distinguishable from anything.
+ assert not type.isPromise()
+
if type.isSequence() or type.isRecord():
if type.isSequence():
wrapperType = "Sequence"
@@ -13660,6 +13611,8 @@ class ForwardDeclarationBuilder:
# Note: Spidermonkey interfaces are typedefs, so can't be
# forward-declared
+ elif t.isPromise():
+ self.addInMozillaDom("Promise")
elif t.isCallback():
self.addInMozillaDom(t.callback.identifier.name)
elif t.isDictionary():
@@ -14114,10 +14067,13 @@ class CGNativeMember(ClassMethod):
else:
defaultValue = "%s(0)" % enumName
return enumName, defaultValue, "return ${declName};\n"
- if type.isGeckoInterface():
- iface = type.unroll().inner
- result = CGGeneric(self.descriptorProvider.getDescriptor(
- iface.identifier.name).prettyNativeType)
+ if type.isGeckoInterface() or type.isPromise():
+ if type.isGeckoInterface():
+ iface = type.unroll().inner
+ result = CGGeneric(self.descriptorProvider.getDescriptor(
+ iface.identifier.name).prettyNativeType)
+ else:
+ result = CGGeneric("Promise")
if self.resultAlreadyAddRefed:
if isMember:
holder = "RefPtr"
@@ -14320,11 +14276,18 @@ class CGNativeMember(ClassMethod):
# auto-wrapping in Nullable
return CGUnionStruct.unionTypeDecl(type, isMember), True, False
+ if type.isPromise():
+ assert not type.nullable()
+ if optional or isMember:
+ typeDecl = "OwningNonNull<Promise>"
+ else:
+ typeDecl = "Promise&"
+ return (typeDecl, False, False)
+
if type.isGeckoInterface() and not type.isCallbackInterface():
iface = type.unroll().inner
argIsPointer = type.nullable() or iface.isExternal()
- forceOwningType = (iface.isCallback() or isMember or
- iface.identifier.name == "Promise")
+ forceOwningType = (iface.isCallback() or isMember)
if argIsPointer:
if (optional or isMember) and forceOwningType:
typeDecl = "RefPtr<%s>"
@@ -16958,7 +16921,10 @@ class CGEventGetter(CGNativeMember):
def getMethodBody(self):
type = self.member.type
memberName = CGDictionary.makeMemberName(self.member.identifier.name)
- if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface():
+ if ((type.isPrimitive() and type.tag() in builtinNames) or
+ type.isEnum() or
+ type.isPromise() or
+ type.isGeckoInterface()):
return "return " + memberName + ";\n"
if type.isDOMString() or type.isByteString() or type.isUSVString():
return "aRetVal = " + memberName + ";\n"
@@ -17369,6 +17335,8 @@ class CGEventClass(CGBindingImplClass):
nativeType = CGGeneric("nsString")
elif type.isByteString():
nativeType = CGGeneric("nsCString")
+ elif type.isPromise():
+ nativeType = CGGeneric("RefPtr<Promise>")
elif type.isGeckoInterface():
iface = type.unroll().inner
nativeType = self.descriptor.getDescriptor(
@@ -17393,7 +17361,7 @@ class CGEventClass(CGBindingImplClass):
innerType = type.inner
if (not innerType.isPrimitive() and not innerType.isEnum() and
not innerType.isDOMString() and not innerType.isByteString() and
- not innerType.isGeckoInterface()):
+ not innerType.isPromise() and not innerType.isGeckoInterface()):
raise TypeError("Don't know how to properly manage GC/CC for "
"event member of type %s" %
type)
diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py
index f80c19c33..a56f2f2fd 100644
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -468,13 +468,6 @@ class Descriptor(DescriptorProvider):
self.wrapperCache = (not self.interface.isCallback() and
not self.interface.isIteratorInterface() and
desc.get('wrapperCache', True))
- # Nasty temporary hack for supporting both DOM and SpiderMonkey promises
- # without too much pain
- if self.interface.identifier.name == "Promise":
- assert self.wrapperCache
- # But really, we're only wrappercached if we have an interface
- # object (that is, when we're not using SpiderMonkey promises).
- self.wrapperCache = self.interface.hasInterfaceObject()
self.name = interface.identifier.name
diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg
index 142ccfdd6..c47f75875 100644
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -84,11 +84,6 @@ MSG_DEF(MSG_NOTIFICATION_PERMISSION_DENIED, 0, JSEXN_TYPEERR, "Permission to sho
MSG_DEF(MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER, 0, JSEXN_TYPEERR, "Notification constructor cannot be used in ServiceWorkerGlobalScope. Use registration.showNotification() instead.")
MSG_DEF(MSG_INVALID_SCOPE, 2, JSEXN_TYPEERR, "Invalid scope trying to resolve {0} with base URL {1}.")
MSG_DEF(MSG_INVALID_KEYFRAME_OFFSETS, 0, JSEXN_TYPEERR, "Keyframes with specified offsets must be in order and all be in the range [0, 1].")
-MSG_DEF(MSG_ILLEGAL_PROMISE_CONSTRUCTOR, 0, JSEXN_TYPEERR, "Non-constructor value passed to NewPromiseCapability.")
-MSG_DEF(MSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY, 0, JSEXN_TYPEERR, "GetCapabilitiesExecutor function already invoked with non-undefined values.")
-MSG_DEF(MSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
-MSG_DEF(MSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
-MSG_DEF(MSG_PROMISE_ARG_NOT_ITERABLE, 1, JSEXN_TYPEERR, "{0} is not iterable")
MSG_DEF(MSG_IS_NOT_PROMISE, 1, JSEXN_TYPEERR, "{0} is not a Promise")
MSG_DEF(MSG_SW_INSTALL_ERROR, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} encountered an error during installation.")
MSG_DEF(MSG_SW_SCRIPT_THREW, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} threw an exception during script evaluation.")
diff --git a/dom/bindings/ToJSValue.cpp b/dom/bindings/ToJSValue.cpp
index d84428fb3..7afff41e2 100644
--- a/dom/bindings/ToJSValue.cpp
+++ b/dom/bindings/ToJSValue.cpp
@@ -7,9 +7,7 @@
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/Exceptions.h"
-#ifdef SPIDERMONKEY_PROMISE
#include "mozilla/dom/Promise.h"
-#endif // SPIDERMONKEY_PROMISE
#include "nsAString.h"
#include "nsContentUtils.h"
#include "nsStringBuffer.h"
@@ -66,15 +64,13 @@ ToJSValue(JSContext* aCx,
return true;
}
-#ifdef SPIDERMONKEY_PROMISE
bool
ToJSValue(JSContext* aCx, Promise& aArgument,
JS::MutableHandle<JS::Value> aValue)
{
aValue.setObject(*aArgument.PromiseObj());
- return true;
+ return MaybeWrapObjectValue(aCx, aValue);
}
-#endif // SPIDERMONKEY_PROMISE
} // namespace dom
} // namespace mozilla
diff --git a/dom/bindings/ToJSValue.h b/dom/bindings/ToJSValue.h
index 2021c0b4c..76e91c7bc 100644
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -306,13 +306,11 @@ ToJSValue(JSContext* aCx,
return ToJSValue(aCx, *aArgument, aValue);
}
-#ifdef SPIDERMONKEY_PROMISE
// Accept Promise objects, which need special handling.
MOZ_MUST_USE bool
ToJSValue(JSContext* aCx,
Promise& aArgument,
JS::MutableHandle<JS::Value> aValue);
-#endif // SPIDERMONKEY_PROMISE
// Accept arrays of other things we accept
template <typename T>
diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h
index a86abcd9d..75313e255 100644
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -344,7 +344,7 @@ typedef TypedArray<uint8_t, js::UnwrapSharedArrayBuffer, JS_GetSharedArrayBuffer
// A class for converting an nsTArray to a TypedArray
// Note: A TypedArrayCreator must not outlive the nsTArray it was created from.
// So this is best used to pass from things that understand nsTArray to
-// things that understand TypedArray, as with Promise::ArgumentToJSValue.
+// things that understand TypedArray, as with ToJSValue.
template<typename TypedArrayType>
class TypedArrayCreator
{
diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py
index 8c32a8738..4f602365b 100644
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1597,11 +1597,7 @@ class IDLInterface(IDLInterfaceOrNamespace):
args = attr.args() if attr.hasArgs() else []
- if self.identifier.name == "Promise":
- promiseType = BuiltinTypes[IDLBuiltinType.Types.any]
- else:
- promiseType = None
- retType = IDLWrapperType(self.location, self, promiseType)
+ retType = IDLWrapperType(self.location, self)
if identifier == "Constructor" or identifier == "ChromeConstructor":
name = "constructor"
@@ -1988,7 +1984,8 @@ class IDLType(IDLObject):
'callback',
'union',
'sequence',
- 'record'
+ 'record',
+ 'promise'
)
def __init__(self, location, name):
@@ -2142,9 +2139,8 @@ class IDLUnresolvedType(IDLType):
Unresolved types are interface types
"""
- def __init__(self, location, name, promiseInnerType=None):
+ def __init__(self, location, name):
IDLType.__init__(self, location, name)
- self._promiseInnerType = promiseInnerType
def isComplete(self):
return False
@@ -2171,11 +2167,8 @@ class IDLUnresolvedType(IDLType):
assert self.name.name == obj.identifier.name
return IDLCallbackType(self.location, obj)
- if self._promiseInnerType and not self._promiseInnerType.isComplete():
- self._promiseInnerType = self._promiseInnerType.complete(scope)
-
name = self.name.resolve(scope, None)
- return IDLWrapperType(self.location, obj, self._promiseInnerType)
+ return IDLWrapperType(self.location, obj)
def isDistinguishableFrom(self, other):
raise TypeError("Can't tell whether an unresolved type is or is not "
@@ -2285,7 +2278,9 @@ class IDLNullableType(IDLParameterizedType):
return self.inner.isInterface()
def isPromise(self):
- return self.inner.isPromise()
+ # There is no such thing as a nullable Promise.
+ assert not self.inner.isPromise()
+ return False
def isCallbackInterface(self):
return self.inner.isCallbackInterface()
@@ -2698,13 +2693,11 @@ class IDLTypedef(IDLObjectWithIdentifier):
class IDLWrapperType(IDLType):
- def __init__(self, location, inner, promiseInnerType=None):
+ def __init__(self, location, inner):
IDLType.__init__(self, location, inner.identifier.name)
self.inner = inner
self._identifier = inner.identifier
self.builtin = False
- assert not promiseInnerType or inner.identifier.name == "Promise"
- self._promiseInnerType = promiseInnerType
def __eq__(self, other):
return (isinstance(other, IDLWrapperType) and
@@ -2754,14 +2747,6 @@ class IDLWrapperType(IDLType):
def isEnum(self):
return isinstance(self.inner, IDLEnum)
- def isPromise(self):
- return (isinstance(self.inner, IDLInterface) and
- self.inner.identifier.name == "Promise")
-
- def promiseInnerType(self):
- assert self.isPromise()
- return self._promiseInnerType
-
def isSerializable(self):
if self.isInterface():
if self.inner.isExternal():
@@ -2793,8 +2778,6 @@ class IDLWrapperType(IDLType):
assert False
def isDistinguishableFrom(self, other):
- if self.isPromise():
- return False
if other.isPromise():
return False
if other.isUnion():
@@ -2841,10 +2824,6 @@ class IDLWrapperType(IDLType):
# Let's say true, though ideally we'd only do this when
# exposureSet contains the primary global's name.
return True
- if (self.isPromise() and
- # Check the internal type
- not self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)):
- return False
return iface.exposureSet.issuperset(exposureSet)
def _getDependentObjects(self):
@@ -2872,6 +2851,45 @@ class IDLWrapperType(IDLType):
return set()
+class IDLPromiseType(IDLParameterizedType):
+ def __init__(self, location, innerType):
+ IDLParameterizedType.__init__(self, location, "Promise", innerType)
+
+ def __eq__(self, other):
+ return (isinstance(other, IDLPromiseType) and
+ self.promiseInnerType() == other.promiseInnerType())
+
+ def __str__(self):
+ return self.inner.__str__() + "Promise"
+
+ def isPromise(self):
+ return True
+
+ def promiseInnerType(self):
+ return self.inner
+
+ def tag(self):
+ return IDLType.Tags.promise
+
+ def complete(self, scope):
+ self.inner = self.promiseInnerType().complete(scope)
+ return self
+
+ def unroll(self):
+ # We do not unroll our inner. Just stop at ourselves. That
+ # lets us add headers for both ourselves and our inner as
+ # needed.
+ return self
+
+ def isDistinguishableFrom(self, other):
+ # Promises are not distinguishable from anything.
+ return False
+
+ def isExposedInAllOf(self, exposureSet):
+ # Check the internal type
+ return self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)
+
+
class IDLBuiltinType(IDLType):
Types = enum(
@@ -3990,7 +4008,9 @@ class IDLAttribute(IDLInterfaceMember):
raise WebIDLError("An attribute with [PutForwards] must have an "
"interface type as its type", [self.location])
- if not self.type.isInterface() and self.getExtendedAttribute("SameObject"):
+ if (not self.type.isInterface() and
+ not self.type.isPromise() and
+ self.getExtendedAttribute("SameObject")):
raise WebIDLError("An attribute with [SameObject] must have an "
"interface type as its type", [self.location])
@@ -6394,17 +6414,13 @@ class Parser(Tokenizer):
type = IDLSequenceType(self.getLocation(p, 1), innerType)
p[0] = self.handleNullable(type, p[5])
- # Note: Promise<void> is allowed, so we want to parametrize on
- # ReturnType, not Type. Also, we want this to end up picking up
- # the Promise interface for now, hence the games with IDLUnresolvedType.
+ # Note: Promise<void> is allowed, so we want to parameterize on
+ # ReturnType, not Type. Promise types can't be null, hence no "Null" in there.
def p_NonAnyTypePromiseType(self, p):
"""
- NonAnyType : PROMISE LT ReturnType GT Null
+ NonAnyType : PROMISE LT ReturnType GT
"""
- innerType = p[3]
- promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise")
- type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
- p[0] = self.handleNullable(type, p[5])
+ p[0] = IDLPromiseType(self.getLocation(p, 1), p[3])
def p_NonAnyTypeRecordType(self, p):
"""
@@ -6423,7 +6439,7 @@ class Parser(Tokenizer):
if p[1].name == "Promise":
raise WebIDLError("Promise used without saying what it's "
- "parametrized over",
+ "parameterized over",
[self.getLocation(p, 1)])
type = None
diff --git a/dom/bindings/parser/tests/test_distinguishability.py b/dom/bindings/parser/tests/test_distinguishability.py
index d7780c1ff..ac515a01d 100644
--- a/dom/bindings/parser/tests/test_distinguishability.py
+++ b/dom/bindings/parser/tests/test_distinguishability.py
@@ -263,7 +263,6 @@ def WebIDLTest(parser, harness):
callback Callback2 = long(short arg);
dictionary Dict {};
dictionary Dict2 {};
- interface _Promise {};
interface TestInterface {%s
};
"""
diff --git a/dom/bindings/parser/tests/test_promise.py b/dom/bindings/parser/tests/test_promise.py
index 55bc07680..091381fab 100644
--- a/dom/bindings/parser/tests/test_promise.py
+++ b/dom/bindings/parser/tests/test_promise.py
@@ -2,7 +2,6 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- interface _Promise {};
interface A {
legacycaller Promise<any> foo();
};
@@ -18,7 +17,6 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- interface _Promise {};
interface A {
Promise<any> foo();
long foo(long arg);
@@ -35,7 +33,6 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- interface _Promise {};
interface A {
long foo(long arg);
Promise<any> foo();
@@ -50,7 +47,6 @@ def WebIDLTest(parser, harness):
parser = parser.reset()
parser.parse("""
- interface _Promise {};
interface A {
Promise<any> foo();
Promise<any> foo(long arg);
diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp
index f636a9101..e5279345d 100644
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -30,7 +30,6 @@
#include "nsJSPrincipals.h"
#include "nsJSUtils.h"
#include "nsPIDOMWindow.h"
-#include "PromiseCallback.h"
#include "PromiseDebugging.h"
#include "PromiseNativeHandler.h"
#include "PromiseWorkerProxy.h"
@@ -49,467 +48,45 @@ Atomic<uintptr_t> gIDGenerator(0);
using namespace workers;
-#ifndef SPIDERMONKEY_PROMISE
-// This class processes the promise's callbacks with promise's result.
-class PromiseReactionJob final : public Runnable
-{
-public:
- PromiseReactionJob(Promise* aPromise,
- PromiseCallback* aCallback,
- const JS::Value& aValue)
- : mPromise(aPromise)
- , mCallback(aCallback)
- , mValue(CycleCollectedJSContext::Get()->Context(), aValue)
- {
- MOZ_ASSERT(aPromise);
- MOZ_ASSERT(aCallback);
- MOZ_COUNT_CTOR(PromiseReactionJob);
- }
-
- virtual
- ~PromiseReactionJob()
- {
- NS_ASSERT_OWNINGTHREAD(PromiseReactionJob);
- MOZ_COUNT_DTOR(PromiseReactionJob);
- }
-
-protected:
- NS_IMETHOD
- Run() override
- {
- NS_ASSERT_OWNINGTHREAD(PromiseReactionJob);
-
- MOZ_ASSERT(mPromise->GetWrapper()); // It was preserved!
-
- AutoJSAPI jsapi;
- if (!jsapi.Init(mPromise->GetWrapper())) {
- return NS_ERROR_FAILURE;
- }
- JSContext* cx = jsapi.cx();
-
- JS::Rooted<JS::Value> value(cx, mValue);
- if (!MaybeWrapValue(cx, &value)) {
- NS_WARNING("Failed to wrap value into the right compartment.");
- JS_ClearPendingException(cx);
- return NS_OK;
- }
-
- JS::Rooted<JSObject*> asyncStack(cx, mPromise->mAllocationStack);
-
- {
- Maybe<JS::AutoSetAsyncStackForNewCalls> sas;
- if (asyncStack) {
- sas.emplace(cx, asyncStack, "Promise");
- }
- mCallback->Call(cx, value);
- }
-
- return NS_OK;
- }
-
-private:
- RefPtr<Promise> mPromise;
- RefPtr<PromiseCallback> mCallback;
- JS::PersistentRooted<JS::Value> mValue;
- NS_DECL_OWNINGTHREAD;
-};
-
-/*
- * Utilities for thenable callbacks.
- *
- * A thenable is a { then: function(resolve, reject) { } }.
- * `then` is called with a resolve and reject callback pair.
- * Since only one of these should be called at most once (first call wins), the
- * two keep a reference to each other in SLOT_DATA. When either of them is
- * called, the references are cleared. Further calls are ignored.
- */
-namespace {
-void
-LinkThenableCallables(JSContext* aCx, JS::Handle<JSObject*> aResolveFunc,
- JS::Handle<JSObject*> aRejectFunc)
-{
- js::SetFunctionNativeReserved(aResolveFunc, Promise::SLOT_DATA,
- JS::ObjectValue(*aRejectFunc));
- js::SetFunctionNativeReserved(aRejectFunc, Promise::SLOT_DATA,
- JS::ObjectValue(*aResolveFunc));
-}
-
-/*
- * Returns false if callback was already called before, otherwise breaks the
- * links and returns true.
- */
-bool
-MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle<JSObject*> aFunc)
-{
- JS::Value otherFuncVal =
- js::GetFunctionNativeReserved(aFunc, Promise::SLOT_DATA);
-
- if (!otherFuncVal.isObject()) {
- return false;
- }
-
- JSObject* otherFuncObj = &otherFuncVal.toObject();
- MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj,
- Promise::SLOT_DATA).isObject());
-
- // Break both references.
- js::SetFunctionNativeReserved(aFunc, Promise::SLOT_DATA,
- JS::UndefinedValue());
- js::SetFunctionNativeReserved(otherFuncObj, Promise::SLOT_DATA,
- JS::UndefinedValue());
-
- return true;
-}
-
-Promise*
-GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc)
-{
- JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc,
- Promise::SLOT_PROMISE);
-
- MOZ_ASSERT(promiseVal.isObject());
-
- Promise* promise;
- UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise);
- return promise;
-}
-} // namespace
-
-// Runnable to resolve thenables.
-// Equivalent to the specification's ResolvePromiseViaThenableTask.
-class PromiseResolveThenableJob final : public Runnable
-{
-public:
- PromiseResolveThenableJob(Promise* aPromise,
- JS::Handle<JSObject*> aThenable,
- PromiseInit* aThen)
- : mPromise(aPromise)
- , mThenable(CycleCollectedJSContext::Get()->Context(), aThenable)
- , mThen(aThen)
- {
- MOZ_ASSERT(aPromise);
- MOZ_COUNT_CTOR(PromiseResolveThenableJob);
- }
-
- virtual
- ~PromiseResolveThenableJob()
- {
- NS_ASSERT_OWNINGTHREAD(PromiseResolveThenableJob);
- MOZ_COUNT_DTOR(PromiseResolveThenableJob);
- }
-
-protected:
- NS_IMETHOD
- Run() override
- {
- NS_ASSERT_OWNINGTHREAD(PromiseResolveThenableJob);
-
- MOZ_ASSERT(mPromise->GetWrapper()); // It was preserved!
-
- AutoJSAPI jsapi;
- // If we ever change which compartment we're working in here, make sure to
- // fix the fast-path for resolved-with-a-Promise in ResolveInternal.
- if (!jsapi.Init(mPromise->GetWrapper())) {
- return NS_ERROR_FAILURE;
- }
- JSContext* cx = jsapi.cx();
-
- JS::Rooted<JSObject*> resolveFunc(cx,
- mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Resolve));
-
- if (!resolveFunc) {
- mPromise->HandleException(cx);
- return NS_OK;
- }
-
- JS::Rooted<JSObject*> rejectFunc(cx,
- mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Reject));
- if (!rejectFunc) {
- mPromise->HandleException(cx);
- return NS_OK;
- }
-
- LinkThenableCallables(cx, resolveFunc, rejectFunc);
-
- ErrorResult rv;
-
- JS::Rooted<JSObject*> rootedThenable(cx, mThenable);
-
- mThen->Call(rootedThenable, resolveFunc, rejectFunc, rv,
- "promise thenable", CallbackObject::eRethrowExceptions,
- mPromise->Compartment());
-
- rv.WouldReportJSException();
- if (rv.Failed()) {
- JS::Rooted<JS::Value> exn(cx);
- { // Scope for JSAutoCompartment
-
- // Convert the ErrorResult to a JS exception object that we can reject
- // ourselves with. This will be exactly the exception that would get
- // thrown from a binding method whose ErrorResult ended up with
- // whatever is on "rv" right now.
- JSAutoCompartment ac(cx, mPromise->GlobalJSObject());
- DebugOnly<bool> conversionResult = ToJSValue(cx, rv, &exn);
- MOZ_ASSERT(conversionResult);
- }
-
- bool couldMarkAsCalled = MarkAsCalledIfNotCalledBefore(cx, resolveFunc);
-
- // If we could mark as called, neither of the callbacks had been called
- // when the exception was thrown. So we can reject the Promise.
- if (couldMarkAsCalled) {
- bool ok = JS_WrapValue(cx, &exn);
- MOZ_ASSERT(ok);
- if (!ok) {
- NS_WARNING("Failed to wrap value into the right compartment.");
- }
-
- mPromise->RejectInternal(cx, exn);
- }
- // At least one of resolveFunc or rejectFunc have been called, so ignore
- // the exception. FIXME(nsm): This should be reported to the error
- // console though, for debugging.
- }
-
- return rv.StealNSResult();
- }
-
-private:
- RefPtr<Promise> mPromise;
- JS::PersistentRooted<JSObject*> mThenable;
- RefPtr<PromiseInit> mThen;
- NS_DECL_OWNINGTHREAD;
-};
-
-// A struct implementing
-// <http://www.ecma-international.org/ecma-262/6.0/#sec-promisecapability-records>.
-// While the spec holds on to these in some places, in practice those places
-// don't actually need everything from this struct, so we explicitly grab
-// members from it as needed in those situations. That allows us to make this a
-// stack-only struct and keep the rooting simple.
-//
-// We also add an optimization for the (common) case when we discover that the
-// Promise constructor we're supposed to use is in fact the canonical Promise
-// constructor. In that case we will just set mNativePromise in our
-// PromiseCapability and not set mPromise/mResolve/mReject; the correct
-// callbacks will be the standard Promise ones, and we don't really want to
-// synthesize JSFunctions for them in that situation.
-struct MOZ_STACK_CLASS Promise::PromiseCapability
-{
- explicit PromiseCapability(JSContext* aCx)
- : mPromise(aCx)
- , mResolve(aCx)
- , mReject(aCx)
- {}
-
- // Take an exception on aCx and try to convert it into a promise rejection.
- // Note that this can result in a new exception being thrown on aCx, or an
- // exception getting thrown on aRv. On entry to this method, aRv is assumed
- // to not be a failure. This should only be called if NewPromiseCapability
- // succeeded on this PromiseCapability.
- void RejectWithException(JSContext* aCx, ErrorResult& aRv);
-
- // Return a JS::Value representing the promise. This should only be called if
- // NewPromiseCapability succeeded on this PromiseCapability. It makes no
- // guarantees about compartments (e.g. in the mNativePromise case it's in the
- // compartment of the reflector, but in the mPromise case it might be in the
- // compartment of some cross-compartment wrapper for a reflector).
- JS::Value PromiseValue() const;
-
- // All the JS::Value fields of this struct are actually objects, but for our
- // purposes it's simpler to store them as JS::Value.
-
- // [[Promise]].
- JS::Rooted<JSObject*> mPromise;
- // [[Resolve]]. Value in the context compartment.
- JS::Rooted<JS::Value> mResolve;
- // [[Reject]]. Value in the context compartment.
- JS::Rooted<JS::Value> mReject;
- // If mNativePromise is non-null, we should use it, not mPromise.
- RefPtr<Promise> mNativePromise;
-
-private:
- // We don't want to allow creation of temporaries of this type, ever.
- PromiseCapability(const PromiseCapability&) = delete;
- PromiseCapability(PromiseCapability&&) = delete;
-};
-
-void
-Promise::PromiseCapability::RejectWithException(JSContext* aCx,
- ErrorResult& aRv)
-{
- // This method basically implements
- // http://www.ecma-international.org/ecma-262/6.0/#sec-ifabruptrejectpromise
- // or at least the parts of it that happen if we have an abrupt completion.
-
- MOZ_ASSERT(!aRv.Failed());
- MOZ_ASSERT(mNativePromise || mPromise,
- "NewPromiseCapability didn't succeed");
-
- JS::Rooted<JS::Value> exn(aCx);
- if (!JS_GetPendingException(aCx, &exn)) {
- // This is an uncatchable exception, so can't be converted into a rejection.
- // Just rethrow that on aRv.
- aRv.ThrowUncatchableException();
- return;
- }
-
- JS_ClearPendingException(aCx);
-
- // If we have a native promise, just reject it without trying to call out into
- // JS.
- if (mNativePromise) {
- mNativePromise->MaybeRejectInternal(aCx, exn);
- return;
- }
-
- JS::Rooted<JS::Value> ignored(aCx);
- if (!JS::Call(aCx, JS::UndefinedHandleValue, mReject, JS::HandleValueArray(exn),
- &ignored)) {
- aRv.NoteJSContextException(aCx);
- }
-}
-
-JS::Value
-Promise::PromiseCapability::PromiseValue() const
-{
- MOZ_ASSERT(mNativePromise || mPromise,
- "NewPromiseCapability didn't succeed");
-
- if (mNativePromise) {
- return JS::ObjectValue(*mNativePromise->GetWrapper());
- }
-
- return JS::ObjectValue(*mPromise);
-}
-
-#endif // SPIDERMONKEY_PROMISE
-
// Promise
NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
-#ifndef SPIDERMONKEY_PROMISE
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
- tmp->MaybeReportRejectedOnce();
-#else
- tmp->mResult = JS::UndefinedValue();
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-#endif // SPIDERMONKEY_PROMISE
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
-#ifndef SPIDERMONKEY_PROMISE
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectCallbacks)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-#else // SPIDERMONKEY_PROMISE
tmp->mPromiseObj = nullptr;
-#endif // SPIDERMONKEY_PROMISE
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
-#ifndef SPIDERMONKEY_PROMISE
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks)
-#endif // SPIDERMONKEY_PROMISE
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
-#ifndef SPIDERMONKEY_PROMISE
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAllocationStack)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRejectionStack)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFullfillmentStack)
- NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-#else // SPIDERMONKEY_PROMISE
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPromiseObj);
-#endif // SPIDERMONKEY_PROMISE
NS_IMPL_CYCLE_COLLECTION_TRACE_END
-#ifndef SPIDERMONKEY_PROMISE
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Promise)
- if (tmp->IsBlack()) {
- tmp->mResult.exposeToActiveJS();
- tmp->mAllocationStack.exposeToActiveJS();
- tmp->mRejectionStack.exposeToActiveJS();
- tmp->mFullfillmentStack.exposeToActiveJS();
- return true;
- }
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
-
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Promise)
- return tmp->IsBlackAndDoesNotNeedTracing(tmp);
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
-
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Promise)
- return tmp->IsBlack();
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
-#endif // SPIDERMONKEY_PROMISE
-
NS_IMPL_CYCLE_COLLECTING_ADDREF(Promise)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Promise)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Promise)
-#ifndef SPIDERMONKEY_PROMISE
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-#endif // SPIDERMONKEY_PROMISE
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(Promise)
NS_INTERFACE_MAP_END
Promise::Promise(nsIGlobalObject* aGlobal)
: mGlobal(aGlobal)
-#ifndef SPIDERMONKEY_PROMISE
- , mResult(JS::UndefinedValue())
- , mAllocationStack(nullptr)
- , mRejectionStack(nullptr)
- , mFullfillmentStack(nullptr)
- , mState(Pending)
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
- , mHadRejectCallback(false)
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
- , mTaskPending(false)
- , mResolvePending(false)
- , mIsLastInChain(true)
- , mWasNotifiedAsUncaught(false)
- , mID(0)
-#else // SPIDERMONKEY_PROMISE
, mPromiseObj(nullptr)
-#endif // SPIDERMONKEY_PROMISE
{
MOZ_ASSERT(mGlobal);
mozilla::HoldJSObjects(this);
-
-#ifndef SPIDERMONKEY_PROMISE
- mCreationTimestamp = TimeStamp::Now();
-#endif // SPIDERMONKEY_PROMISE
}
Promise::~Promise()
{
-#ifndef SPIDERMONKEY_PROMISE
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
- MaybeReportRejectedOnce();
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-#endif // SPIDERMONKEY_PROMISE
mozilla::DropJSObjects(this);
}
-#ifdef SPIDERMONKEY_PROMISE
-
-bool
-Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
- JS::MutableHandle<JSObject*> aWrapper)
-{
-#ifdef DEBUG
- binding_detail::AssertReflectorHasGivenProto(aCx, mPromiseObj, aGivenProto);
-#endif // DEBUG
- aWrapper.set(mPromiseObj);
- return true;
-}
-
// static
already_AddRefed<Promise>
Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
@@ -884,8 +461,7 @@ Promise::HandleException(JSContext* aCx)
JS::Rooted<JS::Value> exn(aCx);
if (JS_GetPendingException(aCx, &exn)) {
JS_ClearPendingException(aCx);
- // This is only called from MaybeSomething, so it's OK to MaybeReject here,
- // unlike in the version that's used when !SPIDERMONKEY_PROMISE.
+ // This is only called from MaybeSomething, so it's OK to MaybeReject here.
MaybeReject(aCx, exn);
}
}
@@ -902,80 +478,6 @@ Promise::CreateFromExisting(nsIGlobalObject* aGlobal,
return p.forget();
}
-#else // SPIDERMONKEY_PROMISE
-
-JSObject*
-Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
- return PromiseBinding::Wrap(aCx, this, aGivenProto);
-}
-
-already_AddRefed<Promise>
-Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv,
- JS::Handle<JSObject*> aDesiredProto)
-{
- if (!aGlobal) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return nullptr;
- }
- RefPtr<Promise> p = new Promise(aGlobal);
- p->CreateWrapper(aDesiredProto, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
- return p.forget();
-}
-
-void
-Promise::CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv)
-{
- AutoJSAPI jsapi;
- if (!jsapi.Init(mGlobal)) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return;
- }
- JSContext* cx = jsapi.cx();
-
- JS::Rooted<JS::Value> wrapper(cx);
- if (!GetOrCreateDOMReflector(cx, this, &wrapper, aDesiredProto)) {
- JS_ClearPendingException(cx);
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
- return;
- }
-
- dom::PreserveWrapper(this);
-
- // Now grab our allocation stack
- if (!CaptureStack(cx, mAllocationStack)) {
- JS_ClearPendingException(cx);
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
- return;
- }
-
- JS::RootedObject obj(cx, &wrapper.toObject());
- JS::dbg::onNewPromise(cx, obj);
-}
-
-void
-Promise::MaybeResolve(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- MaybeResolveInternal(aCx, aValue);
-}
-
-void
-Promise::MaybeReject(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- MaybeRejectInternal(aCx, aValue);
-}
-
-#endif // SPIDERMONKEY_PROMISE
-
void
Promise::MaybeResolveWithUndefined()
{
@@ -999,7 +501,6 @@ Promise::MaybeRejectWithUndefined()
MaybeSomething(JS::UndefinedHandleValue, &Promise::MaybeReject);
}
-#ifdef SPIDERMONKEY_PROMISE
void
Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise)
{
@@ -1026,7 +527,6 @@ Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise)
// Now post an event to do the real reporting async
NS_DispatchToMainThread(new AsyncErrorReporter(xpcReport));
}
-#endif // defined(SPIDERMONKEY_PROMISE)
bool
Promise::PerformMicroTaskCheckpoint()
@@ -1132,1422 +632,6 @@ Promise::PerformWorkerDebuggerMicroTaskCheckpoint()
}
}
-#ifndef SPIDERMONKEY_PROMISE
-
-/* static */ bool
-Promise::JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-{
- JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
-
- JS::Rooted<JS::Value> v(aCx,
- js::GetFunctionNativeReserved(&args.callee(),
- SLOT_PROMISE));
- MOZ_ASSERT(v.isObject());
-
- Promise* promise;
- if (NS_FAILED(UNWRAP_OBJECT(Promise, &v.toObject(), promise))) {
- return Throw(aCx, NS_ERROR_UNEXPECTED);
- }
-
- v = js::GetFunctionNativeReserved(&args.callee(), SLOT_DATA);
- PromiseCallback::Task task = static_cast<PromiseCallback::Task>(v.toInt32());
-
- if (task == PromiseCallback::Resolve) {
- if (!promise->CaptureStack(aCx, promise->mFullfillmentStack)) {
- return false;
- }
- promise->MaybeResolveInternal(aCx, args.get(0));
- } else {
- promise->MaybeRejectInternal(aCx, args.get(0));
- if (!promise->CaptureStack(aCx, promise->mRejectionStack)) {
- return false;
- }
- }
-
- args.rval().setUndefined();
- return true;
-}
-
-/*
- * Common bits of (JSCallbackThenableResolver/JSCallbackThenableRejecter).
- * Resolves/rejects the Promise if it is ok to do so, based on whether either of
- * the callbacks have been called before or not.
- */
-/* static */ bool
-Promise::ThenableResolverCommon(JSContext* aCx, uint32_t aTask,
- unsigned aArgc, JS::Value* aVp)
-{
- JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
- JS::Rooted<JSObject*> thisFunc(aCx, &args.callee());
- if (!MarkAsCalledIfNotCalledBefore(aCx, thisFunc)) {
- // A function from this pair has been called before.
- args.rval().setUndefined();
- return true;
- }
-
- Promise* promise = GetPromise(aCx, thisFunc);
- MOZ_ASSERT(promise);
-
- if (aTask == PromiseCallback::Resolve) {
- promise->ResolveInternal(aCx, args.get(0));
- } else {
- promise->RejectInternal(aCx, args.get(0));
- }
-
- args.rval().setUndefined();
- return true;
-}
-
-/* static */ bool
-Promise::JSCallbackThenableResolver(JSContext* aCx,
- unsigned aArgc, JS::Value* aVp)
-{
- return ThenableResolverCommon(aCx, PromiseCallback::Resolve, aArgc, aVp);
-}
-
-/* static */ bool
-Promise::JSCallbackThenableRejecter(JSContext* aCx,
- unsigned aArgc, JS::Value* aVp)
-{
- return ThenableResolverCommon(aCx, PromiseCallback::Reject, aArgc, aVp);
-}
-
-/* static */ JSObject*
-Promise::CreateFunction(JSContext* aCx, Promise* aPromise, int32_t aTask)
-{
- // If this function ever changes, make sure to update
- // WrapperPromiseCallback::GetDependentPromise.
- JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback,
- 1 /* nargs */, 0 /* flags */,
- nullptr);
- if (!func) {
- return nullptr;
- }
-
- JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
-
- JS::Rooted<JS::Value> promiseObj(aCx);
- if (!dom::GetOrCreateDOMReflector(aCx, aPromise, &promiseObj)) {
- return nullptr;
- }
-
- JS::ExposeValueToActiveJS(promiseObj);
- js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj);
- js::SetFunctionNativeReserved(obj, SLOT_DATA, JS::Int32Value(aTask));
-
- return obj;
-}
-
-/* static */ JSObject*
-Promise::CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask)
-{
- JSNative whichFunc =
- aTask == PromiseCallback::Resolve ? JSCallbackThenableResolver :
- JSCallbackThenableRejecter ;
-
- JSFunction* func = js::NewFunctionWithReserved(aCx, whichFunc,
- 1 /* nargs */, 0 /* flags */,
- nullptr);
- if (!func) {
- return nullptr;
- }
-
- JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
-
- JS::Rooted<JS::Value> promiseObj(aCx);
- if (!dom::GetOrCreateDOMReflector(aCx, aPromise, &promiseObj)) {
- return nullptr;
- }
-
- JS::ExposeValueToActiveJS(promiseObj);
- js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj);
-
- return obj;
-}
-
-/* static */ already_AddRefed<Promise>
-Promise::Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
- ErrorResult& aRv, JS::Handle<JSObject*> aDesiredProto)
-{
- nsCOMPtr<nsIGlobalObject> global;
- global = do_QueryInterface(aGlobal.GetAsSupports());
- if (!global) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return nullptr;
- }
-
- RefPtr<Promise> promise = Create(global, aRv, aDesiredProto);
- if (aRv.Failed()) {
- return nullptr;
- }
-
- promise->CallInitFunction(aGlobal, aInit, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
-
- return promise.forget();
-}
-
-void
-Promise::CallInitFunction(const GlobalObject& aGlobal,
- PromiseInit& aInit, ErrorResult& aRv)
-{
- JSContext* cx = aGlobal.Context();
-
- JS::Rooted<JSObject*> resolveFunc(cx,
- CreateFunction(cx, this,
- PromiseCallback::Resolve));
- if (!resolveFunc) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return;
- }
-
- JS::Rooted<JSObject*> rejectFunc(cx,
- CreateFunction(cx, this,
- PromiseCallback::Reject));
- if (!rejectFunc) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return;
- }
-
- aInit.Call(resolveFunc, rejectFunc, aRv, "promise initializer",
- CallbackObject::eRethrowExceptions, Compartment());
- aRv.WouldReportJSException();
-
- if (aRv.Failed()) {
- if (aRv.IsUncatchableException()) {
- // Just propagate this to the caller.
- return;
- }
-
- // There are two possibilities here. Either we've got a rethrown exception,
- // or we reported that already and synthesized a generic NS_ERROR_FAILURE on
- // the ErrorResult. In the former case, it doesn't much matter how we get
- // the exception JS::Value from the ErrorResult to us, since we'll just end
- // up wrapping it into the right compartment as needed if we hand it to
- // someone. But in the latter case we have to ensure that the new exception
- // object we create is created in our reflector compartment, not in our
- // current compartment, because in the case when we're a Promise constructor
- // called over Xrays creating it in the current compartment would mean
- // rejecting with a value that can't be accessed by code that can call
- // then() on this Promise.
- //
- // Luckily, MaybeReject(aRv) does exactly what we want here: it enters our
- // reflector compartment before trying to produce a JS::Value from the
- // ErrorResult.
- MaybeReject(aRv);
- }
-}
-
-#define GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT 0
-#define GET_CAPABILITIES_EXECUTOR_REJECT_SLOT 1
-
-namespace {
-bool
-GetCapabilitiesExecutor(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-{
- // Implements
- // http://www.ecma-international.org/ecma-262/6.0/#sec-getcapabilitiesexecutor-functions
- // except we store the [[Resolve]] and [[Reject]] in our own internal slots,
- // not in a PromiseCapability. The PromiseCapability will then read them from
- // us.
- JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
-
- // Step 1 is an assert.
-
- // Step 2 doesn't need to be done, because it's just giving a name to the
- // PromiseCapability record which is supposed to be stored in an internal
- // slot. But we don't store that at all, per the comment above; we just
- // directly store its [[Resolve]] and [[Reject]] members.
-
- // Steps 3 and 4.
- if (!js::GetFunctionNativeReserved(&args.callee(),
- GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT).isUndefined() ||
- !js::GetFunctionNativeReserved(&args.callee(),
- GET_CAPABILITIES_EXECUTOR_REJECT_SLOT).isUndefined()) {
- ErrorResult rv;
- rv.ThrowTypeError<MSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY>();
- return !rv.MaybeSetPendingException(aCx);
- }
-
- // Step 5.
- js::SetFunctionNativeReserved(&args.callee(),
- GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT,
- args.get(0));
-
- // Step 6.
- js::SetFunctionNativeReserved(&args.callee(),
- GET_CAPABILITIES_EXECUTOR_REJECT_SLOT,
- args.get(1));
-
- // Step 7.
- args.rval().setUndefined();
- return true;
-}
-} // anonymous namespace
-
-/* static */ void
-Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal,
- JS::Handle<JS::Value> aConstructor,
- bool aForceCallbackCreation,
- PromiseCapability& aCapability,
- ErrorResult& aRv)
-{
- // Implements
- // http://www.ecma-international.org/ecma-262/6.0/#sec-newpromisecapability
-
- if (!aConstructor.isObject() ||
- !JS::IsConstructor(&aConstructor.toObject())) {
- aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
- return;
- }
-
- // Step 2 is a note.
- // Step 3 is already done because we got the PromiseCapability passed in.
-
- // Optimization: Check whether constructor is in fact the canonical
- // Promise constructor for aGlobal.
- JS::Rooted<JSObject*> global(aCx, aGlobal->GetGlobalJSObject());
- {
- // Scope for the JSAutoCompartment, since we need to enter the compartment
- // of global to get constructors from it. Save the compartment we used to
- // be in, though; we'll need it later.
- JS::Rooted<JSObject*> callerGlobal(aCx, JS::CurrentGlobalOrNull(aCx));
- JSAutoCompartment ac(aCx, global);
-
- // Now wrap aConstructor into the compartment of aGlobal, so comparing it to
- // the canonical Promise for that compartment actually makes sense.
- JS::Rooted<JS::Value> constructorValue(aCx, aConstructor);
- if (!MaybeWrapObjectValue(aCx, &constructorValue)) {
- aRv.NoteJSContextException(aCx);
- return;
- }
-
- JSObject* defaultCtor = PromiseBinding::GetConstructorObject(aCx);
- if (!defaultCtor) {
- aRv.NoteJSContextException(aCx);
- return;
- }
- if (defaultCtor == &constructorValue.toObject()) {
- // This is the canonical Promise constructor.
- aCapability.mNativePromise = Promise::Create(aGlobal, aRv);
- if (aForceCallbackCreation) {
- // We have to be a bit careful here. We want to create these functions
- // in the compartment in which they would be created if we actually
- // invoked the constructor via JS::Construct below. That means our
- // callerGlobal compartment if aConstructor is an Xray and the reflector
- // compartment of the promise we're creating otherwise. But note that
- // our callerGlobal compartment is precisely the reflector compartment
- // unless the call was done over Xrays, because the reflector
- // compartment comes from xpc::XrayAwareCalleeGlobal. So we really just
- // want to create these functions in the callerGlobal compartment.
- MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(&aConstructor.toObject()) ||
- callerGlobal == global);
- JSAutoCompartment ac2(aCx, callerGlobal);
-
- JSObject* resolveFuncObj =
- CreateFunction(aCx, aCapability.mNativePromise,
- PromiseCallback::Resolve);
- if (!resolveFuncObj) {
- aRv.NoteJSContextException(aCx);
- return;
- }
- aCapability.mResolve.setObject(*resolveFuncObj);
-
- JSObject* rejectFuncObj =
- CreateFunction(aCx, aCapability.mNativePromise,
- PromiseCallback::Reject);
- if (!rejectFuncObj) {
- aRv.NoteJSContextException(aCx);
- return;
- }
- aCapability.mReject.setObject(*rejectFuncObj);
- }
- return;
- }
- }
-
- // Step 4.
- // We can create our get-capabilities function in the calling compartment. It
- // will work just as if we did |new promiseConstructor(function(a,b){}).
- // Notably, if we're called over Xrays that's all fine, because we will end up
- // creating the callbacks in the caller compartment in that case.
- JSFunction* getCapabilitiesFunc =
- js::NewFunctionWithReserved(aCx, GetCapabilitiesExecutor,
- 2 /* nargs */,
- 0 /* flags */,
- nullptr);
- if (!getCapabilitiesFunc) {
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
- return;
- }
-
- JS::Rooted<JSObject*> getCapabilitiesObj(aCx);
- getCapabilitiesObj = JS_GetFunctionObject(getCapabilitiesFunc);
-
- // Step 5 doesn't need to be done, since we're not actually storing a
- // PromiseCapability in the executor; see the comments in
- // GetCapabilitiesExecutor above.
-
- // Step 6 and step 7.
- JS::Rooted<JS::Value> getCapabilities(aCx,
- JS::ObjectValue(*getCapabilitiesObj));
- JS::Rooted<JSObject*> promiseObj(aCx);
- if (!JS::Construct(aCx, aConstructor,
- JS::HandleValueArray(getCapabilities),
- &promiseObj)) {
- aRv.NoteJSContextException(aCx);
- return;
- }
-
- // Step 8 plus copying over the value to the PromiseCapability.
- JS::Rooted<JS::Value> v(aCx);
- v = js::GetFunctionNativeReserved(getCapabilitiesObj,
- GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT);
- if (!v.isObject() || !JS::IsCallable(&v.toObject())) {
- aRv.ThrowTypeError<MSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE>();
- return;
- }
- aCapability.mResolve = v;
-
- // Step 9 plus copying over the value to the PromiseCapability.
- v = js::GetFunctionNativeReserved(getCapabilitiesObj,
- GET_CAPABILITIES_EXECUTOR_REJECT_SLOT);
- if (!v.isObject() || !JS::IsCallable(&v.toObject())) {
- aRv.ThrowTypeError<MSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE>();
- return;
- }
- aCapability.mReject = v;
-
- // Step 10.
- aCapability.mPromise = promiseObj;
-
- // Step 11 doesn't need anything, since the PromiseCapability was passed in.
-}
-
-/* static */ void
-Promise::Resolve(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
- 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.resolve
-
- JSContext* cx = aGlobal.Context();
-
- nsCOMPtr<nsIGlobalObject> global =
- do_QueryInterface(aGlobal.GetAsSupports());
- if (!global) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return;
- }
-
- // Steps 1 and 2.
- if (!aThisv.isObject()) {
- aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
- return;
- }
-
- // Step 3. If a Promise was passed and matches our constructor, just return it.
- if (aValue.isObject()) {
- JS::Rooted<JSObject*> valueObj(cx, &aValue.toObject());
- Promise* nextPromise;
- nsresult rv = UNWRAP_OBJECT(Promise, valueObj, nextPromise);
-
- if (NS_SUCCEEDED(rv)) {
- JS::Rooted<JS::Value> constructor(cx);
- if (!JS_GetProperty(cx, valueObj, "constructor", &constructor)) {
- aRv.NoteJSContextException(cx);
- return;
- }
-
- // Cheat instead of calling JS_SameValue, since we know one's an object.
- if (aThisv == constructor) {
- aRetval.setObject(*valueObj);
- return;
- }
- }
- }
-
- // Step 4.
- PromiseCapability capability(cx);
- NewPromiseCapability(cx, global, aThisv, false, capability, aRv);
- // Step 5.
- if (aRv.Failed()) {
- return;
- }
-
- // Step 6.
- Promise* p = capability.mNativePromise;
- if (p) {
- p->MaybeResolveInternal(cx, aValue);
- p->mFullfillmentStack = p->mAllocationStack;
- } else {
- JS::Rooted<JS::Value> value(cx, aValue);
- JS::Rooted<JS::Value> ignored(cx);
- if (!JS::Call(cx, JS::UndefinedHandleValue /* thisVal */,
- capability.mResolve, JS::HandleValueArray(value),
- &ignored)) {
- // Step 7.
- aRv.NoteJSContextException(cx);
- return;
- }
- }
-
- // Step 8.
- aRetval.set(capability.PromiseValue());
-}
-
-/* static */ already_AddRefed<Promise>
-Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
- JS::Handle<JS::Value> aValue, ErrorResult& aRv)
-{
- RefPtr<Promise> promise = Create(aGlobal, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
-
- promise->MaybeResolveInternal(aCx, aValue);
- return promise.forget();
-}
-
-/* static */ void
-Promise::Reject(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
- 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;
- }
-
- // Steps 1 and 2.
- if (!aThisv.isObject()) {
- aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
- return;
- }
-
- // 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(cx);
- return;
- }
- }
-
- // Step 7.
- aRetval.set(capability.PromiseValue());
-}
-
-/* static */ already_AddRefed<Promise>
-Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
- JS::Handle<JS::Value> aValue, ErrorResult& aRv)
-{
- RefPtr<Promise> promise = Create(aGlobal, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
-
- promise->MaybeRejectInternal(aCx, aValue);
- return promise.forget();
-}
-
-namespace {
-void
-SpeciesConstructor(JSContext* aCx,
- JS::Handle<JSObject*> promise,
- JS::Handle<JS::Value> defaultCtor,
- JS::MutableHandle<JS::Value> ctor,
- ErrorResult& aRv)
-{
- // Implements
- // http://www.ecma-international.org/ecma-262/6.0/#sec-speciesconstructor
-
- // Step 1.
- MOZ_ASSERT(promise);
-
- // Step 2.
- JS::Rooted<JS::Value> constructorVal(aCx);
- if (!JS_GetProperty(aCx, promise, "constructor", &constructorVal)) {
- // Step 3.
- aRv.NoteJSContextException(aCx);
- return;
- }
-
- // Step 4.
- if (constructorVal.isUndefined()) {
- ctor.set(defaultCtor);
- return;
- }
-
- // Step 5.
- if (!constructorVal.isObject()) {
- aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
- return;
- }
-
- // Step 6.
- JS::Rooted<jsid> species(aCx,
- SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species)));
- JS::Rooted<JS::Value> speciesVal(aCx);
- JS::Rooted<JSObject*> constructorObj(aCx, &constructorVal.toObject());
- if (!JS_GetPropertyById(aCx, constructorObj, species, &speciesVal)) {
- // Step 7.
- aRv.NoteJSContextException(aCx);
- return;
- }
-
- // Step 8.
- if (speciesVal.isNullOrUndefined()) {
- ctor.set(defaultCtor);
- return;
- }
-
- // Step 9.
- if (speciesVal.isObject() && JS::IsConstructor(&speciesVal.toObject())) {
- ctor.set(speciesVal);
- return;
- }
-
- // Step 10.
- aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
-}
-} // anonymous namespace
-
-void
-Promise::Then(JSContext* aCx, JS::Handle<JSObject*> aCalleeGlobal,
- AnyCallback* aResolveCallback, AnyCallback* aRejectCallback,
- JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- // Implements
- // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.then
-
- // Step 1.
- JS::Rooted<JS::Value> promiseVal(aCx, JS::ObjectValue(*GetWrapper()));
- if (!MaybeWrapObjectValue(aCx, &promiseVal)) {
- aRv.NoteJSContextException(aCx);
- return;
- }
- JS::Rooted<JSObject*> promiseObj(aCx, &promiseVal.toObject());
- MOZ_ASSERT(promiseObj);
-
- // Step 2 was done by the bindings.
-
- // Step 3. We want to use aCalleeGlobal here because it will do the
- // right thing for us via Xrays (where we won't find @@species on
- // our promise constructor for now).
- JS::Rooted<JS::Value> defaultCtorVal(aCx);
- { // Scope for JSAutoCompartment
- JSAutoCompartment ac(aCx, aCalleeGlobal);
- JSObject* defaultCtor = PromiseBinding::GetConstructorObject(aCx);
- if (!defaultCtor) {
- aRv.NoteJSContextException(aCx);
- return;
- }
- defaultCtorVal.setObject(*defaultCtor);
- }
- if (!MaybeWrapObjectValue(aCx, &defaultCtorVal)) {
- aRv.NoteJSContextException(aCx);
- return;
- }
-
- JS::Rooted<JS::Value> constructor(aCx);
- SpeciesConstructor(aCx, promiseObj, defaultCtorVal, &constructor, aRv);
- if (aRv.Failed()) {
- // Step 4.
- return;
- }
-
- // Step 5.
- GlobalObject globalObj(aCx, GetWrapper());
- if (globalObj.Failed()) {
- aRv.NoteJSContextException(aCx);
- return;
- }
- nsCOMPtr<nsIGlobalObject> globalObject =
- do_QueryInterface(globalObj.GetAsSupports());
- if (!globalObject) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return;
- }
- PromiseCapability capability(aCx);
- NewPromiseCapability(aCx, globalObject, constructor, false, capability, aRv);
- if (aRv.Failed()) {
- // Step 6.
- return;
- }
-
- // Now step 7: start
- // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromisethen
-
- // Step 1 and step 2 are just assertions.
-
- // Step 3 and step 4 are kinda handled for us already; we use null
- // to represent "Identity" and "Thrower".
-
- // Steps 5 and 6. These branch based on whether we know we have a
- // vanilla Promise or not.
- JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
- if (capability.mNativePromise) {
- Promise* promise = capability.mNativePromise;
-
- RefPtr<PromiseCallback> resolveCb =
- PromiseCallback::Factory(promise, global, aResolveCallback,
- PromiseCallback::Resolve);
-
- RefPtr<PromiseCallback> rejectCb =
- PromiseCallback::Factory(promise, global, aRejectCallback,
- PromiseCallback::Reject);
-
- AppendCallbacks(resolveCb, rejectCb);
- } else {
- JS::Rooted<JSObject*> resolveObj(aCx, &capability.mResolve.toObject());
- RefPtr<AnyCallback> resolveFunc =
- new AnyCallback(aCx, resolveObj, GetIncumbentGlobal());
-
- JS::Rooted<JSObject*> rejectObj(aCx, &capability.mReject.toObject());
- RefPtr<AnyCallback> rejectFunc =
- new AnyCallback(aCx, rejectObj, GetIncumbentGlobal());
-
- if (!capability.mPromise) {
- aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
- return;
- }
- JS::Rooted<JSObject*> newPromiseObj(aCx, capability.mPromise);
- // We want to store the reflector itself.
- newPromiseObj = js::CheckedUnwrap(newPromiseObj);
- if (!newPromiseObj) {
- // Just throw something.
- aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
- return;
- }
-
- RefPtr<PromiseCallback> resolveCb;
- if (aResolveCallback) {
- resolveCb = new WrapperPromiseCallback(global, aResolveCallback,
- newPromiseObj,
- resolveFunc, rejectFunc);
- } else {
- resolveCb = new InvokePromiseFuncCallback(global, newPromiseObj,
- resolveFunc);
- }
-
- RefPtr<PromiseCallback> rejectCb;
- if (aRejectCallback) {
- rejectCb = new WrapperPromiseCallback(global, aRejectCallback,
- newPromiseObj,
- resolveFunc, rejectFunc);
- } else {
- rejectCb = new InvokePromiseFuncCallback(global, newPromiseObj,
- rejectFunc);
- }
-
- AppendCallbacks(resolveCb, rejectCb);
- }
-
- aRetval.set(capability.PromiseValue());
-}
-
-void
-Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback,
- JS::MutableHandle<JS::Value> aRetval,
- ErrorResult& aRv)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- // Implements
- // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.catch
-
- // We can't call Promise::Then directly, because someone might have
- // overridden Promise.prototype.then.
- JS::Rooted<JS::Value> promiseVal(aCx, JS::ObjectValue(*GetWrapper()));
- if (!MaybeWrapObjectValue(aCx, &promiseVal)) {
- aRv.NoteJSContextException(aCx);
- return;
- }
- JS::Rooted<JSObject*> promiseObj(aCx, &promiseVal.toObject());
- MOZ_ASSERT(promiseObj);
- JS::AutoValueArray<2> callbacks(aCx);
- callbacks[0].setUndefined();
- if (aRejectCallback) {
- callbacks[1].setObject(*aRejectCallback->Callable());
- // It could be in any compartment, so put it in ours.
- if (!MaybeWrapObjectValue(aCx, callbacks[1])) {
- aRv.NoteJSContextException(aCx);
- return;
- }
- } else {
- callbacks[1].setNull();
- }
- if (!JS_CallFunctionName(aCx, promiseObj, "then", callbacks, aRetval)) {
- aRv.NoteJSContextException(aCx);
- }
-}
-
-/**
- * The CountdownHolder class encapsulates Promise.all countdown functions and
- * the countdown holder parts of the Promises spec. It maintains the result
- * array and AllResolveElementFunctions use SetValue() to set the array indices.
- */
-class CountdownHolder final : public nsISupports
-{
-public:
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CountdownHolder)
-
- CountdownHolder(const GlobalObject& aGlobal, Promise* aPromise,
- uint32_t aCountdown)
- : mPromise(aPromise), mCountdown(aCountdown)
- {
- MOZ_ASSERT(aCountdown != 0);
- JSContext* cx = aGlobal.Context();
-
- // The only time aGlobal.Context() and aGlobal.Get() are not
- // same-compartment is when we're called via Xrays, and in that situation we
- // in fact want to create the array in the callee compartment
-
- JSAutoCompartment ac(cx, aGlobal.Get());
- mValues = JS_NewArrayObject(cx, aCountdown);
- mozilla::HoldJSObjects(this);
- }
-
-private:
- ~CountdownHolder()
- {
- mozilla::DropJSObjects(this);
- }
-
-public:
- void SetValue(uint32_t index, const JS::Handle<JS::Value> aValue)
- {
- MOZ_ASSERT(mCountdown > 0);
-
- AutoJSAPI jsapi;
- if (!jsapi.Init(mValues)) {
- return;
- }
- JSContext* cx = jsapi.cx();
-
- JS::Rooted<JS::Value> value(cx, aValue);
- JS::Rooted<JSObject*> values(cx, mValues);
- if (!JS_WrapValue(cx, &value) ||
- !JS_DefineElement(cx, values, index, value, JSPROP_ENUMERATE)) {
- MOZ_ASSERT(JS_IsExceptionPending(cx));
- JS::Rooted<JS::Value> exn(cx);
- if (!jsapi.StealException(&exn)) {
- mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
- } else {
- mPromise->MaybeReject(cx, exn);
- }
- }
-
- --mCountdown;
- if (mCountdown == 0) {
- JS::Rooted<JS::Value> result(cx, JS::ObjectValue(*mValues));
- mPromise->MaybeResolve(cx, result);
- }
- }
-
-private:
- RefPtr<Promise> mPromise;
- uint32_t mCountdown;
- JS::Heap<JSObject*> mValues;
-};
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(CountdownHolder)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(CountdownHolder)
-NS_IMPL_CYCLE_COLLECTION_CLASS(CountdownHolder)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CountdownHolder)
- NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CountdownHolder)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mValues)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CountdownHolder)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CountdownHolder)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
- tmp->mValues = nullptr;
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-/**
- * An AllResolveElementFunction is the per-promise
- * part of the Promise.all() algorithm.
- * Every Promise in the handler is handed an instance of this as a resolution
- * handler and it sets the relevant index in the CountdownHolder.
- */
-class AllResolveElementFunction final : public PromiseNativeHandler
-{
-public:
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_CLASS(AllResolveElementFunction)
-
- AllResolveElementFunction(CountdownHolder* aHolder, uint32_t aIndex)
- : mCountdownHolder(aHolder), mIndex(aIndex)
- {
- MOZ_ASSERT(aHolder);
- }
-
- void
- ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
- {
- mCountdownHolder->SetValue(mIndex, aValue);
- }
-
- void
- RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
- {
- // Should never be attached to Promise as a reject handler.
- MOZ_CRASH("AllResolveElementFunction should never be attached to a Promise's reject handler!");
- }
-
-private:
- ~AllResolveElementFunction()
- {
- }
-
- RefPtr<CountdownHolder> mCountdownHolder;
- uint32_t mIndex;
-};
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(AllResolveElementFunction)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(AllResolveElementFunction)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AllResolveElementFunction)
- NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTION(AllResolveElementFunction, mCountdownHolder)
-
-static const JSClass PromiseAllDataHolderClass = {
- "PromiseAllDataHolder", JSCLASS_HAS_RESERVED_SLOTS(3)
-};
-
-// Slot indices for objects of class PromiseAllDataHolderClass.
-#define DATA_HOLDER_REMAINING_ELEMENTS_SLOT 0
-#define DATA_HOLDER_VALUES_ARRAY_SLOT 1
-#define DATA_HOLDER_RESOLVE_FUNCTION_SLOT 2
-
-// Slot indices for PromiseAllResolveElement.
-// The RESOLVE_ELEMENT_INDEX_SLOT stores our index unless we've already been
-// called. Then it stores INT32_MIN (which is never a valid index value).
-#define RESOLVE_ELEMENT_INDEX_SLOT 0
-// The RESOLVE_ELEMENT_DATA_HOLDER_SLOT slot stores an object of class
-// PromiseAllDataHolderClass.
-#define RESOLVE_ELEMENT_DATA_HOLDER_SLOT 1
-
-static bool
-PromiseAllResolveElement(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-{
- // Implements
- // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.all-resolve-element-functions
- //
- // See the big comment about compartments in Promise::All "Substep 4" that
- // explains what compartments the various stuff here lives in.
- JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
-
- // Step 1.
- int32_t index =
- js::GetFunctionNativeReserved(&args.callee(),
- RESOLVE_ELEMENT_INDEX_SLOT).toInt32();
- // Step 2.
- if (index == INT32_MIN) {
- args.rval().setUndefined();
- return true;
- }
-
- // Step 3.
- js::SetFunctionNativeReserved(&args.callee(),
- RESOLVE_ELEMENT_INDEX_SLOT,
- JS::Int32Value(INT32_MIN));
-
- // Step 4 already done.
-
- // Step 5.
- JS::Rooted<JSObject*> dataHolder(aCx,
- &js::GetFunctionNativeReserved(&args.callee(),
- RESOLVE_ELEMENT_DATA_HOLDER_SLOT).toObject());
-
- JS::Rooted<JS::Value> values(aCx,
- js::GetReservedSlot(dataHolder, DATA_HOLDER_VALUES_ARRAY_SLOT));
-
- // Step 6, effectively.
- JS::Rooted<JS::Value> resolveFunc(aCx,
- js::GetReservedSlot(dataHolder, DATA_HOLDER_RESOLVE_FUNCTION_SLOT));
-
- // Step 7.
- int32_t remainingElements =
- js::GetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32();
-
- // Step 8.
- JS::Rooted<JSObject*> valuesObj(aCx, &values.toObject());
- if (!JS_DefineElement(aCx, valuesObj, index, args.get(0), JSPROP_ENUMERATE)) {
- return false;
- }
-
- // Step 9.
- remainingElements -= 1;
- js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT,
- JS::Int32Value(remainingElements));
-
- // Step 10.
- if (remainingElements == 0) {
- return JS::Call(aCx, JS::UndefinedHandleValue, resolveFunc,
- JS::HandleValueArray(values), args.rval());
- }
-
- // Step 11.
- args.rval().setUndefined();
- return true;
-}
-
-
-/* static */ void
-Promise::All(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
- JS::Handle<JS::Value> aIterable,
- JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv)
-{
- // Implements http://www.ecma-international.org/ecma-262/6.0/#sec-promise.all
- nsCOMPtr<nsIGlobalObject> global =
- do_QueryInterface(aGlobal.GetAsSupports());
- if (!global) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return;
- }
-
- JSContext* cx = aGlobal.Context();
-
- // Steps 1-5: nothing to do. Note that the @@species bits got removed in
- // https://github.com/tc39/ecma262/pull/211
-
- // Step 6.
- PromiseCapability capability(cx);
- NewPromiseCapability(cx, global, aThisv, true, capability, aRv);
- // Step 7.
- if (aRv.Failed()) {
- return;
- }
-
- MOZ_ASSERT(aThisv.isObject(), "How did NewPromiseCapability succeed?");
- JS::Rooted<JSObject*> constructorObj(cx, &aThisv.toObject());
-
- // After this point we have a useful promise value in "capability", so just go
- // ahead and put it in our retval now. Every single return path below would
- // want to do that anyway.
- aRetval.set(capability.PromiseValue());
- if (!MaybeWrapValue(cx, aRetval)) {
- aRv.NoteJSContextException(cx);
- return;
- }
-
- // The arguments we're going to be passing to "then" on each loop iteration.
- // The second one we know already; the first one will be created on each
- // iteration of the loop.
- JS::AutoValueArray<2> callbackFunctions(cx);
- callbackFunctions[1].set(capability.mReject);
-
- // Steps 8 and 9.
- JS::ForOfIterator iter(cx);
- if (!iter.init(aIterable, JS::ForOfIterator::AllowNonIterable)) {
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- if (!iter.valueIsIterable()) {
- ThrowErrorMessage(cx, MSG_PROMISE_ARG_NOT_ITERABLE,
- "Argument of Promise.all");
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- // Step 10 doesn't need to be done, because ForOfIterator handles it
- // for us.
-
- // Now we jump over to
- // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromiseall
- // and do its steps.
-
- // Substep 4. Create our data holder that holds all the things shared across
- // every step of the iterator. In particular, this holds the
- // remainingElementsCount (as an integer reserved slot), the array of values,
- // and the resolve function from our PromiseCapability.
- //
- // We have to be very careful about which compartments we create things in
- // here. In particular, we have to maintain the invariant that anything
- // stored in a reserved slot is same-compartment with the object whose
- // reserved slot it's in. But we want to create the values array in the
- // Promise reflector compartment, because that array can get exposed to code
- // that has access to the Promise reflector (in particular code from that
- // compartment), and that should work, even if the Promise reflector
- // compartment is less-privileged than our caller compartment.
- //
- // So the plan is as follows: Create the values array in the promise reflector
- // compartment. Create the PromiseAllResolveElement function and the data
- // holder in our current compartment. Store a cross-compartment wrapper to
- // the values array in the holder. This should be OK because the only things
- // we hand the PromiseAllResolveElement function to are the "then" calls we do
- // and in the case when the reflector compartment is not the current
- // compartment those are happening over Xrays anyway, which means they get the
- // canonical "then" function and content can't see our
- // PromiseAllResolveElement.
- JS::Rooted<JSObject*> dataHolder(cx);
- dataHolder = JS_NewObjectWithGivenProto(cx, &PromiseAllDataHolderClass,
- nullptr);
- if (!dataHolder) {
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- JS::Rooted<JSObject*> reflectorGlobal(cx, global->GetGlobalJSObject());
- JS::Rooted<JSObject*> valuesArray(cx);
- { // Scope for JSAutoCompartment.
- JSAutoCompartment ac(cx, reflectorGlobal);
- valuesArray = JS_NewArrayObject(cx, 0);
- }
- if (!valuesArray) {
- // It's important that we've exited the JSAutoCompartment by now, before
- // calling RejectWithException and possibly invoking capability.mReject.
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- // The values array as a value we can pass to a function in our current
- // compartment, or store in the holder's reserved slot.
- JS::Rooted<JS::Value> valuesArrayVal(cx, JS::ObjectValue(*valuesArray));
- if (!MaybeWrapObjectValue(cx, &valuesArrayVal)) {
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT,
- JS::Int32Value(1));
- js::SetReservedSlot(dataHolder, DATA_HOLDER_VALUES_ARRAY_SLOT,
- valuesArrayVal);
- js::SetReservedSlot(dataHolder, DATA_HOLDER_RESOLVE_FUNCTION_SLOT,
- capability.mResolve);
-
- // Substep 5.
- CheckedInt32 index = 0;
-
- // Substep 6.
- JS::Rooted<JS::Value> nextValue(cx);
- while (true) {
- bool done;
- // Steps a, b, c, e, f, g.
- if (!iter.next(&nextValue, &done)) {
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- // Step d.
- if (done) {
- int32_t remainingCount =
- js::GetReservedSlot(dataHolder,
- DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32();
- remainingCount -= 1;
- if (remainingCount == 0) {
- JS::Rooted<JS::Value> ignored(cx);
- if (!JS::Call(cx, JS::UndefinedHandleValue, capability.mResolve,
- JS::HandleValueArray(valuesArrayVal), &ignored)) {
- capability.RejectWithException(cx, aRv);
- }
- return;
- }
- js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT,
- JS::Int32Value(remainingCount));
- // We're all set for now!
- return;
- }
-
- // Step h.
- { // Scope for the JSAutoCompartment we need to work with valuesArray. We
- // mostly do this for performance; we could go ahead and do the define via
- // a cross-compartment proxy instead...
- JSAutoCompartment ac(cx, valuesArray);
- if (!JS_DefineElement(cx, valuesArray, index.value(),
- JS::UndefinedHandleValue, JSPROP_ENUMERATE)) {
- // Have to go back into the caller compartment before we try to touch
- // capability.mReject. Luckily, capability.mReject is guaranteed to be
- // an object in the right compartment here.
- JSAutoCompartment ac2(cx, &capability.mReject.toObject());
- capability.RejectWithException(cx, aRv);
- return;
- }
- }
-
- // Step i. Sadly, we can't take a shortcut here even if
- // capability.mNativePromise exists, because someone could have overridden
- // "resolve" on the canonical Promise constructor.
- JS::Rooted<JS::Value> nextPromise(cx);
- if (!JS_CallFunctionName(cx, constructorObj, "resolve",
- JS::HandleValueArray(nextValue),
- &nextPromise)) {
- // Step j.
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- // Step k.
- JS::Rooted<JSObject*> resolveElement(cx);
- JSFunction* resolveFunc =
- js::NewFunctionWithReserved(cx, PromiseAllResolveElement,
- 1 /* nargs */, 0 /* flags */, nullptr);
- if (!resolveFunc) {
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- resolveElement = JS_GetFunctionObject(resolveFunc);
- // Steps l-p.
- js::SetFunctionNativeReserved(resolveElement,
- RESOLVE_ELEMENT_INDEX_SLOT,
- JS::Int32Value(index.value()));
- js::SetFunctionNativeReserved(resolveElement,
- RESOLVE_ELEMENT_DATA_HOLDER_SLOT,
- JS::ObjectValue(*dataHolder));
-
- // Step q.
- int32_t remainingElements =
- js::GetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32();
- js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT,
- JS::Int32Value(remainingElements + 1));
-
- // Step r. And now we don't know whether nextPromise has an overridden
- // "then" method, so no shortcuts here either.
- callbackFunctions[0].setObject(*resolveElement);
- JS::Rooted<JSObject*> nextPromiseObj(cx);
- JS::Rooted<JS::Value> ignored(cx);
- if (!JS_ValueToObject(cx, nextPromise, &nextPromiseObj) ||
- !JS_CallFunctionName(cx, nextPromiseObj, "then", callbackFunctions,
- &ignored)) {
- // Step s.
- capability.RejectWithException(cx, aRv);
- }
-
- // Step t.
- index += 1;
- if (!index.isValid()) {
- // Let's just claim OOM.
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
- capability.RejectWithException(cx, aRv);
- }
- }
-}
-
-/* static */ already_AddRefed<Promise>
-Promise::All(const GlobalObject& aGlobal,
- const nsTArray<RefPtr<Promise>>& aPromiseList, ErrorResult& aRv)
-{
- nsCOMPtr<nsIGlobalObject> global =
- do_QueryInterface(aGlobal.GetAsSupports());
- if (!global) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return nullptr;
- }
-
- JSContext* cx = aGlobal.Context();
-
- if (aPromiseList.IsEmpty()) {
- JS::Rooted<JSObject*> empty(cx, JS_NewArrayObject(cx, 0));
- if (!empty) {
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
- return nullptr;
- }
- JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*empty));
- // We know "value" is not a promise, so call the Resolve function
- // that doesn't have to check for that.
- return Promise::Resolve(global, cx, value, aRv);
- }
-
- RefPtr<Promise> promise = Create(global, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
- RefPtr<CountdownHolder> holder =
- new CountdownHolder(aGlobal, promise, aPromiseList.Length());
-
- JS::Rooted<JSObject*> obj(cx, JS::CurrentGlobalOrNull(cx));
- if (!obj) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return nullptr;
- }
-
- RefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(promise, obj);
-
- for (uint32_t i = 0; i < aPromiseList.Length(); ++i) {
- RefPtr<PromiseNativeHandler> resolveHandler =
- new AllResolveElementFunction(holder, i);
-
- RefPtr<PromiseCallback> resolveCb =
- new NativePromiseCallback(resolveHandler, Resolved);
-
- // Every promise gets its own resolve callback, which will set the right
- // index in the array to the resolution value.
- aPromiseList[i]->AppendCallbacks(resolveCb, rejectCb);
- }
-
- return promise.forget();
-}
-
-/* static */ void
-Promise::Race(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
- JS::Handle<JS::Value> aIterable, JS::MutableHandle<JS::Value> aRetval,
- ErrorResult& aRv)
-{
- // Implements http://www.ecma-international.org/ecma-262/6.0/#sec-promise.race
- nsCOMPtr<nsIGlobalObject> global =
- do_QueryInterface(aGlobal.GetAsSupports());
- if (!global) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return;
- }
-
- JSContext* cx = aGlobal.Context();
-
- // Steps 1-5: nothing to do. Note that the @@species bits got removed in
- // https://github.com/tc39/ecma262/pull/211
- PromiseCapability capability(cx);
-
- // Step 6.
- NewPromiseCapability(cx, global, aThisv, true, capability, aRv);
- // Step 7.
- if (aRv.Failed()) {
- return;
- }
-
- MOZ_ASSERT(aThisv.isObject(), "How did NewPromiseCapability succeed?");
- JS::Rooted<JSObject*> constructorObj(cx, &aThisv.toObject());
-
- // After this point we have a useful promise value in "capability", so just go
- // ahead and put it in our retval now. Every single return path below would
- // want to do that anyway.
- aRetval.set(capability.PromiseValue());
- if (!MaybeWrapValue(cx, aRetval)) {
- aRv.NoteJSContextException(cx);
- return;
- }
-
- // The arguments we're going to be passing to "then" on each loop iteration.
- JS::AutoValueArray<2> callbackFunctions(cx);
- callbackFunctions[0].set(capability.mResolve);
- callbackFunctions[1].set(capability.mReject);
-
- // Steps 8 and 9.
- JS::ForOfIterator iter(cx);
- if (!iter.init(aIterable, JS::ForOfIterator::AllowNonIterable)) {
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- if (!iter.valueIsIterable()) {
- ThrowErrorMessage(cx, MSG_PROMISE_ARG_NOT_ITERABLE,
- "Argument of Promise.race");
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- // Step 10 doesn't need to be done, because ForOfIterator handles it
- // for us.
-
- // Now we jump over to
- // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromiserace
- // and do its steps.
- JS::Rooted<JS::Value> nextValue(cx);
- while (true) {
- bool done;
- // Steps a, b, c, e, f, g.
- if (!iter.next(&nextValue, &done)) {
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- // Step d.
- if (done) {
- // We're all set!
- return;
- }
-
- // Step h. Sadly, we can't take a shortcut here even if
- // capability.mNativePromise exists, because someone could have overridden
- // "resolve" on the canonical Promise constructor.
- JS::Rooted<JS::Value> nextPromise(cx);
- if (!JS_CallFunctionName(cx, constructorObj, "resolve",
- JS::HandleValueArray(nextValue), &nextPromise)) {
- // Step i.
- capability.RejectWithException(cx, aRv);
- return;
- }
-
- // Step j. And now we don't know whether nextPromise has an overridden
- // "then" method, so no shortcuts here either.
- JS::Rooted<JSObject*> nextPromiseObj(cx);
- JS::Rooted<JS::Value> ignored(cx);
- if (!JS_ValueToObject(cx, nextPromise, &nextPromiseObj) ||
- !JS_CallFunctionName(cx, nextPromiseObj, "then", callbackFunctions,
- &ignored)) {
- // Step k.
- capability.RejectWithException(cx, aRv);
- }
- }
-}
-
-/* static */
-bool
-Promise::PromiseSpecies(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-{
- JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
- args.rval().set(args.thisv());
- return true;
-}
-
-void
-Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- RefPtr<PromiseCallback> resolveCb =
- new NativePromiseCallback(aRunnable, Resolved);
-
- RefPtr<PromiseCallback> rejectCb =
- new NativePromiseCallback(aRunnable, Rejected);
-
- AppendCallbacks(resolveCb, rejectCb);
-}
-
-#endif // SPIDERMONKEY_PROMISE
-
JSObject*
Promise::GlobalJSObject() const
{
@@ -2560,382 +644,6 @@ Promise::Compartment() const
return js::GetObjectCompartment(GlobalJSObject());
}
-#ifndef SPIDERMONKEY_PROMISE
-void
-Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
- PromiseCallback* aRejectCallback)
-{
- if (!mGlobal || mGlobal->IsDying()) {
- return;
- }
-
- MOZ_ASSERT(aResolveCallback);
- MOZ_ASSERT(aRejectCallback);
-
- if (mIsLastInChain && mState == PromiseState::Rejected) {
- // This rejection is now consumed.
- PromiseDebugging::AddConsumedRejection(*this);
- // Note that we may not have had the opportunity to call
- // RunResolveTask() yet, so we may never have called
- // `PromiseDebugging:AddUncaughtRejection`.
- }
- mIsLastInChain = false;
-
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
- // Now that there is a callback, we don't need to report anymore.
- mHadRejectCallback = true;
- RemoveWorkerHolder();
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-
- mResolveCallbacks.AppendElement(aResolveCallback);
- mRejectCallbacks.AppendElement(aRejectCallback);
-
- // If promise's state is fulfilled, queue a task to process our fulfill
- // callbacks with promise's result. If promise's state is rejected, queue a
- // task to process our reject callbacks with promise's result.
- if (mState != Pending) {
- TriggerPromiseReactions();
- }
-}
-#endif // SPIDERMONKEY_PROMISE
-
-#ifndef SPIDERMONKEY_PROMISE
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
-void
-Promise::MaybeReportRejected()
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- if (mState != Rejected || mHadRejectCallback || mResult.isUndefined()) {
- return;
- }
-
- AutoJSAPI jsapi;
- // We may not have a usable global by now (if it got unlinked
- // already), so don't init with it.
- jsapi.Init();
- JSContext* cx = jsapi.cx();
- JS::Rooted<JSObject*> obj(cx, GetWrapper());
- MOZ_ASSERT(obj); // We preserve our wrapper, so should always have one here.
- JS::Rooted<JS::Value> val(cx, mResult);
-
- JSAutoCompartment ac(cx, obj);
- if (!JS_WrapValue(cx, &val)) {
- JS_ClearPendingException(cx);
- return;
- }
-
- js::ErrorReport report(cx);
- RefPtr<Exception> exp;
- bool isObject = val.isObject();
- if (!isObject || NS_FAILED(UNWRAP_OBJECT(Exception, &val.toObject(), exp))) {
- if (!isObject ||
- NS_FAILED(UNWRAP_OBJECT(DOMException, &val.toObject(), exp))) {
- if (!report.init(cx, val, js::ErrorReport::NoSideEffects)) {
- NS_WARNING("Couldn't convert the unhandled rejected value to an exception.");
- JS_ClearPendingException(cx);
- return;
- }
- }
- }
-
- RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
- bool isMainThread = MOZ_LIKELY(NS_IsMainThread());
- bool isChrome = isMainThread ? nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj))
- : GetCurrentThreadWorkerPrivate()->IsChromeWorker();
- nsGlobalWindow* win = isMainThread ? xpc::WindowGlobalOrNull(obj) : nullptr;
- uint64_t windowID = win ? win->AsInner()->WindowID() : 0;
- if (exp) {
- xpcReport->Init(cx, exp, isChrome, windowID);
- } else {
- xpcReport->Init(report.report(), report.toStringResult(),
- isChrome, windowID);
- }
-
- // Now post an event to do the real reporting async
- // Since Promises preserve their wrapper, it is essential to RefPtr<> the
- // AsyncErrorReporter, otherwise if the call to DispatchToMainThread fails, it
- // will leak. See Bug 958684. So... don't use DispatchToMainThread()
- nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
- if (NS_WARN_IF(!mainThread)) {
- // Would prefer NS_ASSERTION, but that causes failure in xpcshell tests
- NS_WARNING("!!! Trying to report rejected Promise after MainThread shutdown");
- }
- if (mainThread) {
- RefPtr<AsyncErrorReporter> r = new AsyncErrorReporter(xpcReport);
- mainThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
- }
-}
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-
-void
-Promise::MaybeResolveInternal(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- if (mResolvePending) {
- return;
- }
-
- ResolveInternal(aCx, aValue);
-}
-
-void
-Promise::MaybeRejectInternal(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- if (mResolvePending) {
- return;
- }
-
- RejectInternal(aCx, aValue);
-}
-
-void
-Promise::HandleException(JSContext* aCx)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- JS::Rooted<JS::Value> exn(aCx);
- if (JS_GetPendingException(aCx, &exn)) {
- JS_ClearPendingException(aCx);
- RejectInternal(aCx, exn);
- }
-}
-
-void
-Promise::ResolveInternal(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
-
- mResolvePending = true;
-
- if (aValue.isObject()) {
- JS::Rooted<JSObject*> valueObj(aCx, &aValue.toObject());
-
- // Thenables.
- JS::Rooted<JS::Value> then(aCx);
- if (!JS_GetProperty(aCx, valueObj, "then", &then)) {
- HandleException(aCx);
- return;
- }
-
- if (then.isObject() && JS::IsCallable(&then.toObject())) {
- // This is the then() function of the thenable aValueObj.
- JS::Rooted<JSObject*> thenObj(aCx, &then.toObject());
-
- // We used to have a fast path here for the case when the following
- // requirements held:
- //
- // 1) valueObj is a Promise.
- // 2) thenObj is a JSFunction backed by our actual Promise::Then
- // implementation.
- //
- // But now that we're doing subclassing in Promise.prototype.then we would
- // also need the following requirements:
- //
- // 3) Getting valueObj.constructor has no side-effects.
- // 4) Getting valueObj.constructor[@@species] has no side-effects.
- // 5) valueObj.constructor[@@species] is a function and calling it has no
- // side-effects (e.g. it's the canonical Promise constructor) and it
- // provides some callback functions to call as arguments to its
- // argument.
- //
- // Ensuring that stuff while not inside SpiderMonkey is painful, so let's
- // drop the fast path for now.
-
- RefPtr<PromiseInit> thenCallback =
- new PromiseInit(nullptr, thenObj, mozilla::dom::GetIncumbentGlobal());
- RefPtr<PromiseResolveThenableJob> task =
- new PromiseResolveThenableJob(this, valueObj, thenCallback);
- context->DispatchToMicroTask(task.forget());
- return;
- }
- }
-
- MaybeSettle(aValue, Resolved);
-}
-
-void
-Promise::RejectInternal(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- mResolvePending = true;
-
- MaybeSettle(aValue, Rejected);
-}
-
-void
-Promise::Settle(JS::Handle<JS::Value> aValue, PromiseState aState)
-{
- MOZ_ASSERT(mGlobal,
- "We really should have a global here. Except we sometimes don't "
- "in the wild for some odd reason");
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- if (!mGlobal || mGlobal->IsDying()) {
- return;
- }
-
- mSettlementTimestamp = TimeStamp::Now();
-
- AutoJSAPI jsapi;
- jsapi.Init();
- JSContext* cx = jsapi.cx();
- JS::RootedObject wrapper(cx, GetWrapper());
- MOZ_ASSERT(wrapper); // We preserved it
- JSAutoCompartment ac(cx, wrapper);
-
- JS::Rooted<JS::Value> value(cx, aValue);
-
- if (!JS_WrapValue(cx, &value)) {
- JS_ClearPendingException(cx);
- value = JS::UndefinedValue();
- }
- SetResult(value);
- SetState(aState);
-
- JS::dbg::onPromiseSettled(cx, wrapper);
-
- if (aState == PromiseState::Rejected &&
- mIsLastInChain) {
- // The Promise has just been rejected, and it is last in chain.
- // We need to inform PromiseDebugging.
- // If the Promise is eventually not the last in chain anymore,
- // we will need to inform PromiseDebugging again.
- PromiseDebugging::AddUncaughtRejection(*this);
- }
-
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
- // If the Promise was rejected, and there is no reject handler already setup,
- // watch for thread shutdown.
- if (aState == PromiseState::Rejected &&
- !mHadRejectCallback &&
- !NS_IsMainThread()) {
- WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
- MOZ_ASSERT(worker);
- worker->AssertIsOnWorkerThread();
-
- mWorkerHolder = new PromiseReportRejectWorkerHolder(this);
- if (NS_WARN_IF(!mWorkerHolder->HoldWorker(worker, Closing))) {
- mWorkerHolder = nullptr;
- // Worker is shutting down, report rejection immediately since it is
- // unlikely that reject callbacks will be added after this point.
- MaybeReportRejectedOnce();
- }
- }
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-
- TriggerPromiseReactions();
-}
-
-void
-Promise::MaybeSettle(JS::Handle<JS::Value> aValue,
- PromiseState aState)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- // Promise.all() or Promise.race() implementations will repeatedly call
- // Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState
- // from asserting.
- if (mState != Pending) {
- return;
- }
-
- Settle(aValue, aState);
-}
-
-void
-Promise::TriggerPromiseReactions()
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- CycleCollectedJSContext* runtime = CycleCollectedJSContext::Get();
-
- nsTArray<RefPtr<PromiseCallback>> callbacks;
- callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
- : mRejectCallbacks);
- mResolveCallbacks.Clear();
- mRejectCallbacks.Clear();
-
- for (uint32_t i = 0; i < callbacks.Length(); ++i) {
- RefPtr<PromiseReactionJob> task =
- new PromiseReactionJob(this, callbacks[i], mResult);
- runtime->DispatchToMicroTask(task.forget());
- }
-}
-
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
-void
-Promise::RemoveWorkerHolder()
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- // The DTOR of this WorkerHolder will release the worker for us.
- mWorkerHolder = nullptr;
-}
-
-bool
-PromiseReportRejectWorkerHolder::Notify(Status aStatus)
-{
- MOZ_ASSERT(aStatus > Running);
- mPromise->MaybeReportRejectedOnce();
- // After this point, `this` has been deleted by RemoveWorkerHolder!
- return true;
-}
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-
-bool
-Promise::CaptureStack(JSContext* aCx, JS::Heap<JSObject*>& aTarget)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- JS::Rooted<JSObject*> stack(aCx);
- if (!JS::CaptureCurrentStack(aCx, &stack)) {
- return false;
- }
- aTarget = stack;
- return true;
-}
-
-void
-Promise::GetDependentPromises(nsTArray<RefPtr<Promise>>& aPromises)
-{
- NS_ASSERT_OWNINGTHREAD(Promise);
-
- // We want to return promises that correspond to then() calls, Promise.all()
- // calls, and Promise.race() calls.
- //
- // For the then() case, we have both resolve and reject callbacks that know
- // what the next promise is.
- //
- // For the race() case, likewise.
- //
- // For the all() case, our reject callback knows what the next promise is, but
- // our resolve callback just knows it needs to notify some
- // PromiseNativeHandler, which itself only has an indirect relationship to the
- // next promise.
- //
- // So we walk over our _reject_ callbacks and ask each of them what promise
- // its dependent promise is.
- for (size_t i = 0; i < mRejectCallbacks.Length(); ++i) {
- Promise* p = mRejectCallbacks[i]->GetDependentPromise();
- if (p) {
- aPromises.AppendElement(p);
- }
- }
-}
-
-#endif // SPIDERMONKEY_PROMISE
-
// A WorkerRunnable to resolve/reject the Promise on the worker thread.
// Calling thread MUST hold PromiseWorkerProxy's mutex before creating this.
class PromiseWorkerProxyRunnable : public WorkerRunnable
@@ -3223,23 +931,6 @@ void Promise::MaybeRejectBrokenly(const nsAString& aArg) {
MaybeSomething(aArg, &Promise::MaybeReject);
}
-#ifndef SPIDERMONKEY_PROMISE
-uint64_t
-Promise::GetID() {
- if (mID != 0) {
- return mID;
- }
- return mID = ++gIDGenerator;
-}
-#endif // SPIDERMONKEY_PROMISE
-
-#ifndef SPIDERMONKEY_PROMISE
-Promise::PromiseState
-Promise::State() const
-{
- return mState;
-}
-#else // SPIDERMONKEY_PROMISE
Promise::PromiseState
Promise::State() const
{
@@ -3256,7 +947,6 @@ Promise::State() const
return PromiseState::Pending;
}
-#endif // SPIDERMONKEY_PROMISE
} // namespace dom
} // namespace mozilla
diff --git a/dom/promise/Promise.h b/dom/promise/Promise.h
index 642603a11..2fe365c46 100644
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -21,17 +21,6 @@
#include "js/TypeDecls.h"
#include "jspubtd.h"
-// Bug 1083361 introduces a new mechanism for tracking uncaught
-// rejections. This #define serves to track down the parts of code
-// that need to be removed once clients have been put together
-// to take advantage of the new mechanism. New code should not
-// depend on code #ifdefed to this #define.
-#define DOM_PROMISE_DEPRECATED_REPORTING !SPIDERMONKEY_PROMISE
-
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
-#include "mozilla/dom/workers/bindings/WorkerHolder.h"
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-
class nsIGlobalObject;
namespace mozilla {
@@ -40,88 +29,36 @@ namespace dom {
class AnyCallback;
class DOMError;
class MediaStreamError;
-class PromiseCallback;
class PromiseInit;
class PromiseNativeHandler;
class PromiseDebugging;
-class Promise;
-
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
-class PromiseReportRejectWorkerHolder : public workers::WorkerHolder
-{
- // PromiseReportRejectWorkerHolder is held by an nsAutoPtr on the Promise
- // which means that this object will be destroyed before the Promise is
- // destroyed.
- Promise* MOZ_NON_OWNING_REF mPromise;
-
-public:
- explicit PromiseReportRejectWorkerHolder(Promise* aPromise)
- : mPromise(aPromise)
- {
- MOZ_ASSERT(mPromise);
- }
-
- virtual bool
- Notify(workers::Status aStatus) override;
-};
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-
#define NS_PROMISE_IID \
{ 0x1b8d6215, 0x3e67, 0x43ba, \
{ 0x8a, 0xf9, 0x31, 0x5e, 0x8f, 0xce, 0x75, 0x65 } }
class Promise : public nsISupports,
-#ifndef SPIDERMONKEY_PROMISE
- // Only wrappercached when we're not using SpiderMonkey
- // promises, because those don't have a useful object moved
- // hook, which wrappercache needs.
- public nsWrapperCache,
-#endif // SPIDERMONKEY_PROMISE
public SupportsWeakPtr<Promise>
{
- friend class NativePromiseCallback;
- friend class PromiseReactionJob;
- friend class PromiseResolverTask;
friend class PromiseTask;
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
- friend class PromiseReportRejectWorkerHolder;
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
friend class PromiseWorkerProxy;
friend class PromiseWorkerProxyRunnable;
- friend class RejectPromiseCallback;
- friend class ResolvePromiseCallback;
- friend class PromiseResolveThenableJob;
- friend class FastPromiseResolveThenableJob;
- friend class WrapperPromiseCallback;
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROMISE_IID)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-#ifdef SPIDERMONKEY_PROMISE
- // We're not skippable, since we're not owned from JS to start with.
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise)
-#else // SPIDERMONKEY_PROMISE
- NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(Promise)
-#endif // SPIDERMONKEY_PROMISE
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(Promise)
// Promise creation tries to create a JS reflector for the Promise, so is
// fallible. Furthermore, we don't want to do JS-wrapping on a 0-refcount
// object, so we addref before doing that and return the addrefed pointer
// here.
-#ifdef SPIDERMONKEY_PROMISE
static already_AddRefed<Promise>
Create(nsIGlobalObject* aGlobal, ErrorResult& aRv);
// Reports a rejected Promise by sending an error report.
static void ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise);
-#else
- static already_AddRefed<Promise>
- Create(nsIGlobalObject* aGlobal, ErrorResult& aRv,
- // Passing null for aDesiredProto will use Promise.prototype.
- JS::Handle<JSObject*> aDesiredProto = nullptr);
-#endif // SPIDERMONKEY_PROMISE
typedef void (Promise::*MaybeFunc)(JSContext* aCx,
JS::Handle<JS::Value> aValue);
@@ -183,11 +120,6 @@ public:
return mGlobal;
}
-#ifdef SPIDERMONKEY_PROMISE
- bool
- WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
- JS::MutableHandle<JSObject*> aWrapper);
-
// Do the equivalent of Promise.resolve in the compartment of aGlobal. The
// compartment of aCx is ignored. Errors are reported on the ErrorResult; if
// aRv comes back !Failed(), this function MUST return a non-null value.
@@ -223,95 +155,17 @@ public:
return mPromiseObj;
}
-#else // SPIDERMONKEY_PROMISE
- JSObject* PromiseObj()
- {
- return GetWrapper();
- }
-
- virtual JSObject*
- WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
- static already_AddRefed<Promise>
- Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
- ErrorResult& aRv, JS::Handle<JSObject*> aDesiredProto);
-
- static void
- Resolve(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
- JS::Handle<JS::Value> aValue,
- JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv);
-
- static already_AddRefed<Promise>
- Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
- JS::Handle<JS::Value> aValue, ErrorResult& aRv);
-
- static void
- Reject(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
- JS::Handle<JS::Value> aValue,
- JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv);
-
- static already_AddRefed<Promise>
- Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
- JS::Handle<JS::Value> aValue, ErrorResult& aRv);
-
- void
- Then(JSContext* aCx,
- // aCalleeGlobal may not be in the compartment of aCx, when called over
- // Xrays.
- JS::Handle<JSObject*> aCalleeGlobal,
- AnyCallback* aResolveCallback, AnyCallback* aRejectCallback,
- JS::MutableHandle<JS::Value> aRetval,
- ErrorResult& aRv);
-
- void
- Catch(JSContext* aCx,
- AnyCallback* aRejectCallback,
- JS::MutableHandle<JS::Value> aRetval,
- ErrorResult& aRv);
-
- static void
- All(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
- JS::Handle<JS::Value> aIterable, JS::MutableHandle<JS::Value> aRetval,
- ErrorResult& aRv);
-
- static already_AddRefed<Promise>
- All(const GlobalObject& aGlobal,
- const nsTArray<RefPtr<Promise>>& aPromiseList, ErrorResult& aRv);
-
- static void
- Race(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
- JS::Handle<JS::Value> aIterable, JS::MutableHandle<JS::Value> aRetval,
- ErrorResult& aRv);
-
- static bool
- PromiseSpecies(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
-#endif // SPIDERMONKEY_PROMISE
-
void AppendNativeHandler(PromiseNativeHandler* aRunnable);
JSObject* GlobalJSObject() const;
JSCompartment* Compartment() const;
-#ifndef SPIDERMONKEY_PROMISE
- // Return a unique-to-the-process identifier for this Promise.
- uint64_t GetID();
-#endif // SPIDERMONKEY_PROMISE
-
-#ifndef SPIDERMONKEY_PROMISE
- enum JSCallbackSlots {
- SLOT_PROMISE = 0,
- SLOT_DATA
- };
-#endif // SPIDERMONKEY_PROMISE
-
-#ifdef SPIDERMONKEY_PROMISE
// Create a dom::Promise from a given SpiderMonkey Promise object.
// aPromiseObj MUST be in the compartment of aGlobal's global JS object.
static already_AddRefed<Promise>
CreateFromExisting(nsIGlobalObject* aGlobal,
JS::Handle<JSObject*> aPromiseObj);
-#endif // SPIDERMONKEY_PROMISE
enum class PromiseState {
Pending,
@@ -335,99 +189,7 @@ protected:
// use the default prototype for the sort of Promise we have.
void CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv);
-#ifndef SPIDERMONKEY_PROMISE
- // Create the JS resolving functions of resolve() and reject(). And provide
- // references to the two functions by calling PromiseInit passed from Promise
- // constructor.
- void CallInitFunction(const GlobalObject& aGlobal, PromiseInit& aInit,
- ErrorResult& aRv);
-
- // The NewPromiseCapability function from
- // <http://www.ecma-international.org/ecma-262/6.0/#sec-newpromisecapability>.
- // Errors are communicated via aRv. If aForceCallbackCreation is
- // true, then this function will ensure that aCapability has a
- // useful mResolve/mReject even if mNativePromise is non-null.
- static void NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal,
- JS::Handle<JS::Value> aConstructor,
- bool aForceCallbackCreation,
- PromiseCapability& aCapability,
- ErrorResult& aRv);
-
- bool IsPending()
- {
- return mResolvePending;
- }
-
- void GetDependentPromises(nsTArray<RefPtr<Promise>>& aPromises);
-
- bool IsLastInChain() const
- {
- return mIsLastInChain;
- }
-
- void SetNotifiedAsUncaught()
- {
- mWasNotifiedAsUncaught = true;
- }
-
- bool WasNotifiedAsUncaught() const
- {
- return mWasNotifiedAsUncaught;
- }
-#endif // SPIDERMONKEY_PROMISE
-
private:
-#ifndef SPIDERMONKEY_PROMISE
- friend class PromiseDebugging;
-
- void SetState(PromiseState aState)
- {
- MOZ_ASSERT(mState == Pending);
- MOZ_ASSERT(aState != Pending);
- mState = aState;
- }
-
- void SetResult(JS::Handle<JS::Value> aValue)
- {
- mResult = aValue;
- }
-
- // This method enqueues promise's resolve/reject callbacks with promise's
- // result. It's executed when the resolver.resolve() or resolver.reject() is
- // called or when the promise already has a result and new callbacks are
- // appended by then() or catch().
- void TriggerPromiseReactions();
-
- void Settle(JS::Handle<JS::Value> aValue, Promise::PromiseState aState);
- void MaybeSettle(JS::Handle<JS::Value> aValue, Promise::PromiseState aState);
-
- void AppendCallbacks(PromiseCallback* aResolveCallback,
- PromiseCallback* aRejectCallback);
-
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
- // If we have been rejected and our mResult is a JS exception,
- // report it to the error console.
- // Use MaybeReportRejectedOnce() for actual calls.
- void MaybeReportRejected();
-
- void MaybeReportRejectedOnce() {
- MaybeReportRejected();
- RemoveWorkerHolder();
- mResult.setUndefined();
- }
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-
- void MaybeResolveInternal(JSContext* aCx,
- JS::Handle<JS::Value> aValue);
- void MaybeRejectInternal(JSContext* aCx,
- JS::Handle<JS::Value> aValue);
-
- void ResolveInternal(JSContext* aCx,
- JS::Handle<JS::Value> aValue);
- void RejectInternal(JSContext* aCx,
- JS::Handle<JS::Value> aValue);
-#endif // SPIDERMONKEY_PROMISE
-
template <typename T>
void MaybeSomething(T& aArgument, MaybeFunc aFunc) {
MOZ_ASSERT(PromiseObj()); // It was preserved!
@@ -444,92 +206,11 @@ private:
(this->*aFunc)(cx, val);
}
-#ifndef SPIDERMONKEY_PROMISE
- // Static methods for the PromiseInit functions.
- static bool
- JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp);
-
- static bool
- ThenableResolverCommon(JSContext* aCx, uint32_t /* PromiseCallback::Task */ aTask,
- unsigned aArgc, JS::Value* aVp);
- static bool
- JSCallbackThenableResolver(JSContext *aCx, unsigned aArgc, JS::Value *aVp);
- static bool
- JSCallbackThenableRejecter(JSContext *aCx, unsigned aArgc, JS::Value *aVp);
-
- static JSObject*
- CreateFunction(JSContext* aCx, Promise* aPromise, int32_t aTask);
-
- static JSObject*
- CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask);
-
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
- void RemoveWorkerHolder();
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-
- // Capture the current stack and store it in aTarget. If false is
- // returned, an exception is presumably pending on aCx.
- bool CaptureStack(JSContext* aCx, JS::Heap<JSObject*>& aTarget);
-#endif // SPIDERMONKEY_PROMISE
-
void HandleException(JSContext* aCx);
RefPtr<nsIGlobalObject> mGlobal;
-#ifndef SPIDERMONKEY_PROMISE
- nsTArray<RefPtr<PromiseCallback> > mResolveCallbacks;
- nsTArray<RefPtr<PromiseCallback> > mRejectCallbacks;
-
- JS::Heap<JS::Value> mResult;
- // A stack that shows where this promise was allocated, if there was
- // JS running at the time. Otherwise null.
- JS::Heap<JSObject*> mAllocationStack;
- // mRejectionStack is only set when the promise is rejected directly from
- // script, by calling Promise.reject() or the rejection callback we pass to
- // the PromiseInit function. Promises that are rejected internally do not
- // have a rejection stack.
- JS::Heap<JSObject*> mRejectionStack;
- // mFullfillmentStack is only set when the promise is fulfilled directly from
- // script, by calling Promise.resolve() or the fulfillment callback we pass to
- // the PromiseInit function. Promises that are fulfilled internally do not
- // have a fulfillment stack.
- JS::Heap<JSObject*> mFullfillmentStack;
- PromiseState mState;
-
-#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
- bool mHadRejectCallback;
-
- // If a rejected promise on a worker has no reject callbacks attached, it
- // needs to know when the worker is shutting down, to report the error on the
- // console before the worker's context is deleted. This feature is used for
- // that purpose.
- nsAutoPtr<PromiseReportRejectWorkerHolder> mWorkerHolder;
-#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
-
- bool mTaskPending;
- bool mResolvePending;
-
- // `true` if this Promise is the last in the chain, or `false` if
- // another Promise has been created from this one by a call to
- // `then`, `all`, `race`, etc.
- bool mIsLastInChain;
-
- // `true` if PromiseDebugging has already notified at least one observer that
- // this promise was left uncaught, `false` otherwise.
- bool mWasNotifiedAsUncaught;
-
- // The time when this promise was created.
- TimeStamp mCreationTimestamp;
-
- // The time when this promise transitioned out of the pending state.
- TimeStamp mSettlementTimestamp;
-
- // Once `GetID()` has been called, a unique-to-the-process identifier for this
- // promise. Until then, `0`.
- uint64_t mID;
-#else // SPIDERMONKEY_PROMISE
JS::Heap<JSObject*> mPromiseObj;
-#endif // SPIDERMONKEY_PROMISE
};
NS_DEFINE_STATIC_IID_ACCESSOR(Promise, NS_PROMISE_IID)
diff --git a/dom/promise/PromiseCallback.cpp b/dom/promise/PromiseCallback.cpp
deleted file mode 100644
index 6ecf983b7..000000000
--- a/dom/promise/PromiseCallback.cpp
+++ /dev/null
@@ -1,571 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "PromiseCallback.h"
-#include "mozilla/dom/Promise.h"
-#include "mozilla/dom/PromiseNativeHandler.h"
-
-#include "jsapi.h"
-#include "jsfriendapi.h"
-#include "jswrapper.h"
-
-namespace mozilla {
-namespace dom {
-
-#ifndef SPIDERMONKEY_PROMISE
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseCallback)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseCallback)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseCallback)
- NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTION_0(PromiseCallback)
-
-PromiseCallback::PromiseCallback()
-{
-}
-
-PromiseCallback::~PromiseCallback()
-{
-}
-
-// ResolvePromiseCallback
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(ResolvePromiseCallback)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ResolvePromiseCallback,
- PromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
- tmp->mGlobal = nullptr;
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ResolvePromiseCallback,
- PromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ResolvePromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ResolvePromiseCallback)
-NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
-
-NS_IMPL_ADDREF_INHERITED(ResolvePromiseCallback, PromiseCallback)
-NS_IMPL_RELEASE_INHERITED(ResolvePromiseCallback, PromiseCallback)
-
-ResolvePromiseCallback::ResolvePromiseCallback(Promise* aPromise,
- JS::Handle<JSObject*> aGlobal)
- : mPromise(aPromise)
- , mGlobal(aGlobal)
-{
- MOZ_ASSERT(aPromise);
- MOZ_ASSERT(aGlobal);
- HoldJSObjects(this);
-}
-
-ResolvePromiseCallback::~ResolvePromiseCallback()
-{
- DropJSObjects(this);
-}
-
-nsresult
-ResolvePromiseCallback::Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- // Run resolver's algorithm with value and the synchronous flag set.
-
- JS::ExposeValueToActiveJS(aValue);
-
- JSAutoCompartment ac(aCx, mGlobal);
- JS::Rooted<JS::Value> value(aCx, aValue);
- if (!JS_WrapValue(aCx, &value)) {
- NS_WARNING("Failed to wrap value into the right compartment.");
- return NS_ERROR_FAILURE;
- }
-
- mPromise->ResolveInternal(aCx, value);
- return NS_OK;
-}
-
-// RejectPromiseCallback
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(RejectPromiseCallback)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(RejectPromiseCallback,
- PromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
- tmp->mGlobal = nullptr;
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(RejectPromiseCallback,
- PromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(RejectPromiseCallback)
-NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(RejectPromiseCallback,
- PromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMPL_ADDREF_INHERITED(RejectPromiseCallback, PromiseCallback)
-NS_IMPL_RELEASE_INHERITED(RejectPromiseCallback, PromiseCallback)
-
-RejectPromiseCallback::RejectPromiseCallback(Promise* aPromise,
- JS::Handle<JSObject*> aGlobal)
- : mPromise(aPromise)
- , mGlobal(aGlobal)
-{
- MOZ_ASSERT(aPromise);
- MOZ_ASSERT(mGlobal);
- HoldJSObjects(this);
-}
-
-RejectPromiseCallback::~RejectPromiseCallback()
-{
- DropJSObjects(this);
-}
-
-nsresult
-RejectPromiseCallback::Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- // Run resolver's algorithm with value and the synchronous flag set.
-
- JS::ExposeValueToActiveJS(aValue);
-
- JSAutoCompartment ac(aCx, mGlobal);
- JS::Rooted<JS::Value> value(aCx, aValue);
- if (!JS_WrapValue(aCx, &value)) {
- NS_WARNING("Failed to wrap value into the right compartment.");
- return NS_ERROR_FAILURE;
- }
-
-
- mPromise->RejectInternal(aCx, value);
- return NS_OK;
-}
-
-// InvokePromiseFuncCallback
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(InvokePromiseFuncCallback)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(InvokePromiseFuncCallback,
- PromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromiseFunc)
- tmp->mGlobal = nullptr;
- tmp->mNextPromiseObj = nullptr;
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(InvokePromiseFuncCallback,
- PromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromiseFunc)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(InvokePromiseFuncCallback)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNextPromiseObj)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(InvokePromiseFuncCallback)
-NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
-
-NS_IMPL_ADDREF_INHERITED(InvokePromiseFuncCallback, PromiseCallback)
-NS_IMPL_RELEASE_INHERITED(InvokePromiseFuncCallback, PromiseCallback)
-
-InvokePromiseFuncCallback::InvokePromiseFuncCallback(JS::Handle<JSObject*> aGlobal,
- JS::Handle<JSObject*> aNextPromiseObj,
- AnyCallback* aPromiseFunc)
- : mGlobal(aGlobal)
- , mNextPromiseObj(aNextPromiseObj)
- , mPromiseFunc(aPromiseFunc)
-{
- MOZ_ASSERT(aGlobal);
- MOZ_ASSERT(aNextPromiseObj);
- MOZ_ASSERT(aPromiseFunc);
- HoldJSObjects(this);
-}
-
-InvokePromiseFuncCallback::~InvokePromiseFuncCallback()
-{
- DropJSObjects(this);
-}
-
-nsresult
-InvokePromiseFuncCallback::Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- // Run resolver's algorithm with value and the synchronous flag set.
-
- JS::ExposeValueToActiveJS(aValue);
-
- JSAutoCompartment ac(aCx, mGlobal);
- JS::Rooted<JS::Value> value(aCx, aValue);
- if (!JS_WrapValue(aCx, &value)) {
- NS_WARNING("Failed to wrap value into the right compartment.");
- return NS_ERROR_FAILURE;
- }
-
- ErrorResult rv;
- JS::Rooted<JS::Value> ignored(aCx);
- mPromiseFunc->Call(value, &ignored, rv);
- // Useful exceptions already got reported.
- rv.SuppressException();
- return NS_OK;
-}
-
-Promise*
-InvokePromiseFuncCallback::GetDependentPromise()
-{
- Promise* promise;
- if (NS_SUCCEEDED(UNWRAP_OBJECT(Promise, mNextPromiseObj, promise))) {
- return promise;
- }
-
- // Oh, well.
- return nullptr;
-}
-
-// WrapperPromiseCallback
-NS_IMPL_CYCLE_COLLECTION_CLASS(WrapperPromiseCallback)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WrapperPromiseCallback,
- PromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextPromise)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveFunc)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectFunc)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
- tmp->mGlobal = nullptr;
- tmp->mNextPromiseObj = nullptr;
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WrapperPromiseCallback,
- PromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextPromise)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveFunc)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectFunc)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WrapperPromiseCallback)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNextPromiseObj)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WrapperPromiseCallback)
-NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
-
-NS_IMPL_ADDREF_INHERITED(WrapperPromiseCallback, PromiseCallback)
-NS_IMPL_RELEASE_INHERITED(WrapperPromiseCallback, PromiseCallback)
-
-WrapperPromiseCallback::WrapperPromiseCallback(Promise* aNextPromise,
- JS::Handle<JSObject*> aGlobal,
- AnyCallback* aCallback)
- : mNextPromise(aNextPromise)
- , mGlobal(aGlobal)
- , mCallback(aCallback)
-{
- MOZ_ASSERT(aNextPromise);
- MOZ_ASSERT(aGlobal);
- HoldJSObjects(this);
-}
-
-WrapperPromiseCallback::WrapperPromiseCallback(JS::Handle<JSObject*> aGlobal,
- AnyCallback* aCallback,
- JS::Handle<JSObject*> aNextPromiseObj,
- AnyCallback* aResolveFunc,
- AnyCallback* aRejectFunc)
- : mNextPromiseObj(aNextPromiseObj)
- , mResolveFunc(aResolveFunc)
- , mRejectFunc(aRejectFunc)
- , mGlobal(aGlobal)
- , mCallback(aCallback)
-{
- MOZ_ASSERT(mNextPromiseObj);
- MOZ_ASSERT(aResolveFunc);
- MOZ_ASSERT(aRejectFunc);
- MOZ_ASSERT(aGlobal);
- HoldJSObjects(this);
-}
-
-WrapperPromiseCallback::~WrapperPromiseCallback()
-{
- DropJSObjects(this);
-}
-
-nsresult
-WrapperPromiseCallback::Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- JS::ExposeValueToActiveJS(aValue);
-
- JSAutoCompartment ac(aCx, mGlobal);
- JS::Rooted<JS::Value> value(aCx, aValue);
- if (!JS_WrapValue(aCx, &value)) {
- NS_WARNING("Failed to wrap value into the right compartment.");
- return NS_ERROR_FAILURE;
- }
-
- ErrorResult rv;
-
- // PromiseReactionTask step 6
- JS::Rooted<JS::Value> retValue(aCx);
- JSCompartment* compartment;
- if (mNextPromise) {
- compartment = mNextPromise->Compartment();
- } else {
- MOZ_ASSERT(mNextPromiseObj);
- compartment = js::GetObjectCompartment(mNextPromiseObj);
- }
- mCallback->Call(value, &retValue, rv, "promise callback",
- CallbackObject::eRethrowExceptions,
- compartment);
-
- rv.WouldReportJSException();
-
- // PromiseReactionTask step 7
- if (rv.Failed()) {
- if (rv.IsUncatchableException()) {
- // We have nothing to resolve/reject the promise with.
- return rv.StealNSResult();
- }
-
- JS::Rooted<JS::Value> value(aCx);
- { // Scope for JSAutoCompartment
- // Convert the ErrorResult to a JS exception object that we can reject
- // ourselves with. This will be exactly the exception that would get
- // thrown from a binding method whose ErrorResult ended up with whatever
- // is on "rv" right now. Do this in the promise reflector compartment.
- Maybe<JSAutoCompartment> ac;
- if (mNextPromise) {
- ac.emplace(aCx, mNextPromise->GlobalJSObject());
- } else {
- ac.emplace(aCx, mNextPromiseObj);
- }
- DebugOnly<bool> conversionResult = ToJSValue(aCx, rv, &value);
- MOZ_ASSERT(conversionResult);
- }
-
- if (mNextPromise) {
- mNextPromise->RejectInternal(aCx, value);
- } else {
- JS::Rooted<JS::Value> ignored(aCx);
- ErrorResult rejectRv;
- mRejectFunc->Call(value, &ignored, rejectRv);
- // This reported any JS exceptions; we just have a pointless exception on
- // there now.
- rejectRv.SuppressException();
- }
- return NS_OK;
- }
-
- // If the return value is the same as the promise itself, throw TypeError.
- if (retValue.isObject()) {
- JS::Rooted<JSObject*> valueObj(aCx, &retValue.toObject());
- valueObj = js::CheckedUnwrap(valueObj);
- JS::Rooted<JSObject*> nextPromiseObj(aCx);
- if (mNextPromise) {
- nextPromiseObj = mNextPromise->GetWrapper();
- } else {
- MOZ_ASSERT(mNextPromiseObj);
- nextPromiseObj = mNextPromiseObj;
- }
- // XXXbz shouldn't this check be over in ResolveInternal anyway?
- if (valueObj == nextPromiseObj) {
- const char* fileName = nullptr;
- uint32_t lineNumber = 0;
-
- // Try to get some information about the callback to report a sane error,
- // but don't try too hard (only deals with scripted functions).
- JS::Rooted<JSObject*> unwrapped(aCx,
- js::CheckedUnwrap(mCallback->Callback()));
-
- if (unwrapped) {
- JSAutoCompartment ac(aCx, unwrapped);
- if (JS_ObjectIsFunction(aCx, unwrapped)) {
- JS::Rooted<JS::Value> asValue(aCx, JS::ObjectValue(*unwrapped));
- JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, asValue));
-
- MOZ_ASSERT(func);
- JSScript* script = JS_GetFunctionScript(aCx, func);
- if (script) {
- fileName = JS_GetScriptFilename(script);
- lineNumber = JS_GetScriptBaseLineNumber(aCx, script);
- }
- }
- }
-
- // We're back in aValue's compartment here.
- JS::Rooted<JSString*> fn(aCx, JS_NewStringCopyZ(aCx, fileName));
- if (!fn) {
- // Out of memory. Promise will stay unresolved.
- JS_ClearPendingException(aCx);
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- JS::Rooted<JSString*> message(aCx,
- JS_NewStringCopyZ(aCx,
- "then() cannot return same Promise that it resolves."));
- if (!message) {
- // Out of memory. Promise will stay unresolved.
- JS_ClearPendingException(aCx);
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- JS::Rooted<JS::Value> typeError(aCx);
- if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, fn, lineNumber, 0,
- nullptr, message, &typeError)) {
- // Out of memory. Promise will stay unresolved.
- JS_ClearPendingException(aCx);
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- if (mNextPromise) {
- mNextPromise->RejectInternal(aCx, typeError);
- } else {
- JS::Rooted<JS::Value> ignored(aCx);
- ErrorResult rejectRv;
- mRejectFunc->Call(typeError, &ignored, rejectRv);
- // This reported any JS exceptions; we just have a pointless exception
- // on there now.
- rejectRv.SuppressException();
- }
- return NS_OK;
- }
- }
-
- // Otherwise, run resolver's resolve with value.
- if (!JS_WrapValue(aCx, &retValue)) {
- NS_WARNING("Failed to wrap value into the right compartment.");
- return NS_ERROR_FAILURE;
- }
-
- if (mNextPromise) {
- mNextPromise->ResolveInternal(aCx, retValue);
- } else {
- JS::Rooted<JS::Value> ignored(aCx);
- ErrorResult resolveRv;
- mResolveFunc->Call(retValue, &ignored, resolveRv);
- // This reported any JS exceptions; we just have a pointless exception
- // on there now.
- resolveRv.SuppressException();
- }
-
- return NS_OK;
-}
-
-Promise*
-WrapperPromiseCallback::GetDependentPromise()
-{
- // Per spec, various algorithms like all() and race() are actually implemented
- // in terms of calling then() but passing it the resolve/reject functions that
- // are passed as arguments to function passed to the Promise constructor.
- // That will cause the promise in question to hold on to a
- // WrapperPromiseCallback, but the dependent promise should really be the one
- // whose constructor those functions came from, not the about-to-be-ignored
- // return value of "then". So try to determine whether we're in that case and
- // if so go ahead and dig the dependent promise out of the function we have.
- JSObject* callable = mCallback->Callable();
- // Unwrap it, in case it's a cross-compartment wrapper. Our caller here is
- // system, so it's really ok to just go and unwrap.
- callable = js::UncheckedUnwrap(callable);
- if (JS_IsNativeFunction(callable, Promise::JSCallback)) {
- JS::Value promiseVal =
- js::GetFunctionNativeReserved(callable, Promise::SLOT_PROMISE);
- Promise* promise;
- UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise);
- return promise;
- }
-
- if (mNextPromise) {
- return mNextPromise;
- }
-
- Promise* promise;
- if (NS_SUCCEEDED(UNWRAP_OBJECT(Promise, mNextPromiseObj, promise))) {
- return promise;
- }
-
- // Oh, well.
- return nullptr;
-}
-
-// NativePromiseCallback
-
-NS_IMPL_CYCLE_COLLECTION_INHERITED(NativePromiseCallback,
- PromiseCallback, mHandler)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(NativePromiseCallback)
-NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
-
-NS_IMPL_ADDREF_INHERITED(NativePromiseCallback, PromiseCallback)
-NS_IMPL_RELEASE_INHERITED(NativePromiseCallback, PromiseCallback)
-
-NativePromiseCallback::NativePromiseCallback(PromiseNativeHandler* aHandler,
- Promise::PromiseState aState)
- : mHandler(aHandler)
- , mState(aState)
-{
- MOZ_ASSERT(aHandler);
-}
-
-NativePromiseCallback::~NativePromiseCallback()
-{
-}
-
-nsresult
-NativePromiseCallback::Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
-{
- JS::ExposeValueToActiveJS(aValue);
-
- if (mState == Promise::Resolved) {
- mHandler->ResolvedCallback(aCx, aValue);
- return NS_OK;
- }
-
- if (mState == Promise::Rejected) {
- mHandler->RejectedCallback(aCx, aValue);
- return NS_OK;
- }
-
- NS_NOTREACHED("huh?");
- return NS_ERROR_FAILURE;
-}
-
-/* static */ PromiseCallback*
-PromiseCallback::Factory(Promise* aNextPromise, JS::Handle<JSObject*> aGlobal,
- AnyCallback* aCallback, Task aTask)
-{
- MOZ_ASSERT(aNextPromise);
-
- // If we have a callback and a next resolver, we have to exec the callback and
- // then propagate the return value to the next resolver->resolve().
- if (aCallback) {
- return new WrapperPromiseCallback(aNextPromise, aGlobal, aCallback);
- }
-
- if (aTask == Resolve) {
- return new ResolvePromiseCallback(aNextPromise, aGlobal);
- }
-
- if (aTask == Reject) {
- return new RejectPromiseCallback(aNextPromise, aGlobal);
- }
-
- MOZ_ASSERT(false, "This should not happen");
- return nullptr;
-}
-
-#endif // SPIDERMONKEY_PROMISE
-
-} // namespace dom
-} // namespace mozilla
diff --git a/dom/promise/PromiseCallback.h b/dom/promise/PromiseCallback.h
deleted file mode 100644
index 9f55e03d0..000000000
--- a/dom/promise/PromiseCallback.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_PromiseCallback_h
-#define mozilla_dom_PromiseCallback_h
-
-#include "mozilla/dom/Promise.h"
-#include "nsCycleCollectionParticipant.h"
-
-namespace mozilla {
-namespace dom {
-
-#ifndef SPIDERMONKEY_PROMISE
-// This is the base class for any PromiseCallback.
-// It's a logical step in the promise chain of callbacks.
-class PromiseCallback : public nsISupports
-{
-protected:
- virtual ~PromiseCallback();
-
-public:
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_CLASS(PromiseCallback)
-
- PromiseCallback();
-
- virtual nsresult Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue) = 0;
-
- // Return the Promise that this callback will end up resolving or
- // rejecting, if any.
- virtual Promise* GetDependentPromise() = 0;
-
- enum Task {
- Resolve,
- Reject
- };
-
- // This factory returns a PromiseCallback object with refcount of 0.
- static PromiseCallback*
- Factory(Promise* aNextPromise, JS::Handle<JSObject*> aObject,
- AnyCallback* aCallback, Task aTask);
-};
-
-// WrapperPromiseCallback execs a JS Callback with a value, and then the return
-// value is sent to either:
-// a) If aNextPromise is non-null, the aNextPromise->ResolveFunction() or to
-// aNextPromise->RejectFunction() if the JS Callback throws.
-// or
-// b) If aNextPromise is null, in which case aResolveFunc and aRejectFunc must
-// be non-null, then to aResolveFunc, unless aCallback threw, in which case
-// aRejectFunc.
-class WrapperPromiseCallback final : public PromiseCallback
-{
-public:
- NS_DECL_ISUPPORTS_INHERITED
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WrapperPromiseCallback,
- PromiseCallback)
-
- nsresult Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue) override;
-
- Promise* GetDependentPromise() override;
-
- // Constructor for when we know we have a vanilla Promise.
- WrapperPromiseCallback(Promise* aNextPromise, JS::Handle<JSObject*> aGlobal,
- AnyCallback* aCallback);
-
- // Constructor for when all we have to work with are resolve/reject functions.
- WrapperPromiseCallback(JS::Handle<JSObject*> aGlobal,
- AnyCallback* aCallback,
- JS::Handle<JSObject*> mNextPromiseObj,
- AnyCallback* aResolveFunc,
- AnyCallback* aRejectFunc);
-
-private:
- ~WrapperPromiseCallback();
-
- // Either mNextPromise is non-null or all three of mNextPromiseObj,
- // mResolveFund and mRejectFunc must are non-null.
- RefPtr<Promise> mNextPromise;
- // mNextPromiseObj is the reflector itself; it may not be in the
- // same compartment as anything else we have.
- JS::Heap<JSObject*> mNextPromiseObj;
- RefPtr<AnyCallback> mResolveFunc;
- RefPtr<AnyCallback> mRejectFunc;
- JS::Heap<JSObject*> mGlobal;
- RefPtr<AnyCallback> mCallback;
-};
-
-// ResolvePromiseCallback calls aPromise->ResolveFunction() with the value
-// received by Call().
-class ResolvePromiseCallback final : public PromiseCallback
-{
-public:
- NS_DECL_ISUPPORTS_INHERITED
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ResolvePromiseCallback,
- PromiseCallback)
-
- nsresult Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue) override;
-
- Promise* GetDependentPromise() override
- {
- return mPromise;
- }
-
- ResolvePromiseCallback(Promise* aPromise, JS::Handle<JSObject*> aGlobal);
-
-private:
- ~ResolvePromiseCallback();
-
- RefPtr<Promise> mPromise;
- JS::Heap<JSObject*> mGlobal;
-};
-
-// RejectPromiseCallback calls aPromise->RejectFunction() with the value
-// received by Call().
-class RejectPromiseCallback final : public PromiseCallback
-{
-public:
- NS_DECL_ISUPPORTS_INHERITED
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(RejectPromiseCallback,
- PromiseCallback)
-
- nsresult Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue) override;
-
- Promise* GetDependentPromise() override
- {
- return mPromise;
- }
-
- RejectPromiseCallback(Promise* aPromise, JS::Handle<JSObject*> aGlobal);
-
-private:
- ~RejectPromiseCallback();
-
- RefPtr<Promise> mPromise;
- JS::Heap<JSObject*> mGlobal;
-};
-
-// InvokePromiseFuncCallback calls the given function with the value
-// received by Call().
-class InvokePromiseFuncCallback final : public PromiseCallback
-{
-public:
- NS_DECL_ISUPPORTS_INHERITED
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(InvokePromiseFuncCallback,
- PromiseCallback)
-
- nsresult Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue) override;
-
- Promise* GetDependentPromise() override;
-
- InvokePromiseFuncCallback(JS::Handle<JSObject*> aGlobal,
- JS::Handle<JSObject*> aNextPromiseObj,
- AnyCallback* aPromiseFunc);
-
-private:
- ~InvokePromiseFuncCallback();
-
- JS::Heap<JSObject*> mGlobal;
- JS::Heap<JSObject*> mNextPromiseObj;
- RefPtr<AnyCallback> mPromiseFunc;
-};
-
-// NativePromiseCallback wraps a PromiseNativeHandler.
-class NativePromiseCallback final : public PromiseCallback
-{
-public:
- NS_DECL_ISUPPORTS_INHERITED
- NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(NativePromiseCallback,
- PromiseCallback)
-
- nsresult Call(JSContext* aCx,
- JS::Handle<JS::Value> aValue) override;
-
- Promise* GetDependentPromise() override
- {
- return nullptr;
- }
-
- NativePromiseCallback(PromiseNativeHandler* aHandler,
- Promise::PromiseState aState);
-
-private:
- ~NativePromiseCallback();
-
- RefPtr<PromiseNativeHandler> mHandler;
- Promise::PromiseState mState;
-};
-
-#endif // SPIDERMONKEY_PROMISE
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_PromiseCallback_h
diff --git a/dom/promise/PromiseDebugging.cpp b/dom/promise/PromiseDebugging.cpp
index fc0942ee4..f3ec33e8b 100644
--- a/dom/promise/PromiseDebugging.cpp
+++ b/dom/promise/PromiseDebugging.cpp
@@ -66,20 +66,6 @@ private:
/* static */ MOZ_THREAD_LOCAL(bool)
FlushRejections::sDispatched;
-#ifndef SPIDERMONKEY_PROMISE
-static Promise*
-UnwrapPromise(JS::Handle<JSObject*> aPromise, ErrorResult& aRv)
-{
- Promise* promise;
- if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Promise, aPromise, promise)))) {
- aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING("Argument"));
- return nullptr;
- }
- return promise;
-}
-#endif // SPIDERMONKEY_PROMISE
-
-#ifdef SPIDERMONKEY_PROMISE
/* static */ void
PromiseDebugging::GetState(GlobalObject& aGlobal, JS::Handle<JSObject*> aPromise,
PromiseDebuggingStateHolder& aState,
@@ -173,34 +159,6 @@ PromiseDebugging::GetFullfillmentStack(GlobalObject& aGlobal,
aStack.set(JS::GetPromiseResolutionSite(obj));
}
-#else
-
-/* static */ void
-PromiseDebugging::GetState(GlobalObject&, JS::Handle<JSObject*> aPromise,
- PromiseDebuggingStateHolder& aState,
- ErrorResult& aRv)
-{
- Promise* promise = UnwrapPromise(aPromise, aRv);
- if (aRv.Failed()) {
- return;
- }
- switch (promise->mState) {
- case Promise::Pending:
- aState.mState = PromiseDebuggingState::Pending;
- break;
- case Promise::Resolved:
- aState.mState = PromiseDebuggingState::Fulfilled;
- aState.mValue = promise->mResult;
- break;
- case Promise::Rejected:
- aState.mState = PromiseDebuggingState::Rejected;
- aState.mReason = promise->mResult;
- break;
- }
-}
-
-#endif // SPIDERMONKEY_PROMISE
-
/*static */ nsString
PromiseDebugging::sIDPrefix;
@@ -232,86 +190,6 @@ PromiseDebugging::FlushUncaughtRejections()
FlushRejections::FlushSync();
}
-#ifndef SPIDERMONKEY_PROMISE
-
-/* static */ void
-PromiseDebugging::GetAllocationStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
- JS::MutableHandle<JSObject*> aStack,
- ErrorResult& aRv)
-{
- Promise* promise = UnwrapPromise(aPromise, aRv);
- if (aRv.Failed()) {
- return;
- }
- aStack.set(promise->mAllocationStack);
-}
-
-/* static */ void
-PromiseDebugging::GetRejectionStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
- JS::MutableHandle<JSObject*> aStack,
- ErrorResult& aRv)
-{
- Promise* promise = UnwrapPromise(aPromise, aRv);
- if (aRv.Failed()) {
- return;
- }
- aStack.set(promise->mRejectionStack);
-}
-
-/* static */ void
-PromiseDebugging::GetFullfillmentStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
- JS::MutableHandle<JSObject*> aStack,
- ErrorResult& aRv)
-{
- Promise* promise = UnwrapPromise(aPromise, aRv);
- if (aRv.Failed()) {
- return;
- }
- aStack.set(promise->mFullfillmentStack);
-}
-
-/* static */ void
-PromiseDebugging::GetDependentPromises(GlobalObject&, JS::Handle<JSObject*> aPromise,
- nsTArray<RefPtr<Promise>>& aPromises,
- ErrorResult& aRv)
-{
- Promise* promise = UnwrapPromise(aPromise, aRv);
- if (aRv.Failed()) {
- return;
- }
- promise->GetDependentPromises(aPromises);
-}
-
-/* static */ double
-PromiseDebugging::GetPromiseLifetime(GlobalObject&,
- JS::Handle<JSObject*> aPromise,
- ErrorResult& aRv)
-{
- Promise* promise = UnwrapPromise(aPromise, aRv);
- if (aRv.Failed()) {
- return 0;
- }
- return (TimeStamp::Now() - promise->mCreationTimestamp).ToMilliseconds();
-}
-
-/* static */ double
-PromiseDebugging::GetTimeToSettle(GlobalObject&, JS::Handle<JSObject*> aPromise,
- ErrorResult& aRv)
-{
- Promise* promise = UnwrapPromise(aPromise, aRv);
- if (aRv.Failed()) {
- return 0;
- }
- if (promise->mState == Promise::Pending) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return 0;
- }
- return (promise->mSettlementTimestamp -
- promise->mCreationTimestamp).ToMilliseconds();
-}
-
-#endif // SPIDERMONKEY_PROMISE
-
/* static */ void
PromiseDebugging::AddUncaughtRejectionObserver(GlobalObject&,
UncaughtRejectionObserver& aObserver)
@@ -337,8 +215,6 @@ PromiseDebugging::RemoveUncaughtRejectionObserver(GlobalObject&,
return false;
}
-#ifdef SPIDERMONKEY_PROMISE
-
/* static */ void
PromiseDebugging::AddUncaughtRejection(JS::HandleObject aPromise)
{
@@ -420,102 +296,5 @@ PromiseDebugging::FlushUncaughtRejectionsInternal()
storage->mConsumedRejections.clear();
}
-#else
-
-/* static */ void
-PromiseDebugging::AddUncaughtRejection(Promise& aPromise)
-{
- CycleCollectedJSContext::Get()->mUncaughtRejections.AppendElement(&aPromise);
- FlushRejections::DispatchNeeded();
-}
-
-/* void */ void
-PromiseDebugging::AddConsumedRejection(Promise& aPromise)
-{
- CycleCollectedJSContext::Get()->mConsumedRejections.AppendElement(&aPromise);
- FlushRejections::DispatchNeeded();
-}
-
-/* static */ void
-PromiseDebugging::GetPromiseID(GlobalObject&,
- JS::Handle<JSObject*> aPromise,
- nsString& aID,
- ErrorResult& aRv)
-{
- Promise* promise = UnwrapPromise(aPromise, aRv);
- if (aRv.Failed()) {
- return;
- }
- uint64_t promiseID = promise->GetID();
- aID = sIDPrefix;
- aID.AppendInt(promiseID);
-}
-
-/* static */ void
-PromiseDebugging::FlushUncaughtRejectionsInternal()
-{
- CycleCollectedJSContext* storage = CycleCollectedJSContext::Get();
-
- // The Promise that have been left uncaught (rejected and last in
- // their chain) since the last call to this function.
- nsTArray<nsCOMPtr<nsISupports>> uncaught;
- storage->mUncaughtRejections.SwapElements(uncaught);
-
- // The Promise that have been left uncaught at some point, but that
- // have eventually had their `then` method called.
- nsTArray<nsCOMPtr<nsISupports>> consumed;
- storage->mConsumedRejections.SwapElements(consumed);
-
- nsTArray<nsCOMPtr<nsISupports>>& observers = storage->mUncaughtRejectionObservers;
-
- nsresult rv;
- // Notify observers of uncaught Promise.
-
- for (size_t i = 0; i < uncaught.Length(); ++i) {
- nsCOMPtr<Promise> promise = do_QueryInterface(uncaught[i], &rv);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
-
- if (!promise->IsLastInChain()) {
- // This promise is not the last in the chain anymore,
- // so the error has been caught at some point.
- continue;
- }
-
- // For the moment, the Promise is still at the end of the
- // chain. Let's inform observers, so that they may decide whether
- // to report it.
- for (size_t j = 0; j < observers.Length(); ++j) {
- ErrorResult err;
- RefPtr<UncaughtRejectionObserver> obs =
- static_cast<UncaughtRejectionObserver*>(observers[j].get());
-
- obs->OnLeftUncaught(*promise, err); // Ignore errors
- }
-
- promise->SetNotifiedAsUncaught();
- }
-
- // Notify observers of consumed Promise.
-
- for (size_t i = 0; i < consumed.Length(); ++i) {
- nsCOMPtr<Promise> promise = do_QueryInterface(consumed[i], &rv);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
-
- if (!promise->WasNotifiedAsUncaught()) {
- continue;
- }
-
- MOZ_ASSERT(!promise->IsLastInChain());
- for (size_t j = 0; j < observers.Length(); ++j) {
- ErrorResult err;
- RefPtr<UncaughtRejectionObserver> obs =
- static_cast<UncaughtRejectionObserver*>(observers[j].get());
-
- obs->OnConsumed(*promise, err); // Ignore errors
- }
- }
-}
-#endif // SPIDERMONKEY_PROMISE
-
} // namespace dom
} // namespace mozilla
diff --git a/dom/promise/PromiseDebugging.h b/dom/promise/PromiseDebugging.h
index 218a64c2e..77397b841 100644
--- a/dom/promise/PromiseDebugging.h
+++ b/dom/promise/PromiseDebugging.h
@@ -52,37 +52,17 @@ public:
JS::MutableHandle<JSObject*> aStack,
ErrorResult& aRv);
-#ifndef SPIDERMONKEY_PROMISE
- static void GetDependentPromises(GlobalObject&,
- JS::Handle<JSObject*> aPromise,
- nsTArray<RefPtr<Promise>>& aPromises,
- ErrorResult& aRv);
- static double GetPromiseLifetime(GlobalObject&,
- JS::Handle<JSObject*> aPromise,
- ErrorResult& aRv);
- static double GetTimeToSettle(GlobalObject&, JS::Handle<JSObject*> aPromise,
- ErrorResult& aRv);
-#endif // SPIDERMONKEY_PROMISE
-
// Mechanism for watching uncaught instances of Promise.
static void AddUncaughtRejectionObserver(GlobalObject&,
UncaughtRejectionObserver& aObserver);
static bool RemoveUncaughtRejectionObserver(GlobalObject&,
UncaughtRejectionObserver& aObserver);
-#ifdef SPIDERMONKEY_PROMISE
// Mark a Promise as having been left uncaught at script completion.
static void AddUncaughtRejection(JS::HandleObject);
// Mark a Promise previously added with `AddUncaughtRejection` as
// eventually consumed.
static void AddConsumedRejection(JS::HandleObject);
-#else
- // Mark a Promise as having been left uncaught at script completion.
- static void AddUncaughtRejection(Promise&);
- // Mark a Promise previously added with `AddUncaughtRejection` as
- // eventually consumed.
- static void AddConsumedRejection(Promise&);
-#endif // SPIDERMONKEY_PROMISE
// Propagate the informations from AddUncaughtRejection
// and AddConsumedRejection to observers.
static void FlushUncaughtRejections();
diff --git a/dom/promise/moz.build b/dom/promise/moz.build
index 11d2a7496..c0e3d79a7 100644
--- a/dom/promise/moz.build
+++ b/dom/promise/moz.build
@@ -11,9 +11,8 @@ EXPORTS.mozilla.dom += [
'PromiseWorkerProxy.h',
]
-UNIFIED_SOURCES += [
+SOURCES += [
'Promise.cpp',
- 'PromiseCallback.cpp',
'PromiseDebugging.cpp',
]
diff --git a/dom/webidl/Promise.webidl b/dom/webidl/Promise.webidl
index 4dcb7d43e..a296553df 100644
--- a/dom/webidl/Promise.webidl
+++ b/dom/webidl/Promise.webidl
@@ -3,75 +3,18 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
- * The origin of this IDL file is
- * http://dom.spec.whatwg.org/#promises
+ * This IDL file contains utilities to help connect JS promises to our
+ * Web IDL infrastructure.
*/
-// TODO We use object instead Function. There is an open issue on WebIDL to
-// have different types for "platform-provided function" and "user-provided
-// function"; for now, we just use "object".
-callback PromiseInit = void (object resolve, object reject);
-
callback PromiseJobCallback = void();
[TreatNonCallableAsNull]
callback AnyCallback = any (any value);
-// When using SpiderMonkey promises, we don't want to define all this stuff;
-// just define a tiny interface to make codegen of Promise arguments and return
-// values work.
-#ifndef SPIDERMONKEY_PROMISE
-[Constructor(PromiseInit init),
- Exposed=(Window,Worker,WorkerDebugger,System)]
-// Need to escape "Promise" so it's treated as an identifier.
-interface _Promise {
- // Have to use "any" (or "object", but "any" is simpler) as the type to
- // support the subclassing behavior, since nothing actually requires the
- // return value of PromiseSubclass.resolve/reject to be a Promise object.
- [NewObject, Throws]
- static any resolve(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.
- // Have to use "any" (or "object", but "any" is simpler) as the type to
- // support the subclassing behavior, since nothing actually requires the
- // return value of PromiseSubclass.then/catch to be a Promise object.
- [NewObject, Throws]
- any then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null,
- [TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
-
- [NewObject, Throws]
- any catch([TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
-
- // Have to use "any" (or "object", but "any" is simpler) as the type to
- // support the subclassing behavior, since nothing actually requires the
- // return value of PromiseSubclass.all to be a Promise object. As a result,
- // we also have to do our argument conversion manually, because we want to
- // convert its exceptions into rejections.
- [NewObject, Throws]
- static any all(optional any iterable);
-
- // Have to use "any" (or "object", but "any" is simpler) as the type to
- // support the subclassing behavior, since nothing actually requires the
- // return value of PromiseSubclass.race to be a Promise object. As a result,
- // we also have to do our argument conversion manually, because we want to
- // convert its exceptions into rejections.
- [NewObject, Throws]
- static any race(optional any iterable);
-};
-#else // SPIDERMONKEY_PROMISE
-[NoInterfaceObject,
- Exposed=(Window,Worker,WorkerDebugger,System)]
-// Need to escape "Promise" so it's treated as an identifier.
-interface _Promise {
-};
-
// Hack to allow us to have JS owning and properly tracing/CCing/etc a
// PromiseNativeHandler.
[NoInterfaceObject,
Exposed=(Window,Worker,System)]
interface PromiseNativeHandler {
};
-#endif // SPIDERMONKEY_PROMISE
diff --git a/dom/webidl/PromiseDebugging.webidl b/dom/webidl/PromiseDebugging.webidl
index 107b8bc65..1a5c1aa32 100644
--- a/dom/webidl/PromiseDebugging.webidl
+++ b/dom/webidl/PromiseDebugging.webidl
@@ -38,11 +38,7 @@ callback interface UncaughtRejectionObserver {
* caught, i.e. if its `then` callback is called, `onConsumed` will
* be called.
*/
-#ifdef SPIDERMONKEY_PROMISE
void onLeftUncaught(object p);
-#else
- void onLeftUncaught(Promise<any> p);
-#endif SPIDERMONKEY_PROMISE
/**
* A Promise previously left uncaught is not the last in its
@@ -51,11 +47,7 @@ callback interface UncaughtRejectionObserver {
* @param p A Promise that was previously left in uncaught state is
* now caught, i.e. it is not the last in its chain anymore.
*/
-#ifdef SPIDERMONKEY_PROMISE
void onConsumed(object p);
-#else
- void onConsumed(Promise<any> p);
-#endif SPIDERMONKEY_PROMISE
};
[ChromeOnly, Exposed=(Window,System)]
@@ -105,42 +97,6 @@ interface PromiseDebugging {
[Throws]
static object? getFullfillmentStack(object p);
-#ifndef SPIDERMONKEY_PROMISE
- /**
- * Get the promises directly depending on a given promise. These are:
- *
- * 1) Return values of then() calls on the promise
- * 2) Return values of Promise.all() if the given promise was passed in as one
- * of the arguments.
- * 3) Return values of Promise.race() if the given promise was passed in as
- * one of the arguments.
- *
- * Once a promise is settled, it will generally notify its dependent promises
- * and forget about them, so this is most useful on unsettled promises.
- *
- * Note that this function only returns the promises that directly depend on
- * p. It does not recursively return promises that depend on promises that
- * depend on p.
- */
- [Throws]
- static sequence<Promise<any>> getDependentPromises(object p);
-
- /**
- * Get the number of milliseconds elapsed since the given promise was created.
- */
- [Throws]
- static DOMHighResTimeStamp getPromiseLifetime(object p);
-
- /*
- * Get the number of milliseconds elapsed between the promise being created
- * and being settled. Throws NS_ERROR_UNEXPECTED if the promise has not
- * settled.
- */
- [Throws]
- static DOMHighResTimeStamp getTimeToSettle(object p);
-
-#endif // SPIDERMONKEY_PROMISE
-
/**
* Watching uncaught rejections on the current thread.
*
diff --git a/dom/webidl/TestInterfaceJS.webidl b/dom/webidl/TestInterfaceJS.webidl
index 2cf8d701a..2757745f0 100644
--- a/dom/webidl/TestInterfaceJS.webidl
+++ b/dom/webidl/TestInterfaceJS.webidl
@@ -70,7 +70,7 @@ interface TestInterfaceJS : EventTarget {
// Tests for promise-rejection behavior
Promise<void> testPromiseWithThrowingChromePromiseInit();
- Promise<void> testPromiseWithThrowingContentPromiseInit(PromiseInit func);
+ Promise<void> testPromiseWithThrowingContentPromiseInit(Function func);
Promise<void> testPromiseWithDOMExceptionThrowingPromiseInit();
Promise<void> testPromiseWithThrowingChromeThenFunction();
Promise<void> testPromiseWithThrowingContentThenFunction(AnyCallback func);
diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build
index 0fe10eff9..172895f97 100644
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -12,8 +12,6 @@ PREPROCESSED_WEBIDL_FILES = [
'HTMLMediaElement.webidl',
'Navigator.webidl',
'Node.webidl',
- 'Promise.webidl',
- 'PromiseDebugging.webidl',
'Window.webidl',
]
@@ -371,6 +369,8 @@ WEBIDL_FILES = [
'PresentationRequest.webidl',
'ProcessingInstruction.webidl',
'ProfileTimelineMarker.webidl',
+ 'Promise.webidl',
+ 'PromiseDebugging.webidl',
'PushEvent.webidl',
'PushManager.webidl',
'PushManager.webidl',