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