Bug 1107592 part 2. Allow chrome JS to directly throw content DOMExceptions that will propagate out to the web script. r=peterv

This commit is contained in:
Boris Zbarsky 2015-01-02 17:08:33 -05:00
parent b805566e95
commit 1525c9e29b
7 changed files with 153 additions and 12 deletions

View File

@ -34,6 +34,8 @@
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/DOMErrorBinding.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/HTMLObjectElement.h"
#include "mozilla/dom/HTMLObjectElementBinding.h"
@ -216,6 +218,10 @@ ErrorResult::ReportJSException(JSContext* cx)
// If JS_WrapValue failed, not much we can do about it... No matter
// what, go ahead and unroot mJSException.
js::RemoveRawValueRoot(cx, &mJSException);
// We no longer have a useful exception but we do want to signal that an error
// occured.
mResult = NS_ERROR_FAILURE;
}
void
@ -224,15 +230,52 @@ ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx)
MOZ_ASSERT(!mMightHaveUnreportedJSException,
"Why didn't you tell us you planned to handle JS exceptions?");
dom::DOMException* domException;
nsresult rv =
UNWRAP_OBJECT(DOMException, &mJSException.toObject(), domException);
if (NS_SUCCEEDED(rv)) {
// We actually have to create a new DOMException object, because the one we
// have has a stack that includes the chrome code that threw it, and in
// particular has the wrong file/line/column information.
nsString message;
domException->GetMessageMoz(message);
nsString name;
domException->GetName(name);
nsRefPtr<dom::DOMException> newException =
new dom::DOMException(nsresult(domException->Result()),
NS_ConvertUTF16toUTF8(message),
NS_ConvertUTF16toUTF8(name),
domException->Code());
JS::Rooted<JS::Value> reflector(aCx);
if (!GetOrCreateDOMReflector(aCx, newException, &reflector)) {
// Well, that threw _an_ exception. Let's forget ours. We can just
// unroot and not change the value, since mJSException is completely
// ignored if mResult is not NS_ERROR_DOM_JS_EXCEPTION and we plan to
// change mResult to a different value.
js::RemoveRawValueRoot(aCx, &mJSException);
// We no longer have a useful exception but we do want to signal that an
// error occured.
mResult = NS_ERROR_FAILURE;
// But do make sure to not ReportJSException here, since we don't have one.
return;
}
mJSException = reflector;
ReportJSException(aCx);
return;
}
dom::DOMError* domError;
nsresult rv = UNWRAP_OBJECT(DOMError, &mJSException.toObject(), domError);
rv = UNWRAP_OBJECT(DOMError, &mJSException.toObject(), domError);
if (NS_FAILED(rv)) {
// Unwrapping really shouldn't fail here, if mExceptionHandling is set to
// Unwrapping really shouldn't fail here: if mExceptionHandling is set to
// eRethrowContentExceptions then the CallSetup destructor only stores an
// exception if it unwraps to DOMError. If we reach this then either
// mExceptionHandling wasn't set to eRethrowContentExceptions and we
// shouldn't be calling ReportJSExceptionFromJSImplementation or something
// went really wrong.
// exception if it unwraps to DOMError or DOMException. If we reach this
// then either mExceptionHandling wasn't set to eRethrowContentExceptions
// and we shouldn't be calling ReportJSExceptionFromJSImplementation or
// something went really wrong.
NS_RUNTIMEABORT("We stored a non-DOMError exception!");
}

View File

