script/dom/
radionodelist.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::HTMLInputElementBinding::HTMLInputElementMethods;
9use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
10use crate::dom::bindings::codegen::Bindings::RadioNodeListBinding::RadioNodeListMethods;
11use crate::dom::bindings::inheritance::Castable;
12use crate::dom::bindings::reflector::reflect_dom_object;
13use crate::dom::bindings::root::DomRoot;
14use crate::dom::bindings::str::DOMString;
15use crate::dom::html::htmlformelement::HTMLFormElement;
16use crate::dom::html::htmlinputelement::{HTMLInputElement, InputType};
17use crate::dom::node::Node;
18use crate::dom::nodelist::{NodeList, NodeListType, RadioList, RadioListMode};
19use crate::dom::window::Window;
20use crate::script_runtime::CanGc;
21
22#[dom_struct]
23pub(crate) struct RadioNodeList {
24    node_list: NodeList,
25}
26
27impl RadioNodeList {
28    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
29    fn new_inherited(list_type: NodeListType) -> RadioNodeList {
30        RadioNodeList {
31            node_list: NodeList::new_inherited(list_type),
32        }
33    }
34
35    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
36    pub(crate) fn new(
37        window: &Window,
38        list_type: NodeListType,
39        can_gc: CanGc,
40    ) -> DomRoot<RadioNodeList> {
41        reflect_dom_object(
42            Box::new(RadioNodeList::new_inherited(list_type)),
43            window,
44            can_gc,
45        )
46    }
47
48    pub(crate) fn new_controls_except_image_inputs(
49        window: &Window,
50        form: &HTMLFormElement,
51        name: &Atom,
52        can_gc: CanGc,
53    ) -> DomRoot<RadioNodeList> {
54        RadioNodeList::new(
55            window,
56            NodeListType::Radio(RadioList::new(
57                form,
58                RadioListMode::ControlsExceptImageInputs,
59                name.clone(),
60            )),
61            can_gc,
62        )
63    }
64
65    pub(crate) fn new_images(
66        window: &Window,
67        form: &HTMLFormElement,
68        name: &Atom,
69        can_gc: CanGc,
70    ) -> DomRoot<RadioNodeList> {
71        RadioNodeList::new(
72            window,
73            NodeListType::Radio(RadioList::new(form, RadioListMode::Images, name.clone())),
74            can_gc,
75        )
76    }
77}
78
79impl RadioNodeListMethods<crate::DomTypeHolder> for RadioNodeList {
80    // https://dom.spec.whatwg.org/#dom-nodelist-length
81    // https://github.com/servo/servo/issues/5875
82    fn Length(&self) -> u32 {
83        self.node_list.Length()
84    }
85
86    // https://html.spec.whatwg.org/multipage/#dom-radionodelist-value
87    fn Value(&self) -> DOMString {
88        self.upcast::<NodeList>()
89            .iter()
90            .filter_map(|node| {
91                // Step 1
92                node.downcast::<HTMLInputElement>().and_then(|input| {
93                    if input.input_type() == InputType::Radio && input.Checked() {
94                        // Step 3-4
95                        let value = input.Value();
96                        Some(if value.is_empty() {
97                            DOMString::from("on")
98                        } else {
99                            value
100                        })
101                    } else {
102                        None
103                    }
104                })
105            })
106            .next()
107            // Step 2
108            .unwrap_or(DOMString::from(""))
109    }
110
111    // https://html.spec.whatwg.org/multipage/#dom-radionodelist-value
112    fn SetValue(&self, value: DOMString) {
113        for node in self.upcast::<NodeList>().iter() {
114            // Step 1
115            if let Some(input) = node.downcast::<HTMLInputElement>() {
116                match input.input_type() {
117                    InputType::Radio if value == *"on" => {
118                        // Step 2
119                        let val = input.Value();
120                        if val.is_empty() || val == value {
121                            input.SetChecked(true);
122                            return;
123                        }
124                    },
125                    InputType::Radio => {
126                        // Step 2
127                        if input.Value() == value {
128                            input.SetChecked(true);
129                            return;
130                        }
131                    },
132                    _ => {},
133                }
134            }
135        }
136    }
137
138    // FIXME: This shouldn't need to be implemented here since NodeList (the parent of
139    // RadioNodeList) implements IndexedGetter.
140    // https://github.com/servo/servo/issues/5875
141    //
142    // https://dom.spec.whatwg.org/#dom-nodelist-item
143    fn IndexedGetter(&self, index: u32) -> Option<DomRoot<Node>> {
144        self.node_list.IndexedGetter(index)
145    }
146}