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