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, 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
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
94impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
95 IterableIterator<D, T>
96{
97 pub(crate) fn new(
99 realm: &mut CurrentRealm,
100 iterable: &T,
101 type_: IteratorType,
102 ) -> DomRoot<Self> {
103 let iterator = Box::new(IterableIterator {
104 reflector: Reflector::new(),
105 type_,
106 iterable: Dom::from_ref(iterable),
107 index: Cell::new(0),
108 _marker: NoTrace(PhantomData),
109 });
110 let global = D::GlobalScope::from_current_realm(realm);
111 <D as DomHelpers<D>>::reflect_dom_object_with_cx(realm, iterator, &*global)
112 }
113
114 #[expect(non_snake_case)]
116 pub fn Next(&self, cx: &mut JSContext, return_value: MutableHandleObject) -> Fallible<()> {
117 let index = self.index.get();
118 rooted!(&in(cx) let mut value = UndefinedValue());
119 let result = if index >= self.iterable.get_iterable_length(cx) {
120 dict_return(cx, return_value, true, value.handle())
121 } else {
122 match self.type_ {
123 IteratorType::Keys => {
124 self.iterable
125 .get_key_at_index(cx, index)
126 .safe_to_jsval(cx, value.handle_mut());
127 dict_return(cx, return_value, false, value.handle())
128 },
129 IteratorType::Values => {
130 self.iterable
131 .get_value_at_index(cx, index)
132 .safe_to_jsval(cx, value.handle_mut());
133 dict_return(cx, return_value, false, value.handle())
134 },
135 IteratorType::Entries => {
136 rooted!(&in(cx) let mut key = UndefinedValue());
137 self.iterable
138 .get_key_at_index(cx, index)
139 .safe_to_jsval(cx, key.handle_mut());
140 self.iterable
141 .get_value_at_index(cx, index)
142 .safe_to_jsval(cx, value.handle_mut());
143 key_and_value_return(cx, return_value, key.handle(), value.handle())
144 },
145 }
146 };
147 self.index.set(index + 1);
148 result
149 }
150}
151
152impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
153 DomObjectWrap<D> for IterableIterator<D, T>
154{
155 const WRAP: unsafe fn(
156 &mut JSContext,
157 &D::GlobalScope,
158 Option<HandleObject>,
159 Box<Self>,
160 ) -> Root<Dom<Self>> = T::ITER_WRAP;
161}
162
163fn dict_return(
164 cx: &mut JSContext,
165 mut result: MutableHandleObject,
166 done: bool,
167 value: HandleValue,
168) -> Fallible<()> {
169 let mut dict = IterableKeyOrValueResult::empty();
170 dict.done = done;
171 dict.value.set(value.get());
172
173 unsafe { result.set(JS_NewObject(cx, ptr::null())) };
174 dict.to_jsobject(cx, result);
175 Ok(())
176}
177
178fn key_and_value_return(
179 cx: &mut JSContext,
180 mut result: MutableHandleObject,
181 key: HandleValue,
182 value: HandleValue,
183) -> Fallible<()> {
184 let mut dict = IterableKeyAndValueResult::empty();
185 dict.done = false;
186 dict.value = Some(
187 vec![key, value]
188 .into_iter()
189 .map(|handle| RootedTraceableBox::from_box(Heap::boxed(handle.get())))
190 .collect(),
191 );
192
193 unsafe { result.set(JS_NewObject(cx, ptr::null())) };
194 dict.to_jsobject(cx, result);
195 Ok(())
196}