1use std::cell::Cell;
8use std::marker::PhantomData;
9
10use dom_struct::dom_struct;
11use js::context::JSContext;
12use js::conversions::ToJSValConvertible;
13use js::jsapi::Heap;
14use js::jsval::UndefinedValue;
15use js::realm::CurrentRealm;
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, GlobalScopeHelpers};
24use crate::js::JS_NewObject;
25use crate::reflector::{DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector};
26use crate::root::{Dom, DomRoot, Root};
27use crate::trace::NoTrace;
28use crate::utils::DOMClass;
29use crate::{DomTypes, JSTraceable};
30
31#[derive(JSTraceable, MallocSizeOf)]
33pub(crate) enum IteratorType {
34 Keys,
36 Values,
38 Entries,
40}
41
42pub trait Iterable {
44 type Key: ToJSValConvertible;
46 type Value: ToJSValConvertible;
48
49 fn get_iterable_length(&self, cx: &mut JSContext) -> u32;
51 fn get_value_at_index(&self, cx: &mut JSContext, index: u32) -> Self::Value;
53 fn get_key_at_index(&self, cx: &mut JSContext, index: u32) -> Self::Key;
55}
56
57pub trait IteratorDerives {
62 fn derives(class: &'static DOMClass) -> bool;
63}
64
65#[dom_struct(no_has_parent)]
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<
79 D: DomTypes,
80 T: DomObjectIteratorWrap<D>
81 + JSTraceable
82 + Iterable
83 + DomGlobalGeneric<D>
84 + IDLInterface
85 + IteratorDerives,
86> IDLInterface for IterableIterator<D, T>
87{
88 fn derives(class: &'static DOMClass) -> bool {
89 <T as IteratorDerives>::derives(class)
90 }
91
92 const PROTO_ID: crate::codegen::PrototypeList::ID = crate::codegen::PrototypeList::ID::Last;
93}
94
95impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
96 IterableIterator<D, T>
97{
98 pub(crate) fn new(
100 realm: &mut CurrentRealm,
101 iterable: &T,
102 type_: IteratorType,
103 ) -> 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 let global = D::GlobalScope::from_current_realm(realm);
112 <D as DomHelpers<D>>::reflect_dom_object(realm, iterator, &*global)
113 }
114
115 #[expect(non_snake_case)]
117 pub fn Next(&self, cx: &mut JSContext, return_value: MutableHandleObject) -> Fallible<()> {
118 let index = self.index.get();
119 rooted!(&in(cx) let mut value = UndefinedValue());
120 let result = if index >= self.iterable.get_iterable_length(cx) {
121 dict_return(cx, return_value, true, value.handle())
122 } else {
123 match self.type_ {
124 IteratorType::Keys => {
125 self.iterable
126 .get_key_at_index(cx, index)
127 .to_jsval(cx, value.handle_mut());
128 dict_return(cx, return_value, false, value.handle())
129 },
130 IteratorType::Values => {
131 self.iterable
132 .get_value_at_index(cx, index)
133 .to_jsval(cx, value.handle_mut());
134 dict_return(cx, return_value, false, value.handle())
135 },
136 IteratorType::Entries => {
137 rooted!(&in(cx) let mut key = UndefinedValue());
138 self.iterable
139 .get_key_at_index(cx, index)
140 .to_jsval(cx, key.handle_mut());
141 self.iterable
142 .get_value_at_index(cx, index)
143 .to_jsval(cx, value.handle_mut());
144 key_and_value_return(cx, return_value, key.handle(), value.handle())
145 },
146 }
147 };
148 self.index.set(index + 1);
149 result
150 }
151}
152
153impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
154 DomObjectWrap<D> for IterableIterator<D, T>
155{
156 const WRAP: unsafe fn(
157 &mut JSContext,
158 &D::GlobalScope,
159 Option<HandleObject>,
160 Box<Self>,
161 ) -> Root<Dom<Self>> = T::ITER_WRAP;
162}
163
164fn dict_return(
165 cx: &mut JSContext,
166 mut result: MutableHandleObject,
167 done: bool,
168 value: HandleValue,
169) -> Fallible<()> {
170 let mut dict = IterableKeyOrValueResult::empty();
171 dict.done = done;
172 dict.value.set(value.get());
173
174 result.set(JS_NewObject(cx, None));
175 dict.to_jsobject(cx, result);
176 Ok(())
177}
178
179fn key_and_value_return(
180 cx: &mut JSContext,
181 mut result: MutableHandleObject,
182 key: HandleValue,
183 value: HandleValue,
184) -> Fallible<()> {
185 let mut dict = IterableKeyAndValueResult::empty();
186 dict.done = false;
187 dict.value = Some(
188 vec![key, value]
189 .into_iter()
190 .map(|handle| Heap::boxed(handle.get()))
191 .collect(),
192 );
193
194 result.set(JS_NewObject(cx, None));
195 dict.to_jsobject(cx, result);
196 Ok(())
197}