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    NewFunctionWithReserved, PromiseState, PromiseUserInputEventHandlingState, RemoveRawValueRoot,
26    SetFunctionNativeReserved,
27};
28use js::jsval::{Int32Value, JSVal, NullValue, ObjectValue, UndefinedValue};
29use js::realm::{AutoRealm, CurrentRealm};
30use js::rust::wrappers::{
31    AddPromiseReactions, CallOriginalPromiseReject, CallOriginalPromiseResolve,
32    GetPromiseIsHandled, GetPromiseState, IsPromiseObject, NewPromiseObject, RejectPromise,
33    ResolvePromise, SetAnyPromiseIsHandled, SetPromiseUserInputEventHandlingState,
34};
35use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime};
36use script_bindings::conversions::SafeToJSValConvertible;
37use script_bindings::settings_stack::run_a_script;
38
39use crate::DomTypeHolder;
40use crate::dom::bindings::conversions::root_from_object;
41use crate::dom::bindings::error::{Error, ErrorToJsval};
42use crate::dom::bindings::reflector::{DomGlobal, DomObject, MutDomObject, Reflector};
43use crate::dom::bindings::root::{AsHandleValue, DomRoot};
44use crate::dom::globalscope::GlobalScope;
45use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
46use crate::microtask::{Microtask, MicrotaskRunnable};
47use crate::realms::{AlreadyInRealm, InRealm, enter_auto_realm, enter_realm};
48use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
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: SafeJSContext);
66}
67
68impl PromiseHelper for Rc<Promise> {
69    #[expect(unsafe_code)]
70    fn initialize(&self, cx: SafeJSContext) {
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        self.reflector.drop_memory(self);
90        unsafe {
91            let object = self.permanent_js_root.get().to_object();
92            assert!(!object.is_null());
93            if let Some(cx) = Runtime::get() {
94                RemoveRawValueRoot(cx.as_ptr(), self.permanent_js_root.get_unsafe());
95            }
96        }
97    }
98}
99
100impl Promise {
101    pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> Rc<Promise> {
102        let realm = enter_realm(global);
103        let comp = InRealm::Entered(&realm);
104        Promise::new_in_current_realm(comp, can_gc)
105    }
106
107    pub(crate) fn new_in_current_realm(_comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
108        let cx = GlobalScope::get_cx();
109        rooted!(in(*cx) let mut obj = ptr::null_mut::<JSObject>());
110        Promise::create_js_promise(cx, obj.handle_mut(), can_gc);
111        Promise::new_with_js_promise(obj.handle(), cx)
112    }
113
114    pub(crate) fn new2(cx: &mut js::context::JSContext, global: &GlobalScope) -> Rc<Promise> {
115        let mut realm = AutoRealm::new(
116            cx,
117            std::ptr::NonNull::new(global.reflector().get_jsobject().get()).unwrap(),
118        );
119        let mut current_realm = realm.current_realm();
120        Promise::new_in_realm(&mut current_realm)
121    }
122
123    pub(crate) fn new_in_realm(current_realm: &mut CurrentRealm) -> Rc<Promise> {
124        let cx = current_realm.deref_mut();
125        rooted!(&in(cx) let mut obj = ptr::null_mut::<JSObject>());
126        Promise::create_js_promise(cx.into(), obj.handle_mut(), CanGc::from_cx(cx));
127        Promise::new_with_js_promise(obj.handle(), cx.into())
128    }
129
130    pub(crate) fn duplicate(&self) -> Rc<Promise> {
131        let cx = GlobalScope::get_cx();
132        Promise::new_with_js_promise(self.reflector().get_jsobject(), cx)
133    }
134
135    #[expect(unsafe_code)]
136    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
137    pub(crate) fn new_with_js_promise(obj: HandleObject, cx: SafeJSContext) -> Rc<Promise> {
138        unsafe {
139            assert!(IsPromiseObject(obj));
140            let promise = Promise {
141                reflector: Reflector::new(),
142                permanent_js_root: Heap::default(),
143            };
144            let promise = Rc::new(promise);
145            promise.init_reflector::<Promise>(obj.get());
146            promise.initialize(cx);
147            promise
148        }
149    }
150
151    #[expect(unsafe_code)]
152    // The apparently-unused CanGc parameter reflects the fact that the JS API calls
153    // like JS_NewFunction can trigger a GC.
154    fn create_js_promise(cx: SafeJSContext, mut obj: MutableHandleObject, _can_gc: CanGc) {
155        unsafe {
156            let do_nothing_func = JS_NewFunction(
157                *cx,
158                Some(do_nothing_promise_executor),
159                /* nargs = */ 2,
160                /* flags = */ 0,
161                ptr::null(),
162            );
163            assert!(!do_nothing_func.is_null());
164            rooted!(in(*cx) let do_nothing_obj = JS_GetFunctionObject(do_nothing_func));
165            assert!(!do_nothing_obj.is_null());
166            obj.set(NewPromiseObject(*cx, do_nothing_obj.handle()));
167            assert!(!obj.is_null());
168            let is_user_interacting = if ScriptThread::is_user_interacting() {
169                PromiseUserInputEventHandlingState::HadUserInteractionAtCreation
170            } else {
171                PromiseUserInputEventHandlingState::DidntHaveUserInteractionAtCreation
172            };
173            SetPromiseUserInputEventHandlingState(obj.handle(), is_user_interacting);
174        }
175    }
176
177    #[expect(unsafe_code)]
178    pub(crate) fn new_resolved(
179        global: &GlobalScope,
180        cx: SafeJSContext,
181        value: impl SafeToJSValConvertible,
182        can_gc: CanGc,
183    ) -> Rc<Promise> {
184        let _ac = JSAutoRealm::new(*cx, global.reflector().get_jsobject().get());
185        rooted!(in(*cx) let mut rval = UndefinedValue());
186        value.safe_to_jsval(cx, rval.handle_mut(), can_gc);
187        unsafe {
188            rooted!(in(*cx) let p = CallOriginalPromiseResolve(*cx, rval.handle()));
189            assert!(!p.handle().is_null());
190            Promise::new_with_js_promise(p.handle(), cx)
191        }
192    }
193
194    #[expect(unsafe_code)]
195    pub(crate) fn new_rejected(
196        global: &GlobalScope,
197        cx: SafeJSContext,
198        value: impl SafeToJSValConvertible,
199        can_gc: CanGc,
200    ) -> Rc<Promise> {
201        let _ac = JSAutoRealm::new(*cx, global.reflector().get_jsobject().get());
202        rooted!(in(*cx) let mut rval = UndefinedValue());
203        value.safe_to_jsval(cx, rval.handle_mut(), can_gc);
204        unsafe {
205            rooted!(in(*cx) let p = CallOriginalPromiseReject(*cx, rval.handle()));
206            assert!(!p.handle().is_null());
207            Promise::new_with_js_promise(p.handle(), cx)
208        }
209    }
210
211    pub(crate) fn resolve_native<T>(&self, val: &T, can_gc: CanGc)
212    where
213        T: SafeToJSValConvertible,
214    {
215        let cx = GlobalScope::get_cx();
216        let _ac = enter_realm(self);
217        rooted!(in(*cx) let mut v = UndefinedValue());
218        val.safe_to_jsval(cx, v.handle_mut(), can_gc);
219        self.resolve(cx, v.handle(), can_gc);
220    }
221
222    #[expect(unsafe_code)]
223    pub(crate) fn resolve(&self, cx: SafeJSContext, value: HandleValue, _can_gc: CanGc) {
224        unsafe {
225            if !ResolvePromise(*cx, self.promise_obj(), value) {
226                JS_ClearPendingException(*cx);
227            }
228        }
229    }
230
231    pub(crate) fn reject_native<T>(&self, val: &T, can_gc: CanGc)
232    where
233        T: SafeToJSValConvertible,
234    {
235        let cx = GlobalScope::get_cx();
236        let _ac = enter_realm(self);
237        rooted!(in(*cx) let mut v = UndefinedValue());
238        val.safe_to_jsval(cx, v.handle_mut(), can_gc);
239        self.reject(cx, v.handle(), can_gc);
240    }
241
242    pub(crate) fn reject_error(&self, error: Error, can_gc: CanGc) {
243        let cx = GlobalScope::get_cx();
244        let _ac = enter_realm(self);
245        rooted!(in(*cx) let mut v = UndefinedValue());
246        error.to_jsval(cx, &self.global(), v.handle_mut(), can_gc);
247        self.reject(cx, v.handle(), can_gc);
248    }
249
250    #[expect(unsafe_code)]
251    pub(crate) fn reject(&self, cx: SafeJSContext, value: HandleValue, _can_gc: CanGc) {
252        unsafe {
253            if !RejectPromise(*cx, self.promise_obj(), value) {
254                JS_ClearPendingException(*cx);
255            }
256        }
257    }
258
259    #[expect(unsafe_code)]
260    pub(crate) fn is_fulfilled(&self) -> bool {
261        let state = unsafe { GetPromiseState(self.promise_obj()) };
262        matches!(state, PromiseState::Rejected | PromiseState::Fulfilled)
263    }
264
265    #[expect(unsafe_code)]
266    pub(crate) fn is_rejected(&self) -> bool {
267        let state = unsafe { GetPromiseState(self.promise_obj()) };
268        matches!(state, PromiseState::Rejected)
269    }
270
271    #[expect(unsafe_code)]
272    pub(crate) fn is_pending(&self) -> bool {
273        let state = unsafe { GetPromiseState(self.promise_obj()) };
274        matches!(state, PromiseState::Pending)
275    }
276
277    #[expect(unsafe_code)]
278    pub(crate) fn promise_obj(&self) -> HandleObject<'_> {
279        let obj = self.reflector().get_jsobject();
280        unsafe {
281            assert!(IsPromiseObject(obj));
282        }
283        obj
284    }
285
286    #[expect(unsafe_code)]
287    pub(crate) fn append_native_handler(
288        &self,
289        handler: &PromiseNativeHandler,
290        realm: InRealm,
291        can_gc: CanGc,
292    ) {
293        run_a_script::<DomTypeHolder, _>(&handler.global_(realm), || {
294            let cx = GlobalScope::get_cx();
295            rooted!(in(*cx) let resolve_func =
296                create_native_handler_function(*cx,
297                                               handler.reflector().get_jsobject(),
298                                               NativeHandlerTask::Resolve,
299                                               can_gc));
300
301            rooted!(in(*cx) let reject_func =
302                create_native_handler_function(*cx,
303                                               handler.reflector().get_jsobject(),
304                                               NativeHandlerTask::Reject,
305                                               can_gc));
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)]
394// The apparently-unused CanGc argument reflects the fact that the JS API calls
395// like NewFunctionWithReserved can trigger a GC.
396fn create_native_handler_function(
397    cx: *mut RawJSContext,
398    holder: HandleObject,
399    task: NativeHandlerTask,
400    _can_gc: CanGc,
401) -> *mut JSObject {
402    unsafe {
403        let func = NewFunctionWithReserved(cx, Some(native_handler_callback), 1, 0, ptr::null());
404        assert!(!func.is_null());
405
406        rooted!(in(cx) let obj = JS_GetFunctionObject(func));
407        assert!(!obj.is_null());
408        SetFunctionNativeReserved(obj.get(), SLOT_NATIVEHANDLER, &ObjectValue(*holder));
409        SetFunctionNativeReserved(obj.get(), SLOT_NATIVEHANDLER_TASK, &Int32Value(task as i32));
410        obj.get()
411    }
412}
413
414impl FromJSValConvertibleRc for Promise {
415    #[expect(unsafe_code)]
416    unsafe fn from_jsval(
417        cx: *mut RawJSContext,
418        value: HandleValue,
419    ) -> Result<ConversionResult<Rc<Promise>>, ()> {
420        if value.get().is_null() {
421            return Ok(ConversionResult::Failure(c"null not allowed".into()));
422        }
423
424        let cx = unsafe { SafeJSContext::from_ptr(cx) };
425        let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
426        let global_scope =
427            unsafe { GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)) };
428
429        let promise = Promise::new_resolved(&global_scope, cx, value, CanGc::note());
430        Ok(ConversionResult::Success(promise))
431    }
432}
433
434/// The success steps of <https://webidl.spec.whatwg.org/#wait-for-all>
435type WaitForAllSuccessSteps = Rc<dyn Fn(Vec<HandleValue>)>;
436
437/// The failure steps of <https://webidl.spec.whatwg.org/#wait-for-all>
438type WaitForAllFailureSteps = Rc<dyn Fn(HandleValue)>;
439
440/// The fulfillment handler for the list of promises in
441/// <https://webidl.spec.whatwg.org/#wait-for-all>.
442#[derive(JSTraceable, MallocSizeOf)]
443#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
444struct WaitForAllFulfillmentHandler {
445    /// The steps to call when all promises are resolved.
446    #[ignore_malloc_size_of = "callbacks are hard"]
447    #[no_trace]
448    success_steps: WaitForAllSuccessSteps,
449
450    /// The results of the promises.
451    #[ignore_malloc_size_of = "mozjs"]
452    #[expect(clippy::vec_box)]
453    result: Rc<RefCell<Vec<Box<Heap<JSVal>>>>>,
454
455    /// The index identifying which promise this handler is attached to.
456    promise_index: usize,
457
458    /// A count of fulfilled promises.
459    #[conditional_malloc_size_of]
460    fulfilled_count: Rc<RefCell<usize>>,
461}
462
463impl Callback for WaitForAllFulfillmentHandler {
464    fn callback(&self, _cx: &mut CurrentRealm, v: HandleValue) {
465        // Let fulfillmentHandler be the following steps given arg:
466
467        let equals_total = {
468            // Set result[promiseIndex] to arg.
469            let result = self.result.borrow_mut();
470            result[self.promise_index].set(v.get());
471
472            // Set fulfilledCount to fulfilledCount + 1.
473            let mut fulfilled_count = self.fulfilled_count.borrow_mut();
474            *fulfilled_count += 1;
475
476            *fulfilled_count == result.len()
477        };
478
479        // If fulfilledCount equals total, then perform successSteps given result.
480        if equals_total {
481            let result_ref = self.result.borrow();
482            let result_handles: Vec<HandleValue> =
483                result_ref.iter().map(|v| v.as_handle_value()).collect();
484
485            (self.success_steps)(result_handles);
486        }
487    }
488}
489
490/// The rejection handler for the list of promises in
491/// <https://webidl.spec.whatwg.org/#wait-for-all>.
492#[derive(Clone, JSTraceable, MallocSizeOf)]
493struct WaitForAllRejectionHandler {
494    /// The steps to call if any promise rejects.
495    #[ignore_malloc_size_of = "callbacks are hard"]
496    #[no_trace]
497    failure_steps: WaitForAllFailureSteps,
498
499    /// Whether any promises have been rejected already.
500    rejected: Cell<bool>,
501}
502
503impl Callback for WaitForAllRejectionHandler {
504    fn callback(&self, _cx: &mut CurrentRealm, v: HandleValue) {
505        // Let rejectionHandlerSteps be the following steps given arg:
506
507        if self.rejected.replace(true) {
508            // If rejected is true, abort these steps.
509            return;
510        }
511
512        // Set rejected to true.
513        // Done above with `replace`.
514        (self.failure_steps)(v);
515    }
516}
517
518/// The microtask for performing successSteps given « » in
519/// <https://webidl.spec.whatwg.org/#wait-for-all>.
520#[derive(JSTraceable, MallocSizeOf)]
521pub(crate) struct WaitForAllSuccessStepsMicrotask {
522    global: DomRoot<GlobalScope>,
523
524    #[ignore_malloc_size_of = "Closure is hard"]
525    #[no_trace]
526    success_steps: WaitForAllSuccessSteps,
527}
528
529impl MicrotaskRunnable for WaitForAllSuccessStepsMicrotask {
530    fn handler(&self, _cx: &mut JSContext) {
531        (self.success_steps)(vec![]);
532    }
533
534    fn enter_realm<'cx>(&self, cx: &'cx mut JSContext) -> AutoRealm<'cx> {
535        enter_auto_realm(cx, &*self.global)
536    }
537}
538
539/// <https://webidl.spec.whatwg.org/#wait-for-all>
540#[cfg_attr(crown, expect(crown::unrooted_must_root))]
541pub(crate) fn wait_for_all(
542    cx: SafeJSContext,
543    global: &GlobalScope,
544    promises: Vec<Rc<Promise>>,
545    success_steps: WaitForAllSuccessSteps,
546    failure_steps: WaitForAllFailureSteps,
547    realm: InRealm,
548    can_gc: CanGc,
549) {
550    // Let fulfilledCount be 0.
551    let fulfilled_count: Rc<RefCell<usize>> = Default::default();
552
553    // Let rejected be false.
554    // Note: done below when constructing a rejection handler.
555
556    // Let rejectionHandlerSteps be the following steps given arg:
557    // Note: implemented with the `WaitForAllRejectionHandler`.
558
559    // Let rejectionHandler be CreateBuiltinFunction(rejectionHandlerSteps, « »):
560    // Note: done as part of attaching the `WaitForAllRejectionHandler` as native rejection handler.
561    let rejection_handler = WaitForAllRejectionHandler {
562        failure_steps,
563        rejected: Default::default(),
564    };
565
566    // Let total be promises’s size.
567    // Note: done using the len of result.
568
569    // If total is 0, then:
570    if promises.is_empty() {
571        // Queue a microtask to perform successSteps given « ».
572        global.enqueue_microtask(Microtask::WaitForAllSuccessSteps(
573            WaitForAllSuccessStepsMicrotask {
574                global: DomRoot::from_ref(global),
575                success_steps,
576            },
577        ));
578
579        // Return.
580        return;
581    }
582
583    // Let index be 0.
584    // Note: done with `enumerate` below.
585
586    // Let result be a list containing total null values.
587    let result: Rc<RefCell<Vec<Box<Heap<JSVal>>>>> = Default::default();
588
589    // For each promise of promises:
590    for (promise_index, promise) in promises.into_iter().enumerate() {
591        let result = result.clone();
592
593        {
594            // Note: adding a null value for this promise result.
595            let mut result_list = result.borrow_mut();
596            rooted!(in(*cx) let null_value = NullValue());
597            result_list.push(Heap::boxed(null_value.get()));
598        }
599
600        // Let promiseIndex be index.
601        // Note: done with `enumerate` above.
602
603        // Let fulfillmentHandler be the following steps given arg:
604        // Note: implemented with the `WaitForAllFulFillmentHandler`.
605
606        // Let fulfillmentHandler be CreateBuiltinFunction(fulfillmentHandler, « »):
607        // Note: passed below to avoid the need to root it.
608
609        // Perform PerformPromiseThen(promise, fulfillmentHandler, rejectionHandler).
610        let handler = PromiseNativeHandler::new(
611            global,
612            Some(Box::new(WaitForAllFulfillmentHandler {
613                success_steps: success_steps.clone(),
614                result,
615                promise_index,
616                fulfilled_count: fulfilled_count.clone(),
617            })),
618            Some(Box::new(rejection_handler.clone())),
619            can_gc,
620        );
621        promise.append_native_handler(&handler, realm, can_gc);
622
623        // Set index to index + 1.
624        // Note: done above with `enumerate`.
625    }
626}
627
628/// <https://webidl.spec.whatwg.org/#waiting-for-all-promise>
629pub(crate) fn wait_for_all_promise(
630    cx: SafeJSContext,
631    global: &GlobalScope,
632    promises: Vec<Rc<Promise>>,
633    realm: InRealm,
634    can_gc: CanGc,
635) -> Rc<Promise> {
636    // Let promise be a new promise of type Promise<sequence<T>> in realm.
637    let promise = Promise::new(global, can_gc);
638    let success_promise = promise.clone();
639    let failure_promise = promise.clone();
640
641    // Let successSteps be the following steps, given results:
642    let success_steps = Rc::new(move |results: Vec<HandleValue>| {
643        // Resolve promise with results.
644        success_promise.resolve_native(&results, can_gc);
645    });
646
647    // Let failureSteps be the following steps, given reason:
648    let failure_steps = Rc::new(move |reason: HandleValue| {
649        // Reject promise with reason.
650        failure_promise.reject_native(&reason, can_gc);
651    });
652
653    // Wait for all with promises, given successSteps and failureSteps.
654    wait_for_all(
655        cx,
656        global,
657        promises,
658        success_steps,
659        failure_steps,
660        realm,
661        can_gc,
662    );
663
664    // Return promise.
665    promise
666}