Skip to main content

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