Skip to main content

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