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