1use std::cell::RefCell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
10use stylo_atoms::Atom;
11
12use crate::dom::ChildrenMutation;
13use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
14use crate::dom::bindings::root::{Dom, DomRoot};
15use crate::dom::bindings::str::DOMString;
16use crate::dom::document::Document;
17use crate::dom::html::htmlelement::HTMLElement;
18use crate::dom::html::htmlformelement::HTMLFormElement;
19use crate::dom::node::Node;
20use crate::dom::window::Window;
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 cx: &mut JSContext,
51 window: &Window,
52 list_type: NodeListType,
53 ) -> DomRoot<NodeList> {
54 reflect_dom_object_with_cx(Box::new(NodeList::new_inherited(list_type)), window, cx)
55 }
56
57 pub(crate) fn new_simple_list<T>(
58 cx: &mut JSContext,
59 window: &Window,
60 iter: T,
61 ) -> DomRoot<NodeList>
62 where
63 T: Iterator<Item = DomRoot<Node>>,
64 {
65 NodeList::new(
66 cx,
67 window,
68 NodeListType::Simple(iter.map(|r| Dom::from_ref(&*r)).collect()),
69 )
70 }
71
72 pub(crate) fn new_simple_list_slice(
73 cx: &mut JSContext,
74 window: &Window,
75 slice: &[&Node],
76 ) -> DomRoot<NodeList> {
77 NodeList::new(
78 cx,
79 window,
80 NodeListType::Simple(slice.iter().map(|r| Dom::from_ref(*r)).collect()),
81 )
82 }
83
84 pub(crate) fn new_child_list(
85 cx: &mut JSContext,
86 window: &Window,
87 node: &Node,
88 ) -> DomRoot<NodeList> {
89 NodeList::new(cx, window, NodeListType::Children(ChildrenList::new(node)))
90 }
91
92 pub(crate) fn new_labels_list(
93 cx: &mut JSContext,
94 window: &Window,
95 element: &HTMLElement,
96 ) -> DomRoot<NodeList> {
97 NodeList::new(cx, window, NodeListType::Labels(LabelsList::new(element)))
98 }
99
100 pub(crate) fn new_elements_by_name_list(
101 cx: &mut JSContext,
102 window: &Window,
103 document: &Document,
104 name: DOMString,
105 ) -> DomRoot<NodeList> {
106 NodeList::new(
107 cx,
108 window,
109 NodeListType::ElementsByName(ElementsByNameList::new(document, name)),
110 )
111 }
112
113 pub(crate) fn empty(cx: &mut JSContext, window: &Window) -> DomRoot<NodeList> {
114 NodeList::new(cx, window, NodeListType::Simple(vec![]))
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 iter(&self) -> impl Iterator<Item = DomRoot<Node>> + '_ {
159 let len = self.Length();
160 (0..len).flat_map(move |i| self.Item(i))
163 }
164}
165
166#[derive(JSTraceable, MallocSizeOf)]
167#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
168pub(crate) struct ChildrenList {
169 node: Dom<Node>,
170 cached_children: RefCell<Option<Vec<Dom<Node>>>>,
171}
172
173impl ChildrenList {
174 pub(crate) fn new(node: &Node) -> ChildrenList {
175 ChildrenList {
176 node: Dom::from_ref(node),
177 cached_children: RefCell::new(None),
178 }
179 }
180
181 pub(crate) fn len(&self) -> u32 {
182 self.node.children_count()
183 }
184
185 pub(crate) fn item(&self, index: u32) -> Option<DomRoot<Node>> {
186 self.cached_children
187 .borrow_mut()
188 .get_or_insert_with(|| {
189 self.node
190 .children()
191 .map(|child| Dom::from_ref(&*child))
192 .collect()
193 })
194 .get(index as usize)
195 .map(|child| DomRoot::from_ref(&**child))
196 }
197
198 pub(crate) fn children_changed(&self, mutation: &ChildrenMutation) {
199 match mutation {
200 ChildrenMutation::Append { .. } |
201 ChildrenMutation::Insert { .. } |
202 ChildrenMutation::Prepend { .. } |
203 ChildrenMutation::Replace { .. } |
204 ChildrenMutation::ReplaceAll { .. } => *self.cached_children.borrow_mut() = None,
205 ChildrenMutation::ChangeText => {},
206 }
207 }
208}
209
210#[derive(JSTraceable, MallocSizeOf)]
219#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
220pub(crate) struct LabelsList {
221 element: Dom<HTMLElement>,
222}
223
224impl LabelsList {
225 pub(crate) fn new(element: &HTMLElement) -> LabelsList {
226 LabelsList {
227 element: Dom::from_ref(element),
228 }
229 }
230
231 pub(crate) fn len(&self) -> u32 {
232 self.element.labels_count()
233 }
234
235 pub(crate) fn item(&self, index: u32) -> Option<DomRoot<Node>> {
236 self.element.label_at(index)
237 }
238}
239
240#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
246pub(crate) enum RadioListMode {
247 ControlsExceptImageInputs,
248 Images,
249}
250
251#[derive(JSTraceable, MallocSizeOf)]
252#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
253pub(crate) struct RadioList {
254 form: Dom<HTMLFormElement>,
255 mode: RadioListMode,
256 #[no_trace]
257 name: Atom,
258}
259
260impl RadioList {
261 pub(crate) fn new(form: &HTMLFormElement, mode: RadioListMode, name: Atom) -> RadioList {
262 RadioList {
263 form: Dom::from_ref(form),
264 mode,
265 name,
266 }
267 }
268
269 pub(crate) fn len(&self) -> u32 {
270 self.form.count_for_radio_list(self.mode, &self.name)
271 }
272
273 pub(crate) fn item(&self, index: u32) -> Option<DomRoot<Node>> {
274 self.form.nth_for_radio_list(index, self.mode, &self.name)
275 }
276}
277
278#[derive(JSTraceable, MallocSizeOf)]
279#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
280pub(crate) struct ElementsByNameList {
281 document: Dom<Document>,
282 name: DOMString,
283}
284
285impl ElementsByNameList {
286 pub(crate) fn new(document: &Document, name: DOMString) -> ElementsByNameList {
287 ElementsByNameList {
288 document: Dom::from_ref(document),
289 name,
290 }
291 }
292
293 pub(crate) fn len(&self) -> u32 {
294 self.document.elements_by_name_count(&self.name)
295 }
296
297 pub(crate) fn item(&self, index: u32) -> Option<DomRoot<Node>> {
298 self.document
299 .nth_element_by_name(index, &self.name)
300 .map(|n| DomRoot::from_ref(&*n))
301 }
302}