Skip to main content

script/dom/promise/
promise.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Native representation of JS Promise values.
6//!
7//! This implementation differs from the traditional Rust DOM object, because the reflector
8//! is provided by SpiderMonkey and has no knowledge of an associated native representation
9//! (ie. dom::Promise). This means that native instances use native reference counting (Rc)
10//! to ensure that no memory is leaked, which means that there can be multiple instances of
11//! native Promise values that refer to the same JS value yet are distinct native objects
12//! (ie. address equality for the native objects is meaningless).
13
14use std::cell::{Cell, RefCell};
15use std::ops::DerefMut;
16use std::ptr;
17use std::rc::Rc;
18
19use dom_struct::dom_struct;
20use js::context::JSContext;
21use js::conversions::{ConversionResult, FromJSValConvertibleRc};
22use js::jsapi::{
23    CallArgs, GetFunctionNativeReserved, Heap, JS_GetFunctionObject, JSContext as RawJSContext,
24    JSObject, PromiseState, PromiseUserInputEventHandlingState, RemoveRawValueRoot,
25    SetFunctionNativeReserved,
26};
27use js::jsval::{Int32Value, JSVal, NullValue, ObjectValue, UndefinedValue};
28use js::realm::{AutoRealm, CurrentRealm};
29use js::rust::wrappers2::{
30    AddPromiseReactions, AddRawValueRoot, CallOriginalPromiseReject, CallOriginalPromiseResolve,
31    GetPromiseIsHandled, GetPromiseState, IsPromiseObject, JS_ClearPendingException,
32    JS_NewFunction, NewFunctionWithReserved, NewPromiseObject, RejectPromise, ResolvePromise,
33    SetAnyPromiseIsHandled, SetPromiseUserInputEventHandlingState,
34};
35use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime};
36use script_bindings::conversions::SafeToJSValConvertible;
37use script_bindings::reflector::{DomObject, MutDomObject, Reflector};
38use script_bindings::settings_stack::run_a_script;
39
40use crate::DomTypeHolder;
41use crate::dom::bindings::conversions::root_from_object;
42use crate::dom::bindings::error::{Error, ErrorToJsval};
43use crate::dom::bindings::reflector::DomGlobal;
44use crate::dom::bindings::root::{AsHandleValue, DomRoot};
45use crate::dom::globalscope::GlobalScope;
46use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
47use crate::microtask::{Microtask, MicrotaskRunnable};
48use crate::realms::enter_auto_realm;
49use crate::script_thread::ScriptThread;
50
51#[dom_struct]
52#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_in_rc)]
53pub(crate) struct Promise {
54    reflector: Reflector,
55    /// Since Promise values are natively reference counted without the knowledge of
56    /// the SpiderMonkey GC, an explicit root for the reflector is stored while any
57    /// native instance exists. This ensures that the reflector will never be GCed
58    /// while native code could still interact with its native representation.
59    #[ignore_malloc_size_of = "SM handles JS values"]
60    permanent_js_root: Heap<JSVal>,
61}
62
63/// Private helper to enable adding new methods to `Rc<Promise>`.
64trait PromiseHelper {
65    fn initialize(&self, cx: &mut JSContext);
66}
67
68impl PromiseHelper for Rc<Promise> {
69    #[expect(unsafe_code)]
70    fn initialize(&self, cx: &mut JSContext) {
71        let obj = self.reflector().get_jsobject();
72        self.permanent_js_root.set(ObjectValue(*obj));
73        unsafe {
74            assert!(AddRawValueRoot(
75                cx,
76                self.permanent_js_root.get_unsafe(),
77                c"Promise::root".as_ptr(),
78            ));
79        }
80    }
81}
82
83// Promise objects are stored inside Rc values, so Drop is run when the last Rc is dropped,
84// rather than when SpiderMonkey runs a GC. This makes it safe to interact with the JS engine unlike
85// Drop implementations for other DOM types.
86impl Drop for Promise {
87    #[expect(unsafe_code)]
88    fn drop(&mut self) {
89        unsafe {
90            let object = self.permanent_js_root.get().to_object();
91            assert!(!object.is_null());
92            if let Some(cx) = Runtime::get() {
93                RemoveRawValueRoot(cx.as_ptr(), self.permanent_js_root.get_unsafe());
94            }
95        }
96    }
97}
98
99impl Promise {
100    pub(crate) fn new(cx: &mut JSContext, global: &GlobalScope) -> Rc<Promise> {
101        let mut realm = enter_auto_realm(cx, global);
102        let cx = &mut realm.current_realm();
103        Promise::new_in_realm(cx)
104    }
105
106    pub(crate) fn new_in_realm(current_realm: &mut CurrentRealm) -> Rc<Promise> {
107        let cx = current_realm.deref_mut();
108        rooted!(&in(cx) let mut obj = ptr::null_mut::<JSObject>());
109        Promise::create_js_promise(cx, obj.handle_mut());
110        Promise::new_with_js_promise(cx, obj.handle())
111    }
112
113    pub(crate) fn duplicate(&self, cx: &mut JSContext) -> Rc<Promise> {
114        Promise::new_with_js_promise(cx, self.reflector().get_jsobject())
115    }
116
117    #[expect(unsafe_code)]
118    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
119    pub(crate) fn new_with_js_promise(cx: &mut JSContext, obj: HandleObject) -> Rc<Promise> {
120        unsafe {
121            assert!(IsPromiseObject(obj));
122        }
123        let promise = Promise {
124            reflector: Reflector::new(),
125            permanent_js_root: Heap::default(),
126        };
127        let promise = Rc::new(promise);
128        unsafe {
129            promise.init_reflector_without_associated_memory(obj.get());
130        }
131        promise.initialize(cx);
132        promise
133    }
134
135    #[expect(unsafe_code)]
136    // The apparently-unused CanGc parameter reflects the fact that the JS API calls
137    // like JS_NewFunction can trigger a GC.
138    fn create_js_promise(cx: &mut JSContext, mut obj: MutableHandleObject) {
139        unsafe {
140            let do_nothing_func = JS_NewFunction(
141                cx,
142                Some(do_nothing_promise_executor),
143                /* nargs = */ 2,
144                /* flags = */ 0,
145                ptr::null(),
146            );
147            assert!(!do_nothing_func.is_null());
148            rooted!(&in(cx) let do_nothing_obj = JS_GetFunctionObject(do_nothing_func));
149            assert!(!do_nothing_obj.is_null());
150            obj.set(NewPromiseObject(cx, do_nothing_obj.handle()));
151            assert!(!obj.is_null());
152            let is_user_interacting = if ScriptThread::is_user_interacting() {
153                PromiseUserInputEventHandlingState::HadUserInteractionAtCreation
154            } else {
155                PromiseUserInputEventHandlingState::DidntHaveUserInteractionAtCreation
156            };
157            SetPromiseUserInputEventHandlingState(obj.handle(), is_user_interacting);
158        }
159    }
160
161    #[expect(unsafe_code)]
162    pub(crate) fn new_resolved(
163        cx: &mut JSContext,
164        global: &GlobalScope,
165        value: impl SafeToJSValConvertible,
166    ) -> Rc<Promise> {
167        let mut realm = enter_auto_realm(cx, global);
168        let cx = &mut realm.current_realm();
169        rooted!(&in(cx) let mut rval = UndefinedValue());
170        value.safe_to_jsval(cx, rval.handle_mut());
171        rooted!(&in(cx) let p = unsafe { CallOriginalPromiseResolve(cx, rval.handle()) });
172        assert!(!p.handle().is_null());
173        Promise::new_with_js_promise(cx, p.handle())
174    }
175
176    #[expect(unsafe_code)]
177    pub(crate) fn new_rejected(
178        cx: &mut JSContext,
179        global: &GlobalScope,
180        value: impl SafeToJSValConvertible,
181    ) -> Rc<Promise> {
182        let mut realm = enter_auto_realm(cx, global);
183        let cx = &mut realm.current_realm();
184        rooted!(&in(cx) let mut rval = UndefinedValue());
185        value.safe_to_jsval(cx, rval.handle_mut());
186        rooted!(&in(cx) let p = unsafe { CallOriginalPromiseReject(cx, rval.handle()) });
187        assert!(!p.handle().is_null());
188        Promise::new_with_js_promise(cx, p.handle())
189    }
190
191    pub(crate) fn resolve_native<T>(&self, cx: &mut JSContext, val: &T)
192    where
193        T: SafeToJSValConvertible,
194    {
195        let mut realm = enter_auto_realm(cx, self);
196        let cx = &mut realm.current_realm();
197        rooted!(&in(cx) let mut v = UndefinedValue());
198        val.safe_to_jsval(cx, v.handle_mut());
199        self.resolve(cx, v.handle());
200    }
201
202    #[expect(unsafe_code)]
203    pub(crate) fn resolve(&self, cx: &mut JSContext, value: HandleValue) {
204        unsafe {
205            if !ResolvePromise(cx, self.promise_obj(), value) {
206                JS_ClearPendingException(cx);
207            }
208        }
209    }
210
211    pub(crate) fn reject_native<T>(&self, cx: &mut JSContext, val: &T)
212    where
213        T: SafeToJSValConvertible,
214    {
215        let mut realm = enter_auto_realm(cx, self);
216        let cx = &mut realm.current_realm();
217        rooted!(&in(cx) let mut v = UndefinedValue());
218        val.safe_to_jsval(cx, v.handle_mut());
219        self.reject(cx, v.handle());
220    }
221
222    pub(crate) fn reject_error(&self, cx: &mut JSContext, error: Error) {
223        let mut realm = enter_auto_realm(cx, self);
224        let cx = &mut realm.current_realm();
225        rooted!(&in(cx) let mut v = UndefinedValue());
226        error.to_jsval(cx, &self.global(), v.handle_mut());
227        self.reject(cx, v.handle());
228    }
229
230    #[expect(unsafe_code)]
231    pub(crate) fn reject(&self, cx: &mut JSContext, value: HandleValue) {
232        unsafe {
233            if !RejectPromise(cx, self.promise_obj(), value) {
234                JS_ClearPendingException(cx);
235            }
236        }
237    }
238
239    #[expect(unsafe_code)]
240    pub(crate) fn is_fulfilled(&self) -> bool {
241        let state = unsafe { GetPromiseState(self.promise_obj()) };
242        matches!(state, PromiseState::Rejected | PromiseState::Fulfilled)
243    }
244
245    #[expect(unsafe_code)]
246    pub(crate) fn is_rejected(&self) -> bool {
247        let state = unsafe { GetPromiseState(self.promise_obj()) };
248        matches!(state, PromiseState::Rejected)
249    }
250
251    #[expect(unsafe_code)]
252    pub(crate) fn is_pending(&self) -> bool {
253        let state = unsafe { GetPromiseState(self.promise_obj()) };
254        matches!(state, PromiseState::Pending)
255    }
256
257    #[expect(unsafe_code)]
258    pub(crate) fn promise_obj(&self) -> HandleObject<'_> {
259        let obj = self.reflector().get_jsobject();
260        unsafe {
261            assert!(IsPromiseObject(obj));
262        }
263        obj
264    }
265
266    #[expect(unsafe_code)]
267    pub(crate) fn append_native_handler(
268        &self,
269        cx: &mut CurrentRealm,
270        handler: &PromiseNativeHandler,
271    ) {
272        run_a_script::<DomTypeHolder, _, _>(cx, &GlobalScope::from_current_realm(cx), |cx| {
273            rooted!(&in(cx) let resolve_func =
274                create_native_handler_function(cx,
275                                               handler.reflector().get_jsobject(),
276                                               NativeHandlerTask::Resolve));
277
278            rooted!(&in(cx) let reject_func =
279                create_native_handler_function(cx,
280                                               handler.reflector().get_jsobject(),
281                                               NativeHandlerTask::Reject));
282
283            unsafe {
284                let ok = AddPromiseReactions(
285                    cx,
286                    self.promise_obj(),
287                    resolve_func.handle(),
288                    reject_func.handle(),
289                );
290                assert!(ok);
291            }
292        })
293    }
294
295    #[expect(unsafe_code)]
296    pub(crate) fn get_promise_is_handled(&self) -> bool {
297        unsafe { GetPromiseIsHandled(self.reflector().get_jsobject()) }
298    }
299
300    #[expect(unsafe_code)]
301    pub(crate) fn set_promise_is_handled(&self, cx: &mut JSContext) -> bool {
302        unsafe { SetAnyPromiseIsHandled(cx, self.reflector().get_jsobject()) }
303    }
304}
305
306#[expect(unsafe_code)]
307unsafe extern "C" fn do_nothing_promise_executor(
308    _cx: *mut RawJSContext,
309    argc: u32,
310    vp: *mut JSVal,
311) -> bool {
312    let args = unsafe { CallArgs::from_vp(vp, argc) };
313    args.rval().set(UndefinedValue());
314    true
315}
316
317const SLOT_NATIVEHANDLER: usize = 0;
318const SLOT_NATIVEHANDLER_TASK: usize = 1;
319
320#[derive(PartialEq)]
321enum NativeHandlerTask {
322    Resolve = 0,
323    Reject = 1,
324}
325
326#[expect(unsafe_code)]
327unsafe extern "C" fn native_handler_callback(
328    cx: *mut RawJSContext,
329    argc: u32,
330    vp: *mut JSVal,
331) -> bool {
332    // SAFETY: it is safe to construct a JSContext from engine hook.
333    let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
334    let mut cx = CurrentRealm::assert(&mut cx);
335    let cx = &mut cx;
336
337    let args = unsafe { CallArgs::from_vp(vp, argc) };
338    let native_handler_value =
339        unsafe { *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER) };
340    rooted!(&in(cx) let native_handler_value = native_handler_value);
341    assert!(native_handler_value.get().is_object());
342
343    let handler = unsafe {
344        root_from_object::<PromiseNativeHandler>(native_handler_value.to_object(), cx.raw_cx())
345    }
346    .expect("unexpected value for native handler in promise native handler callback");
347
348    let native_handler_task_value =
349        unsafe { *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER_TASK) };
350    rooted!(&in(cx) let native_handler_task_value = native_handler_task_value);
351    match native_handler_task_value.to_int32() {
352        native_handler_task_value
353            if native_handler_task_value == NativeHandlerTask::Resolve as i32 =>
354        {
355            handler.resolved_callback(cx, unsafe { HandleValue::from_raw(args.get(0)) })
356        },
357        native_handler_task_value
358            if native_handler_task_value == NativeHandlerTask::Reject as i32 =>
359        {
360            handler.rejected_callback(cx, unsafe { HandleValue::from_raw(args.get(0)) })
361        },
362        _ => panic!("unexpected native handler task value"),
363    };
364
365    true
366}
367
368#[expect(unsafe_code)]
369fn create_native_handler_function(
370    cx: &mut JSContext,
371    holder: HandleObject,
372    task: NativeHandlerTask,
373) -> *mut JSObject {
374    unsafe {
375        let func = NewFunctionWithReserved(cx, Some(native_handler_callback), 1, 0, ptr::null());
376        assert!(!func.is_null());
377
378        rooted!(&in(cx) let obj = JS_GetFunctionObject(func));
379        assert!(!obj.is_null());
380        SetFunctionNativeReserved(obj.get(), SLOT_NATIVEHANDLER, &ObjectValue(*holder));
381        SetFunctionNativeReserved(obj.get(), SLOT_NATIVEHANDLER_TASK, &Int32Value(task as i32));
382        obj.get()
383    }
384}
385
386impl FromJSValConvertibleRc for Promise {
387    #[expect(unsafe_code)]
388    unsafe fn from_jsval(
389        _cx: *mut RawJSContext,
390        value: HandleValue,
391    ) -> Result<ConversionResult<Rc<Promise>>, ()> {
392        // TODO https://github.com/servo/mozjs/issues/749
393        let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
394        Self::safe_from_jsval(&mut cx, value)
395    }
396
397    fn safe_from_jsval(
398        cx: &mut JSContext,
399        value: HandleValue,
400    ) -> Result<ConversionResult<Rc<Promise>>, ()> {
401        if value.get().is_null() {
402            return Ok(ConversionResult::Failure(c"null not allowed".into()));
403        }
404
405        let realm = CurrentRealm::assert(cx);
406        let global_scope = GlobalScope::from_current_realm(&realm);
407
408        let promise = Promise::new_resolved(cx, &global_scope, value);
409        Ok(ConversionResult::Success(promise))
410    }
411}
412
413/// The success steps of <https://webidl.spec.whatwg.org/#wait-for-all>
414type WaitForAllSuccessSteps = Rc<dyn Fn(&mut JSContext, Vec<HandleValue>)>;
415
416/// The failure steps of <https://webidl.spec.whatwg.org/#wait-for-all>
417type WaitForAllFailureSteps = Rc<dyn Fn(&mut JSContext, HandleValue)>;
418
419/// The fulfillment handler for the list of promises in
420/// <https://webidl.spec.whatwg.org/#wait-for-all>.
421#[derive(JSTraceable, MallocSizeOf)]
422#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
423struct WaitForAllFulfillmentHandler {
424    /// The steps to call when all promises are resolved.
425    #[ignore_malloc_size_of = "callbacks are hard"]
426    #[no_trace]
427    success_steps: WaitForAllSuccessSteps,
428
429    /// The results of the promises.
430    #[ignore_malloc_size_of = "mozjs"]
431    #[expect(clippy::vec_box)]
432    result: Rc<RefCell<Vec<Box<Heap<JSVal>>>>>,
433
434    /// The index identifying which promise this handler is attached to.
435    promise_index: usize,
436
437    /// A count of fulfilled promises.
438    #[conditional_malloc_size_of]
439    fulfilled_count: Rc<RefCell<usize>>,
440}
441
442impl Callback for WaitForAllFulfillmentHandler {
443    fn callback(&self, cx: &mut CurrentRealm, v: HandleValue) {
444        // Let fulfillmentHandler be the following steps given arg:
445
446        let equals_total = {
447            // Set result[promiseIndex] to arg.
448            let result = self.result.borrow_mut();
449            result[self.promise_index].set(v.get());
450
451            // Set fulfilledCount to fulfilledCount + 1.
452            let mut fulfilled_count = self.fulfilled_count.borrow_mut();
453            *fulfilled_count += 1;
454
455            *fulfilled_count == result.len()
456        };
457
458        // If fulfilledCount equals total, then perform successSteps given result.
459        if equals_total {
460            let result_ref = self.result.borrow();
461            let result_handles: Vec<HandleValue> =
462                result_ref.iter().map(|v| v.as_handle_value()).collect();
463
464            (self.success_steps)(cx, result_handles);
465        }
466    }
467}
468
469/// The rejection handler for the list of promises in
470/// <https://webidl.spec.whatwg.org/#wait-for-all>.
471#[derive(Clone, JSTraceable, MallocSizeOf)]
472struct WaitForAllRejectionHandler {
473    /// The steps to call if any promise rejects.
474    #[ignore_malloc_size_of = "callbacks are hard"]
475    #[no_trace]
476    failure_steps: WaitForAllFailureSteps,
477
478    /// Whether any promises have been rejected already.
479    rejected: Cell<bool>,
480}
481
482impl Callback for WaitForAllRejectionHandler {
483    fn callback(&self, cx: &mut CurrentRealm, v: HandleValue) {
484        // Let rejectionHandlerSteps be the following steps given arg:
485
486        if self.rejected.replace(true) {
487            // If rejected is true, abort these steps.
488            return;
489        }
490
491        // Set rejected to true.
492        // Done above with `replace`.
493        (self.failure_steps)(cx, v);
494    }
495}
496
497/// The microtask for performing successSteps given « » in
498/// <https://webidl.spec.whatwg.org/#wait-for-all>.
499#[derive(JSTraceable, MallocSizeOf)]
500pub(crate) struct WaitForAllSuccessStepsMicrotask {
501    global: DomRoot<GlobalScope>,
502
503    #[ignore_malloc_size_of = "Closure is hard"]
504    #[no_trace]
505    success_steps: WaitForAllSuccessSteps,
506}
507
508impl MicrotaskRunnable for WaitForAllSuccessStepsMicrotask {
509    fn handler(&self, cx: &mut JSContext) {
510        (self.success_steps)(cx, vec![]);
511    }
512
513    fn enter_realm<'cx>(&self, cx: &'cx mut JSContext) -> AutoRealm<'cx> {
514        enter_auto_realm(cx, &*self.global)
515    }
516}
517
518/// <https://webidl.spec.whatwg.org/#wait-for-all>
519#[cfg_attr(crown, expect(crown::unrooted_must_root))]
520fn wait_for_all(
521    cx: &mut CurrentRealm,
522    global: &GlobalScope,
523    promises: Vec<Rc<Promise>>,
524    success_steps: WaitForAllSuccessSteps,
525    failure_steps: WaitForAllFailureSteps,
526) {
527    // Let fulfilledCount be 0.
528    let fulfilled_count: Rc<RefCell<usize>> = Default::default();
529
530    // Let rejected be false.
531    // Note: done below when constructing a rejection handler.
532
533    // Let rejectionHandlerSteps be the following steps given arg:
534    // Note: implemented with the `WaitForAllRejectionHandler`.
535
536    // Let rejectionHandler be CreateBuiltinFunction(rejectionHandlerSteps, « »):
537    // Note: done as part of attaching the `WaitForAllRejectionHandler` as native rejection handler.
538    let rejection_handler = WaitForAllRejectionHandler {
539        failure_steps,
540        rejected: Default::default(),
541    };
542
543    // Let total be promises’s size.
544    // Note: done using the len of result.
545
546    // If total is 0, then:
547    if promises.is_empty() {
548        // Queue a microtask to perform successSteps given « ».
549        global.enqueue_microtask(
550            cx,
551            Microtask::WaitForAllSuccessSteps(WaitForAllSuccessStepsMicrotask {
552                global: DomRoot::from_ref(global),
553                success_steps,
554            }),
555        );
556
557        // Return.
558        return;
559    }
560
561    // Let index be 0.
562    // Note: done with `enumerate` below.
563
564    // Let result be a list containing total null values.
565    let result: Rc<RefCell<Vec<Box<Heap<JSVal>>>>> = Default::default();
566
567    // For each promise of promises:
568    for (promise_index, promise) in promises.into_iter().enumerate() {
569        let result = result.clone();
570
571        {
572            // Note: adding a null value for this promise result.
573            let mut result_list = result.borrow_mut();
574            rooted!(&in(cx) let null_value = NullValue());
575            result_list.push(Heap::boxed(null_value.get()));
576        }
577
578        // Let promiseIndex be index.
579        // Note: done with `enumerate` above.
580
581        // Let fulfillmentHandler be the following steps given arg:
582        // Note: implemented with the `WaitForAllFulFillmentHandler`.
583
584        // Let fulfillmentHandler be CreateBuiltinFunction(fulfillmentHandler, « »):
585        // Note: passed below to avoid the need to root it.
586
587        // Perform PerformPromiseThen(promise, fulfillmentHandler, rejectionHandler).
588        let handler = PromiseNativeHandler::new(
589            cx,
590            global,
591            Some(Box::new(WaitForAllFulfillmentHandler {
592                success_steps: success_steps.clone(),
593                result,
594                promise_index,
595                fulfilled_count: fulfilled_count.clone(),
596            })),
597            Some(Box::new(rejection_handler.clone())),
598        );
599        promise.append_native_handler(cx, &handler);
600
601        // Set index to index + 1.
602        // Note: done above with `enumerate`.
603    }
604}
605
606/// <https://webidl.spec.whatwg.org/#waiting-for-all-promise>
607pub(crate) fn wait_for_all_promise(
608    cx: &mut CurrentRealm,
609    global: &GlobalScope,
610    promises: Vec<Rc<Promise>>,
611) -> Rc<Promise> {
612    // Let promise be a new promise of type Promise<sequence<T>> in realm.
613    let promise = Promise::new(cx, global);
614    let success_promise = promise.clone();
615    let failure_promise = promise.clone();
616
617    // Let successSteps be the following steps, given results:
618    let success_steps = Rc::new(move |cx: &mut JSContext, results: Vec<HandleValue>| {
619        // Resolve promise with results.
620        success_promise.resolve_native(cx, &results);
621    });
622
623    // Let failureSteps be the following steps, given reason:
624    let failure_steps = Rc::new(move |cx: &mut JSContext, reason: HandleValue| {
625        // Reject promise with reason.
626        failure_promise.reject_native(cx, &reason);
627    });
628
629    // Wait for all with promises, given successSteps and failureSteps.
630    wait_for_all(cx, global, promises, success_steps, failure_steps);
631
632    // Return promise.
633    promise
634}