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