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