Backed out changeset b09d48d2b473 (bug 1655101) for causing mochitest webgl failures...
[gecko.git] / dom / promise / Promise-inl.h
bloba7a6ebd0fdbeebab6b4d1340a6ccc82c9eacdb01
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_inl_h
8 #define mozilla_dom_Promise_inl_h
10 #include <type_traits>
11 #include <utility>
13 #include "mozilla/AlreadyAddRefed.h"
14 #include "mozilla/ResultExtensions.h"
15 #include "mozilla/dom/BindingUtils.h"
16 #include "mozilla/dom/Promise.h"
17 #include "mozilla/dom/PromiseNativeHandler.h"
18 #include "nsCycleCollectionParticipant.h"
20 namespace mozilla::dom {
22 class PromiseNativeThenHandlerBase : public PromiseNativeHandler {
23 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
24 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PromiseNativeThenHandlerBase)
26 PromiseNativeThenHandlerBase(Promise* aPromise) : mPromise(aPromise) {}
28 virtual bool HasResolvedCallback() = 0;
29 virtual bool HasRejectedCallback() = 0;
31 MOZ_CAN_RUN_SCRIPT void ResolvedCallback(JSContext* aCx,
32 JS::Handle<JS::Value> aValue,
33 ErrorResult& aRv) override;
35 MOZ_CAN_RUN_SCRIPT void RejectedCallback(JSContext* aCx,
36 JS::Handle<JS::Value> aValue,
37 ErrorResult& aRv) override;
39 protected:
40 virtual ~PromiseNativeThenHandlerBase() = default;
42 MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> CallResolveCallback(
43 JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) = 0;
44 MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> CallRejectCallback(
45 JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) = 0;
47 virtual void Traverse(nsCycleCollectionTraversalCallback&) = 0;
48 virtual void Unlink() = 0;
49 virtual void Trace(const TraceCallbacks& aCallbacks, void* aClosure) = 0;
51 RefPtr<Promise> mPromise;
54 namespace {
56 template <typename T, bool = IsRefcounted<std::remove_pointer_t<T>>::value,
57 bool = (std::is_convertible_v<T, nsISupports*> ||
58 std::is_convertible_v<T*, nsISupports*>)>
59 struct StorageTypeHelper {
60 using Type = T;
63 template <typename T>
64 struct StorageTypeHelper<T, true, true> {
65 using Type = nsCOMPtr<T>;
68 template <typename T>
69 struct StorageTypeHelper<nsCOMPtr<T>, true, true> {
70 using Type = nsCOMPtr<T>;
73 template <typename T>
74 struct StorageTypeHelper<T*, true, false> {
75 using Type = RefPtr<T>;
78 template <typename T>
79 struct StorageTypeHelper<JS::Handle<T>, false, false> {
80 using Type = JS::Heap<T>;
83 template <template <typename> class SmartPtr, typename T>
84 struct StorageTypeHelper<SmartPtr<T>, true, false>
85 : std::enable_if<std::is_convertible_v<SmartPtr<T>, T*>, RefPtr<T>> {
86 using Type = typename StorageTypeHelper::enable_if::type;
89 template <typename T>
90 using StorageType = typename StorageTypeHelper<std::decay_t<T>>::Type;
92 // Helpers to choose the correct argument type based on the storage type. Smart
93 // pointers are converted to the corresponding raw pointer type. Everything else
94 // is passed by move reference.
96 // Note: We can't just use std::forward for this because the input type may be a
97 // raw pointer which does not match the argument type, and while the
98 // spec-compliant behavior there should still give us the expected results, MSVC
99 // considers it an illegal use of std::forward.
100 template <template <typename> class SmartPtr, typename T>
101 decltype(std::declval<SmartPtr<T>>().get()) ArgType(SmartPtr<T>& aVal) {
102 return aVal.get();
105 template <typename T>
106 T&& ArgType(T& aVal) {
107 return std::move(aVal);
110 using ::ImplCycleCollectionUnlink;
112 template <typename ResolveCallback, typename RejectCallback, typename ArgsTuple,
113 typename JSArgsTuple>
114 class NativeThenHandler;
116 template <typename ResolveCallback, typename RejectCallback, typename... Args,
117 typename... JSArgs>
118 class NativeThenHandler<ResolveCallback, RejectCallback, std::tuple<Args...>,
119 std::tuple<JSArgs...>>
120 final : public PromiseNativeThenHandlerBase {
121 public:
123 * @param aPromise A promise that will be settled by the result of the
124 * callbacks. Any thrown value to ErrorResult passed to those callbacks will
125 * be used to reject the promise, otherwise the promise will be resolved with
126 * the return value.
127 * @param aOnResolve A resolve callback
128 * @param aOnReject A reject callback
129 * @param aArgs The custom arguments to be passed to the both callbacks. The
130 * handler class will grab them to make them live long enough and to allow
131 * cycle collection.
132 * @param aJSArgs The JS arguments to be passed to the both callbacks, after
133 * native arguments. The handler will also grab them and allow garbage
134 * collection.
136 * XXX(krosylight): ideally there should be two signatures, with or without a
137 * promise parameter. Unfortunately doing so confuses the compiler and errors
138 * out, because nothing prevents promise from being ResolveCallback.
140 NativeThenHandler(Promise* aPromise, Maybe<ResolveCallback>&& aOnResolve,
141 Maybe<RejectCallback>&& aOnReject,
142 std::tuple<std::remove_reference_t<Args>...>&& aArgs,
143 std::tuple<std::remove_reference_t<JSArgs>...>&& aJSArgs)
144 : PromiseNativeThenHandlerBase(aPromise),
145 mOnResolve(std::forward<Maybe<ResolveCallback>>(aOnResolve)),
146 mOnReject(std::forward<Maybe<RejectCallback>>(aOnReject)),
147 mArgs(std::forward<decltype(aArgs)>(aArgs)),
148 mJSArgs(std::forward<decltype(aJSArgs)>(aJSArgs)) {
149 if constexpr (std::tuple_size<decltype(mJSArgs)>::value > 0) {
150 mozilla::HoldJSObjects(this);
154 protected:
155 ~NativeThenHandler() override {
156 if constexpr (std::tuple_size<decltype(mJSArgs)>::value > 0) {
157 mozilla::DropJSObjects(this);
161 void Traverse(nsCycleCollectionTraversalCallback& cb) override {
162 auto* tmp = this;
163 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArgs)
166 void Unlink() override {
167 auto* tmp = this;
168 NS_IMPL_CYCLE_COLLECTION_UNLINK(mArgs)
169 NS_IMPL_CYCLE_COLLECTION_UNLINK(mJSArgs)
172 void Trace(const TraceCallbacks& aCallbacks, void* aClosure) override {
173 std::apply(
174 [&aCallbacks, aClosure](auto&&... aArgs) {
175 (aCallbacks.Trace(&aArgs, "mJSArgs[]", aClosure), ...);
177 mJSArgs);
180 bool HasResolvedCallback() override { return mOnResolve.isSome(); }
181 bool HasRejectedCallback() override { return mOnReject.isSome(); }
183 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CallResolveCallback(
184 JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) override {
185 return CallCallback(aCx, *mOnResolve, aValue, aRv);
187 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CallRejectCallback(
188 JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) override {
189 return CallCallback(aCx, *mOnReject, aValue, aRv);
192 // mJSArgs are marked with Trace() above, so they can be safely converted to
193 // Handles. But we should not circumvent the read barrier, so call
194 // exposeToActiveJS explicitly.
195 template <typename T>
196 static JS::Handle<T> GetJSArgHandleForCall(JS::Heap<T>& aArg) {
197 aArg.exposeToActiveJS();
198 return JS::Handle<T>::fromMarkedLocation(aArg.address());
201 template <typename TCallback, size_t... Indices, size_t... JSIndices>
202 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CallCallback(
203 JSContext* aCx, const TCallback& aHandler, JS::Handle<JS::Value> aValue,
204 ErrorResult& aRv, std::index_sequence<Indices...>,
205 std::index_sequence<JSIndices...>) {
206 return aHandler(aCx, aValue, aRv, ArgType(std::get<Indices>(mArgs))...,
207 GetJSArgHandleForCall(std::get<JSIndices>(mJSArgs))...);
210 template <typename TCallback>
211 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CallCallback(
212 JSContext* aCx, const TCallback& aHandler, JS::Handle<JS::Value> aValue,
213 ErrorResult& aRv) {
214 return CallCallback(aCx, aHandler, aValue, aRv,
215 std::index_sequence_for<Args...>{},
216 std::index_sequence_for<JSArgs...>{});
219 Maybe<ResolveCallback> mOnResolve;
220 Maybe<RejectCallback> mOnReject;
222 std::tuple<StorageType<Args>...> mArgs;
223 std::tuple<StorageType<JSArgs>...> mJSArgs;
226 } // anonymous namespace
228 template <typename ResolveCallback, typename RejectCallback, typename... Args,
229 typename... JSArgs>
230 Result<RefPtr<Promise>, nsresult>
231 Promise::ThenCatchWithCycleCollectedArgsJSImpl(
232 Maybe<ResolveCallback>&& aOnResolve, Maybe<RejectCallback>&& aOnReject,
233 std::tuple<Args...>&& aArgs, std::tuple<JSArgs...>&& aJSArgs) {
234 using HandlerType =
235 NativeThenHandler<ResolveCallback, RejectCallback, std::tuple<Args...>,
236 std::tuple<JSArgs...>>;
238 ErrorResult rv;
239 RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
240 if (rv.Failed()) {
241 return Err(rv.StealNSResult());
244 auto* handler = new (fallible)
245 HandlerType(promise, std::forward<Maybe<ResolveCallback>>(aOnResolve),
246 std::forward<Maybe<RejectCallback>>(aOnReject),
247 std::forward<std::tuple<Args...>>(aArgs),
248 std::forward<std::tuple<JSArgs...>>(aJSArgs));
250 if (!handler) {
251 return Err(NS_ERROR_OUT_OF_MEMORY);
254 AppendNativeHandler(handler);
255 return std::move(promise);
258 template <typename ResolveCallback, typename RejectCallback, typename... Args>
259 Promise::ThenResult<ResolveCallback, Args...>
260 Promise::ThenCatchWithCycleCollectedArgsImpl(
261 Maybe<ResolveCallback>&& aOnResolve, Maybe<RejectCallback>&& aOnReject,
262 Args&&... aArgs) {
263 return ThenCatchWithCycleCollectedArgsJSImpl(
264 std::forward<Maybe<ResolveCallback>>(aOnResolve),
265 std::forward<Maybe<RejectCallback>>(aOnReject),
266 std::make_tuple(std::forward<Args>(aArgs)...), std::make_tuple());
269 template <typename ResolveCallback, typename RejectCallback, typename... Args>
270 Promise::ThenResult<ResolveCallback, Args...>
271 Promise::ThenCatchWithCycleCollectedArgs(ResolveCallback&& aOnResolve,
272 RejectCallback&& aOnReject,
273 Args&&... aArgs) {
274 return ThenCatchWithCycleCollectedArgsImpl(Some(aOnResolve), Some(aOnReject),
275 std::forward<Args>(aArgs)...);
278 template <typename Callback, typename... Args>
279 Promise::ThenResult<Callback, Args...> Promise::ThenWithCycleCollectedArgs(
280 Callback&& aOnResolve, Args&&... aArgs) {
281 return ThenCatchWithCycleCollectedArgsImpl(Some(aOnResolve),
282 Maybe<Callback>(Nothing()),
283 std::forward<Args>(aArgs)...);
286 template <typename Callback, typename... Args>
287 Promise::ThenResult<Callback, Args...> Promise::CatchWithCycleCollectedArgs(
288 Callback&& aOnReject, Args&&... aArgs) {
289 return ThenCatchWithCycleCollectedArgsImpl(Maybe<Callback>(Nothing()),
290 Some(aOnReject),
291 std::forward<Args>(aArgs)...);
294 template <typename ResolveCallback, typename RejectCallback, typename ArgsTuple,
295 typename JSArgsTuple>
296 Result<RefPtr<Promise>, nsresult> Promise::ThenCatchWithCycleCollectedArgsJS(
297 ResolveCallback&& aOnResolve, RejectCallback&& aOnReject, ArgsTuple&& aArgs,
298 JSArgsTuple&& aJSArgs) {
299 return ThenCatchWithCycleCollectedArgsJSImpl(
300 Some(aOnResolve), Some(aOnReject), std::forward<ArgsTuple>(aArgs),
301 std::forward<JSArgsTuple>(aJSArgs));
304 template <typename Callback, typename ArgsTuple, typename JSArgsTuple>
305 Result<RefPtr<Promise>, nsresult> Promise::ThenWithCycleCollectedArgsJS(
306 Callback&& aOnResolve, ArgsTuple&& aArgs, JSArgsTuple&& aJSArgs) {
307 return ThenCatchWithCycleCollectedArgsJSImpl(
308 Some(aOnResolve), Maybe<Callback>(Nothing()),
309 std::forward<ArgsTuple>(aArgs), std::forward<JSArgsTuple>(aJSArgs));
312 template <typename ResolveCallback, typename RejectCallback, typename... Args>
313 void Promise::AddCallbacksWithCycleCollectedArgs(ResolveCallback&& aOnResolve,
314 RejectCallback&& aOnReject,
315 Args&&... aArgs) {
316 auto onResolve =
317 [aOnResolve](JSContext* aCx, JS::Handle<JS::Value> value,
318 ErrorResult& aRv,
319 StorageType<Args>&&... aArgs) -> already_AddRefed<Promise> {
320 aOnResolve(aCx, value, aRv, aArgs...);
321 return nullptr;
323 auto onReject =
324 [aOnReject](JSContext* aCx, JS::Handle<JS::Value> value, ErrorResult& aRv,
325 StorageType<Args>&&... aArgs) -> already_AddRefed<Promise> {
326 aOnReject(aCx, value, aRv, aArgs...);
327 return nullptr;
330 // Note: explicit template parameters for clang<7/gcc<8 without "Template
331 // argument deduction for class templates" support
332 AppendNativeHandler(
333 new NativeThenHandler<decltype(onResolve), decltype(onReject),
334 std::tuple<Args...>, std::tuple<>>(
335 nullptr, Some(onResolve), Some(onReject),
336 std::make_tuple(std::forward<Args>(aArgs)...), std::make_tuple()));
339 } // namespace mozilla::dom
341 #endif // mozilla_dom_Promise_inl_h