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
Родитель 7af8dba16f
Коммит 8fd98d4057
7 изменённых файлов: 153 добавлений и 12 удалений

Просмотреть файл

@ -34,6 +34,8 @@
#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/DOMError.h" #include "mozilla/dom/DOMError.h"
#include "mozilla/dom/DOMErrorBinding.h" #include "mozilla/dom/DOMErrorBinding.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/ElementBinding.h" #include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/HTMLObjectElement.h" #include "mozilla/dom/HTMLObjectElement.h"
#include "mozilla/dom/HTMLObjectElementBinding.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 // If JS_WrapValue failed, not much we can do about it... No matter
// what, go ahead and unroot mJSException. // what, go ahead and unroot mJSException.
js::RemoveRawValueRoot(cx, &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 void
@ -224,15 +230,52 @@ ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx)
MOZ_ASSERT(!mMightHaveUnreportedJSException, MOZ_ASSERT(!mMightHaveUnreportedJSException,
"Why didn't you tell us you planned to handle JS exceptions?"); "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; dom::DOMError* domError;
nsresult rv = UNWRAP_OBJECT(DOMError, &mJSException.toObject(), domError); rv = UNWRAP_OBJECT(DOMError, &mJSException.toObject(), domError);
if (NS_FAILED(rv)) { 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 // eRethrowContentExceptions then the CallSetup destructor only stores an
// exception if it unwraps to DOMError. If we reach this then either // exception if it unwraps to DOMError or DOMException. If we reach this
// mExceptionHandling wasn't set to eRethrowContentExceptions and we // then either mExceptionHandling wasn't set to eRethrowContentExceptions
// shouldn't be calling ReportJSExceptionFromJSImplementation or something // and we shouldn't be calling ReportJSExceptionFromJSImplementation or
// went really wrong. // something went really wrong.
NS_RUNTIMEABORT("We stored a non-DOMError exception!"); NS_RUNTIMEABORT("We stored a non-DOMError exception!");
} }

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -14,7 +14,10 @@ function TestInterfaceJS(anyArg, objectArg) {}
TestInterfaceJS.prototype = { TestInterfaceJS.prototype = {
classID: Components.ID("{2ac4e026-cf25-47d5-b067-78d553c3cad8}"), classID: Components.ID("{2ac4e026-cf25-47d5-b067-78d553c3cad8}"),
contractID: "@mozilla.org/dom/test-interface-js;1", 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) { __init: function (anyArg, objectArg, dictionaryArg) {
this._anyAttr = undefined; this._anyAttr = undefined;
@ -59,6 +62,15 @@ TestInterfaceJS.prototype = {
testSequenceOverload: function(arg) {}, testSequenceOverload: function(arg) {},
testSequenceUnion: 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]) this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJS])

Просмотреть файл

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

Просмотреть файл

@ -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>

Просмотреть файл

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