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 #ifndef mozilla_dom_Promise_h
8 #define mozilla_dom_Promise_h
11 #include <type_traits>
13 #include "ErrorList.h"
14 #include "js/RootingAPI.h"
15 #include "js/TypeDecls.h"
16 #include "mozilla/AlreadyAddRefed.h"
17 #include "mozilla/Assertions.h"
18 #include "mozilla/ErrorResult.h"
19 #include "mozilla/RefPtr.h"
20 #include "mozilla/Result.h"
21 #include "mozilla/WeakPtr.h"
22 #include "mozilla/dom/AutoEntryScript.h"
23 #include "mozilla/dom/ScriptSettings.h"
24 #include "mozilla/dom/ToJSValue.h"
25 #include "nsCycleCollectionParticipant.h"
27 #include "nsISupports.h"
30 class nsCycleCollectionTraversalCallback
;
31 class nsIGlobalObject
;
42 class MediaStreamError
;
44 class PromiseNativeHandler
;
45 class PromiseDebugging
;
47 class Promise
: public SupportsWeakPtr
{
48 friend class PromiseTask
;
49 friend class PromiseWorkerProxy
;
50 friend class PromiseWorkerProxyRunnable
;
53 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Promise
)
54 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(Promise
)
56 enum PropagateUserInteraction
{
57 eDontPropagateUserInteraction
,
58 ePropagateUserInteraction
61 // Promise creation tries to create a JS reflector for the Promise, so is
62 // fallible. Furthermore, we don't want to do JS-wrapping on a 0-refcount
63 // object, so we addref before doing that and return the addrefed pointer
65 // Pass ePropagateUserInteraction for aPropagateUserInteraction if you want
66 // the promise resolve handler to be called as if we were handling user
67 // input events in case we are currently handling user input events.
68 static already_AddRefed
<Promise
> Create(
69 nsIGlobalObject
* aGlobal
, ErrorResult
& aRv
,
70 PropagateUserInteraction aPropagateUserInteraction
=
71 eDontPropagateUserInteraction
);
73 // Reports a rejected Promise by sending an error report.
74 static void ReportRejectedPromise(JSContext
* aCx
, JS::HandleObject aPromise
);
76 typedef void (Promise::*MaybeFunc
)(JSContext
* aCx
,
77 JS::Handle
<JS::Value
> aValue
);
79 // Helpers for using Promise from C++.
80 // Most DOM objects are handled already. To add a new type T, add a
81 // ToJSValue overload in ToJSValue.h.
82 // aArg is a const reference so we can pass rvalues like integer constants
84 void MaybeResolve(T
&& aArg
) {
85 MaybeSomething(std::forward
<T
>(aArg
), &Promise::MaybeResolve
);
88 void MaybeResolveWithUndefined();
90 void MaybeReject(JS::Handle
<JS::Value
> aValue
) {
91 MaybeSomething(aValue
, &Promise::MaybeReject
);
94 // This method is deprecated. Consumers should MaybeRejectWithDOMException if
95 // they are rejecting with a DOMException, or use one of the other
96 // MaybeReject* methods otherwise. If they have a random nsresult which may
97 // or may not correspond to a DOMException type, they should consider using an
98 // appropriate DOMException-type nsresult with an informative message and
99 // calling MaybeRejectWithDOMException.
100 inline void MaybeReject(nsresult aArg
) {
101 MOZ_ASSERT(NS_FAILED(aArg
));
102 MaybeSomething(aArg
, &Promise::MaybeReject
);
105 inline void MaybeReject(ErrorResult
&& aArg
) {
106 MOZ_ASSERT(aArg
.Failed());
107 MaybeSomething(std::move(aArg
), &Promise::MaybeReject
);
108 // That should have consumed aArg.
109 MOZ_ASSERT(!aArg
.Failed());
112 void MaybeReject(const RefPtr
<MediaStreamError
>& aArg
);
114 void MaybeRejectWithUndefined();
116 void MaybeResolveWithClone(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
);
117 void MaybeRejectWithClone(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
);
119 // Facilities for rejecting with various spec-defined exception values.
120 #define DOMEXCEPTION(name, err) \
121 inline void MaybeRejectWith##name(const nsACString& aMessage) { \
123 res.Throw##name(aMessage); \
124 MaybeReject(std::move(res)); \
127 void MaybeRejectWith##name(const char(&aMessage)[N]) { \
128 MaybeRejectWith##name(nsLiteralCString(aMessage)); \
131 #include "mozilla/dom/DOMExceptionNames.h"
135 template <ErrNum errorNumber
, typename
... Ts
>
136 void MaybeRejectWithTypeError(Ts
&&... aMessageArgs
) {
138 res
.ThrowTypeError
<errorNumber
>(std::forward
<Ts
>(aMessageArgs
)...);
139 MaybeReject(std::move(res
));
142 inline void MaybeRejectWithTypeError(const nsACString
& aMessage
) {
144 res
.ThrowTypeError(aMessage
);
145 MaybeReject(std::move(res
));
149 void MaybeRejectWithTypeError(const char (&aMessage
)[N
]) {
150 MaybeRejectWithTypeError(nsLiteralCString(aMessage
));
153 template <ErrNum errorNumber
, typename
... Ts
>
154 void MaybeRejectWithRangeError(Ts
&&... aMessageArgs
) {
156 res
.ThrowRangeError
<errorNumber
>(std::forward
<Ts
>(aMessageArgs
)...);
157 MaybeReject(std::move(res
));
160 inline void MaybeRejectWithRangeError(const nsACString
& aMessage
) {
162 res
.ThrowRangeError(aMessage
);
163 MaybeReject(std::move(res
));
167 void MaybeRejectWithRangeError(const char (&aMessage
)[N
]) {
168 MaybeRejectWithRangeError(nsLiteralCString(aMessage
));
171 // DO NOT USE MaybeRejectBrokenly with in new code. Promises should be
172 // rejected with Error instances.
173 // Note: MaybeRejectBrokenly is a template so we can use it with DOMException
174 // without instantiating the DOMException specialization of MaybeSomething in
175 // every translation unit that includes this header, because that would
176 // require use to include DOMException.h either here or in all those
177 // translation units.
178 template <typename T
>
179 void MaybeRejectBrokenly(const T
& aArg
); // Not implemented by default; see
180 // specializations in the .cpp for
181 // the T values we support.
183 // Mark a settled promise as already handled so that rejections will not
184 // be reported as unhandled.
185 void SetSettledPromiseIsHandled();
189 nsIGlobalObject
* GetParentObject() const { return GetGlobalObject(); }
191 // Do the equivalent of Promise.resolve in the compartment of aGlobal. The
192 // compartment of aCx is ignored. Errors are reported on the ErrorResult; if
193 // aRv comes back !Failed(), this function MUST return a non-null value.
194 // Pass ePropagateUserInteraction for aPropagateUserInteraction if you want
195 // the promise resolve handler to be called as if we were handling user
196 // input events in case we are currently handling user input events.
197 static already_AddRefed
<Promise
> Resolve(
198 nsIGlobalObject
* aGlobal
, JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
200 PropagateUserInteraction aPropagateUserInteraction
=
201 eDontPropagateUserInteraction
);
203 // Do the equivalent of Promise.reject in the compartment of aGlobal. The
204 // compartment of aCx is ignored. Errors are reported on the ErrorResult; if
205 // aRv comes back !Failed(), this function MUST return a non-null value.
206 static already_AddRefed
<Promise
> Reject(nsIGlobalObject
* aGlobal
,
208 JS::Handle
<JS::Value
> aValue
,
211 // Do the equivalent of Promise.all in the current compartment of aCx. Errors
212 // are reported on the ErrorResult; if aRv comes back !Failed(), this function
213 // MUST return a non-null value.
214 // Pass ePropagateUserInteraction for aPropagateUserInteraction if you want
215 // the promise resolve handler to be called as if we were handling user
216 // input events in case we are currently handling user input events.
217 static already_AddRefed
<Promise
> All(
218 JSContext
* aCx
, const nsTArray
<RefPtr
<Promise
>>& aPromiseList
,
220 PropagateUserInteraction aPropagateUserInteraction
=
221 eDontPropagateUserInteraction
);
223 void Then(JSContext
* aCx
,
224 // aCalleeGlobal may not be in the compartment of aCx, when called
226 JS::Handle
<JSObject
*> aCalleeGlobal
, AnyCallback
* aResolveCallback
,
227 AnyCallback
* aRejectCallback
, JS::MutableHandle
<JS::Value
> aRetval
,
230 template <typename Callback
, typename
... Args
>
231 using IsHandlerCallback
=
232 std::is_same
<already_AddRefed
<Promise
>,
233 decltype(std::declval
<Callback
>()(
234 (JSContext
*)(nullptr),
235 std::declval
<JS::Handle
<JS::Value
>>(),
236 std::declval
<Args
>()...))>;
238 template <typename Callback
, typename
... Args
>
240 std::enable_if_t
<IsHandlerCallback
<Callback
, Args
...>::value
,
241 Result
<RefPtr
<Promise
>, nsresult
>>;
243 // Similar to the JavaScript Then() function. Accepts a single lambda function
244 // argument, which it attaches as a native resolution handler, and returns a
245 // new promise which resolves with that handler's return value, or propagates
246 // any rejections from this promise.
248 // Any additional arguments passed after the callback function are stored and
249 // passed as additional arguments to the function when it is called. These
250 // values will participate in cycle collection for the promise handler, and
251 // therefore may safely form reference cycles with the promise chain.
253 // Any strong references required by the callback should be passed in this
254 // manner, rather than using lambda capture, lambda captures do not support
255 // cycle collection, and can easily lead to leaks.
257 // Does not currently support rejection handlers.
258 template <typename Callback
, typename
... Args
>
259 ThenResult
<Callback
, Args
...> ThenWithCycleCollectedArgs(
260 Callback
&& aOnResolve
, Args
&&... aArgs
);
262 Result
<RefPtr
<Promise
>, nsresult
> ThenWithoutCycleCollection(
264 already_AddRefed
<Promise
>(JSContext
*, JS::HandleValue
)>& aCallback
);
266 JSObject
* PromiseObj() const { return mPromiseObj
; }
268 void AppendNativeHandler(PromiseNativeHandler
* aRunnable
);
270 nsIGlobalObject
* GetGlobalObject() const { return mGlobal
; }
272 // Create a dom::Promise from a given SpiderMonkey Promise object.
273 // aPromiseObj MUST be in the compartment of aGlobal's global JS object.
274 // Pass ePropagateUserInteraction for aPropagateUserInteraction if you want
275 // the promise resolve handler to be called as if we were handling user
276 // input events in case we are currently handling user input events.
277 static already_AddRefed
<Promise
> CreateFromExisting(
278 nsIGlobalObject
* aGlobal
, JS::Handle
<JSObject
*> aPromiseObj
,
279 PropagateUserInteraction aPropagateUserInteraction
=
280 eDontPropagateUserInteraction
);
282 enum class PromiseState
{ Pending
, Resolved
, Rejected
};
284 PromiseState
State() const;
287 // Legacy method for throwing DOMExceptions. Only used by media code at this
288 // point, via DetailedPromise. Do NOT add new uses! When this is removed,
289 // remove the friend declaration in ErrorResult.h.
290 inline void MaybeRejectWithDOMException(nsresult rv
,
291 const nsACString
& aMessage
) {
293 res
.ThrowDOMException(rv
, aMessage
);
294 MaybeReject(std::move(res
));
297 struct PromiseCapability
;
299 // Do NOT call this unless you're Promise::Create or
300 // Promise::CreateFromExisting. I wish we could enforce that from inside this
301 // class too, somehow.
302 explicit Promise(nsIGlobalObject
* aGlobal
);
306 // Do JS-wrapping after Promise creation.
307 // Pass ePropagateUserInteraction for aPropagateUserInteraction if you want
308 // the promise resolve handler to be called as if we were handling user
309 // input events in case we are currently handling user input events.
310 void CreateWrapper(ErrorResult
& aRv
,
311 PropagateUserInteraction aPropagateUserInteraction
=
312 eDontPropagateUserInteraction
);
315 void MaybeResolve(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
);
316 void MaybeReject(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
);
318 template <typename T
>
319 void MaybeSomething(T
&& aArgument
, MaybeFunc aFunc
) {
320 MOZ_ASSERT(PromiseObj()); // It was preserved!
322 AutoAllowLegacyScriptExecution exemption
;
323 AutoEntryScript
aes(mGlobal
, "Promise resolution or rejection");
324 JSContext
* cx
= aes
.cx();
326 JS::Rooted
<JS::Value
> val(cx
);
327 if (!ToJSValue(cx
, std::forward
<T
>(aArgument
), &val
)) {
332 (this->*aFunc
)(cx
, val
);
335 void HandleException(JSContext
* aCx
);
337 bool MaybePropagateUserInputEventHandling();
339 RefPtr
<nsIGlobalObject
> mGlobal
;
341 JS::Heap
<JSObject
*> mPromiseObj
;
345 } // namespace mozilla
348 // These functions are used in the implementation of ffi bindings for
349 // dom::Promise from Rust in xpcom crate.
350 void DomPromise_AddRef(mozilla::dom::Promise
* aPromise
);
351 void DomPromise_Release(mozilla::dom::Promise
* aPromise
);
352 void DomPromise_RejectWithVariant(mozilla::dom::Promise
* aPromise
,
353 nsIVariant
* aVariant
);
354 void DomPromise_ResolveWithVariant(mozilla::dom::Promise
* aPromise
,
355 nsIVariant
* aVariant
);
358 #endif // mozilla_dom_Promise_h