зеркало из https://github.com/mozilla/gecko-dev.git
Bug 983300 part 2. Introduce a GenericPromiseReturningBindingMethod for methods that return Promise return value. r=khuey,bholley
This method effectively catches exceptions from GenericBindingMethod and converts them into rejected promises. This handles calls to Promise-returning APIs from everything except Ion fast paths.
This commit is contained in:
Родитель
f40021934e
Коммит
a4309c8b4e
|
@ -37,6 +37,7 @@
|
|||
#include "mozilla/dom/HTMLSharedObjectElement.h"
|
||||
#include "mozilla/dom/HTMLEmbedElementBinding.h"
|
||||
#include "mozilla/dom/HTMLAppletElementBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -2257,5 +2258,77 @@ GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
|
|||
return method(cx, obj, self, JSJitMethodCallArgs(args));
|
||||
}
|
||||
|
||||
bool
|
||||
GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
|
||||
{
|
||||
// Make sure to save the callee before someone maybe messes with rval().
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
JS::Rooted<JSObject*> callee(cx, &args.callee());
|
||||
|
||||
// We could invoke GenericBindingMethod here, but that involves an
|
||||
// extra call. Manually inline it instead.
|
||||
const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
|
||||
prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
|
||||
if (!args.thisv().isObject()) {
|
||||
ThrowInvalidThis(cx, args,
|
||||
MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
|
||||
protoID);
|
||||
return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
|
||||
args.rval());
|
||||
}
|
||||
JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
|
||||
|
||||
void* self;
|
||||
{
|
||||
nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
|
||||
if (NS_FAILED(rv)) {
|
||||
ThrowInvalidThis(cx, args,
|
||||
GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
|
||||
protoID);
|
||||
return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
|
||||
args.rval());
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(info->type() == JSJitInfo::Method);
|
||||
JSJitMethodOp method = info->method;
|
||||
bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
|
||||
if (ok) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
|
||||
args.rval());
|
||||
}
|
||||
|
||||
bool
|
||||
ConvertExceptionToPromise(JSContext* cx,
|
||||
JSObject* promiseScope,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
{
|
||||
GlobalObject global(cx, promiseScope);
|
||||
if (global.Failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> exn(cx);
|
||||
if (!JS_GetPendingException(cx, &exn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_ClearPendingException(cx);
|
||||
ErrorResult rv;
|
||||
nsRefPtr<Promise> promise = Promise::Reject(global, cx, exn, rv);
|
||||
if (rv.Failed()) {
|
||||
// We just give up. Make sure to not leak memory on the
|
||||
// ErrorResult, but then just put the original exception back.
|
||||
ThrowMethodFailedWithDetails(cx, rv, "", "");
|
||||
JS_SetPendingException(cx, exn);
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> wrapScope(cx, JS::CurrentGlobalOrNull(cx));
|
||||
return WrapNewBindingObject(cx, wrapScope, promise, rval);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -2457,6 +2457,22 @@ GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp);
|
|||
bool
|
||||
GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
bool
|
||||
GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
// ConvertExceptionToPromise should only be called when we have an error
|
||||
// condition (e.g. returned false from a JSAPI method). Note that there may be
|
||||
// no exception on cx, in which case this is an uncatchable failure that will
|
||||
// simply be propagated. Otherwise this method will attempt to convert the
|
||||
// exception to a Promise rejected with the exception that it will store in
|
||||
// rval.
|
||||
//
|
||||
// promiseScope should be the scope in which the Promise should be created.
|
||||
bool
|
||||
ConvertExceptionToPromise(JSContext* cx,
|
||||
JSObject* promiseScope,
|
||||
JS::MutableHandle<JS::Value> rval);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -1656,7 +1656,9 @@ class MethodDefiner(PropertyDefiner):
|
|||
"length": methodLength(m),
|
||||
"flags": "JSPROP_ENUMERATE",
|
||||
"condition": PropertyDefiner.getControllingCondition(m),
|
||||
"allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable")}
|
||||
"allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
|
||||
"returnsPromise": m.returnsPromise(),
|
||||
}
|
||||
if isChromeOnly(m):
|
||||
self.chrome.append(method)
|
||||
else:
|
||||
|
@ -1734,9 +1736,19 @@ class MethodDefiner(PropertyDefiner):
|
|||
# JSTypedMethodJitInfo.
|
||||
jitinfo = ("reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor)
|
||||
if m.get("allowCrossOriginThis", False):
|
||||
if m.get("returnsPromise", False):
|
||||
raise TypeError("%s returns a Promise but should "
|
||||
"be allowed cross-origin?" %
|
||||
accessor)
|
||||
accessor = "genericCrossOriginMethod"
|
||||
elif self.descriptor.needsSpecialGenericOps():
|
||||
if m.get("returnsPromise", False):
|
||||
raise TypeError("%s returns a Promise but needs "
|
||||
"special generic ops?" %
|
||||
accessor)
|
||||
accessor = "genericMethod"
|
||||
elif m.get("returnsPromise", False):
|
||||
accessor = "GenericPromiseReturningBindingMethod"
|
||||
else:
|
||||
accessor = "GenericBindingMethod"
|
||||
else:
|
||||
|
|
|
@ -3431,6 +3431,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||
[attr.location])
|
||||
IDLInterfaceMember.handleExtendedAttribute(self, attr)
|
||||
|
||||
def returnsPromise(self):
|
||||
return self._overloads[0].returnType.isPromise()
|
||||
|
||||
def _getDependentObjects(self):
|
||||
deps = set()
|
||||
for overload in self._overloads:
|
||||
|
|
Загрузка…
Ссылка в новой задаче