@ -8,6 +8,8 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/DOMErrorBinding.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.h"
#include "jsfriendapi.h"
#include "nsIScriptGlobalObject.h"
#include "nsIXPConnect.h"
@ -190,8 +192,8 @@ CallbackObject::CallSetup::ShouldRethrowException(JS::Handle<JS::Value> aExcepti
MOZ_ASSERT(mExceptionHandling == eRethrowContentExceptions);
// For eRethrowContentExceptions we only want to throw an exception if the
// object that was thrown is a DOMError object in the caller compartment
// (which we stored in mCompartment).
// object that was thrown is a DOMError or DOMException object in the caller
// compartment (which we stored in mCompartment).
if (!aException.isObject()) {
return false;
@ -204,7 +206,9 @@ CallbackObject::CallSetup::ShouldRethrowException(JS::Handle<JS::Value> aExcepti
}
DOMError* domError;
return NS_SUCCEEDED(UNWRAP_OBJECT(DOMError, obj, domError));
DOMException* domException;
return NS_SUCCEEDED(UNWRAP_OBJECT(DOMError, obj, domError)) ||
NS_SUCCEEDED(UNWRAP_OBJECT(DOMException, obj, domException));
}
CallbackObject::CallSetup::~CallSetup()

View File

@ -85,8 +85,8 @@ public:
// Report any exception and don't throw it to the caller code.
eReportExceptions,
// Throw an exception to the caller code if the thrown exception is a
// binding object for a DOMError from the caller's scope, otherwise report
// it.
// binding object for a DOMError or DOMException from the caller's scope,
// otherwise report it.
eRethrowContentExceptions,
// Throw any exception to the caller code.
eRethrowExceptions

View File

@ -14,7 +14,10 @@ function TestInterfaceJS(anyArg, objectArg) {}
TestInterfaceJS.prototype = {
classID: Components.ID("{2ac4e026-cf25-47d5-b067-78d553c3cad8}"),
contractID: "@mozilla.org/dom/test-interface-js;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
Ci.nsIDOMGlobalPropertyInitializer]),
init: function(win) { this._win = win; },
__init: function (anyArg, objectArg, dictionaryArg) {
this._anyAttr = undefined;
@ -59,6 +62,15 @@ TestInterfaceJS.prototype = {
testSequenceOverload: function(arg) {},
testSequenceUnion: function(arg) {},
testThrowDOMError: function() {
throw new this._win.DOMError("NotSupportedError", "We are a DOMError");
},
testThrowDOMException: function() {
throw new this._win.DOMException("We are a DOMException",
"NotSupportedError");
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJS])

View File

@ -53,3 +53,5 @@ skip-if = debug == false
[test_traceProtos.html]
[test_sequence_detection.html]
skip-if = debug == false
[test_exception_options_from_jsimplemented.html]
skip-if = debug == false

View File

@ -0,0 +1,73 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1107592
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1107592</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1107592 **/
SimpleTest.waitForExplicitFinish();
function doTest() {
var t = new TestInterfaceJS();
try {
t.testThrowDOMError();
} catch (e) {
ok(e instanceof Error, "Should have an Error here");
ok(!(e instanceof DOMException), "Should not have DOMException here");
ok(!("code" in e), "Should not have a 'code' property");
ise(e.name, "Error", "Should not have an interesting name here");
ise(e.message, "We are a DOMError", "Should have the right message");
ise(e.stack,
"doTest@http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html:20:7\n",
"Exception stack should still only show our code");
ise(e.fileName,
"http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html",
"Should have the right file name");
ise(e.lineNumber, 20, "Should have the right line number");
ise(e.columnNumber, 6, "Should have the right column number");
}
try {
t.testThrowDOMException();
} catch (e) {
ok(e instanceof Error, "Should also have an Error here");
ok(e instanceof DOMException, "Should have DOMException here");
ise(e.name, "NotSupportedError", "Should have the right name here");
ise(e.message, "We are a DOMException",
"Should also have the right message");
ise(e.code, DOMException.NOT_SUPPORTED_ERR,
"Should have the right 'code'");
ise(e.stack,
"doTest@http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html:38:6\n",
"Exception stack should still only show our code");
ise(e.filename,
"http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html",
"Should still have the right file name");
ise(e.lineNumber, 38, "Should still have the right line number");
todo_is(e.columnNumber, 6,
"No column number support for DOMException yet");
}
SimpleTest.finish();
}
SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
doTest);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1107592">Mozilla Bug 1107592</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -48,4 +48,11 @@ interface TestInterfaceJS {
void testSequenceOverload(DOMString arg);
void testSequenceUnion((sequence<DOMString> or DOMString) arg);
// Tests for exception-throwing behavior
[Throws]
void testThrowDOMError();
[Throws]
void testThrowDOMException();
};