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