tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ErrorResult.h (36150B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /**
      8 * A set of structs for tracking exceptions that need to be thrown to JS:
      9 * ErrorResult and IgnoredErrorResult.
     10 *
     11 * Conceptually, these structs represent either success or an exception in the
     12 * process of being thrown.  This means that a failing ErrorResult _must_ be
     13 * handled in one of the following ways before coming off the stack:
     14 *
     15 * 1) Suppressed via SuppressException().
     16 * 2) Converted to a pure nsresult return value via StealNSResult().
     17 * 3) Converted to an actual pending exception on a JSContext via
     18 *    MaybeSetPendingException.
     19 * 4) Converted to an exception JS::Value (probably to then reject a Promise
     20 *    with) via dom::ToJSValue.
     21 *
     22 * An IgnoredErrorResult will automatically do the first of those four things.
     23 */
     24 
     25 #ifndef mozilla_ErrorResult_h
     26 #define mozilla_ErrorResult_h
     27 
     28 #include <new>
     29 #include <utility>
     30 
     31 #include "js/ErrorReport.h"
     32 #include "js/GCAnnotations.h"
     33 #include "js/Value.h"
     34 #include "mozilla/Assertions.h"
     35 #include "mozilla/Attributes.h"
     36 #include "mozilla/Utf8.h"
     37 #include "nsISupportsImpl.h"
     38 #include "nsString.h"
     39 #include "nsTArray.h"
     40 #include "nscore.h"
     41 
     42 namespace IPC {
     43 class Message;
     44 class MessageReader;
     45 class MessageWriter;
     46 template <typename>
     47 struct ParamTraits;
     48 }  // namespace IPC
     49 class PickleIterator;
     50 
     51 namespace mozilla {
     52 
     53 namespace dom {
     54 
     55 class Promise;
     56 
     57 enum ErrNum : uint16_t {
     58 #define MSG_DEF(_name, _argc, _has_context, _exn, _str) _name,
     59 #include "mozilla/dom/Errors.msg"
     60 #undef MSG_DEF
     61  Err_Limit
     62 };
     63 
     64 // Debug-only compile-time table of the number of arguments of each error, for
     65 // use in static_assert.
     66 #if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__))
     67 uint16_t constexpr ErrorFormatNumArgs[] = {
     68 #  define MSG_DEF(_name, _argc, _has_context, _exn, _str) _argc,
     69 #  include "mozilla/dom/Errors.msg"
     70 #  undef MSG_DEF
     71 };
     72 #endif
     73 
     74 // Table of whether various error messages want a context arg.
     75 bool constexpr ErrorFormatHasContext[] = {
     76 #define MSG_DEF(_name, _argc, _has_context, _exn, _str) _has_context,
     77 #include "mozilla/dom/Errors.msg"
     78 #undef MSG_DEF
     79 };
     80 
     81 // Table of the kinds of exceptions error messages will produce.
     82 JSExnType constexpr ErrorExceptionType[] = {
     83 #define MSG_DEF(_name, _argc, _has_context, _exn, _str) _exn,
     84 #include "mozilla/dom/Errors.msg"
     85 #undef MSG_DEF
     86 };
     87 
     88 uint16_t GetErrorArgCount(const ErrNum aErrorNumber);
     89 
     90 namespace binding_detail {
     91 void ThrowErrorMessage(JSContext* aCx, const unsigned aErrorNumber, ...);
     92 }  // namespace binding_detail
     93 
     94 template <ErrNum errorNumber, typename... Ts>
     95 inline bool ThrowErrorMessage(JSContext* aCx, Ts&&... aArgs) {
     96 #if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__))
     97  static_assert(ErrorFormatNumArgs[errorNumber] == sizeof...(aArgs),
     98                "Pass in the right number of arguments");
     99 #endif
    100  binding_detail::ThrowErrorMessage(aCx, static_cast<unsigned>(errorNumber),
    101                                    std::forward<Ts>(aArgs)...);
    102  return false;
    103 }
    104 
    105 template <typename CharT>
    106 struct TStringArrayAppender {
    107  static void Append(nsTArray<nsTString<CharT>>& aArgs, uint16_t aCount) {
    108    MOZ_RELEASE_ASSERT(aCount == 0,
    109                       "Must give at least as many string arguments as are "
    110                       "required by the ErrNum.");
    111  }
    112 
    113  // Allow passing nsAString/nsACString instances for our args.
    114  template <typename... Ts>
    115  static void Append(nsTArray<nsTString<CharT>>& aArgs, uint16_t aCount,
    116                     const nsTSubstring<CharT>& aFirst, Ts&&... aOtherArgs) {
    117    if (aCount == 0) {
    118      MOZ_ASSERT(false,
    119                 "There should not be more string arguments provided than are "
    120                 "required by the ErrNum.");
    121      return;
    122    }
    123    aArgs.AppendElement(aFirst);
    124    Append(aArgs, aCount - 1, std::forward<Ts>(aOtherArgs)...);
    125  }
    126 
    127  // Also allow passing literal instances for our args.
    128  template <int N, typename... Ts>
    129  static void Append(nsTArray<nsTString<CharT>>& aArgs, uint16_t aCount,
    130                     const CharT (&aFirst)[N], Ts&&... aOtherArgs) {
    131    if (aCount == 0) {
    132      MOZ_ASSERT(false,
    133                 "There should not be more string arguments provided than are "
    134                 "required by the ErrNum.");
    135      return;
    136    }
    137    aArgs.AppendElement(nsTLiteralString<CharT>(aFirst));
    138    Append(aArgs, aCount - 1, std::forward<Ts>(aOtherArgs)...);
    139  }
    140 };
    141 
    142 using StringArrayAppender = TStringArrayAppender<char16_t>;
    143 using CStringArrayAppender = TStringArrayAppender<char>;
    144 
    145 }  // namespace dom
    146 
    147 class ErrorResult;
    148 class OOMReporter;
    149 class CopyableErrorResult;
    150 
    151 namespace binding_danger {
    152 
    153 /**
    154 * Templated implementation class for various ErrorResult-like things.  The
    155 * instantiations differ only in terms of their cleanup policies (used in the
    156 * destructor), which they can specify via the template argument.  Note that
    157 * this means it's safe to reinterpret_cast between the instantiations unless
    158 * you plan to invoke the destructor through such a cast pointer.
    159 *
    160 * A cleanup policy consists of two booleans: whether to assert that we've been
    161 * reported or suppressed, and whether to then go ahead and suppress the
    162 * exception.
    163 */
    164 template <typename CleanupPolicy>
    165 class TErrorResult {
    166 public:
    167  TErrorResult()
    168      : mResult(NS_OK)
    169 #ifdef DEBUG
    170        ,
    171        mMightHaveUnreportedJSException(false),
    172        mUnionState(HasNothing)
    173 #endif
    174  {
    175  }
    176 
    177  ~TErrorResult() {
    178    AssertInOwningThread();
    179 
    180    if (CleanupPolicy::assertHandled) {
    181      // Consumers should have called one of MaybeSetPendingException
    182      // (possibly via ToJSValue), StealNSResult, and SuppressException
    183      AssertReportedOrSuppressed();
    184    }
    185 
    186    if (CleanupPolicy::suppress) {
    187      SuppressException();
    188    }
    189 
    190    // And now assert that we're in a good final state.
    191    AssertReportedOrSuppressed();
    192  }
    193 
    194  TErrorResult(TErrorResult&& aRHS)
    195      // Initialize mResult and whatever else we need to default-initialize, so
    196      // the ClearUnionData call in our operator= will do the right thing
    197      // (nothing).
    198      : TErrorResult() {
    199    *this = std::move(aRHS);
    200  }
    201  TErrorResult& operator=(TErrorResult&& aRHS);
    202 
    203  explicit TErrorResult(nsresult aRv) : TErrorResult() { AssignErrorCode(aRv); }
    204 
    205  operator ErrorResult&();
    206  operator const ErrorResult&() const;
    207  operator OOMReporter&();
    208 
    209  // This method is deprecated.  Consumers should Throw*Error with the
    210  // appropriate DOMException name if they are throwing a DOMException.  If they
    211  // have a random nsresult which may or may not correspond to a DOMException
    212  // type, they should consider using an appropriate DOMException with an
    213  // informative message and calling the relevant Throw*Error.
    214  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG Throw(nsresult rv) {
    215    MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
    216    AssignErrorCode(rv);
    217  }
    218 
    219  // Duplicate our current state on the given TErrorResult object.  Any
    220  // existing errors or messages on the target will be suppressed before
    221  // cloning.  Our own error state remains unchanged.
    222  void CloneTo(TErrorResult& aRv) const;
    223 
    224  // Use SuppressException when you want to suppress any exception that might be
    225  // on the TErrorResult.  After this call, the TErrorResult will be back a "no
    226  // exception thrown" state.
    227  void SuppressException();
    228 
    229  // Use StealNSResult() when you want to safely convert the TErrorResult to
    230  // an nsresult that you will then return to a caller.  This will
    231  // SuppressException(), since there will no longer be a way to report it.
    232  nsresult StealNSResult() {
    233    nsresult rv = ErrorCode();
    234    SuppressException();
    235    // Don't propagate out our internal error codes that have special meaning.
    236    if (rv == NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR ||
    237        rv == NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR ||
    238        rv == NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION ||
    239        rv == NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION) {
    240      // What to pick here?
    241      return NS_ERROR_DOM_INVALID_STATE_ERR;
    242    }
    243 
    244    return rv;
    245  }
    246 
    247  // Use MaybeSetPendingException to convert a TErrorResult to a pending
    248  // exception on the given JSContext.  This is the normal "throw an exception"
    249  // codepath.
    250  //
    251  // The return value is false if the TErrorResult represents success, true
    252  // otherwise.  This does mean that in JSAPI method implementations you can't
    253  // just use this as |return rv.MaybeSetPendingException(cx)| (though you could
    254  // |return !rv.MaybeSetPendingException(cx)|), but in practice pretty much any
    255  // consumer would want to do some more work on the success codepath.  So
    256  // instead the way you use this is:
    257  //
    258  //   if (rv.MaybeSetPendingException(cx)) {
    259  //     bail out here
    260  //   }
    261  //   go on to do something useful
    262  //
    263  // The success path is inline, since it should be the common case and we don't
    264  // want to pay the price of a function call in some of the consumers of this
    265  // method in the common case.
    266  //
    267  // Note that a true return value does NOT mean there is now a pending
    268  // exception on aCx, due to uncatchable exceptions.  It should still be
    269  // considered equivalent to a JSAPI failure in terms of what callers should do
    270  // after true is returned.
    271  //
    272  // After this call, the TErrorResult will no longer return true from Failed(),
    273  // since the exception will have moved to the JSContext.
    274  //
    275  // If "context" is not null and our exception has a useful message string, the
    276  // string "%s: ", with the value of "context" replacing %s, will be prepended
    277  // to the message string.  The passed-in string must be ASCII.
    278  [[nodiscard]] bool MaybeSetPendingException(
    279      JSContext* cx, const char* description = nullptr) {
    280    WouldReportJSException();
    281    if (!Failed()) {
    282      return false;
    283    }
    284 
    285    SetPendingException(cx, description);
    286    return true;
    287  }
    288 
    289  // Use StealExceptionFromJSContext to convert a pending exception on a
    290  // JSContext to a TErrorResult.  This function must be called only when a
    291  // JSAPI operation failed.  It assumes that lack of pending exception on the
    292  // JSContext means an uncatchable exception was thrown.
    293  //
    294  // Codepaths that might call this method must call MightThrowJSException even
    295  // if the relevant JSAPI calls do not fail.
    296  //
    297  // When this function returns, JS_IsExceptionPending(cx) will definitely be
    298  // false.
    299  void StealExceptionFromJSContext(JSContext* cx);
    300 
    301  template <dom::ErrNum errorNumber, typename... Ts>
    302  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG
    303  ThrowTypeError(Ts&&... messageArgs) {
    304    static_assert(dom::ErrorExceptionType[errorNumber] == JSEXN_TYPEERR,
    305                  "Throwing a non-TypeError via ThrowTypeError");
    306    ThrowErrorWithMessage<errorNumber>(NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR,
    307                                       std::forward<Ts>(messageArgs)...);
    308  }
    309 
    310  // To be used when throwing a TypeError with a completely custom
    311  // message string that's only used in one spot.
    312  inline void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG
    313  ThrowTypeError(const nsACString& aMessage) {
    314    this->template ThrowTypeError<dom::MSG_ONE_OFF_TYPEERR>(aMessage);
    315  }
    316 
    317  // To be used when throwing a TypeError with a completely custom
    318  // message string that's a string literal that's only used in one spot.
    319  template <int N>
    320  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG
    321  ThrowTypeError(const char (&aMessage)[N]) {
    322    ThrowTypeError(nsLiteralCString(aMessage));
    323  }
    324 
    325  template <dom::ErrNum errorNumber, typename... Ts>
    326  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG
    327  ThrowRangeError(Ts&&... messageArgs) {
    328    static_assert(dom::ErrorExceptionType[errorNumber] == JSEXN_RANGEERR,
    329                  "Throwing a non-RangeError via ThrowRangeError");
    330    ThrowErrorWithMessage<errorNumber>(NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR,
    331                                       std::forward<Ts>(messageArgs)...);
    332  }
    333 
    334  // To be used when throwing a RangeError with a completely custom
    335  // message string that's only used in one spot.
    336  inline void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG
    337  ThrowRangeError(const nsACString& aMessage) {
    338    this->template ThrowRangeError<dom::MSG_ONE_OFF_RANGEERR>(aMessage);
    339  }
    340 
    341  // To be used when throwing a RangeError with a completely custom
    342  // message string that's a string literal that's only used in one spot.
    343  template <int N>
    344  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG
    345  ThrowRangeError(const char (&aMessage)[N]) {
    346    ThrowRangeError(nsLiteralCString(aMessage));
    347  }
    348 
    349  bool IsErrorWithMessage() const {
    350    return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR ||
    351           ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR;
    352  }
    353 
    354  // Facilities for throwing a preexisting JS exception value via this
    355  // TErrorResult.  The contract is that any code which might end up calling
    356  // ThrowJSException() or StealExceptionFromJSContext() must call
    357  // MightThrowJSException() even if no exception is being thrown.  Code that
    358  // conditionally calls ToJSValue on this TErrorResult only if Failed() must
    359  // first call WouldReportJSException even if this TErrorResult has not failed.
    360  //
    361  // The exn argument to ThrowJSException can be in any compartment.  It does
    362  // not have to be in the compartment of cx.  If someone later uses it, they
    363  // will wrap it into whatever compartment they're working in, as needed.
    364  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG
    365  ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn);
    366  bool IsJSException() const {
    367    return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION;
    368  }
    369 
    370  // Facilities for throwing DOMExceptions of whatever type a spec calls for.
    371  // If an empty message string is passed to one of these Throw*Error functions,
    372  // the default message string for the relevant type of DOMException will be
    373  // used.  The passed-in string must be UTF-8.
    374 #define DOMEXCEPTION(name, err)                                \
    375  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG Throw##name( \
    376      const nsACString& aMessage) {                            \
    377    ThrowDOMException(err, aMessage);                          \
    378  }                                                            \
    379                                                               \
    380  template <int N>                                             \
    381  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG Throw##name( \
    382      const char (&aMessage)[N]) {                             \
    383    ThrowDOMException(err, aMessage);                          \
    384  }
    385 
    386 #include "mozilla/dom/DOMExceptionNames.h"
    387 
    388 #undef DOMEXCEPTION
    389 
    390  bool IsDOMException() const {
    391    return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION;
    392  }
    393 
    394  // Flag on the TErrorResult that whatever needs throwing has been
    395  // thrown on the JSContext already and we should not mess with it.
    396  // If nothing was thrown, this becomes an uncatchable exception.
    397  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG
    398  NoteJSContextException(JSContext* aCx);
    399 
    400  // Check whether the TErrorResult says to just throw whatever is on
    401  // the JSContext already.
    402  bool IsJSContextException() {
    403    return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT;
    404  }
    405 
    406  // Support for uncatchable exceptions.
    407  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG ThrowUncatchableException() {
    408    Throw(NS_ERROR_UNCATCHABLE_EXCEPTION);
    409  }
    410  bool IsUncatchableException() const {
    411    return ErrorCode() == NS_ERROR_UNCATCHABLE_EXCEPTION;
    412  }
    413 
    414  void MOZ_ALWAYS_INLINE MightThrowJSException() {
    415 #ifdef DEBUG
    416    mMightHaveUnreportedJSException = true;
    417 #endif
    418  }
    419  void MOZ_ALWAYS_INLINE WouldReportJSException() {
    420 #ifdef DEBUG
    421    mMightHaveUnreportedJSException = false;
    422 #endif
    423  }
    424 
    425  // In the future, we can add overloads of Throw that take more
    426  // interesting things, like strings or DOM exception types or
    427  // something if desired.
    428 
    429  // Backwards-compat to make conversion simpler.  We don't call
    430  // Throw() here because people can easily pass success codes to
    431  // this.  This operator is deprecated and ideally shouldn't be used.
    432  void operator=(nsresult rv) { AssignErrorCode(rv); }
    433 
    434  bool Failed() const { return NS_FAILED(mResult); }
    435 
    436  bool ErrorCodeIs(nsresult rv) const { return mResult == rv; }
    437 
    438  // For use in logging ONLY.
    439  uint32_t ErrorCodeAsInt() const { return static_cast<uint32_t>(ErrorCode()); }
    440 
    441  bool operator==(const ErrorResult& aRight) const;
    442 
    443 protected:
    444  nsresult ErrorCode() const { return mResult; }
    445 
    446  // Helper methods for throwing DOMExceptions, for now.  We can try to get rid
    447  // of these once EME code is fixed to not use them and we decouple
    448  // DOMExceptions from nsresult.
    449  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG
    450  ThrowDOMException(nsresult rv, const nsACString& message);
    451 
    452  // Same thing, but using a string literal.
    453  template <int N>
    454  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG
    455  ThrowDOMException(nsresult rv, const char (&aMessage)[N]) {
    456    ThrowDOMException(rv, nsLiteralCString(aMessage));
    457  }
    458 
    459  // Allow Promise to call the above methods when it really needs to.
    460  // Unfortunately, we can't have the definition of Promise here, so can't mark
    461  // just it's MaybeRejectWithDOMException method as a friend.  In any case,
    462  // hopefully it's all temporary until we sort out the EME bits.
    463  friend class dom::Promise;
    464 
    465  // Implementation of MaybeSetPendingException for the case when we're a
    466  // failure result.  See documentation of MaybeSetPendingException for the
    467  // "context" argument.
    468  void SetPendingException(JSContext* cx, const char* context);
    469 
    470 private:
    471 #ifdef DEBUG
    472  enum UnionState {
    473    HasMessage,
    474    HasDOMExceptionInfo,
    475    HasJSException,
    476    HasNothing
    477  };
    478 #endif  // DEBUG
    479 
    480  friend struct IPC::ParamTraits<TErrorResult>;
    481  friend struct IPC::ParamTraits<ErrorResult>;
    482  void SerializeMessage(IPC::MessageWriter* aWriter) const;
    483  bool DeserializeMessage(IPC::MessageReader* aReader);
    484 
    485  void SerializeDOMExceptionInfo(IPC::MessageWriter* aWriter) const;
    486  bool DeserializeDOMExceptionInfo(IPC::MessageReader* aReader);
    487 
    488  // Helper method that creates a new Message for this TErrorResult,
    489  // and returns the arguments array from that Message.
    490  nsTArray<nsCString>& CreateErrorMessageHelper(const dom::ErrNum errorNumber,
    491                                                nsresult errorType);
    492 
    493  // Helper method to replace invalid UTF-8 characters with the replacement
    494  // character. aValidUpTo is the number of characters that are known to be
    495  // valid. The string might be truncated if we encounter an OOM error.
    496  static void EnsureUTF8Validity(nsCString& aValue, size_t aValidUpTo);
    497 
    498  template <dom::ErrNum errorNumber, typename... Ts>
    499  void ThrowErrorWithMessage(nsresult errorType, Ts&&... messageArgs) {
    500 #if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__))
    501    static_assert(dom::ErrorFormatNumArgs[errorNumber] ==
    502                      sizeof...(messageArgs) +
    503                          int(dom::ErrorFormatHasContext[errorNumber]),
    504                  "Pass in the right number of arguments");
    505 #endif
    506 
    507    ClearUnionData();
    508 
    509    nsTArray<nsCString>& messageArgsArray =
    510        CreateErrorMessageHelper(errorNumber, errorType);
    511    uint16_t argCount = dom::GetErrorArgCount(errorNumber);
    512    if (dom::ErrorFormatHasContext[errorNumber]) {
    513      // Insert an empty string arg at the beginning and reduce our arg count to
    514      // still be appended accordingly.
    515      MOZ_ASSERT(argCount > 0,
    516                 "Must have at least one arg if we have a context!");
    517      MOZ_ASSERT(messageArgsArray.Length() == 0,
    518                 "Why do we already have entries in the array?");
    519      --argCount;
    520      messageArgsArray.AppendElement();
    521    }
    522    dom::CStringArrayAppender::Append(messageArgsArray, argCount,
    523                                      std::forward<Ts>(messageArgs)...);
    524    for (nsCString& arg : messageArgsArray) {
    525      size_t validUpTo = Utf8ValidUpTo(arg);
    526      if (validUpTo != arg.Length()) {
    527        EnsureUTF8Validity(arg, validUpTo);
    528      }
    529    }
    530 #ifdef DEBUG
    531    mUnionState = HasMessage;
    532 #endif  // DEBUG
    533  }
    534 
    535  MOZ_ALWAYS_INLINE void AssertInOwningThread() const {
    536 #ifdef DEBUG
    537    if (CleanupPolicy::assertSameThread) {
    538      NS_ASSERT_OWNINGTHREAD(TErrorResult);
    539    }
    540 #endif
    541  }
    542 
    543  void AssignErrorCode(nsresult aRv) {
    544    MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR,
    545               "Use ThrowTypeError()");
    546    MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR,
    547               "Use ThrowRangeError()");
    548    MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
    549    MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION,
    550               "Use ThrowJSException()");
    551    MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
    552    MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION,
    553               "Use Throw*Error for the appropriate DOMException name");
    554    MOZ_ASSERT(!IsDOMException(), "Don't overwrite DOM exceptions");
    555    MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS,
    556               "May need to bring back ThrowNotEnoughArgsError");
    557    MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT,
    558               "Use NoteJSContextException");
    559    mResult = aRv;
    560  }
    561 
    562  void ClearMessage();
    563  void ClearDOMExceptionInfo();
    564 
    565  // ClearUnionData will try to clear the data in our mExtra union.  After this
    566  // the union may be in an uninitialized state (e.g. mMessage or
    567  // mDOMExceptionInfo may point to deleted memory, or mJSException may be a
    568  // JS::Value containing an invalid gcthing) and the caller must either
    569  // reinitialize it or change mResult to something that will not involve us
    570  // touching the union anymore.
    571  void ClearUnionData();
    572 
    573  // Methods for setting various specific kinds of pending exceptions.  See
    574  // documentation of MaybeSetPendingException for the "context" argument.
    575  void SetPendingExceptionWithMessage(JSContext* cx, const char* context);
    576  void SetPendingJSException(JSContext* cx);
    577  void SetPendingDOMException(JSContext* cx, const char* context);
    578  void SetPendingGenericErrorException(JSContext* cx);
    579 
    580  MOZ_ALWAYS_INLINE void AssertReportedOrSuppressed() {
    581    MOZ_ASSERT(!Failed());
    582    MOZ_ASSERT(!mMightHaveUnreportedJSException);
    583    MOZ_ASSERT(mUnionState == HasNothing);
    584  }
    585 
    586  // Special values of mResult:
    587  // NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR -- ThrowTypeError() called on us.
    588  // NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR -- ThrowRangeError() called on us.
    589  // NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION -- ThrowJSException() called
    590  //                                               on us.
    591  // NS_ERROR_UNCATCHABLE_EXCEPTION -- ThrowUncatchableException called on us.
    592  // NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION -- ThrowDOMException() called
    593  //                                               on us.
    594  nsresult mResult;
    595 
    596  struct Message;
    597  struct DOMExceptionInfo;
    598  union Extra {
    599    // mMessage is set by ThrowErrorWithMessage and reported (and deallocated)
    600    // by SetPendingExceptionWithMessage.
    601    MOZ_INIT_OUTSIDE_CTOR
    602    Message* mMessage;  // valid when IsErrorWithMessage()
    603 
    604    // mJSException is set (and rooted) by ThrowJSException and reported (and
    605    // unrooted) by SetPendingJSException.
    606    MOZ_INIT_OUTSIDE_CTOR
    607    JS::Value mJSException;  // valid when IsJSException()
    608 
    609    // mDOMExceptionInfo is set by ThrowDOMException and reported (and
    610    // deallocated) by SetPendingDOMException.
    611    MOZ_INIT_OUTSIDE_CTOR
    612    DOMExceptionInfo* mDOMExceptionInfo;  // valid when IsDOMException()
    613 
    614    // |mJSException| has a non-trivial constructor and therefore MUST be
    615    // placement-new'd into existence.
    616    MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
    617    Extra() {}  // NOLINT
    618    MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
    619  } mExtra;
    620 
    621  Message* InitMessage(Message* aMessage) {
    622    // The |new| here switches the active arm of |mExtra|, from the compiler's
    623    // point of view.  Mere assignment *won't* necessarily do the right thing!
    624    new (&mExtra.mMessage) Message*(aMessage);
    625    return mExtra.mMessage;
    626  }
    627 
    628  JS::Value& InitJSException() {
    629    // The |new| here switches the active arm of |mExtra|, from the compiler's
    630    // point of view.  Mere assignment *won't* necessarily do the right thing!
    631    new (&mExtra.mJSException) JS::Value();  // sets to undefined
    632    return mExtra.mJSException;
    633  }
    634 
    635  DOMExceptionInfo* InitDOMExceptionInfo(DOMExceptionInfo* aDOMExceptionInfo) {
    636    // The |new| here switches the active arm of |mExtra|, from the compiler's
    637    // point of view.  Mere assignment *won't* necessarily do the right thing!
    638    new (&mExtra.mDOMExceptionInfo) DOMExceptionInfo*(aDOMExceptionInfo);
    639    return mExtra.mDOMExceptionInfo;
    640  }
    641 
    642 #ifdef DEBUG
    643  // Used to keep track of codepaths that might throw JS exceptions,
    644  // for assertion purposes.
    645  bool mMightHaveUnreportedJSException;
    646 
    647  // Used to keep track of what's stored in our union right now.  Note
    648  // that this may be set to HasNothing even if our mResult suggests
    649  // we should have something, if we have already cleaned up the
    650  // something.
    651  UnionState mUnionState;
    652 
    653  // The thread that created this TErrorResult
    654  NS_DECL_OWNINGTHREAD;
    655 #endif
    656 
    657  // Not to be implemented, to make sure people always pass this by
    658  // reference, not by value.
    659  TErrorResult(const TErrorResult&) = delete;
    660  void operator=(const TErrorResult&) = delete;
    661 } JS_HAZ_ROOTED;
    662 
    663 struct JustAssertCleanupPolicy {
    664  static const bool assertHandled = true;
    665  static const bool suppress = false;
    666  static const bool assertSameThread = true;
    667 };
    668 
    669 struct AssertAndSuppressCleanupPolicy {
    670  static const bool assertHandled = true;
    671  static const bool suppress = true;
    672  static const bool assertSameThread = true;
    673 };
    674 
    675 struct JustSuppressCleanupPolicy {
    676  static const bool assertHandled = false;
    677  static const bool suppress = true;
    678  static const bool assertSameThread = true;
    679 };
    680 
    681 struct ThreadSafeJustSuppressCleanupPolicy {
    682  static const bool assertHandled = false;
    683  static const bool suppress = true;
    684  static const bool assertSameThread = false;
    685 };
    686 
    687 }  // namespace binding_danger
    688 
    689 // A class people should normally use on the stack when they plan to actually
    690 // do something with the exception.
    691 class ErrorResult : public binding_danger::TErrorResult<
    692                        binding_danger::AssertAndSuppressCleanupPolicy> {
    693  typedef binding_danger::TErrorResult<
    694      binding_danger::AssertAndSuppressCleanupPolicy>
    695      BaseErrorResult;
    696 
    697 public:
    698  ErrorResult() = default;
    699 
    700  ErrorResult(ErrorResult&& aRHS) = default;
    701  // Explicitly allow moving out of a CopyableErrorResult into an ErrorResult.
    702  // This is implemented below so it can see the definition of
    703  // CopyableErrorResult.
    704  inline explicit ErrorResult(CopyableErrorResult&& aRHS);
    705 
    706  explicit ErrorResult(nsresult aRv) : BaseErrorResult(aRv) {}
    707 
    708  // This operator is deprecated and ideally shouldn't be used.
    709  void operator=(nsresult rv) { BaseErrorResult::operator=(rv); }
    710 
    711  ErrorResult& operator=(ErrorResult&& aRHS) = default;
    712 
    713  // Not to be implemented, to make sure people always pass this by
    714  // reference, not by value.
    715  ErrorResult(const ErrorResult&) = delete;
    716  ErrorResult& operator=(const ErrorResult&) = delete;
    717 };
    718 
    719 template <typename CleanupPolicy>
    720 binding_danger::TErrorResult<CleanupPolicy>::operator ErrorResult&() {
    721  return *static_cast<ErrorResult*>(
    722      reinterpret_cast<TErrorResult<AssertAndSuppressCleanupPolicy>*>(this));
    723 }
    724 
    725 template <typename CleanupPolicy>
    726 binding_danger::TErrorResult<CleanupPolicy>::operator const ErrorResult&()
    727    const {
    728  return *static_cast<const ErrorResult*>(
    729      reinterpret_cast<const TErrorResult<AssertAndSuppressCleanupPolicy>*>(
    730          this));
    731 }
    732 
    733 // A class for use when an ErrorResult should just automatically be ignored.
    734 // This doesn't inherit from ErrorResult so we don't make two separate calls to
    735 // SuppressException.
    736 class IgnoredErrorResult : public binding_danger::TErrorResult<
    737                               binding_danger::JustSuppressCleanupPolicy> {};
    738 
    739 // A class for use when an ErrorResult needs to be copied to a lambda, into
    740 // an IPDL structure, etc.  Since this will often involve crossing thread
    741 // boundaries this class will assert if you try to copy a JS exception.  Only
    742 // use this if you are propagating internal errors.  In general its best
    743 // to use ErrorResult by default and only convert to a CopyableErrorResult when
    744 // you need it.
    745 class CopyableErrorResult
    746    : public binding_danger::TErrorResult<
    747          binding_danger::ThreadSafeJustSuppressCleanupPolicy> {
    748  typedef binding_danger::TErrorResult<
    749      binding_danger::ThreadSafeJustSuppressCleanupPolicy>
    750      BaseErrorResult;
    751 
    752 public:
    753  CopyableErrorResult() = default;
    754 
    755  explicit CopyableErrorResult(const ErrorResult& aRight) : BaseErrorResult() {
    756    auto val = reinterpret_cast<const CopyableErrorResult&>(aRight);
    757    operator=(val);
    758  }
    759 
    760  CopyableErrorResult(CopyableErrorResult&& aRHS) = default;
    761 
    762  explicit CopyableErrorResult(ErrorResult&& aRHS) : BaseErrorResult() {
    763    // We must not copy JS exceptions since it can too easily lead to
    764    // off-thread use.  Assert this and fall back to a generic error
    765    // in release builds.
    766    MOZ_DIAGNOSTIC_ASSERT(
    767        !aRHS.IsJSException(),
    768        "Attempt to copy from ErrorResult with a JS exception value.");
    769    if (aRHS.IsJSException()) {
    770      aRHS.SuppressException();
    771      Throw(NS_ERROR_FAILURE);
    772    } else {
    773      // We could avoid the cast here if we had a move constructor on
    774      // TErrorResult templated on the cleanup policy type, but then we'd have
    775      // to either inline the impl or force all possible instantiations or
    776      // something.  This is a bit simpler, and not that different from our copy
    777      // constructor.
    778      auto val = reinterpret_cast<CopyableErrorResult&&>(aRHS);
    779      operator=(val);
    780    }
    781  }
    782 
    783  explicit CopyableErrorResult(nsresult aRv) : BaseErrorResult(aRv) {}
    784 
    785  // This operator is deprecated and ideally shouldn't be used.
    786  void operator=(nsresult rv) { BaseErrorResult::operator=(rv); }
    787 
    788  CopyableErrorResult& operator=(CopyableErrorResult&& aRHS) = default;
    789 
    790  CopyableErrorResult(const CopyableErrorResult& aRight) : BaseErrorResult() {
    791    operator=(aRight);
    792  }
    793 
    794  CopyableErrorResult& operator=(const CopyableErrorResult& aRight) {
    795    // We must not copy JS exceptions since it can too easily lead to
    796    // off-thread use.  Assert this and fall back to a generic error
    797    // in release builds.
    798    MOZ_DIAGNOSTIC_ASSERT(
    799        !IsJSException(),
    800        "Attempt to copy to ErrorResult with a JS exception value.");
    801    MOZ_DIAGNOSTIC_ASSERT(
    802        !aRight.IsJSException(),
    803        "Attempt to copy from ErrorResult with a JS exception value.");
    804    if (aRight.IsJSException()) {
    805      SuppressException();
    806      Throw(NS_ERROR_FAILURE);
    807    } else {
    808      aRight.CloneTo(*this);
    809    }
    810    return *this;
    811  }
    812 
    813  // Disallow implicit converstion to non-const ErrorResult&, because that would
    814  // allow people to throw exceptions on us while bypassing our checks for JS
    815  // exceptions.
    816  operator ErrorResult&() = delete;
    817 
    818  // Allow conversion to ErrorResult&& so we can move out of ourselves into
    819  // an ErrorResult.
    820  operator ErrorResult&&() && {
    821    auto* val = reinterpret_cast<ErrorResult*>(this);
    822    return std::move(*val);
    823  }
    824 };
    825 
    826 inline ErrorResult::ErrorResult(CopyableErrorResult&& aRHS)
    827    : ErrorResult(reinterpret_cast<ErrorResult&&>(aRHS)) {}
    828 
    829 namespace dom::binding_detail {
    830 
    831 enum class ErrorFor {
    832  getter,
    833  setter,
    834 };
    835 
    836 template <ErrorFor ErrorType>
    837 struct ErrorDescriptionFor {
    838  const char* mInterface;
    839  const char* mMember;
    840 };
    841 
    842 class FastErrorResult : public mozilla::binding_danger::TErrorResult<
    843                            mozilla::binding_danger::JustAssertCleanupPolicy> {
    844 public:
    845  using TErrorResult::MaybeSetPendingException;
    846 
    847  template <ErrorFor ErrorType>
    848  [[nodiscard]] bool MaybeSetPendingException(
    849      JSContext* aCx, const ErrorDescriptionFor<ErrorType>& aDescription) {
    850    WouldReportJSException();
    851    if (!Failed()) {
    852      return false;
    853    }
    854 
    855    nsAutoCString description(aDescription.mInterface);
    856    description.Append('.');
    857    description.Append(aDescription.mMember);
    858    if constexpr (ErrorType == ErrorFor::getter) {
    859      description.AppendLiteral(" getter");
    860    } else {
    861      static_assert(ErrorType == ErrorFor::setter);
    862      description.AppendLiteral(" setter");
    863    }
    864    SetPendingException(aCx, description.get());
    865    return true;
    866  }
    867 };
    868 
    869 }  // namespace dom::binding_detail
    870 
    871 // We want an OOMReporter class that has the following properties:
    872 //
    873 // 1) Can be cast to from any ErrorResult-like type.
    874 // 2) Has a fast destructor (because we want to use it from bindings).
    875 // 3) Won't be randomly instantiated by non-binding code (because the fast
    876 //    destructor is not so safe).
    877 // 4) Doesn't look ugly on the callee side (e.g. isn't in the binding_detail or
    878 //    binding_danger namespace).
    879 //
    880 // We do this by creating a class that can't actually be constructed directly
    881 // but can be cast to from ErrorResult-like types, both implicitly and
    882 // explicitly.
    883 class OOMReporter : private dom::binding_detail::FastErrorResult {
    884 public:
    885  void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG ReportOOM() {
    886    Throw(NS_ERROR_OUT_OF_MEMORY);
    887  }
    888 
    889  // A method that turns a FastErrorResult into an OOMReporter, which we use in
    890  // codegen to ensure that callees don't take an ErrorResult when they should
    891  // only be taking an OOMReporter.  The idea is that we can then just have a
    892  // FastErrorResult on the stack and call this to produce the thing to pass to
    893  // callees.
    894  static OOMReporter& From(FastErrorResult& aRv) { return aRv; }
    895 
    896 private:
    897  // TErrorResult is a friend so its |operator OOMReporter&()| can work.
    898  template <typename CleanupPolicy>
    899  friend class binding_danger::TErrorResult;
    900 
    901  OOMReporter() : dom::binding_detail::FastErrorResult() {}
    902 };
    903 
    904 template <typename CleanupPolicy>
    905 binding_danger::TErrorResult<CleanupPolicy>::operator OOMReporter&() {
    906  return *static_cast<OOMReporter*>(
    907      reinterpret_cast<TErrorResult<JustAssertCleanupPolicy>*>(this));
    908 }
    909 
    910 // A class for use when an ErrorResult should just automatically be
    911 // ignored.  This is designed to be passed as a temporary only, like
    912 // so:
    913 //
    914 //    foo->Bar(IgnoreErrors());
    915 class MOZ_TEMPORARY_CLASS IgnoreErrors {
    916 public:
    917  operator ErrorResult&() && { return mInner; }
    918  operator OOMReporter&() && { return mInner; }
    919 
    920 private:
    921  // We don't use an ErrorResult member here so we don't make two separate calls
    922  // to SuppressException (one from us, one from the ErrorResult destructor
    923  // after asserting).
    924  binding_danger::TErrorResult<binding_danger::JustSuppressCleanupPolicy>
    925      mInner;
    926 } JS_HAZ_ROOTED;
    927 
    928 /******************************************************************************
    929 ** Macros for checking results
    930 ******************************************************************************/
    931 
    932 #define RETURN_NSRESULT_ON_FAILURE(res)                                        \
    933  do {                                                                         \
    934    (res).WouldReportJSException();                                            \
    935    if ((res).Failed()) {                                                      \
    936      NS_WARNING(nsPrintfCString(                                              \
    937                     "RETURN_NSRESULT_ON_FAILURE(%s) failed with result 0x%X", \
    938                     #res, (res).ErrorCodeAsInt())                             \
    939                     .get());                                                  \
    940      return (res).StealNSResult();                                            \
    941    }                                                                          \
    942  } while (0)
    943 
    944 }  // namespace mozilla
    945 
    946 #endif /* mozilla_ErrorResult_h */