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