diff --git a/dom/base/domerr.msg b/dom/base/domerr.msg index 32f20f321fb..e10a29245cf 100644 --- a/dom/base/domerr.msg +++ b/dom/base/domerr.msg @@ -119,3 +119,5 @@ DOM4_MSG_DEF(ReadOnlyError, "A mutation operation was attempted in a READ_ONLY l DOM4_MSG_DEF(InvalidStateError, "A mutation operation was attempted on a file storage that did not allow mutations.", NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR) DOM4_MSG_DEF(AbortError, "A request was aborted, for example through a call to LockedFile.abort.", NS_ERROR_DOM_FILEHANDLE_ABORT_ERR) + +DOM_MSG_DEF(NS_ERROR_DOM_JS_EXCEPTION, "A callback threw an exception") diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 58c91641736..37b0e04f1f9 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -61,6 +61,14 @@ ErrorResult::ThrowTypeError(const dom::ErrNum errorNumber, ...) { va_list ap; va_start(ap, errorNumber); + if (IsJSException()) { + // We have rooted our mJSException, and we don't have the info + // needed to unroot here, so just bail. + va_end(ap); + MOZ_ASSERT(false, + "Ignoring ThrowTypeError call because we have a JS exception"); + return; + } if (IsTypeError()) { delete mMessage; } @@ -107,6 +115,39 @@ ErrorResult::ClearMessage() } } +void +ErrorResult::ThrowJSException(JSContext* cx, JS::Value exn) +{ + MOZ_ASSERT(mMightHaveUnreportedJSException, + "Why didn't you tell us you planned to throw a JS exception?"); + + if (IsTypeError()) { + delete mMessage; + } + + if (!JS_AddNamedValueRoot(cx, &mJSException, "ErrorResult::mJSException")) { + // Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have + // in fact rooted mJSException. + mResult = NS_ERROR_OUT_OF_MEMORY; + } else { + mJSException = exn; + mResult = NS_ERROR_DOM_JS_EXCEPTION; + } +} + +void +ErrorResult::ReportJSException(JSContext* cx) +{ + MOZ_ASSERT(!mMightHaveUnreportedJSException, + "Why didn't you tell us you planned to handle JS exceptions?"); + if (JS_WrapValue(cx, &mJSException)) { + JS_SetPendingException(cx, mJSException); + } + // If JS_WrapValue failed, not much we can do about it... No matter + // what, go ahead and unroot mJSException. + JS_RemoveValueRoot(cx, &mJSException); +} + namespace dom { bool diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index eb52c1d3aa4..829567b920d 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -62,6 +62,10 @@ ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv, rv.ReportTypeError(cx); return false; } + if (rv.IsJSException()) { + rv.ReportJSException(cx); + return false; + } return Throw(cx, rv.ErrorCode()); } diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 378fe61805b..56405762511 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -3686,6 +3686,7 @@ class CGCallGenerator(CGThing): if isFallible: self.cgRoot.prepend(CGGeneric("ErrorResult rv;")) + self.cgRoot.append(CGGeneric("rv.WouldReportJSException();")); self.cgRoot.append(CGGeneric("if (rv.Failed()) {")) self.cgRoot.append(CGIndenter(errorReport)) self.cgRoot.append(CGGeneric("}")) diff --git a/dom/bindings/ErrorResult.h b/dom/bindings/ErrorResult.h index 16cc5c4dc62..20dba9b21fd 100644 --- a/dom/bindings/ErrorResult.h +++ b/dom/bindings/ErrorResult.h @@ -35,10 +35,15 @@ class ErrorResult { public: ErrorResult() { mResult = NS_OK; +#ifdef DEBUG + mMightHaveUnreportedJSException = false; +#endif } + #ifdef DEBUG ~ErrorResult() { MOZ_ASSERT_IF(IsTypeError(), !mMessage); + MOZ_ASSERT(!mMightHaveUnreportedJSException); } #endif @@ -46,6 +51,8 @@ public: MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success"); MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()"); MOZ_ASSERT(!IsTypeError(), "Don't overwite TypeError"); + MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()"); + MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions"); mResult = rv; } @@ -54,6 +61,27 @@ public: void ClearMessage(); bool IsTypeError() const { return ErrorCode() == NS_ERROR_TYPE_ERR; } + // Facilities for throwing a preexisting JS exception value via this + // ErrorResult. The contract is that any code which might end up calling + // ThrowJSException() must call MightThrowJSException() even if no exception + // is being thrown. Code that would call ReportJSException as needed must + // first call WouldReportJSException even if this ErrorResult has not failed. + void ThrowJSException(JSContext* cx, JS::Value exn); + void ReportJSException(JSContext* cx); + bool IsJSException() const { return ErrorCode() == NS_ERROR_DOM_JS_EXCEPTION; } + void MOZ_ALWAYS_INLINE MightThrowJSException() + { +#ifdef DEBUG + mMightHaveUnreportedJSException = true; +#endif + } + void MOZ_ALWAYS_INLINE WouldReportJSException() + { +#ifdef DEBUG + mMightHaveUnreportedJSException = false; +#endif + } + // In the future, we can add overloads of Throw that take more // interesting things, like strings or DOM exception types or // something if desired. @@ -64,6 +92,8 @@ public: void operator=(nsresult rv) { MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()"); MOZ_ASSERT(!IsTypeError(), "Don't overwite TypeError"); + MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()"); + MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions"); mResult = rv; } @@ -78,8 +108,20 @@ public: private: nsresult mResult; struct Message; - // Do not use nsAutoPtr to avoid extra initalizatoin and check. - Message* mMessage; + // mMessage is set by ThrowTypeError and cleared (and deallocatd) by + // ReportTypeError. + // mJSException is set (and rooted) by ThrowJSException and unrooted + // by ReportJSException. + union { + Message* mMessage; // valid when IsTypeError() + JS::Value mJSException; // valid when IsJSException() + }; + +#ifdef DEBUG + // Used to keep track of codepaths that might throw JS exceptions, + // for assertion purposes. + bool mMightHaveUnreportedJSException; +#endif // Not to be implemented, to make sure people always pass this by // reference, not by value. diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h index c16ce010dc6..f970a4f0f6f 100644 --- a/xpcom/base/ErrorList.h +++ b/xpcom/base/ErrorList.h @@ -501,7 +501,7 @@ /* ======================================================================= */ #define MODULE NS_ERROR_MODULE_DOM /* XXX If you add a new DOM error code, also add an error string to - * dom/src/base/domerr.msg */ + * dom/base/domerr.msg */ /* Standard DOM error codes: http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html */ ERROR(NS_ERROR_DOM_INDEX_SIZE_ERR, FAILURE(1)), @@ -548,6 +548,7 @@ ERROR(NS_ERROR_DOM_BAD_URI, FAILURE(1012)), ERROR(NS_ERROR_DOM_RETVAL_UNDEFINED, FAILURE(1013)), ERROR(NS_ERROR_DOM_QUOTA_REACHED, FAILURE(1014)), + ERROR(NS_ERROR_DOM_JS_EXCEPTION, FAILURE(1015)), #undef MODULE