script/dom/html/
htmlformcontrolscollection.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use stylo_atoms::Atom;
7
8use crate::dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
9use crate::dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods;
10use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
11use crate::dom::bindings::codegen::UnionTypes::RadioNodeListOrElement;
12use crate::dom::bindings::inheritance::Castable;
13use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
14use crate::dom::bindings::root::{Dom, DomRoot};
15use crate::dom::bindings::str::DOMString;
16use crate::dom::element::Element;
17use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
18use crate::dom::html::htmlformelement::HTMLFormElement;
19use crate::dom::node::Node;
20use crate::dom::radionodelist::RadioNodeList;
21use crate::dom::window::Window;
22use crate::script_runtime::CanGc;
23
24#[dom_struct]
25pub(crate) struct HTMLFormControlsCollection {
26    collection: HTMLCollection,
27    form: Dom<HTMLFormElement>,
28}
29
30impl HTMLFormControlsCollection {
31    fn new_inherited(
32        form: &HTMLFormElement,
33        filter: Box<dyn CollectionFilter + 'static>,
34    ) -> HTMLFormControlsCollection {
35        let root_of_form = form
36            .upcast::<Node>()
37            .GetRootNode(&GetRootNodeOptions::empty());
38        HTMLFormControlsCollection {
39            collection: HTMLCollection::new_inherited(&root_of_form, filter),
40            form: Dom::from_ref(form),
41        }
42    }
43
44    pub(crate) fn new(
45        window: &Window,
46        form: &HTMLFormElement,
47        filter: Box<dyn CollectionFilter + 'static>,
48        can_gc: CanGc,
49    ) -> DomRoot<HTMLFormControlsCollection> {
50        reflect_dom_object(
51            Box::new(HTMLFormControlsCollection::new_inherited(form, filter)),
52            window,
53            can_gc,
54        )
55    }
56}
57
58impl HTMLFormControlsCollectionMethods<crate::DomTypeHolder> for HTMLFormControlsCollection {
59    // FIXME: This shouldn't need to be implemented here since HTMLCollection (the parent of
60    // HTMLFormControlsCollection) implements Length
61    // https://dom.spec.whatwg.org/#dom-htmlcollection-length
62    fn Length(&self) -> u32 {
63        self.collection.Length()
64    }
65
66    // https://html.spec.whatwg.org/multipage/#dom-htmlformcontrolscollection-nameditem
67    fn NamedItem(&self, name: DOMString, can_gc: CanGc) -> Option<RadioNodeListOrElement> {
68        // Step 1
69        if name.is_empty() {
70            return None;
71        }
72
73        let name = Atom::from(name);
74
75        let mut filter_map = self.collection.elements_iter().filter_map(|elem| {
76            if elem.get_name().is_some_and(|n| n == name) ||
77                elem.get_id().is_some_and(|i| i == name)
78            {
79                Some(elem)
80            } else {
81                None
82            }
83        });
84
85        if let Some(elem) = filter_map.next() {
86            let mut peekable = filter_map.peekable();
87            // Step 2
88            if peekable.peek().is_none() {
89                Some(RadioNodeListOrElement::Element(elem))
90            } else {
91                // Step 4-5
92                let global = self.global();
93                let window = global.as_window();
94                // There is only one way to get an HTMLCollection,
95                // specifically HTMLFormElement::Elements(),
96                // and the collection filter excludes image inputs.
97                Some(RadioNodeListOrElement::RadioNodeList(
98                    RadioNodeList::new_controls_except_image_inputs(
99                        window, &self.form, &name, can_gc,
100                    ),
101                ))
102            }
103        // Step 3
104        } else {
105            None
106        }
107    }
108
109    // https://html.spec.whatwg.org/multipage/#dom-htmlformcontrolscollection-nameditem
110    fn NamedGetter(&self, name: DOMString, can_gc: CanGc) -> Option<RadioNodeListOrElement> {
111        self.NamedItem(name, can_gc)
112    }
113
114    // https://html.spec.whatwg.org/multipage/#the-htmlformcontrolscollection-interface:supported-property-names
115    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
116        self.collection.SupportedPropertyNames()
117    }
118
119    // FIXME: This shouldn't need to be implemented here since HTMLCollection (the parent of
120    // HTMLFormControlsCollection) implements IndexedGetter.
121    // https://github.com/servo/servo/issues/5875
122    //
123    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
124    fn IndexedGetter(&self, index: u32) -> Option<DomRoot<Element>> {
125        self.collection.IndexedGetter(index)
126    }
127}