script_bindings/
iterable.rs1use std::cell::Cell;
8use std::marker::PhantomData;
9use std::ptr;
10use std::ptr::NonNull;
11
12use dom_struct::dom_struct;
13use js::conversions::ToJSValConvertible;
14use js::jsapi::{Heap, JSObject};
15use js::jsval::UndefinedValue;
16use js::rust::{HandleObject, HandleValue, MutableHandleObject};
17
18use crate::codegen::GenericBindings::IterableIteratorBinding::{
19 IterableKeyAndValueResult, IterableKeyOrValueResult,
20};
21use crate::conversions::IDLInterface;
22use crate::error::Fallible;
23use crate::interfaces::DomHelpers;
24use crate::realms::InRealm;
25use crate::reflector::{DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector};
26use crate::root::{Dom, DomRoot, Root};
27use crate::script_runtime::{CanGc, JSContext};
28use crate::trace::{NoTrace, RootedTraceableBox};
29use crate::utils::DOMClass;
30use crate::{DomTypes, JSTraceable};
31
32#[derive(JSTraceable, MallocSizeOf)]
34pub(crate) enum IteratorType {
35 Keys,
37 Values,
39 Entries,
41}
42
43pub trait Iterable {
45 type Key: ToJSValConvertible;
47 type Value: ToJSValConvertible;
49 fn get_iterable_length(&self) -> u32;
51 fn get_value_at_index(&self, index: u32) -> Self::Value;
53 fn get_key_at_index(&self, index: u32) -> Self::Key;
55}
56
57pub trait IteratorDerives {
62 fn derives(class: &'static DOMClass) -> bool;
63}
64
65#[dom_struct]
67pub struct IterableIterator<
68 D: DomTypes,
69 T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>,
70> {
71 reflector: Reflector,
72 iterable: Dom<T>,
73 type_: IteratorType,
74 index: Cell<u32>,
75 _marker: NoTrace<PhantomData<D>>,
76}
77
78impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable> IterableIterator<D, T> {
79 pub fn global_(&self, realm: InRealm) -> DomRoot<D::GlobalScope> {
80 <Self as DomGlobalGeneric<D>>::global_(self, realm)
81 }
82}
83
84impl<
85 D: DomTypes,
86 T: DomObjectIteratorWrap<D>
87 + JSTraceable
88 + Iterable
89 + DomGlobalGeneric<D>
90 + IDLInterface
91 + IteratorDerives,
92> IDLInterface for IterableIterator<D, T>
93{
94 fn derives(class: &'static DOMClass) -> bool {
95 <T as IteratorDerives>::derives(class)
96 }
97}
98
99impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
100 IterableIterator<D, T>
101{
102 pub(crate) fn new(iterable: &T, type_: IteratorType, realm: InRealm) -> DomRoot<Self> {
104 let iterator = Box::new(IterableIterator {
105 reflector: Reflector::new(),
106 type_,
107 iterable: Dom::from_ref(iterable),
108 index: Cell::new(0),
109 _marker: NoTrace(PhantomData),
110 });
111 <D as DomHelpers<D>>::reflect_dom_object(
112 iterator,
113 &*iterable.global_(realm),
114 CanGc::deprecated_note(),
115 )
116 }
117
118 #[expect(non_snake_case)]
120 pub fn Next(&self, cx: JSContext) -> Fallible<NonNull<JSObject>> {
121 let index = self.index.get();
122 rooted!(in(*cx) let mut value = UndefinedValue());
123 rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
124 let result = if index >= self.iterable.get_iterable_length() {
125 dict_return(cx, rval.handle_mut(), true, value.handle())
126 } else {
127 match self.type_ {
128 IteratorType::Keys => {
129 unsafe {
130 self.iterable
131 .get_key_at_index(index)
132 .to_jsval(*cx, value.handle_mut());
133 }
134 dict_return(cx, rval.handle_mut(), false, value.handle())
135 },
136 IteratorType::Values => {
137 unsafe {
138 self.iterable
139 .get_value_at_index(index)
140 .to_jsval(*cx, value.handle_mut());
141 }
142 dict_return(cx, rval.handle_mut(), false, value.handle())
143 },
144 IteratorType::Entries => {
145 rooted!(in(*cx) let mut key = UndefinedValue());
146 unsafe {
147 self.iterable
148 .get_key_at_index(index)
149 .to_jsval(*cx, key.handle_mut());
150 self.iterable
151 .get_value_at_index(index)
152 .to_jsval(*cx, value.handle_mut());
153 }
154 key_and_value_return(cx, rval.handle_mut(), key.handle(), value.handle())
155 },
156 }
157 };
158 self.index.set(index + 1);
159 result.map(|_| NonNull::new(rval.get()).expect("got a null pointer"))
160 }
161}
162
163impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
164 DomObjectWrap<D> for IterableIterator<D, T>
165{
166 const WRAP: unsafe fn(
167 &mut js::context::JSContext,
168 &D::GlobalScope,
169 Option<HandleObject>,
170 Box<Self>,
171 ) -> Root<Dom<Self>> = T::ITER_WRAP;
172}
173
174fn dict_return(
175 cx: JSContext,
176 mut result: MutableHandleObject,
177 done: bool,
178 value: HandleValue,
179) -> Fallible<()> {
180 let mut dict = IterableKeyOrValueResult::empty();
181 dict.done = done;
182 dict.value.set(value.get());
183 rooted!(in(*cx) let mut dict_value = UndefinedValue());
184 unsafe {
185 dict.to_jsval(*cx, dict_value.handle_mut());
186 }
187 result.set(dict_value.to_object());
188 Ok(())
189}
190
191fn key_and_value_return(
192 cx: JSContext,
193 mut result: MutableHandleObject,
194 key: HandleValue,
195 value: HandleValue,
196) -> Fallible<()> {
197 let mut dict = IterableKeyAndValueResult::empty();
198 dict.done = false;
199 dict.value = Some(
200 vec![key, value]
201 .into_iter()
202 .map(|handle| RootedTraceableBox::from_box(Heap::boxed(handle.get())))
203 .collect(),
204 );
205 rooted!(in(*cx) let mut dict_value = UndefinedValue());
206 unsafe {
207 dict.to_jsval(*cx, dict_value.handle_mut());
208 }
209 result.set(dict_value.to_object());
210 Ok(())
211}