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