1use std::cell::RefCell;
6
7use dom_struct::dom_struct;
8use js::context::{JSContext, NoGC};
9use script_bindings::dom::UnrootedDom;
10use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
11use stylo_atoms::Atom;
12
13use crate::dom::ChildrenMutation;
14use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
15use crate::dom::bindings::root::{Dom, DomRoot};
16use crate::dom::bindings::str::DOMString;
17use crate::dom::document::Document;
18use crate::dom::html::htmlelement::HTMLElement;
19use crate::dom::html::htmlformelement::HTMLFormElement;
20use crate::dom::node::Node;
21use crate::dom::window::Window;
22
23#[derive(JSTraceable, MallocSizeOf)]
24#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
25pub(crate) enum NodeListType {
26 Simple(Vec<Dom<Node>>),
27 Children(ChildrenList),
28 Labels(LabelsList),
29 Radio(RadioList),
30 ElementsByName(ElementsByNameList),
31}
32
33#[dom_struct]
35pub(crate) struct NodeList {
36 reflector_: Reflector,
37 list_type: NodeListType,
38}
39
40impl NodeList {
41 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
42 pub(crate) fn new_inherited(list_type: NodeListType) -> NodeList {
43 NodeList {
44 reflector_: Reflector::new(),
45 list_type,
46 }
47 }
48
49 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
50 pub(crate) fn new(
51 cx: &mut JSContext,
52 window: &Window,
53 list_type: NodeListType,
54 ) -> DomRoot<NodeList> {
55 reflect_dom_object_with_cx(Box::new(NodeList::new_inherited(list_type)), window, cx)
56 }
57
58 pub(crate) fn new_simple_list<T>(
59 cx: &mut JSContext,
60 window: &Window,
61 iter: T,
62 ) -> DomRoot<NodeList>
63 where
64 T: Iterator<Item = DomRoot<Node>>,
65 {
66 NodeList::new(
67 cx,
68 window,
69 NodeListType::Simple(iter.map(|r| Dom::from_ref(&*r)).collect()),
70 )
71 }
72
73 pub(crate) fn new_simple_list_slice(
74 cx: &mut JSContext,
75 window: &Window,
76 slice: &[&Node],
77 ) -> DomRoot<NodeList> {
78 NodeList::new(
79 cx,
80 window,
81 NodeListType::Simple(slice.iter().map(|r| Dom::from_ref(*r)).collect()),
82 )
83 }
84
85 pub(crate) fn new_child_list(
86 cx: &mut JSContext,
87 window: &Window,
88 node: &Node,
89 ) -> DomRoot<NodeList> {
90 NodeList::new(cx, window, NodeListType::Children(ChildrenList::new(node)))
91 }
92
93 pub(crate) fn new_labels_list(
94 cx: &mut JSContext,
95 window: &Window,
96 element: &HTMLElement,
97 ) -> DomRoot<NodeList> {
98 NodeList::new(cx, window, NodeListType::Labels(LabelsList::new(element)))
99 }
100
101 pub(crate) fn new_elements_by_name_list(
102 cx: &mut JSContext,
103 window: &Window,
104 document: &Document,
105 name: DOMString,
106 ) -> DomRoot<NodeList> {
107 NodeList::new(
108 cx,
109 window,
110 NodeListType::ElementsByName(ElementsByNameList::new(document, name)),
111 )
112 }
113
114 pub(crate) fn empty(cx: &mut JSContext, window: &Window) -> DomRoot<NodeList> {
115 NodeList::new(cx, window, NodeListType::Simple(vec![]))
116 }
117}
118
119impl NodeListMethods<crate::DomTypeHolder> for NodeList {
120 fn Length(&self) -> u32 {
122 match self.list_type {
123 NodeListType::Simple(ref elems) => elems.len() as u32,
124 NodeListType::Children(ref list) => list.len(),
125 NodeListType::Labels(ref list) => list.len(),
126 NodeListType::Radio(ref list) => list.len(),
127 NodeListType::ElementsByName(ref list) => list.len(),
128 }
129 }
130
131 fn Item(&self, no_gc: &NoGC, index: u32) -> Option<DomRoot<Node>> {
133 self.item_unrooted(no_gc, index)
134 .map(|item| item.as_rooted())
135 }
136
137 fn IndexedGetter(&self, no_gc: &NoGC, index: u32) -> Option<DomRoot<Node>> {
139 self.Item(no_gc, index)
140 }
141}
142
143impl NodeList {
144 pub(crate) fn as_children_list(&self) -> &ChildrenList {
145 if let NodeListType::Children(ref list) = self.list_type {
146 list
147 } else {
148 panic!("called as_children_list() on a non-children node list")
149 }
150 }
151
152 pub(crate) fn item_unrooted<'a>(
153 &self,
154 no_gc: &'a NoGC,
155 index: u32,
156 ) -> Option<UnrootedDom<'a, Node>> {
157 match self.list_type {
158 NodeListType::Simple(ref elems) => elems
159 .get(index as usize)
160 .map(|node| UnrootedDom::from_dom(node.clone(), no_gc)),
161 NodeListType::Children(ref list) => list.item(no_gc, index),
162 NodeListType::Labels(ref list) => list.item(no_gc, index),
163 NodeListType::Radio(ref list) => list.item(no_gc, index),
164 NodeListType::ElementsByName(ref list) => list.item(no_gc, index),
165 }
166 }
167
168 pub(crate) fn iter<'a>(
169 &'a self,
170 no_gc: &'a NoGC,
171 ) -> impl Iterator<Item = UnrootedDom<'a, Node>> {
172 let len = self.Length();
173 (0..len).flat_map(move |i| self.item_unrooted(no_gc, i))
176 }
177}
178
179#[derive(JSTraceable, MallocSizeOf)]
180#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
181pub(crate) struct ChildrenList {
182 node: Dom<Node>,
183 cached_children: RefCell<Option<Vec<Dom<Node>>>>,
184}
185
186impl ChildrenList {
187 pub(crate) fn new(node: &Node) -> ChildrenList {
188 ChildrenList {
189 node: Dom::from_ref(node),
190 cached_children: RefCell::new(None),
191 }
192 }
193
194 pub(crate) fn len(&self) -> u32 {
195 self.node.children_count()
196 }
197
198 pub(crate) fn item<'a>(&self, no_gc: &'a NoGC, index: u32) -> Option<UnrootedDom<'a, Node>> {
199 self.cached_children
200 .borrow_mut()
201 .get_or_insert_with(|| {
202 self.node
203 .children_unrooted(no_gc)
204 .map(|child| (*child).clone())
205 .collect()
206 })
207 .get(index as usize)
208 .map(|child| UnrootedDom::from_dom(child.clone(), no_gc))
209 }
210
211 pub(crate) fn children_changed(&self, mutation: &ChildrenMutation) {
212 match mutation {
213 ChildrenMutation::Append { .. } |
214 ChildrenMutation::Insert { .. } |
215 ChildrenMutation::Prepend { .. } |
216 ChildrenMutation::Replace { .. } |
217 ChildrenMutation::ReplaceAll { .. } => *self.cached_children.borrow_mut() = None,
218 ChildrenMutation::ChangeText => {},
219 }
220 }
221}
222
223#[derive(JSTraceable, MallocSizeOf)]
232#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
233pub(crate) struct LabelsList {
234 element: Dom<HTMLElement>,
235}
236
237impl LabelsList {
238 pub(crate) fn new(element: &HTMLElement) -> LabelsList {
239 LabelsList {
240 element: Dom::from_ref(element),
241 }
242 }
243
244 pub(crate) fn len(&self) -> u32 {
245 self.element.labels_count()
246 }
247
248 pub(crate) fn item<'a>(&self, no_gc: &'a NoGC, index: u32) -> Option<UnrootedDom<'a, Node>> {
249 self.element.label_at(no_gc, index)
250 }
251}
252
253#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
259pub(crate) enum RadioListMode {
260 ControlsExceptImageInputs,
261 Images,
262}
263
264#[derive(JSTraceable, MallocSizeOf)]
265#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
266pub(crate) struct RadioList {
267 form: Dom<HTMLFormElement>,
268 mode: RadioListMode,
269 #[no_trace]
270 name: Atom,
271}
272
273impl RadioList {
274 pub(crate) fn new(form: &HTMLFormElement, mode: RadioListMode, name: Atom) -> RadioList {
275 RadioList {
276 form: Dom::from_ref(form),
277 mode,
278 name,
279 }
280 }
281
282 pub(crate) fn len(&self) -> u32 {
283 self.form.count_for_radio_list(self.mode, &self.name)
284 }
285
286 pub(crate) fn item<'a>(&self, no_gc: &'a NoGC, index: u32) -> Option<UnrootedDom<'a, Node>> {
287 self.form
288 .nth_for_radio_list(no_gc, index, self.mode, &self.name)
289 }
290}
291
292#[derive(JSTraceable, MallocSizeOf)]
293#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
294pub(crate) struct ElementsByNameList {
295 document: Dom<Document>,
296 name: DOMString,
297}
298
299impl ElementsByNameList {
300 pub(crate) fn new(document: &Document, name: DOMString) -> ElementsByNameList {
301 ElementsByNameList {
302 document: Dom::from_ref(document),
303 name,
304 }
305 }
306
307 pub(crate) fn len(&self) -> u32 {
308 self.document.elements_by_name_count(&self.name)
309 }
310
311 pub(crate) fn item<'a>(&self, no_gc: &'a NoGC, index: u32) -> Option<UnrootedDom<'a, Node>> {
312 self.document.nth_element_by_name(no_gc, index, &self.name)
313 }
314}