Skip to main content

script/dom/html/
htmloptgroupelement.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 html5ever::{LocalName, Prefix, local_name};
7use js::context::JSContext;
8use js::rust::HandleObject;
9use script_bindings::str::DOMString;
10use stylo_dom::ElementState;
11
12use crate::dom::bindings::codegen::Bindings::HTMLOptGroupElementBinding::HTMLOptGroupElementMethods;
13use crate::dom::bindings::codegen::GenericBindings::NodeBinding::Node_Binding::NodeMethods;
14use crate::dom::bindings::inheritance::Castable;
15use crate::dom::bindings::root::DomRoot;
16use crate::dom::document::Document;
17use crate::dom::element::attributes::storage::AttrRef;
18use crate::dom::element::{AttributeMutation, Element};
19use crate::dom::html::htmlelement::HTMLElement;
20use crate::dom::html::htmloptionelement::HTMLOptionElement;
21use crate::dom::html::htmlselectelement::HTMLSelectElement;
22use crate::dom::node::{BindContext, Node, UnbindContext};
23use crate::dom::validation::Validatable;
24use crate::dom::validitystate::ValidationFlags;
25use crate::dom::virtualmethods::VirtualMethods;
26
27/// <https://html.spec.whatwg.org/multipage/#htmloptgroupelement>
28#[dom_struct]
29pub(crate) struct HTMLOptGroupElement {
30    htmlelement: HTMLElement,
31}
32
33impl HTMLOptGroupElement {
34    fn new_inherited(
35        local_name: LocalName,
36        prefix: Option<Prefix>,
37        document: &Document,
38    ) -> HTMLOptGroupElement {
39        HTMLOptGroupElement {
40            htmlelement: HTMLElement::new_inherited_with_state(
41                ElementState::ENABLED,
42                local_name,
43                prefix,
44                document,
45            ),
46        }
47    }
48
49    pub(crate) fn new(
50        cx: &mut js::context::JSContext,
51        local_name: LocalName,
52        prefix: Option<Prefix>,
53        document: &Document,
54        proto: Option<HandleObject>,
55    ) -> DomRoot<HTMLOptGroupElement> {
56        Node::reflect_node_with_proto(
57            cx,
58            Box::new(HTMLOptGroupElement::new_inherited(
59                local_name, prefix, document,
60            )),
61            document,
62            proto,
63        )
64    }
65
66    fn update_select_validity(&self, cx: &mut JSContext) {
67        if let Some(select) = self.owner_select_element() {
68            select
69                .validity_state(cx)
70                .perform_validation_and_update(cx, ValidationFlags::all());
71        }
72    }
73
74    fn owner_select_element(&self) -> Option<DomRoot<HTMLSelectElement>> {
75        self.upcast::<Node>()
76            .GetParentNode()
77            .and_then(DomRoot::downcast)
78    }
79}
80
81impl HTMLOptGroupElementMethods<crate::DomTypeHolder> for HTMLOptGroupElement {
82    // https://html.spec.whatwg.org/multipage/#dom-optgroup-disabled
83    make_bool_getter!(Disabled, "disabled");
84
85    // https://html.spec.whatwg.org/multipage/#dom-optgroup-disabled
86    make_bool_setter!(SetDisabled, "disabled");
87
88    // https://html.spec.whatwg.org/multipage/#dom-optgroup-label
89    make_getter!(Label, "label");
90
91    // https://html.spec.whatwg.org/multipage/#dom-optgroup-label
92    make_setter!(SetLabel, "label");
93}
94
95impl VirtualMethods for HTMLOptGroupElement {
96    fn super_type(&self) -> Option<&dyn VirtualMethods> {
97        Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
98    }
99
100    fn attribute_mutated(
101        &self,
102        cx: &mut js::context::JSContext,
103        attr: AttrRef<'_>,
104        mutation: AttributeMutation,
105    ) {
106        self.super_type()
107            .unwrap()
108            .attribute_mutated(cx, attr, mutation);
109        if attr.local_name() == &local_name!("disabled") {
110            let disabled_state = match mutation {
111                AttributeMutation::Set(None, _) => true,
112                AttributeMutation::Set(Some(_), _) => {
113                    // Option group was already disabled.
114                    return;
115                },
116                AttributeMutation::Removed => false,
117            };
118            let el = self.upcast::<Element>();
119            el.set_disabled_state(disabled_state);
120            el.set_enabled_state(!disabled_state);
121            let options = el
122                .upcast::<Node>()
123                .children()
124                .filter(|child| child.is::<HTMLOptionElement>())
125                .map(|child| DomRoot::from_ref(child.downcast::<HTMLOptionElement>().unwrap()));
126            if disabled_state {
127                for option in options {
128                    let el = option.upcast::<Element>();
129                    el.set_disabled_state(true);
130                    el.set_enabled_state(false);
131                }
132            } else {
133                for option in options {
134                    let el = option.upcast::<Element>();
135                    el.check_disabled_attribute();
136                }
137            }
138        }
139    }
140
141    fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
142        if let Some(super_type) = self.super_type() {
143            super_type.bind_to_tree(cx, context);
144        }
145
146        self.update_select_validity(cx);
147    }
148
149    fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
150        self.super_type().unwrap().unbind_from_tree(cx, context);
151
152        if let Some(select) = context.parent.downcast::<HTMLSelectElement>() {
153            select
154                .validity_state(cx)
155                .perform_validation_and_update(cx, ValidationFlags::all());
156        }
157    }
158}