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