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 "js/GCAnnotations.h"
31 #include "js/Value.h"
32 #include "nscore.h"
33 #include "nsString.h"
34 #include "mozilla/Assertions.h"
35 #include "mozilla/Attributes.h"
36 #include "mozilla/Move.h"
37 #include "nsTArray.h"
38 #include "nsISupportsImpl.h"
39 
40 namespace IPC {
41 class Message;
42 template <typename>
43 struct ParamTraits;
44 }  // namespace IPC
45 class PickleIterator;
46 
47 namespace mozilla {
48 
49 namespace dom {
50 
51 enum ErrNum {
52 #define MSG_DEF(_name, _argc, _exn, _str) _name,
53 #include "mozilla/dom/Errors.msg"
54 #undef MSG_DEF
55   Err_Limit
56 };
57 
58 // Debug-only compile-time table of the number of arguments of each error, for
59 // use in static_assert.
60 #if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__))
61 uint16_t constexpr ErrorFormatNumArgs[] = {
62 #define MSG_DEF(_name, _argc, _exn, _str) _argc,
63 #include "mozilla/dom/Errors.msg"
64 #undef MSG_DEF
65 };
66 #endif
67 
68 uint16_t GetErrorArgCount(const ErrNum aErrorNumber);
69 
70 namespace binding_detail {
71 void ThrowErrorMessage(JSContext* aCx, const unsigned aErrorNumber, ...);
72 }  // namespace binding_detail
73 
74 template <typename... Ts>
ThrowErrorMessage(JSContext * aCx,const ErrNum aErrorNumber,Ts &&...aArgs)75 inline bool ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber,
76                               Ts&&... aArgs) {
77   binding_detail::ThrowErrorMessage(aCx, static_cast<unsigned>(aErrorNumber),
78                                     mozilla::Forward<Ts>(aArgs)...);
79   return false;
80 }
81 
82 struct StringArrayAppender {
AppendStringArrayAppender83   static void Append(nsTArray<nsString>& aArgs, uint16_t aCount) {
84     MOZ_RELEASE_ASSERT(aCount == 0,
85                        "Must give at least as many string arguments as are "
86                        "required by the ErrNum.");
87   }
88 
89   template <typename... Ts>
AppendStringArrayAppender90   static void Append(nsTArray<nsString>& aArgs, uint16_t aCount,
91                      const nsAString& aFirst, Ts&&... aOtherArgs) {
92     if (aCount == 0) {
93       MOZ_ASSERT(false,
94                  "There should not be more string arguments provided than are "
95                  "required by the ErrNum.");
96       return;
97     }
98     aArgs.AppendElement(aFirst);
99     Append(aArgs, aCount - 1, Forward<Ts>(aOtherArgs)...);
100   }
101 };
102 
103 }  // namespace dom
104 
105 class ErrorResult;
106 class OOMReporter;
107 
108 namespace binding_danger {
109 
110 /**
111  * Templated implementation class for various ErrorResult-like things.  The
112  * instantiations differ only in terms of their cleanup policies (used in the
113  * destructor), which they can specify via the template argument.  Note that
114  * this means it's safe to reinterpret_cast between the instantiations unless
115  * you plan to invoke the destructor through such a cast pointer.
116  *
117  * A cleanup policy consists of two booleans: whether to assert that we've been
118  * reported or suppressed, and whether to then go ahead and suppress the
119  * exception.
120  */
121 template <typename CleanupPolicy>
122 class TErrorResult {
123  public:
TErrorResult()124   TErrorResult()
125       : mResult(NS_OK)
126 #ifdef DEBUG
127         ,
128         mMightHaveUnreportedJSException(false),
129         mUnionState(HasNothing)
130 #endif
131   {
132   }
133 
~TErrorResult()134   ~TErrorResult() {
135     AssertInOwningThread();
136 
137     if (CleanupPolicy::assertHandled) {
138       // Consumers should have called one of MaybeSetPendingException
139       // (possibly via ToJSValue), StealNSResult, and SuppressException
140       AssertReportedOrSuppressed();
141     }
142 
143     if (CleanupPolicy::suppress) {
144       SuppressException();
145     }
146 
147     // And now assert that we're in a good final state.
148     AssertReportedOrSuppressed();
149   }
150 
TErrorResult(TErrorResult && aRHS)151   TErrorResult(TErrorResult&& aRHS)
152       // Initialize mResult and whatever else we need to default-initialize, so
153       // the ClearUnionData call in our operator= will do the right thing
154       // (nothing).
155       : TErrorResult() {
156     *this = Move(aRHS);
157   }
158   TErrorResult& operator=(TErrorResult&& aRHS);
159 
TErrorResult(nsresult aRv)160   explicit TErrorResult(nsresult aRv) : TErrorResult() { AssignErrorCode(aRv); }
161 
162   operator ErrorResult&();
163   operator OOMReporter&();
164 
Throw(nsresult rv)165   void MOZ_MUST_RETURN_FROM_CALLER Throw(nsresult rv) {
166     MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
167     AssignErrorCode(rv);
168   }
169 
170   // This method acts identically to the `Throw` method, however, it does not
171   // have the MOZ_MUST_RETURN_FROM_CALLER static analysis annotation. It is
172   // intended to be used in situations when additional work needs to be
173   // performed in the calling function after the Throw method is called.
174   //
175   // In general you should prefer using `Throw`, and returning after an error,
176   // for example:
177   //
178   //   if (condition) {
179   //     aRv.Throw(NS_ERROR_FAILURE);
180   //     return;
181   //   }
182   //
183   // or
184   //
185   //   if (condition) {
186   //     aRv.Throw(NS_ERROR_FAILURE);
187   //   }
188   //   return;
189   //
190   // However, if you need to do some other work after throwing, such as:
191   //
192   //   if (condition) {
193   //     aRv.ThrowWithCustomCleanup(NS_ERROR_FAILURE);
194   //   }
195   //   // Do some important clean-up work which couldn't happen earlier.
196   //   // We want to do this clean-up work in both the success and failure
197   //   cases. CleanUpImportantState(); return;
198   //
199   // Then you'll need to use ThrowWithCustomCleanup to get around the static
200   // analysis, which would complain that you are doing work after the call to
201   // `Throw()`.
ThrowWithCustomCleanup(nsresult rv)202   void ThrowWithCustomCleanup(nsresult rv) { Throw(rv); }
203 
204   // Duplicate our current state on the given TErrorResult object.  Any
205   // existing errors or messages on the target will be suppressed before
206   // cloning.  Our own error state remains unchanged.
207   void CloneTo(TErrorResult& aRv) const;
208 
209   // Use SuppressException when you want to suppress any exception that might be
210   // on the TErrorResult.  After this call, the TErrorResult will be back a "no
211   // exception thrown" state.
212   void SuppressException();
213 
214   // Use StealNSResult() when you want to safely convert the TErrorResult to
215   // an nsresult that you will then return to a caller.  This will
216   // SuppressException(), since there will no longer be a way to report it.
StealNSResult()217   nsresult StealNSResult() {
218     nsresult rv = ErrorCode();
219     SuppressException();
220     // Don't propagate out our internal error codes that have special meaning.
221     if (rv == NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR ||
222         rv == NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR ||
223         rv == NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION ||
224         rv == NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION) {
225       // What to pick here?
226       return NS_ERROR_DOM_INVALID_STATE_ERR;
227     }
228 
229     return rv;
230   }
231 
232   // Use MaybeSetPendingException to convert a TErrorResult to a pending
233   // exception on the given JSContext.  This is the normal "throw an exception"
234   // codepath.
235   //
236   // The return value is false if the TErrorResult represents success, true
237   // otherwise.  This does mean that in JSAPI method implementations you can't
238   // just use this as |return rv.MaybeSetPendingException(cx)| (though you could
239   // |return !rv.MaybeSetPendingException(cx)|), but in practice pretty much any
240   // consumer would want to do some more work on the success codepath.  So
241   // instead the way you use this is:
242   //
243   //   if (rv.MaybeSetPendingException(cx)) {
244   //     bail out here
245   //   }
246   //   go on to do something useful
247   //
248   // The success path is inline, since it should be the common case and we don't
249   // want to pay the price of a function call in some of the consumers of this
250   // method in the common case.
251   //
252   // Note that a true return value does NOT mean there is now a pending
253   // exception on aCx, due to uncatchable exceptions.  It should still be
254   // considered equivalent to a JSAPI failure in terms of what callers should do
255   // after true is returned.
256   //
257   // After this call, the TErrorResult will no longer return true from Failed(),
258   // since the exception will have moved to the JSContext.
259   MOZ_MUST_USE
MaybeSetPendingException(JSContext * cx)260   bool MaybeSetPendingException(JSContext* cx) {
261     WouldReportJSException();
262     if (!Failed()) {
263       return false;
264     }
265 
266     SetPendingException(cx);
267     return true;
268   }
269 
270   // Use StealExceptionFromJSContext to convert a pending exception on a
271   // JSContext to a TErrorResult.  This function must be called only when a
272   // JSAPI operation failed.  It assumes that lack of pending exception on the
273   // JSContext means an uncatchable exception was thrown.
274   //
275   // Codepaths that might call this method must call MightThrowJSException even
276   // if the relevant JSAPI calls do not fail.
277   //
278   // When this function returns, JS_IsExceptionPending(cx) will definitely be
279   // false.
280   void StealExceptionFromJSContext(JSContext* cx);
281 
282   template <dom::ErrNum errorNumber, typename... Ts>
ThrowTypeError(Ts &&...messageArgs)283   void ThrowTypeError(Ts&&... messageArgs) {
284     ThrowErrorWithMessage<errorNumber>(NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR,
285                                        Forward<Ts>(messageArgs)...);
286   }
287 
288   template <dom::ErrNum errorNumber, typename... Ts>
ThrowRangeError(Ts &&...messageArgs)289   void ThrowRangeError(Ts&&... messageArgs) {
290     ThrowErrorWithMessage<errorNumber>(NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR,
291                                        Forward<Ts>(messageArgs)...);
292   }
293 
IsErrorWithMessage()294   bool IsErrorWithMessage() const {
295     return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR ||
296            ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR;
297   }
298 
299   // Facilities for throwing a preexisting JS exception value via this
300   // TErrorResult.  The contract is that any code which might end up calling
301   // ThrowJSException() or StealExceptionFromJSContext() must call
302   // MightThrowJSException() even if no exception is being thrown.  Code that
303   // conditionally calls ToJSValue on this TErrorResult only if Failed() must
304   // first call WouldReportJSException even if this TErrorResult has not failed.
305   //
306   // The exn argument to ThrowJSException can be in any compartment.  It does
307   // not have to be in the compartment of cx.  If someone later uses it, they
308   // will wrap it into whatever compartment they're working in, as needed.
309   void ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn);
IsJSException()310   bool IsJSException() const {
311     return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION;
312   }
313 
314   // Facilities for throwing a DOMException.  If an empty message string is
315   // passed to ThrowDOMException, the default message string for the given
316   // nsresult will be used.  The passed-in string must be UTF-8.  The nsresult
317   // passed in must be one we create DOMExceptions for; otherwise you may get an
318   // XPConnect Exception.
319   void ThrowDOMException(nsresult rv,
320                          const nsACString& message = EmptyCString());
IsDOMException()321   bool IsDOMException() const {
322     return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION;
323   }
324 
325   // Flag on the TErrorResult that whatever needs throwing has been
326   // thrown on the JSContext already and we should not mess with it.
327   // If nothing was thrown, this becomes an uncatchable exception.
328   void NoteJSContextException(JSContext* aCx);
329 
330   // Check whether the TErrorResult says to just throw whatever is on
331   // the JSContext already.
IsJSContextException()332   bool IsJSContextException() {
333     return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT;
334   }
335 
336   // Support for uncatchable exceptions.
ThrowUncatchableException()337   void MOZ_MUST_RETURN_FROM_CALLER ThrowUncatchableException() {
338     Throw(NS_ERROR_UNCATCHABLE_EXCEPTION);
339   }
IsUncatchableException()340   bool IsUncatchableException() const {
341     return ErrorCode() == NS_ERROR_UNCATCHABLE_EXCEPTION;
342   }
343 
MightThrowJSException()344   void MOZ_ALWAYS_INLINE MightThrowJSException() {
345 #ifdef DEBUG
346     mMightHaveUnreportedJSException = true;
347 #endif
348   }
WouldReportJSException()349   void MOZ_ALWAYS_INLINE WouldReportJSException() {
350 #ifdef DEBUG
351     mMightHaveUnreportedJSException = false;
352 #endif
353   }
354 
355   // In the future, we can add overloads of Throw that take more
356   // interesting things, like strings or DOM exception types or
357   // something if desired.
358 
359   // Backwards-compat to make conversion simpler.  We don't call
360   // Throw() here because people can easily pass success codes to
361   // this.
362   void operator=(nsresult rv) { AssignErrorCode(rv); }
363 
Failed()364   bool Failed() const { return NS_FAILED(mResult); }
365 
ErrorCodeIs(nsresult rv)366   bool ErrorCodeIs(nsresult rv) const { return mResult == rv; }
367 
368   // For use in logging ONLY.
ErrorCodeAsInt()369   uint32_t ErrorCodeAsInt() const { return static_cast<uint32_t>(ErrorCode()); }
370 
371  protected:
ErrorCode()372   nsresult ErrorCode() const { return mResult; }
373 
374  private:
375 #ifdef DEBUG
376   enum UnionState {
377     HasMessage,
378     HasDOMExceptionInfo,
379     HasJSException,
380     HasNothing
381   };
382 #endif  // DEBUG
383 
384   friend struct IPC::ParamTraits<TErrorResult>;
385   friend struct IPC::ParamTraits<ErrorResult>;
386   void SerializeMessage(IPC::Message* aMsg) const;
387   bool DeserializeMessage(const IPC::Message* aMsg, PickleIterator* aIter);
388 
389   void SerializeDOMExceptionInfo(IPC::Message* aMsg) const;
390   bool DeserializeDOMExceptionInfo(const IPC::Message* aMsg,
391                                    PickleIterator* aIter);
392 
393   // Helper method that creates a new Message for this TErrorResult,
394   // and returns the arguments array from that Message.
395   nsTArray<nsString>& CreateErrorMessageHelper(const dom::ErrNum errorNumber,
396                                                nsresult errorType);
397 
398   template <dom::ErrNum errorNumber, typename... Ts>
399   void ThrowErrorWithMessage(nsresult errorType, Ts&&... messageArgs) {
400 #if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__))
401     static_assert(
402         dom::ErrorFormatNumArgs[errorNumber] == sizeof...(messageArgs),
403         "Pass in the right number of arguments");
404 #endif
405 
406     ClearUnionData();
407 
408     nsTArray<nsString>& messageArgsArray =
409         CreateErrorMessageHelper(errorNumber, errorType);
410     uint16_t argCount = dom::GetErrorArgCount(errorNumber);
411     dom::StringArrayAppender::Append(messageArgsArray, argCount,
412                                      Forward<Ts>(messageArgs)...);
413 #ifdef DEBUG
414     mUnionState = HasMessage;
415 #endif  // DEBUG
416   }
417 
418   MOZ_ALWAYS_INLINE void AssertInOwningThread() const {
419 #ifdef DEBUG
420     NS_ASSERT_OWNINGTHREAD(TErrorResult);
421 #endif
422   }
423 
424   void AssignErrorCode(nsresult aRv) {
425     MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR,
426                "Use ThrowTypeError()");
427     MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR,
428                "Use ThrowRangeError()");
429     MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
430     MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION,
431                "Use ThrowJSException()");
432     MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
433     MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION,
434                "Use ThrowDOMException()");
435     MOZ_ASSERT(!IsDOMException(), "Don't overwrite DOM exceptions");
436     MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS,
437                "May need to bring back ThrowNotEnoughArgsError");
438     MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT,
439                "Use NoteJSContextException");
440     mResult = aRv;
441   }
442 
443   void ClearMessage();
444   void ClearDOMExceptionInfo();
445 
446   // ClearUnionData will try to clear the data in our
447   // mMessage/mJSException/mDOMExceptionInfo union.  After this the union may be
448   // in an uninitialized state (e.g. mMessage or mDOMExceptionInfo may be
449   // pointing to deleted memory) and the caller must either reinitialize it or
450   // change mResult to something that will not involve us touching the union
451   // anymore.
452   void ClearUnionData();
453 
454   // Implementation of MaybeSetPendingException for the case when we're a
455   // failure result.
456   void SetPendingException(JSContext* cx);
457 
458   // Methods for setting various specific kinds of pending exceptions.
459   void SetPendingExceptionWithMessage(JSContext* cx);
460   void SetPendingJSException(JSContext* cx);
461   void SetPendingDOMException(JSContext* cx);
462   void SetPendingGenericErrorException(JSContext* cx);
463 
464   MOZ_ALWAYS_INLINE void AssertReportedOrSuppressed() {
465     MOZ_ASSERT(!Failed());
466     MOZ_ASSERT(!mMightHaveUnreportedJSException);
467     MOZ_ASSERT(mUnionState == HasNothing);
468   }
469 
470   // Special values of mResult:
471   // NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR -- ThrowTypeError() called on us.
472   // NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR -- ThrowRangeError() called on us.
473   // NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION -- ThrowJSException() called
474   //                                               on us.
475   // NS_ERROR_UNCATCHABLE_EXCEPTION -- ThrowUncatchableException called on us.
476   // NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION -- ThrowDOMException() called
477   //                                               on us.
478   nsresult mResult;
479 
480   struct Message;
481   struct DOMExceptionInfo;
482   // mMessage is set by ThrowErrorWithMessage and reported (and deallocated) by
483   // SetPendingExceptionWithMessage.
484   // mJSException is set (and rooted) by ThrowJSException and reported
485   // (and unrooted) by SetPendingJSException.
486   // mDOMExceptionInfo is set by ThrowDOMException and reported
487   // (and deallocated) by SetPendingDOMException.
488   union {
489     Message* mMessage;                    // valid when IsErrorWithMessage()
490     JS::UninitializedValue mJSException;  // valid when IsJSException()
491     DOMExceptionInfo* mDOMExceptionInfo;  // valid when IsDOMException()
492   };
493 
494 #ifdef DEBUG
495   // Used to keep track of codepaths that might throw JS exceptions,
496   // for assertion purposes.
497   bool mMightHaveUnreportedJSException;
498 
499   // Used to keep track of what's stored in our union right now.  Note
500   // that this may be set to HasNothing even if our mResult suggests
501   // we should have something, if we have already cleaned up the
502   // something.
503   UnionState mUnionState;
504 
505   // The thread that created this TErrorResult
506   NS_DECL_OWNINGTHREAD;
507 #endif
508 
509   // Not to be implemented, to make sure people always pass this by
510   // reference, not by value.
511   TErrorResult(const TErrorResult&) = delete;
512   void operator=(const TErrorResult&) = delete;
513 };
514 
515 struct JustAssertCleanupPolicy {
516   static const bool assertHandled = true;
517   static const bool suppress = false;
518 };
519 
520 struct AssertAndSuppressCleanupPolicy {
521   static const bool assertHandled = true;
522   static const bool suppress = true;
523 };
524 
525 struct JustSuppressCleanupPolicy {
526   static const bool assertHandled = false;
527   static const bool suppress = true;
528 };
529 
530 }  // namespace binding_danger
531 
532 // A class people should normally use on the stack when they plan to actually
533 // do something with the exception.
534 class ErrorResult : public binding_danger::TErrorResult<
535                         binding_danger::AssertAndSuppressCleanupPolicy> {
536   typedef binding_danger::TErrorResult<
537       binding_danger::AssertAndSuppressCleanupPolicy>
538       BaseErrorResult;
539 
540  public:
541   ErrorResult() : BaseErrorResult() {}
542 
543   ErrorResult(ErrorResult&& aRHS) : BaseErrorResult(Move(aRHS)) {}
544 
545   explicit ErrorResult(nsresult aRv) : BaseErrorResult(aRv) {}
546 
547   void operator=(nsresult rv) { BaseErrorResult::operator=(rv); }
548 
549   ErrorResult& operator=(ErrorResult&& aRHS) {
550     BaseErrorResult::operator=(Move(aRHS));
551     return *this;
552   }
553 
554  private:
555   // Not to be implemented, to make sure people always pass this by
556   // reference, not by value.
557   ErrorResult(const ErrorResult&) = delete;
558   void operator=(const ErrorResult&) = delete;
559 };
560 
561 template <typename CleanupPolicy>
562 binding_danger::TErrorResult<CleanupPolicy>::operator ErrorResult&() {
563   return *static_cast<ErrorResult*>(
564       reinterpret_cast<TErrorResult<AssertAndSuppressCleanupPolicy>*>(this));
565 }
566 
567 // A class for use when an ErrorResult should just automatically be ignored.
568 // This doesn't inherit from ErrorResult so we don't make two separate calls to
569 // SuppressException.
570 class IgnoredErrorResult : public binding_danger::TErrorResult<
571                                binding_danger::JustSuppressCleanupPolicy> {};
572 
573 // A class for use when an ErrorResult should just automatically be
574 // ignored.  This is designed to be passed as a temporary only, like
575 // so:
576 //
577 //    foo->Bar(IgnoreErrors());
578 class MOZ_TEMPORARY_CLASS IgnoreErrors {
579  public:
580   operator ErrorResult&() && { return mInner; }
581 
582  private:
583   // We don't use an ErrorResult member here so we don't make two separate calls
584   // to SuppressException (one from us, one from the ErrorResult destructor
585   // after asserting).
586   binding_danger::TErrorResult<binding_danger::JustSuppressCleanupPolicy>
587       mInner;
588 };
589 
590 namespace dom {
591 namespace binding_detail {
592 class FastErrorResult : public mozilla::binding_danger::TErrorResult<
593                             mozilla::binding_danger::JustAssertCleanupPolicy> {
594 };
595 }  // namespace binding_detail
596 }  // namespace dom
597 
598 // This part is a bit annoying.  We want an OOMReporter class that has the
599 // following properties:
600 //
601 // 1) Can be cast to from any ErrorResult-like type.
602 // 2) Has a fast destructor (because we want to use it from bindings).
603 // 3) Won't be randomly instantiated by non-binding code (because the fast
604 //    destructor is not so safe.
605 // 4) Doesn't look ugly on the callee side (e.g. isn't in the binding_detail or
606 //    binding_danger namespace).
607 //
608 // We do this by having two classes: The class callees should use, which has the
609 // things we want and a private constructor, and a friend subclass in the
610 // binding_danger namespace that can be used to construct it.
611 namespace binding_danger {
612 class OOMReporterInstantiator;
613 }  // namespace binding_danger
614 
615 class OOMReporter : private dom::binding_detail::FastErrorResult {
616  public:
617   void ReportOOM() { Throw(NS_ERROR_OUT_OF_MEMORY); }
618 
619  private:
620   // OOMReporterInstantiator is a friend so it can call our constructor and
621   // MaybeSetPendingException.
622   friend class binding_danger::OOMReporterInstantiator;
623 
624   // TErrorResult is a friend so its |operator OOMReporter&()| can work.
625   template <typename CleanupPolicy>
626   friend class binding_danger::TErrorResult;
627 
628   OOMReporter() : dom::binding_detail::FastErrorResult() {}
629 };
630 
631 namespace binding_danger {
632 class OOMReporterInstantiator : public OOMReporter {
633  public:
634   OOMReporterInstantiator() : OOMReporter() {}
635 
636   // We want to be able to call MaybeSetPendingException from codegen.  The one
637   // on OOMReporter is not callable directly, because it comes from a private
638   // superclass.  But we're a friend, so _we_ can call it.
639   bool MaybeSetPendingException(JSContext* cx) {
640     return OOMReporter::MaybeSetPendingException(cx);
641   }
642 };
643 }  // namespace binding_danger
644 
645 template <typename CleanupPolicy>
646 binding_danger::TErrorResult<CleanupPolicy>::operator OOMReporter&() {
647   return *static_cast<OOMReporter*>(
648       reinterpret_cast<TErrorResult<JustAssertCleanupPolicy>*>(this));
649 }
650 
651   /******************************************************************************
652    ** Macros for checking results
653    ******************************************************************************/
654 
655 #define ENSURE_SUCCESS(res, ret)                \
656   do {                                          \
657     if (res.Failed()) {                         \
658       nsCString msg;                            \
659       msg.AppendPrintf(                         \
660           "ENSURE_SUCCESS(%s, %s) failed with " \
661           "result 0x%X",                        \
662           #res, #ret, res.ErrorCodeAsInt());    \
663       NS_WARNING(msg.get());                    \
664       return ret;                               \
665     }                                           \
666   } while (0)
667 
668 #define ENSURE_SUCCESS_VOID(res)                 \
669   do {                                           \
670     if (res.Failed()) {                          \
671       nsCString msg;                             \
672       msg.AppendPrintf(                          \
673           "ENSURE_SUCCESS_VOID(%s) failed with " \
674           "result 0x%X",                         \
675           #res, res.ErrorCodeAsInt());           \
676       NS_WARNING(msg.get());                     \
677       return;                                    \
678     }                                            \
679   } while (0)
680 
681 }  // namespace mozilla
682 
683 #endif /* mozilla_ErrorResult_h */
684