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>
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
;
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
;
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
{
64 struct StorageTypeHelper
<T
, true, true> {
65 using Type
= nsCOMPtr
<T
>;
69 struct StorageTypeHelper
<nsCOMPtr
<T
>, true, true> {
70 using Type
= nsCOMPtr
<T
>;
74 struct StorageTypeHelper
<T
*, true, false> {
75 using Type
= RefPtr
<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
;
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
) {
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
,
118 class NativeThenHandler
<ResolveCallback
, RejectCallback
, std::tuple
<Args
...>,
119 std::tuple
<JSArgs
...>>
120 final
: public PromiseNativeThenHandlerBase
{
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
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
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
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);
155 ~NativeThenHandler() override
{
156 if constexpr (std::tuple_size
<decltype(mJSArgs
)>::value
> 0) {
157 mozilla::DropJSObjects(this);
161 void Traverse(nsCycleCollectionTraversalCallback
& cb
) override
{
163 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArgs
)
166 void Unlink() override
{
168 NS_IMPL_CYCLE_COLLECTION_UNLINK(mArgs
)
169 NS_IMPL_CYCLE_COLLECTION_UNLINK(mJSArgs
)
172 void Trace(const TraceCallbacks
& aCallbacks
, void* aClosure
) override
{
174 [&aCallbacks
, aClosure
](auto&&... aArgs
) {
175 (aCallbacks
.Trace(&aArgs
, "mJSArgs[]", aClosure
), ...);
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
,
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
,
230 Result
<RefPtr
<Promise
>, nsresult
>
231 Promise::ThenCatchWithCycleCollectedArgsJSImpl(
232 Maybe
<ResolveCallback
>&& aOnResolve
, Maybe
<RejectCallback
>&& aOnReject
,
233 std::tuple
<Args
...>&& aArgs
, std::tuple
<JSArgs
...>&& aJSArgs
) {
235 NativeThenHandler
<ResolveCallback
, RejectCallback
, std::tuple
<Args
...>,
236 std::tuple
<JSArgs
...>>;
239 RefPtr
<Promise
> promise
= Promise::Create(GetParentObject(), rv
);
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
));
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
,
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
,
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()),
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
,
317 [aOnResolve
](JSContext
* aCx
, JS::Handle
<JS::Value
> value
,
319 StorageType
<Args
>&&... aArgs
) -> already_AddRefed
<Promise
> {
320 aOnResolve(aCx
, value
, aRv
, aArgs
...);
324 [aOnReject
](JSContext
* aCx
, JS::Handle
<JS::Value
> value
, ErrorResult
& aRv
,
325 StorageType
<Args
>&&... aArgs
) -> already_AddRefed
<Promise
> {
326 aOnReject(aCx
, value
, aRv
, aArgs
...);
330 // Note: explicit template parameters for clang<7/gcc<8 without "Template
331 // argument deduction for class templates" support
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