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