summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dom/base/DOMException.cpp45
-rw-r--r--dom/base/DOMException.h3
-rw-r--r--dom/base/ScriptSettings.cpp97
-rw-r--r--dom/base/ScriptSettings.h11
-rw-r--r--dom/base/nsGlobalWindow.cpp1
-rw-r--r--dom/base/nsJSUtils.cpp65
-rw-r--r--dom/base/nsJSUtils.h18
-rw-r--r--dom/base/nsScriptLoader.cpp1
-rw-r--r--dom/bindings/CallbackObject.cpp29
-rw-r--r--dom/bindings/CallbackObject.h1
-rw-r--r--dom/bindings/test/mochitest.ini1
-rw-r--r--dom/bindings/test/test_callback_exceptions.html17
-rw-r--r--dom/jsurl/nsJSProtocolHandler.cpp15
-rw-r--r--dom/plugins/base/nsNPAPIPlugin.cpp1
-rw-r--r--dom/webidl/DOMException.webidl2
-rw-r--r--dom/xbl/nsXBLProtoImplField.cpp7
-rw-r--r--js/src/jscntxt.cpp8
-rw-r--r--js/src/jsexn.cpp6
-rw-r--r--js/src/jsexn.h14
-rw-r--r--js/xpconnect/idl/xpcexception.idl5
-rw-r--r--xpcom/base/ErrorList.h6
-rw-r--r--xpcom/base/nsIException.idl4
22 files changed, 168 insertions, 189 deletions
diff --git a/dom/base/DOMException.cpp b/dom/base/DOMException.cpp
index d84d198df..a9caf8968 100644
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -173,7 +173,6 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(Exception)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Exception)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -184,7 +183,6 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Exception)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mInner)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mThrownJSVal.setNull();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@@ -244,7 +242,7 @@ Exception::Exception(const nsACString& aMessage,
}
}
- Initialize(aMessage, aResult, aName, location, aData, nullptr);
+ Initialize(aMessage, aResult, aName, location, aData);
}
Exception::Exception()
@@ -403,18 +401,6 @@ Exception::GetData(nsISupports** aData)
return NS_OK;
}
-/* readonly attribute nsIException inner; */
-NS_IMETHODIMP
-Exception::GetInner(nsIException** aException)
-{
- NS_ENSURE_ARG_POINTER(aException);
- NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
-
- nsCOMPtr<nsIException> inner = mInner;
- inner.forget(aException);
- return NS_OK;
-}
-
/* AUTF8String toString (); */
NS_IMETHODIMP
Exception::ToString(nsACString& _retval)
@@ -463,7 +449,7 @@ Exception::ToString(nsACString& _retval)
NS_IMETHODIMP
Exception::Initialize(const nsACString& aMessage, nsresult aResult,
const nsACString& aName, nsIStackFrame *aLocation,
- nsISupports *aData, nsIException *aInner)
+ nsISupports *aData)
{
NS_ENSURE_FALSE(mInitialized, NS_ERROR_ALREADY_INITIALIZED);
@@ -483,7 +469,6 @@ Exception::Initialize(const nsACString& aMessage, nsresult aResult,
}
mData = aData;
- mInner = aInner;
mInitialized = true;
return NS_OK;
@@ -553,13 +538,6 @@ Exception::GetLocation() const
}
already_AddRefed<nsISupports>
-Exception::GetInner() const
-{
- nsCOMPtr<nsIException> inner = mInner;
- return inner.forget();
-}
-
-already_AddRefed<nsISupports>
Exception::GetData() const
{
nsCOMPtr<nsISupports> data = mData;
@@ -632,25 +610,6 @@ DOMException::ToString(nsACString& aReturn)
nsAutoCString location;
- if (mInner) {
- nsString filename;
- mInner->GetFilename(filename);
-
- if (!filename.IsEmpty()) {
- uint32_t line_nr = 0;
-
- mInner->GetLineNumber(&line_nr);
-
- char *temp = PR_smprintf("%s Line: %d",
- NS_ConvertUTF16toUTF8(filename).get(),
- line_nr);
- if (temp) {
- location.Assign(temp);
- PR_smprintf_free(temp);
- }
- }
- }
-
if (location.IsEmpty()) {
location = defaultLocation;
}
diff --git a/dom/base/DOMException.h b/dom/base/DOMException.h
index f2b2dadea..97f2d272c 100644
--- a/dom/base/DOMException.h
+++ b/dom/base/DOMException.h
@@ -83,8 +83,6 @@ public:
already_AddRefed<nsIStackFrame> GetLocation() const;
- already_AddRefed<nsISupports> GetInner() const;
-
already_AddRefed<nsISupports> GetData() const;
void GetStack(nsAString& aStack, ErrorResult& aRv) const;
@@ -110,7 +108,6 @@ protected:
nsCOMPtr<nsISupports> mData;
nsString mFilename;
int mLineNumber;
- nsCOMPtr<nsIException> mInner;
bool mInitialized;
bool mHoldingJSVal;
diff --git a/dom/base/ScriptSettings.cpp b/dom/base/ScriptSettings.cpp
index 3fbfb9043..daf8ebb33 100644
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -312,36 +312,15 @@ AutoJSAPI::~AutoJSAPI()
{
if (mOwnErrorReporting) {
MOZ_ASSERT(NS_IsMainThread(), "See corresponding assertion in TakeOwnershipOfErrorReporting()");
- JS::ContextOptionsRef(cx()).setAutoJSAPIOwnsErrorReporting(mOldAutoJSAPIOwnsErrorReporting);
- if (HasException()) {
-
- // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null
- // compartment when the destructor is called. However, the JS engine
- // requires us to be in a compartment when we fetch the pending exception.
- // In this case, we enter the privileged junk scope and don't dispatch any
- // error events.
- JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx()));
- if (!errorGlobal)
- errorGlobal = xpc::PrivilegedJunkScope();
- JSAutoCompartment ac(cx(), errorGlobal);
- nsCOMPtr<nsPIDOMWindow> win = xpc::WindowGlobalOrNull(errorGlobal);
- JS::Rooted<JS::Value> exn(cx());
- js::ErrorReport jsReport(cx());
- if (StealException(&exn) && jsReport.init(cx(), exn)) {
- nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
- xpcReport->Init(jsReport.report(), jsReport.message(),
- nsContentUtils::IsCallerChrome(),
- win ? win->WindowID() : 0);
- if (win) {
- DispatchScriptErrorEvent(win, JS_GetRuntime(cx()), xpcReport, exn);
- } else {
- xpcReport->LogToConsole();
- }
- } else {
- NS_WARNING("OOMed while acquiring uncaught exception from JSAPI");
- }
- }
+ ReportException();
+
+ // We need to do this _after_ processing the existing exception, because the
+ // JS engine can throw while doing that, and uses this bit to determine what
+ // to do in that case: squelch the exception if the bit is set, otherwise
+ // call the error reporter. Calling WarningOnlyErrorReporter with a
+ // non-warning will assert, so we need to make sure we do the former.
+ JS::ContextOptionsRef(cx()).setAutoJSAPIOwnsErrorReporting(mOldAutoJSAPIOwnsErrorReporting);
}
if (mOldErrorReporter.isSome()) {
@@ -502,17 +481,61 @@ AutoJSAPI::TakeOwnershipOfErrorReporting()
JS_SetErrorReporter(rt, WarningOnlyErrorReporter);
}
+void
+AutoJSAPI::ReportException()
+{
+ MOZ_ASSERT(OwnsErrorReporting(), "This is not our exception to report!");
+ if (!HasException()) {
+ return;
+ }
+
+ // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null
+ // compartment when the destructor is called. However, the JS engine
+ // requires us to be in a compartment when we fetch the pending exception.
+ // In this case, we enter the privileged junk scope and don't dispatch any
+ // error events.
+ JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx()));
+ if (!errorGlobal)
+ errorGlobal = xpc::PrivilegedJunkScope();
+ JSAutoCompartment ac(cx(), errorGlobal);
+ nsCOMPtr<nsPIDOMWindow> win = xpc::WindowGlobalOrNull(errorGlobal);
+ JS::Rooted<JS::Value> exn(cx());
+ js::ErrorReport jsReport(cx());
+ if (StealException(&exn) && jsReport.init(cx(), exn)) {
+ nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
+ xpcReport->Init(jsReport.report(), jsReport.message(),
+ nsContentUtils::IsCallerChrome(),
+ win ? win->WindowID() : 0);
+ if (win) {
+ DispatchScriptErrorEvent(win, JS_GetRuntime(cx()), xpcReport, exn);
+ } else {
+ xpcReport->LogToConsole();
+ }
+ } else {
+ NS_WARNING("OOMed while acquiring uncaught exception from JSAPI");
+ }
+}
+
+bool
+AutoJSAPI::PeekException(JS::MutableHandle<JS::Value> aVal)
+{
+ MOZ_ASSERT_IF(mIsMainThread, CxPusherIsStackTop());
+ MOZ_ASSERT(HasException());
+ MOZ_ASSERT(js::GetContextCompartment(cx()));
+ if (!JS_GetPendingException(cx(), aVal)) {
+ return false;
+ }
+ return true;
+}
+
bool
AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal)
{
- MOZ_ASSERT(CxPusherIsStackTop());
- MOZ_ASSERT(HasException());
- MOZ_ASSERT(js::GetContextCompartment(cx()));
- if (!JS_GetPendingException(cx(), aVal)) {
- return false;
- }
- JS_ClearPendingException(cx());
- return true;
+ if (!PeekException(aVal)) {
+ return false;
+ }
+ JS_ClearPendingException(cx());
+ return true;
}
AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
diff --git a/dom/base/ScriptSettings.h b/dom/base/ScriptSettings.h
index f74d434c1..a9c1084cc 100644
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -273,6 +273,9 @@ public:
// while keeping the old behavior as the default.
void TakeOwnershipOfErrorReporting();
bool OwnsErrorReporting() { return mOwnErrorReporting; }
+ // If HasException, report it. Otherwise, a no-op. This must be
+ // called only if OwnsErrorReporting().
+ void ReportException();
bool HasException() const {
MOZ_ASSERT(CxPusherIsStackTop());
@@ -287,6 +290,14 @@ public:
// into the current compartment.
bool StealException(JS::MutableHandle<JS::Value> aVal);
+ // Peek the current exception from the JS engine, without stealing it.
+ // Callers must ensure that HasException() is true, and that cx() is in a
+ // non-null compartment.
+ //
+ // Note that this fails if and only if we OOM while wrapping the exception
+ // into the current compartment.
+ bool PeekException(JS::MutableHandle<JS::Value> aVal);
+
void ClearException() {
MOZ_ASSERT(CxPusherIsStackTop());
JS_ClearPendingException(cx());
diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
index 400b2109f..01266e899 100644
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -12413,6 +12413,7 @@ nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
// New script entry point required, due to the "Create a script" sub-step of
// http://www.whatwg.org/specs/web-apps/current-work/#timer-initialization-steps
AutoEntryScript entryScript(this, true, aScx->GetNativeContext());
+ entryScript.TakeOwnershipOfErrorReporting();
JS::CompileOptions options(entryScript.cx());
options.setFileAndLine(filename, lineNo)
.setVersion(JSVERSION_DEFAULT);
diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp
index 6441d1f33..d9c68b4d2 100644
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -98,41 +98,6 @@ nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext)
return innerWindowID;
}
-void
-nsJSUtils::ReportPendingException(JSContext *aContext)
-{
- if (JS_IsExceptionPending(aContext)) {
- bool saved = JS_SaveFrameChain(aContext);
- {
- // JS_SaveFrameChain set the compartment of aContext to null, so we need
- // to enter a compartment. The question is, which one? We don't want to
- // enter the original compartment of aContext (or the compartment of the
- // current exception on aContext, for that matter) because when we
- // JS_ReportPendingException the JS engine can try to duck-type the
- // exception and produce a JSErrorReport. It will then pass that
- // JSErrorReport to the error reporter on aContext, which might expose
- // information from it to script via onerror handlers. So it's very
- // important that the duck typing happen in the same compartment as the
- // onerror handler. In practice, that's the compartment of the window (or
- // otherwise default global) of aContext, so use that here.
- nsIScriptContext* scx = GetScriptContextFromJSContext(aContext);
- JS::Rooted<JSObject*> scope(aContext);
- scope = scx ? scx->GetWindowProxy() : nullptr;
- if (!scope) {
- // The SafeJSContext has no default object associated with it.
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(aContext == nsContentUtils::GetSafeJSContext());
- scope = xpc::UnprivilegedJunkScope(); // Usage approved by bholley
- }
- JSAutoCompartment ac(aContext, scope);
- JS_ReportPendingException(aContext);
- }
- if (saved) {
- JS_RestoreFrameChain(aContext);
- }
- }
-}
-
nsresult
nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
JS::AutoObjectVector& aScopeChain,
@@ -199,12 +164,11 @@ nsJSUtils::EvaluateString(JSContext* aCx,
PROFILER_LABEL("nsJSUtils", "EvaluateString",
js::ProfileEntry::Category::JS);
+ MOZ_ASSERT(JS::ContextOptionsRef(aCx).autoJSAPIOwnsErrorReporting(),
+ "Caller must own error reporting");
MOZ_ASSERT_IF(aCompileOptions.versionSet,
aCompileOptions.version != JSVERSION_UNKNOWN);
MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, !aCompileOptions.noScriptRval);
- MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, !aCompileOptions.noScriptRval);
- // Note that the above assert means that if aCompileOptions.noScriptRval then
- // also aEvaluateOptions.reportUncaught.
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
MOZ_ASSERT(aSrcBuf.get());
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
@@ -225,13 +189,6 @@ nsJSUtils::EvaluateString(JSContext* aCx,
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(ssm->ScriptAllowed(aEvaluationGlobal), NS_OK);
- mozilla::Maybe<AutoDontReportUncaught> dontReport;
- if (!aEvaluateOptions.reportUncaught) {
- // We need to prevent AutoLastFrameCheck from reporting and clearing
- // any pending exceptions.
- dontReport.emplace(aCx);
- }
-
bool ok = true;
// Scope the JSAutoCompartment so that we can later wrap the return value
// into the caller's cx.
@@ -275,24 +232,14 @@ nsJSUtils::EvaluateString(JSContext* aCx,
}
if (!ok) {
- if (aEvaluateOptions.reportUncaught) {
- ReportPendingException(aCx);
- if (!aCompileOptions.noScriptRval) {
- aRetValue.setUndefined();
- }
- } else {
- rv = JS_IsExceptionPending(aCx) ? NS_ERROR_FAILURE
- : NS_ERROR_OUT_OF_MEMORY;
- JS::Rooted<JS::Value> exn(aCx);
- JS_GetPendingException(aCx, &exn);
- MOZ_ASSERT(!aCompileOptions.noScriptRval); // we asserted this on entry
- aRetValue.set(exn);
- JS_ClearPendingException(aCx);
+ rv = NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW;
+ if (!aCompileOptions.noScriptRval) {
+ aRetValue.setUndefined();
}
}
// Wrap the return value into whatever compartment aCx was in.
- if (!aCompileOptions.noScriptRval) {
+ if (ok && !aCompileOptions.noScriptRval) {
if (!JS_WrapValue(aCx, aRetValue)) {
return NS_ERROR_OUT_OF_MEMORY;
}
diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h
index d7ac67767..4679151c0 100644
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -52,13 +52,6 @@ public:
*/
static uint64_t GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext);
- /**
- * Report a pending exception on aContext, if any. Note that this
- * can be called when the context has a JS stack. If that's the
- * case, the stack will be set aside before reporting the exception.
- */
- static void ReportPendingException(JSContext *aContext);
-
static nsresult CompileFunction(mozilla::dom::AutoJSAPI& jsapi,
JS::AutoObjectVector& aScopeChain,
JS::CompileOptions& aOptions,
@@ -70,12 +63,10 @@ public:
struct MOZ_STACK_CLASS EvaluateOptions {
bool coerceToString;
- bool reportUncaught;
JS::AutoObjectVector scopeChain;
explicit EvaluateOptions(JSContext* cx)
: coerceToString(false)
- , reportUncaught(true)
, scopeChain(cx)
{}
@@ -83,16 +74,13 @@ public:
coerceToString = aCoerce;
return *this;
}
-
- EvaluateOptions& setReportUncaught(bool aReport) {
- reportUncaught = aReport;
- return *this;
- }
};
// aEvaluationGlobal is the global to evaluate in. The return value
// will then be wrapped back into the compartment aCx is in when
- // this function is called.
+ // this function is called. For all the EvaluateString overloads,
+ // the JSContext must come from an AutoJSAPI that has had
+ // TakeOwnershipOfErrorReporting() called on it.
static nsresult EvaluateString(JSContext* aCx,
const nsAString& aScript,
JS::Handle<JSObject*> aEvaluationGlobal,
diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp
index 6f6adc6d0..739ad1430 100644
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -1138,6 +1138,7 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
// New script entry point required, due to the "Create a script" sub-step of
// http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
AutoEntryScript entryScript(globalObject, true, context->GetNativeContext());
+ entryScript.TakeOwnershipOfErrorReporting();
JS::Rooted<JSObject*> global(entryScript.cx(),
globalObject->GetGlobalJSObject());
diff --git a/dom/bindings/CallbackObject.cpp b/dom/bindings/CallbackObject.cpp
index 2c0aca913..fee6b2e84 100644
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -180,12 +180,8 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
// And now we're ready to go.
mCx = cx;
- // Make sure the JS engine doesn't report exceptions we want to re-throw
- if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) ||
- mExceptionHandling == eRethrowExceptions) {
- mSavedJSContextOptions = JS::ContextOptionsRef(cx);
- JS::ContextOptionsRef(cx).setDontReportUncaught(true);
- }
+ // Make sure the JS engine doesn't report exceptions we want to re-throw.
+ mAutoEntryScript->TakeOwnershipOfErrorReporting();
}
bool
@@ -251,18 +247,18 @@ CallbackObject::CallSetup::~CallSetup()
// Now, if we have a JSContext, report any pending errors on it, unless we
// were told to re-throw them.
if (mCx) {
- bool needToDealWithException = JS_IsExceptionPending(mCx);
+ bool needToDealWithException = mAutoEntryScript->HasException();
if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) ||
mExceptionHandling == eRethrowExceptions) {
- // Restore the old context options
- JS::ContextOptionsRef(mCx) = mSavedJSContextOptions;
mErrorResult.MightThrowJSException();
+ MOZ_ASSERT(mAutoEntryScript->OwnsErrorReporting());
if (needToDealWithException) {
JS::Rooted<JS::Value> exn(mCx);
- if (JS_GetPendingException(mCx, &exn) &&
+ if (mAutoEntryScript->PeekException(&exn) &&
ShouldRethrowException(exn)) {
+ mAutoEntryScript->ClearException();
+ MOZ_ASSERT(!mAutoEntryScript->HasException());
mErrorResult.ThrowJSException(mCx, exn);
- JS_ClearPendingException(mCx);
needToDealWithException = false;
}
}
@@ -270,7 +266,7 @@ CallbackObject::CallSetup::~CallSetup()
if (needToDealWithException) {
// Either we're supposed to report our exceptions, or we're supposed to
- // re-throw them but we failed to JS_GetPendingException. Either way,
+ // re-throw them but we failed to get the exception value. Either way,
// just report the pending exception, if any.
//
// We don't use nsJSUtils::ReportPendingException here because all it
@@ -281,7 +277,9 @@ CallbackObject::CallSetup::~CallSetup()
// screw up our compartment, which is exactly what we do not want.
//
// XXXbz FIXME: bug 979525 means we don't always JS_SaveFrameChain here,
- // so we need to go ahead and do that.
+ // so we need to go ahead and do that. This is also the reason we don't
+ // just rely on ~AutoJSAPI reporting the exception for us. I think if we
+ // didn't need to JS_SaveFrameChain here, we could just rely on that.
JS::Rooted<JSObject*> oldGlobal(mCx, JS::CurrentGlobalOrNull(mCx));
MOZ_ASSERT(oldGlobal, "How can we not have a global here??");
bool saved = JS_SaveFrameChain(mCx);
@@ -292,7 +290,10 @@ CallbackObject::CallSetup::~CallSetup()
MOZ_ASSERT(!JS::DescribeScriptedCaller(mCx),
"Our comment above about JS_SaveFrameChain having been "
"called is a lie?");
- JS_ReportPendingException(mCx);
+ // Note that we don't JS_ReportPendingException here because we want to
+ // go through our AutoEntryScript's reporting mechanism instead, since
+ // it currently owns error reporting.
+ mAutoEntryScript->ReportException();
}
if (saved) {
JS_RestoreFrameChain(mCx);
diff --git a/dom/bindings/CallbackObject.h b/dom/bindings/CallbackObject.h
index f21e890e8..a5c144acf 100644
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -218,7 +218,6 @@ protected:
// we should re-throw them.
ErrorResult& mErrorResult;
const ExceptionHandling mExceptionHandling;
- JS::ContextOptions mSavedJSContextOptions;
const bool mIsMainThread;
};
};
diff --git a/dom/bindings/test/mochitest.ini b/dom/bindings/test/mochitest.ini
index 4ea9b0789..0b1118f8c 100644
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -59,3 +59,4 @@ skip-if = debug == false
skip-if = debug == false
[test_worker_UnwrapArg.html]
[test_crossOriginWindowSymbolAccess.html]
+[test_callback_exceptions.html]
diff --git a/dom/bindings/test/test_callback_exceptions.html b/dom/bindings/test/test_callback_exceptions.html
new file mode 100644
index 000000000..fad6319f7
--- /dev/null
+++ b/dom/bindings/test/test_callback_exceptions.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for ...</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+promise_test(function(t) {
+ var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL, JSON.parse);
+ return promise_rejects(t, new SyntaxError,
+ Promise.resolve().then(iterator.nextNode.bind(iterator)));
+}, "Trying to use JSON.parse as filter should throw a catchable SyntaxError exception even when the filter is invoked async");
+
+promise_test(function(t) {
+ return promise_rejects(t, new SyntaxError, Promise.resolve('{').then(JSON.parse));
+}, "Trying to use JSON.parse as a promise callback should allow the next promise to handle the resulting exception.");
+</script>
diff --git a/dom/jsurl/nsJSProtocolHandler.cpp b/dom/jsurl/nsJSProtocolHandler.cpp
index 83a32a05b..1be74c627 100644
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -249,6 +249,10 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
// http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
AutoEntryScript entryScript(innerGlobal, true,
scriptContext->GetNativeContext());
+ // We want to make sure we report any exceptions that happen before we
+ // return, since whatever happens inside our execution shouldn't affect any
+ // other scripts that might happen to be running.
+ entryScript.TakeOwnershipOfErrorReporting();
JSContext* cx = entryScript.cx();
JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject());
NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
@@ -276,20 +280,13 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script),
globalJSObject, options, evalOptions, &v);
- // If there's an error on cx as a result of that call, report
- // it now -- either we're just running under the event loop,
- // so we shouldn't propagate JS exceptions out of here, or we
- // can't be sure that our caller is JS (and if it's not we'll
- // lose the error), or it might be JS that then proceeds to
- // cause an error of its own (which will also make us lose
- // this error).
- ::JS_ReportPendingException(cx);
-
if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
return NS_ERROR_MALFORMED_URI;
} else if (v.isUndefined()) {
return NS_ERROR_DOM_RETVAL_UNDEFINED;
} else {
+ MOZ_ASSERT(rv != NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW,
+ "How did we get a non-undefined return value?");
nsAutoJSString result;
if (!result.init(cx, v)) {
return NS_ERROR_OUT_OF_MEMORY;
diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp
index 935044697..b04e4632f 100644
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -1508,6 +1508,7 @@ _evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result)
}
dom::AutoEntryScript aes(win);
+ aes.TakeOwnershipOfErrorReporting();
JSContext* cx = aes.cx();
JS::Rooted<JSObject*> obj(cx, nsNPObjWrapper::GetNewOrUsed(npp, cx, npobj));
diff --git a/dom/webidl/DOMException.webidl b/dom/webidl/DOMException.webidl
index 95a6250d8..f83f05c69 100644
--- a/dom/webidl/DOMException.webidl
+++ b/dom/webidl/DOMException.webidl
@@ -52,8 +52,6 @@ interface ExceptionMembers
// this was only ever usefully available to chrome JS.
[ChromeOnly]
readonly attribute StackFrame? location;
- // An inner exception that triggered this, if available.
- readonly attribute nsISupports? inner;
// Arbitary data for the implementation.
readonly attribute nsISupports? data;
diff --git a/dom/xbl/nsXBLProtoImplField.cpp b/dom/xbl/nsXBLProtoImplField.cpp
index 8c2295bbc..a76fd0b16 100644
--- a/dom/xbl/nsXBLProtoImplField.cpp
+++ b/dom/xbl/nsXBLProtoImplField.cpp
@@ -405,6 +405,7 @@ nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
// We are going to run script via EvaluateString, so we need a script entry
// point, but as this is XBL related it does not appear in the HTML spec.
AutoEntryScript entryScript(globalObject, true);
+ entryScript.TakeOwnershipOfErrorReporting();
JSContext* cx = entryScript.cx();
NS_ASSERTION(!::JS_IsExceptionPending(cx),
@@ -440,6 +441,12 @@ nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
return rv;
}
+ if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW) {
+ // Report the exception now, before we try using the JSContext for
+ // the JS_DefineUCProperty call.
+ entryScript.ReportException();
+ }
+
// Now, enter the node's compartment, wrap the eval result, and define it on
// the bound node.
JSAutoCompartment ac2(cx, aBoundNode);
diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp
index 9b1a3d709..e703b4c13 100644
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -229,6 +229,14 @@ ReportError(JSContext* cx, const char* message, JSErrorReport* reportp,
if (js_ErrorToException(cx, message, reportp, callback, userRef)) {
return;
}
+
+ /*
+ * The AutoJSAPI error reporter only allows warnings to be reported so
+ * just ignore this error rather than try to report it.
+ */
+ if (cx->options().autoJSAPIOwnsErrorReporting() && !JSREPORT_IS_WARNING(reportp->flags))
+ return;
+
}
/*
diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp
index f49140e8f..06b20042b 100644
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -607,6 +607,12 @@ js_ErrorToException(JSContext* cx, const char* message, JSErrorReport* reportp,
static bool
IsDuckTypedErrorObject(JSContext* cx, HandleObject exnObject, const char** filename_strp)
{
+ /*
+ * This function is called from ErrorReport::init and so should not generate
+ * any new exceptions.
+ */
+ AutoClearPendingException acpe(cx);
+
bool found;
if (!JS_HasProperty(cx, exnObject, js_message_str, &found) || !found)
return false;
diff --git a/js/src/jsexn.h b/js/src/jsexn.h
index 24010960f..376a191de 100644
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -116,4 +116,18 @@ ExnTypeFromProtoKey(JSProtoKey key)
return type;
}
+class AutoClearPendingException
+{
+ JSContext *cx;
+
+ public:
+ explicit AutoClearPendingException(JSContext *cxArg)
+ : cx(cxArg)
+ { }
+
+ ~AutoClearPendingException() {
+ cx->clearPendingException();
+ }
+};
+
#endif /* jsexn_h */
diff --git a/js/xpconnect/idl/xpcexception.idl b/js/xpconnect/idl/xpcexception.idl
index 7587ce990..beaeb1bc5 100644
--- a/js/xpconnect/idl/xpcexception.idl
+++ b/js/xpconnect/idl/xpcexception.idl
@@ -7,7 +7,7 @@
#include "nsISupports.idl"
#include "nsIException.idl"
-[scriptable, uuid(dd250248-2586-4ec1-a68f-8d14ef452517)]
+[scriptable, uuid(875e6645-e762-4da6-9ec8-bf19ab0050df)]
interface nsIXPCException : nsIException
{
// inherits methods from nsIException
@@ -16,8 +16,7 @@ interface nsIXPCException : nsIException
in nsresult aResult,
in AUTF8String aName,
in nsIStackFrame aLocation,
- in nsISupports aData,
- in nsIException aInner);
+ in nsISupports aData);
};
/* this goes into the C++ header verbatim. */
diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h
index 7a7270fa1..d8c073923 100644
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -529,6 +529,12 @@
* the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.
*/
ERROR(NS_SUCCESS_DOM_NO_OPERATION, SUCCESS(1)),
+
+ /*
+ * A success code that indicates that evaluating a string of JS went
+ * just fine except it threw an exception.
+ */
+ ERROR(NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW, SUCCESS(2)),
#undef MODULE
diff --git a/xpcom/base/nsIException.idl b/xpcom/base/nsIException.idl
index d770382a6..1f9702d2e 100644
--- a/xpcom/base/nsIException.idl
+++ b/xpcom/base/nsIException.idl
@@ -12,7 +12,7 @@
[ptr] native JSContext(JSContext);
-[scriptable, uuid(4ed5cd87-401a-425a-8d8d-c28fbc1e88b2)]
+[scriptable, uuid(4371b5bf-6845-487f-8d9d-3f1e4a9badd2)]
interface nsIStackFrame : nsISupports
{
// see nsIProgrammingLanguage for list of language consts
@@ -69,8 +69,6 @@ interface nsIException : nsISupports
// A stack trace, if available.
readonly attribute nsIStackFrame location;
- // An inner exception that triggered this, if available.
- readonly attribute nsIException inner;
// Arbitary data for the implementation.
readonly attribute nsISupports data;