Skip to main content

script_bindings/
iterable.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//! Implementation of `iterable<...>` and `iterable<..., ...>` WebIDL declarations.
6
7use std::cell::Cell;
8use std::marker::PhantomData;
9use std::ptr;
10
11use dom_struct::dom_struct;
12use js::context::JSContext;
13use js::conversions::ToJSValConvertible;
14use js::jsapi::Heap;
15use js::jsval::UndefinedValue;
16use js::realm::CurrentRealm;
17use js::rust::wrappers2::JS_NewObject;
18use js::rust::{HandleObject, HandleValue, MutableHandleObject};
19
20use crate::codegen::GenericBindings::IterableIteratorBinding::{
21    IterableKeyAndValueResult, IterableKeyOrValueResult,
22};
23use crate::conversions::IDLInterface;
24use crate::error::Fallible;
25use crate::interfaces::{DomHelpers, GlobalScopeHelpers};
26use crate::reflector::{DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector};
27use crate::root::{Dom, DomRoot, Root};
28use crate::trace::NoTrace;
29use crate::utils::DOMClass;
30use crate::{DomTypes, JSTraceable};
31
32/// The values that an iterator will iterate over.
33#[derive(JSTraceable, MallocSizeOf)]
34pub(crate) enum IteratorType {
35    /// The keys of the iterable object.
36    Keys,
37    /// The values of the iterable object.
38    Values,
39    /// The keys and values of the iterable object combined.
40    Entries,
41}
42
43/// A DOM object that can be iterated over using a pair value iterator.
44pub trait Iterable {
45    /// The type of the key of the iterator pair.
46    type Key: ToJSValConvertible;
47    /// The type of the value of the iterator pair.
48    type Value: ToJSValConvertible;
49
50    /// Return the number of entries that can be iterated over.
51    fn get_iterable_length(&self, cx: &mut JSContext) -> u32;
52    /// Return the value at the provided index.
53    fn get_value_at_index(&self, cx: &mut JSContext, index: u32) -> Self::Value;
54    /// Return the key at the provided index.
55    fn get_key_at_index(&self, cx: &mut JSContext, index: u32) -> Self::Key;
56}
57
58/// A version of the [IDLInterface] trait that is specific to types that have
59/// iterators defined for them. This allows the `script` crate to define the
60/// derives check for the concrete interface type, while the [IteratableIterator]
61/// type defined in this module can be parameterized over an unknown generic.
62pub trait IteratorDerives {
63    fn derives(class: &'static DOMClass) -> bool;
64}
65
66/// An iterator over the iterable entries of a given DOM interface.
67#[dom_struct(no_has_parent)]
68pub struct IterableIterator<
69    D: DomTypes,
70    T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>,
71> {
72    reflector: Reflector,
73    iterable: Dom<T>,
74    type_: IteratorType,
75    index: Cell<u32>,
76    _marker: NoTrace<PhantomData<D>>,
77}
78
79impl<
80    D: DomTypes,
81    T: DomObjectIteratorWrap<D>
82        + JSTraceable
83        + Iterable
84        + DomGlobalGeneric<D>
85        + IDLInterface
86        + IteratorDerives,
87> IDLInterface for IterableIterator<D, T>
88{
89    fn derives(class: &'static DOMClass) -> bool {
90        <T as IteratorDerives>::derives(class)
91    }
92
93    const PROTO_ID: crate::codegen::PrototypeList::ID = crate::codegen::PrototypeList::ID::Last;
94}
95
96impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
97    IterableIterator<D, T>
98{
99    /// Create a new iterator instance for the provided iterable DOM interface.
100    pub(crate) fn new(
101        realm: &mut CurrentRealm,
102        iterable: &T,
103        type_: IteratorType,
104    ) -> DomRoot<Self> {
105        let iterator = Box::new(IterableIterator {
106            reflector: Reflector::new(),
107            type_,
108            iterable: Dom::from_ref(iterable),
109            index: Cell::new(0),
110            _marker: NoTrace(PhantomData),
111        });
112        let global = D::GlobalScope::from_current_realm(realm);
113        <D as DomHelpers<D>>::reflect_dom_object(realm, iterator, &*global)
114    }
115
116    /// Return the next value from the iterable object.
117    #[expect(non_snake_case)]
118    pub fn Next(&self, cx: &mut JSContext, return_value: MutableHandleObject) -> Fallible<()> {
119        let index = self.index.get();
120        rooted!(&in(cx) let mut value = UndefinedValue());
121        let result = if index >= self.iterable.get_iterable_length(cx) {
122            dict_return(cx, return_value, true, value.handle())
123        } else {
124            match self.type_ {
125                IteratorType::Keys => {
126                    self.iterable
127                        .get_key_at_index(cx, index)
128                        .to_jsval(cx, value.handle_mut());
129                    dict_return(cx, return_value, false, value.handle())
130                },
131                IteratorType::Values => {
132                    self.iterable
133                        .get_value_at_index(cx, index)
134                        .to_jsval(cx, value.handle_mut());
135                    dict_return(cx, return_value, false, value.handle())
136                },
137                IteratorType::Entries => {
138                    rooted!(&in(cx) let mut key = UndefinedValue());
139                    self.iterable
140                        .get_key_at_index(cx, index)
141                        .to_jsval(cx, key.handle_mut());
142                    self.iterable
143                        .get_value_at_index(cx, index)
144                        .to_jsval(cx, value.handle_mut());
145                    key_and_value_return(cx, return_value, key.handle(), value.handle())
146                },
147            }
148        };
149        self.index.set(index + 1);
150        result
151    }
152}
153
154impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
155    DomObjectWrap<D> for IterableIterator<D, T>
156{
157    const WRAP: unsafe fn(
158        &mut JSContext,
159        &D::GlobalScope,
160        Option<HandleObject>,
161        Box<Self>,
162    ) -> Root<Dom<Self>> = T::ITER_WRAP;
163}
164
165fn dict_return(
166    cx: &mut JSContext,
167    mut result: MutableHandleObject,
168    done: bool,
169    value: HandleValue,
170) -> Fallible<()> {
171    let mut dict = IterableKeyOrValueResult::empty();
172    dict.done = done;
173    dict.value.set(value.get());
174
175    unsafe { result.set(JS_NewObject(cx, ptr::null())) };
176    dict.to_jsobject(cx, result);
177    Ok(())
178}
179
180fn key_and_value_return(
181    cx: &mut JSContext,
182    mut result: MutableHandleObject,
183    key: HandleValue,
184    value: HandleValue,
185) -> Fallible<()> {
186    let mut dict = IterableKeyAndValueResult::empty();
187    dict.done = false;
188    dict.value = Some(
189        vec![key, value]
190            .into_iter()
191            .map(|handle| Heap::boxed(handle.get()))
192            .collect(),
193    );
194
195    unsafe { result.set(JS_NewObject(cx, ptr::null())) };
196    dict.to_jsobject(cx, result);
197    Ok(())
198}