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