summaryrefslogtreecommitdiffstats
path: root/dom/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'dom/bindings')
-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
11 files changed, 286 insertions, 366 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);