1use 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#[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
50 fn get_iterable_length(&self, cx: &mut JSContext) -> u32;
52 fn get_value_at_index(&self, cx: &mut JSContext, index: u32) -> Self::Value;
54 fn get_key_at_index(&self, cx: &mut JSContext, index: u32) -> Self::Key;
56}
57
58pub trait IteratorDerives {
63 fn derives(class: &'static DOMClass) -> bool;
64}
65
66#[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 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 #[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}