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