Bug 1700051: part 26) Correct typo in comment of `mozInlineSpellWordUtil::BuildSoftTe...
[gecko.git] / dom / promise / Promise.h
blob5327b2f23bc6865c5251681c2fdaf4d80eeb4f5b
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
10 #include <functional>
11 #include <type_traits>
12 #include <utility>
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"
26 #include "nsError.h"
27 #include "nsISupports.h"
28 #include "nsString.h"
30 class nsCycleCollectionTraversalCallback;
31 class nsIGlobalObject;
33 namespace JS {
34 class Value;
37 namespace mozilla {
39 namespace dom {
41 class AnyCallback;
42 class MediaStreamError;
43 class PromiseInit;
44 class PromiseNativeHandler;
45 class PromiseDebugging;
47 class Promise : public SupportsWeakPtr {
48 friend class PromiseTask;
49 friend class PromiseWorkerProxy;
50 friend class PromiseWorkerProxyRunnable;
52 public:
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
64 // here.
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
83 template <typename T>
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) { \
122 ErrorResult res; \
123 res.Throw##name(aMessage); \
124 MaybeReject(std::move(res)); \
126 template <int N> \
127 void MaybeRejectWith##name(const char(&aMessage)[N]) { \
128 MaybeRejectWith##name(nsLiteralCString(aMessage)); \
131 #include "mozilla/dom/DOMExceptionNames.h"
133 #undef DOMEXCEPTION
135 template <ErrNum errorNumber, typename... Ts>
136 void MaybeRejectWithTypeError(Ts&&... aMessageArgs) {
137 ErrorResult res;
138 res.ThrowTypeError<errorNumber>(std::forward<Ts>(aMessageArgs)...);
139 MaybeReject(std::move(res));
142 inline void MaybeRejectWithTypeError(const nsACString& aMessage) {
143 ErrorResult res;
144 res.ThrowTypeError(aMessage);
145 MaybeReject(std::move(res));
148 template <int N>
149 void MaybeRejectWithTypeError(const char (&aMessage)[N]) {
150 MaybeRejectWithTypeError(nsLiteralCString(aMessage));
153 template <ErrNum errorNumber, typename... Ts>
154 void MaybeRejectWithRangeError(Ts&&... aMessageArgs) {
155 ErrorResult res;
156 res.ThrowRangeError<errorNumber>(std::forward<Ts>(aMessageArgs)...);
157 MaybeReject(std::move(res));
160 inline void MaybeRejectWithRangeError(const nsACString& aMessage) {
161 ErrorResult res;
162 res.ThrowRangeError(aMessage);
163 MaybeReject(std::move(res));
166 template <int N>
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();
187 // WebIDL
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,
199 ErrorResult& aRv,
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,
207 JSContext* aCx,
208 JS::Handle<JS::Value> aValue,
209 ErrorResult& aRv);
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,
219 ErrorResult& aRv,
220 PropagateUserInteraction aPropagateUserInteraction =
221 eDontPropagateUserInteraction);
223 void Then(JSContext* aCx,
224 // aCalleeGlobal may not be in the compartment of aCx, when called
225 // over Xrays.
226 JS::Handle<JSObject*> aCalleeGlobal, AnyCallback* aResolveCallback,
227 AnyCallback* aRejectCallback, JS::MutableHandle<JS::Value> aRetval,
228 ErrorResult& aRv);
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>
239 using ThenResult =
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(
263 const std::function<
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;
286 protected:
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) {
292 ErrorResult res;
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);
304 virtual ~Promise();
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);
314 private:
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)) {
328 HandleException(cx);
329 return;
332 (this->*aFunc)(cx, val);
335 void HandleException(JSContext* aCx);
337 bool MaybePropagateUserInputEventHandling();
339 RefPtr<nsIGlobalObject> mGlobal;
341 JS::Heap<JSObject*> mPromiseObj;
344 } // namespace dom
345 } // namespace mozilla
347 extern "C" {
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