Bug 742194 part 1. Add support for throwing uncatchable exceptions to Web IDL bindings. People keep asking for this. r=khuey

This commit is contained in:
Boris Zbarsky 2015-02-20 23:58:36 -05:00
parent 04da2350f4
commit b1e1e53e87
7 changed files with 35 additions and 1 deletions

View File

@ -2622,6 +2622,9 @@ ConvertExceptionToPromise(JSContext* cx,
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;
}

View File

@ -103,6 +103,13 @@ ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv,
const char* memberName,
bool reportJSContentExceptions = false)
{
if (rv.IsUncatchableException()) {
// Nuke any existing exception on aCx, to make sure we're uncatchable.
JS_ClearPendingException(cx);
// Don't do any reporting. Just return false, to create an
// uncatchable exception.
return false;
}
if (rv.IsErrorWithMessage()) {
rv.ReportErrorWithMessage(cx);
return false;

View File

@ -97,6 +97,14 @@ public:
const char* memberName);
bool IsNotEnoughArgsError() const { return ErrorCode() == NS_ERROR_XPC_NOT_ENOUGH_ARGS; }
// Support for uncatchable exceptions.
void ThrowUncatchableException() {
Throw(NS_ERROR_UNCATCHABLE_EXCEPTION);
}
bool IsUncatchableException() const {
return ErrorCode() == NS_ERROR_UNCATCHABLE_EXCEPTION;
}
// StealJSException steals the JS Exception from the object. This method must
// be called only if IsJSException() returns true. This method also resets the
// ErrorCode() to NS_OK. The value will be ensured to be sanitized wrt to the

View File

@ -88,6 +88,12 @@ ThrowExceptionObject(JSContext* aCx, Exception* aException)
bool
Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
{
if (aRv == NS_ERROR_UNCATCHABLE_EXCEPTION) {
// Nuke any existing exception on aCx, to make sure we're uncatchable.
JS_ClearPendingException(aCx);
return false;
}
if (JS_IsExceptionPending(aCx)) {
// Don't clobber the existing exception.
return false;
@ -128,6 +134,8 @@ Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
void
ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv, const char* aMessage)
{
MOZ_ASSERT(aRv != NS_ERROR_UNCATCHABLE_EXCEPTION,
"Doesn't make sense to report uncatchable exceptions!");
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(aWindow))) {
return;

View File

@ -66,6 +66,8 @@ ToJSValue(JSContext* aCx,
JS::MutableHandle<JS::Value> aValue)
{
MOZ_ASSERT(aArgument.Failed());
MOZ_ASSERT(!aArgument.IsUncatchableException(),
"Doesn't make sense to convert uncatchable exception to a JS value!");
AutoForceSetExceptionOnContext forceExn(aCx);
DebugOnly<bool> throwResult = ThrowMethodFailedWithDetails(aCx, aArgument, "", "");
MOZ_ASSERT(!throwResult);

View File

@ -100,7 +100,10 @@ XPCThrower::ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx)
* If there is a pending exception when the native call returns and
* it has the same error result as returned by the native call, then
* the native call may be passing through an error from a previous JS
* call. So we'll just throw that exception into our JS.
* call. So we'll just throw that exception into our JS. Note that
* we don't need to worry about NS_ERROR_UNCATCHABLE_EXCEPTION,
* because presumably there would be no pending exception for that
* nsresult!
*/
if (CheckForPendingException(result, ccx))

View File

@ -519,6 +519,9 @@
ERROR(NS_ERROR_DOM_QUOTA_REACHED, FAILURE(1014)),
ERROR(NS_ERROR_DOM_JS_EXCEPTION, FAILURE(1015)),
/* A way to represent uncatchable exceptions */
ERROR(NS_ERROR_UNCATCHABLE_EXCEPTION, FAILURE(1016)),
/* May be used to indicate when e.g. setting a property value didn't
* actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
* the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.