2015-05-03 22:32:37 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-06 05:15:11 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A struct for tracking exceptions that need to be thrown to JS.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef mozilla_ErrorResult_h
|
|
|
|
#define mozilla_ErrorResult_h
|
|
|
|
|
2012-11-07 03:23:14 +04:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
2013-04-30 18:04:52 +04:00
|
|
|
#include "js/Value.h"
|
2012-05-06 05:15:11 +04:00
|
|
|
#include "nscore.h"
|
2013-07-31 12:12:46 +04:00
|
|
|
#include "nsStringGlue.h"
|
2012-08-24 08:08:08 +04:00
|
|
|
#include "mozilla/Assertions.h"
|
2015-04-07 05:22:03 +03:00
|
|
|
#include "mozilla/Move.h"
|
2015-09-15 21:47:04 +03:00
|
|
|
#include "nsTArray.h"
|
2015-04-07 04:36:55 +03:00
|
|
|
|
2015-04-07 04:36:55 +03:00
|
|
|
namespace IPC {
|
|
|
|
class Message;
|
|
|
|
template <typename> struct ParamTraits;
|
2015-07-13 18:25:42 +03:00
|
|
|
} // namespace IPC
|
2015-04-07 04:36:55 +03:00
|
|
|
|
2012-05-06 05:15:11 +04:00
|
|
|
namespace mozilla {
|
|
|
|
|
2012-11-07 03:23:14 +04:00
|
|
|
namespace dom {
|
|
|
|
|
|
|
|
enum ErrNum {
|
2015-02-04 14:46:26 +03:00
|
|
|
#define MSG_DEF(_name, _argc, _exn, _str) \
|
2012-11-07 03:23:14 +04:00
|
|
|
_name,
|
|
|
|
#include "mozilla/dom/Errors.msg"
|
|
|
|
#undef MSG_DEF
|
|
|
|
Err_Limit
|
|
|
|
};
|
|
|
|
|
2015-10-05 19:38:14 +03:00
|
|
|
// Debug-only compile-time table of the number of arguments of each error, for use in static_assert.
|
|
|
|
#if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__))
|
|
|
|
uint16_t constexpr ErrorFormatNumArgs[] = {
|
|
|
|
#define MSG_DEF(_name, _argc, _exn, _str) \
|
|
|
|
_argc,
|
|
|
|
#include "mozilla/dom/Errors.msg"
|
|
|
|
#undef MSG_DEF
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2015-09-15 21:47:04 +03:00
|
|
|
uint16_t
|
|
|
|
GetErrorArgCount(const ErrNum aErrorNumber);
|
|
|
|
|
2013-08-22 10:34:48 +04:00
|
|
|
bool
|
|
|
|
ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, ...);
|
|
|
|
|
2015-09-15 21:47:04 +03:00
|
|
|
struct StringArrayAppender
|
|
|
|
{
|
|
|
|
static void Append(nsTArray<nsString>& aArgs, uint16_t aCount)
|
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(aCount == 0, "Must give at least as many string arguments as are required by the ErrNum.");
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename... Ts>
|
|
|
|
static void Append(nsTArray<nsString>& aArgs, uint16_t aCount, const nsAString* aFirst, Ts... aOtherArgs)
|
|
|
|
{
|
|
|
|
if (aCount == 0) {
|
|
|
|
MOZ_ASSERT(false, "There should not be more string arguments provided than are required by the ErrNum.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
aArgs.AppendElement(*aFirst);
|
|
|
|
Append(aArgs, aCount - 1, aOtherArgs...);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-11-07 03:23:14 +04:00
|
|
|
} // namespace dom
|
|
|
|
|
2012-05-06 05:15:11 +04:00
|
|
|
class ErrorResult {
|
|
|
|
public:
|
2015-10-09 23:48:10 +03:00
|
|
|
ErrorResult()
|
|
|
|
: mResult(NS_OK)
|
2013-02-19 20:54:40 +04:00
|
|
|
#ifdef DEBUG
|
2015-10-09 23:48:10 +03:00
|
|
|
, mMightHaveUnreportedJSException(false)
|
2015-10-09 23:48:10 +03:00
|
|
|
, mUnionState(HasNothing)
|
2013-02-19 20:54:40 +04:00
|
|
|
#endif
|
2015-10-09 23:48:10 +03:00
|
|
|
{
|
2012-05-06 05:15:11 +04:00
|
|
|
}
|
2013-02-19 20:54:40 +04:00
|
|
|
|
2012-11-07 03:23:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
~ErrorResult() {
|
2015-02-04 14:46:26 +03:00
|
|
|
MOZ_ASSERT_IF(IsErrorWithMessage(), !mMessage);
|
2015-10-09 23:48:10 +03:00
|
|
|
MOZ_ASSERT_IF(IsDOMException(), !mDOMExceptionInfo);
|
2013-02-19 20:54:40 +04:00
|
|
|
MOZ_ASSERT(!mMightHaveUnreportedJSException);
|
2015-10-09 23:48:10 +03:00
|
|
|
MOZ_ASSERT(mUnionState == HasNothing);
|
2012-11-07 03:23:14 +04:00
|
|
|
}
|
2015-10-09 23:48:10 +03:00
|
|
|
#endif // DEBUG
|
2012-05-06 05:15:11 +04:00
|
|
|
|
2015-04-07 05:22:03 +03:00
|
|
|
ErrorResult(ErrorResult&& aRHS)
|
2015-10-09 23:48:10 +03:00
|
|
|
// Initialize mResult and whatever else we need to default-initialize, so
|
|
|
|
// the ClearUnionData call in our operator= will do the right thing
|
|
|
|
// (nothing).
|
|
|
|
: ErrorResult()
|
2015-04-07 05:22:03 +03:00
|
|
|
{
|
|
|
|
*this = Move(aRHS);
|
|
|
|
}
|
|
|
|
ErrorResult& operator=(ErrorResult&& aRHS);
|
|
|
|
|
2015-04-16 22:00:15 +03:00
|
|
|
explicit ErrorResult(nsresult aRv)
|
|
|
|
: ErrorResult()
|
|
|
|
{
|
|
|
|
AssignErrorCode(aRv);
|
|
|
|
}
|
|
|
|
|
2012-05-06 05:15:11 +04:00
|
|
|
void Throw(nsresult rv) {
|
|
|
|
MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
|
2015-04-16 22:00:15 +03:00
|
|
|
AssignErrorCode(rv);
|
2012-05-06 05:15:11 +04:00
|
|
|
}
|
|
|
|
|
2015-04-27 05:38:17 +03:00
|
|
|
// Use SuppressException when you want to suppress any exception that might be
|
|
|
|
// on the ErrorResult. After this call, the ErrorResult will be back a "no
|
|
|
|
// exception thrown" state.
|
|
|
|
void SuppressException();
|
|
|
|
|
|
|
|
// Use StealNSResult() when you want to safely convert the ErrorResult to an
|
|
|
|
// nsresult that you will then return to a caller. This will
|
|
|
|
// SuppressException(), since there will no longer be a way to report it.
|
|
|
|
nsresult StealNSResult() {
|
|
|
|
nsresult rv = ErrorCode();
|
|
|
|
SuppressException();
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2015-10-05 19:38:14 +03:00
|
|
|
template<dom::ErrNum errorNumber, typename... Ts>
|
|
|
|
void ThrowTypeError(Ts... messageArgs)
|
2015-09-15 21:47:04 +03:00
|
|
|
{
|
2015-10-05 19:38:14 +03:00
|
|
|
ThrowErrorWithMessage<errorNumber>(NS_ERROR_TYPE_ERR, messageArgs...);
|
2015-09-15 21:47:04 +03:00
|
|
|
}
|
|
|
|
|
2015-10-05 19:38:14 +03:00
|
|
|
template<dom::ErrNum errorNumber, typename... Ts>
|
|
|
|
void ThrowRangeError(Ts... messageArgs)
|
2015-09-15 21:47:04 +03:00
|
|
|
{
|
2015-10-05 19:38:14 +03:00
|
|
|
ThrowErrorWithMessage<errorNumber>(NS_ERROR_RANGE_ERR, messageArgs...);
|
2015-09-15 21:47:04 +03:00
|
|
|
}
|
|
|
|
|
2015-02-04 14:46:26 +03:00
|
|
|
void ReportErrorWithMessage(JSContext* cx);
|
|
|
|
bool IsErrorWithMessage() const { return ErrorCode() == NS_ERROR_TYPE_ERR || ErrorCode() == NS_ERROR_RANGE_ERR; }
|
2012-11-07 03:23:14 +04:00
|
|
|
|
2013-02-19 20:54:40 +04:00
|
|
|
// 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
|
2015-06-16 23:44:11 +03:00
|
|
|
// is being thrown. Code that would call ReportJSException or
|
2013-06-12 05:41:21 +04:00
|
|
|
// StealJSException as needed must first call WouldReportJSException even if
|
|
|
|
// this ErrorResult has not failed.
|
2014-03-05 17:32:27 +04:00
|
|
|
//
|
|
|
|
// The exn argument to ThrowJSException can be in any compartment. It does
|
|
|
|
// not have to be in the compartment of cx. If someone later uses it, they
|
|
|
|
// will wrap it into whatever compartment they're working in, as needed.
|
2013-04-11 22:31:06 +04:00
|
|
|
void ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn);
|
2013-02-19 20:54:40 +04:00
|
|
|
void ReportJSException(JSContext* cx);
|
|
|
|
bool IsJSException() const { return ErrorCode() == NS_ERROR_DOM_JS_EXCEPTION; }
|
2013-06-12 05:41:21 +04:00
|
|
|
|
2015-10-09 23:48:10 +03:00
|
|
|
// Facilities for throwing a DOMException. If an empty message string is
|
|
|
|
// passed to ThrowDOMException, the default message string for the given
|
|
|
|
// nsresult will be used. The passed-in string must be UTF-8. The nsresult
|
|
|
|
// passed in must be one we create DOMExceptions for; otherwise you may get an
|
|
|
|
// XPConnect Exception.
|
|
|
|
void ThrowDOMException(nsresult rv, const nsACString& message = EmptyCString());
|
|
|
|
void ReportDOMException(JSContext* cx);
|
|
|
|
bool IsDOMException() const { return ErrorCode() == NS_ERROR_DOM_DOMEXCEPTION; }
|
|
|
|
|
2015-04-27 16:18:52 +03:00
|
|
|
// Report a generic error. This should only be used if we're not
|
|
|
|
// some more specific exception type.
|
|
|
|
void ReportGenericError(JSContext* cx);
|
|
|
|
|
2015-02-21 07:58:36 +03:00
|
|
|
// Support for uncatchable exceptions.
|
|
|
|
void ThrowUncatchableException() {
|
|
|
|
Throw(NS_ERROR_UNCATCHABLE_EXCEPTION);
|
|
|
|
}
|
|
|
|
bool IsUncatchableException() const {
|
|
|
|
return ErrorCode() == NS_ERROR_UNCATCHABLE_EXCEPTION;
|
|
|
|
}
|
|
|
|
|
2013-06-12 05:41:21 +04:00
|
|
|
// StealJSException steals the JS Exception from the object. This method must
|
|
|
|
// be called only if IsJSException() returns true. This method also resets the
|
2015-04-27 16:18:52 +03:00
|
|
|
// error code to NS_OK.
|
2013-06-12 05:41:21 +04:00
|
|
|
void StealJSException(JSContext* cx, JS::MutableHandle<JS::Value> value);
|
|
|
|
|
2013-02-19 20:54:40 +04:00
|
|
|
void MOZ_ALWAYS_INLINE MightThrowJSException()
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
mMightHaveUnreportedJSException = true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
void MOZ_ALWAYS_INLINE WouldReportJSException()
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
mMightHaveUnreportedJSException = false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-05-06 05:15:11 +04:00
|
|
|
// In the future, we can add overloads of Throw that take more
|
|
|
|
// interesting things, like strings or DOM exception types or
|
|
|
|
// something if desired.
|
|
|
|
|
|
|
|
// Backwards-compat to make conversion simpler. We don't call
|
|
|
|
// Throw() here because people can easily pass success codes to
|
|
|
|
// this.
|
|
|
|
void operator=(nsresult rv) {
|
2015-04-16 22:00:15 +03:00
|
|
|
AssignErrorCode(rv);
|
2012-05-06 05:15:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Failed() const {
|
|
|
|
return NS_FAILED(mResult);
|
|
|
|
}
|
|
|
|
|
2015-04-27 16:18:52 +03:00
|
|
|
bool ErrorCodeIs(nsresult rv) const {
|
|
|
|
return mResult == rv;
|
|
|
|
}
|
|
|
|
|
2015-04-27 16:18:52 +03:00
|
|
|
// For use in logging ONLY.
|
|
|
|
uint32_t ErrorCodeAsInt() const {
|
|
|
|
return static_cast<uint32_t>(ErrorCode());
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
nsresult ErrorCode() const {
|
|
|
|
return mResult;
|
|
|
|
}
|
|
|
|
|
2012-05-06 05:15:11 +04:00
|
|
|
private:
|
2015-10-09 23:48:10 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
enum UnionState {
|
|
|
|
HasMessage,
|
|
|
|
HasDOMExceptionInfo,
|
|
|
|
HasJSException,
|
|
|
|
HasNothing
|
|
|
|
};
|
|
|
|
#endif // DEBUG
|
|
|
|
|
2015-04-16 22:00:15 +03:00
|
|
|
friend struct IPC::ParamTraits<ErrorResult>;
|
|
|
|
void SerializeMessage(IPC::Message* aMsg) const;
|
|
|
|
bool DeserializeMessage(const IPC::Message* aMsg, void** aIter);
|
|
|
|
|
2015-10-09 23:48:10 +03:00
|
|
|
void SerializeDOMExceptionInfo(IPC::Message* aMsg) const;
|
|
|
|
bool DeserializeDOMExceptionInfo(const IPC::Message* aMsg, void** aIter);
|
|
|
|
|
2015-09-15 21:47:04 +03:00
|
|
|
// Helper method that creates a new Message for this ErrorResult,
|
|
|
|
// and returns the arguments array from that Message.
|
|
|
|
nsTArray<nsString>& CreateErrorMessageHelper(const dom::ErrNum errorNumber, nsresult errorType);
|
|
|
|
|
2015-10-05 19:38:14 +03:00
|
|
|
template<dom::ErrNum errorNumber, typename... Ts>
|
|
|
|
void ThrowErrorWithMessage(nsresult errorType, Ts... messageArgs)
|
2015-09-15 21:47:04 +03:00
|
|
|
{
|
2015-10-05 19:38:14 +03:00
|
|
|
#if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__))
|
|
|
|
static_assert(dom::ErrorFormatNumArgs[errorNumber] == sizeof...(messageArgs),
|
|
|
|
"Pass in the right number of arguments");
|
|
|
|
#endif
|
|
|
|
|
2015-10-09 23:48:10 +03:00
|
|
|
ClearUnionData();
|
|
|
|
|
2015-09-15 21:47:04 +03:00
|
|
|
nsTArray<nsString>& messageArgsArray = CreateErrorMessageHelper(errorNumber, errorType);
|
|
|
|
uint16_t argCount = dom::GetErrorArgCount(errorNumber);
|
2015-09-15 21:47:04 +03:00
|
|
|
dom::StringArrayAppender::Append(messageArgsArray, argCount, messageArgs...);
|
2015-09-15 21:47:04 +03:00
|
|
|
#ifdef DEBUG
|
2015-10-09 23:48:10 +03:00
|
|
|
mUnionState = HasMessage;
|
|
|
|
#endif // DEBUG
|
2015-09-15 21:47:04 +03:00
|
|
|
}
|
2015-04-16 22:00:15 +03:00
|
|
|
|
|
|
|
void AssignErrorCode(nsresult aRv) {
|
|
|
|
MOZ_ASSERT(aRv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
|
|
|
|
MOZ_ASSERT(aRv != NS_ERROR_RANGE_ERR, "Use ThrowRangeError()");
|
|
|
|
MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
|
|
|
|
MOZ_ASSERT(aRv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
|
|
|
|
MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
|
2015-10-09 23:48:10 +03:00
|
|
|
MOZ_ASSERT(aRv != NS_ERROR_DOM_DOMEXCEPTION, "Use ThrowDOMException()");
|
|
|
|
MOZ_ASSERT(!IsDOMException(), "Don't overwrite DOM exceptions");
|
2015-07-10 05:51:28 +03:00
|
|
|
MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "May need to bring back ThrowNotEnoughArgsError");
|
2015-04-16 22:00:15 +03:00
|
|
|
mResult = aRv;
|
|
|
|
}
|
|
|
|
|
2015-04-27 05:38:17 +03:00
|
|
|
void ClearMessage();
|
2015-10-09 23:48:10 +03:00
|
|
|
void ClearDOMExceptionInfo();
|
|
|
|
|
|
|
|
// ClearUnionData will try to clear the data in our
|
|
|
|
// mMessage/mJSException/mDOMExceptionInfo union. After this the union may be
|
|
|
|
// in an uninitialized state (e.g. mMessage or mDOMExceptionInfo may be
|
|
|
|
// pointing to deleted memory) and the caller must either reinitialize it or
|
|
|
|
// change mResult to something that will not involve us touching the union
|
|
|
|
// anymore.
|
2015-10-09 23:48:10 +03:00
|
|
|
void ClearUnionData();
|
|
|
|
|
2015-10-09 23:48:10 +03:00
|
|
|
// Special values of mResult:
|
|
|
|
// NS_ERROR_TYPE_ERR -- ThrowTypeError() called on us.
|
|
|
|
// NS_ERROR_RANGE_ERR -- ThrowRangeError() called on us.
|
|
|
|
// NS_ERROR_DOM_JS_EXCEPTION -- ThrowJSException() called on us.
|
|
|
|
// NS_ERROR_UNCATCHABLE_EXCEPTION -- ThrowUncatchableException called on us.
|
|
|
|
// NS_ERROR_DOM_DOMEXCEPTION -- ThrowDOMException() called on us.
|
2012-05-06 05:15:11 +04:00
|
|
|
nsresult mResult;
|
2015-10-09 23:48:10 +03:00
|
|
|
|
2012-11-07 03:23:14 +04:00
|
|
|
struct Message;
|
2015-10-09 23:48:10 +03:00
|
|
|
struct DOMExceptionInfo;
|
|
|
|
// mMessage is set by ThrowErrorWithMessage and reported (and deallocated) by
|
2015-02-04 14:46:26 +03:00
|
|
|
// ReportErrorWithMessage.
|
2015-10-09 23:48:10 +03:00
|
|
|
// mJSException is set (and rooted) by ThrowJSException and reported
|
|
|
|
// (and unrooted) by ReportJSException.
|
|
|
|
// mDOMExceptionInfo is set by ThrowDOMException and reported
|
|
|
|
// (and deallocated) by ReportDOMException.
|
2013-02-19 20:54:40 +04:00
|
|
|
union {
|
2015-02-04 14:46:26 +03:00
|
|
|
Message* mMessage; // valid when IsErrorWithMessage()
|
2013-02-19 20:54:40 +04:00
|
|
|
JS::Value mJSException; // valid when IsJSException()
|
2015-10-09 23:48:10 +03:00
|
|
|
DOMExceptionInfo* mDOMExceptionInfo; // valid when IsDOMException()
|
2013-02-19 20:54:40 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// Used to keep track of codepaths that might throw JS exceptions,
|
|
|
|
// for assertion purposes.
|
|
|
|
bool mMightHaveUnreportedJSException;
|
2015-10-09 23:48:10 +03:00
|
|
|
|
|
|
|
// Used to keep track of what's stored in our union right now. Note
|
|
|
|
// that this may be set to HasNothing even if our mResult suggests
|
|
|
|
// we should have something, if we have already cleaned up the
|
|
|
|
// something.
|
|
|
|
UnionState mUnionState;
|
2013-02-19 20:54:40 +04:00
|
|
|
#endif
|
2012-05-15 22:23:29 +04:00
|
|
|
|
|
|
|
// Not to be implemented, to make sure people always pass this by
|
|
|
|
// reference, not by value.
|
2015-01-07 02:35:02 +03:00
|
|
|
ErrorResult(const ErrorResult&) = delete;
|
2015-04-07 05:20:35 +03:00
|
|
|
void operator=(const ErrorResult&) = delete;
|
2012-05-06 05:15:11 +04:00
|
|
|
};
|
|
|
|
|
2013-07-31 12:12:46 +04:00
|
|
|
/******************************************************************************
|
|
|
|
** Macros for checking results
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
#define ENSURE_SUCCESS(res, ret) \
|
|
|
|
do { \
|
|
|
|
if (res.Failed()) { \
|
|
|
|
nsCString msg; \
|
|
|
|
msg.AppendPrintf("ENSURE_SUCCESS(%s, %s) failed with " \
|
2015-04-27 16:18:52 +03:00
|
|
|
"result 0x%X", #res, #ret, res.ErrorCodeAsInt()); \
|
2013-07-31 12:12:46 +04:00
|
|
|
NS_WARNING(msg.get()); \
|
|
|
|
return ret; \
|
|
|
|
} \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
#define ENSURE_SUCCESS_VOID(res) \
|
|
|
|
do { \
|
|
|
|
if (res.Failed()) { \
|
|
|
|
nsCString msg; \
|
|
|
|
msg.AppendPrintf("ENSURE_SUCCESS_VOID(%s) failed with " \
|
2015-04-27 16:18:52 +03:00
|
|
|
"result 0x%X", #res, res.ErrorCodeAsInt()); \
|
2013-07-31 12:12:46 +04:00
|
|
|
NS_WARNING(msg.get()); \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
} while(0)
|
|
|
|
|
2012-05-06 05:15:11 +04:00
|
|
|
} // namespace mozilla
|
|
|
|
|
|
|
|
#endif /* mozilla_ErrorResult_h */
|