script/dom/html/form_controls/
htmlfieldsetelement.rs1use std::default::Default;
6
7use dom_struct::dom_struct;
8use html5ever::{LocalName, Prefix, local_name};
9use js::context::{JSContext, NoGC};
10use js::rust::HandleObject;
11use stylo_dom::ElementState;
12
13use crate::dom::activation::Activatable;
14use crate::dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding::HTMLFieldSetElementMethods;
15use crate::dom::bindings::inheritance::Castable;
16use crate::dom::bindings::root::{DomRoot, MutNullableDom};
17use crate::dom::bindings::str::DOMString;
18use crate::dom::document::Document;
19use crate::dom::element::attributes::storage::AttrRef;
20use crate::dom::element::{AttributeMutation, Element};
21use crate::dom::html::htmlcollection::HTMLCollection;
22use crate::dom::html::htmlelement::HTMLElement;
23use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
24use crate::dom::html::htmllegendelement::HTMLLegendElement;
25use crate::dom::iterators::ShadowIncluding;
26use crate::dom::node::virtualmethods::VirtualMethods;
27use crate::dom::node::{Node, NodeTraits};
28use crate::dom::validation::Validatable;
29use crate::dom::validitystate::ValidityState;
30
31#[dom_struct]
32pub(crate) struct HTMLFieldSetElement {
33 htmlelement: HTMLElement,
34 form_owner: MutNullableDom<HTMLFormElement>,
35 validity_state: MutNullableDom<ValidityState>,
36}
37
38impl HTMLFieldSetElement {
39 fn new_inherited(
40 local_name: LocalName,
41 prefix: Option<Prefix>,
42 document: &Document,
43 ) -> HTMLFieldSetElement {
44 HTMLFieldSetElement {
45 htmlelement: HTMLElement::new_inherited_with_state(
46 ElementState::ENABLED | ElementState::VALID,
47 local_name,
48 prefix,
49 document,
50 ),
51 form_owner: Default::default(),
52 validity_state: Default::default(),
53 }
54 }
55
56 pub(crate) fn new(
57 cx: &mut js::context::JSContext,
58 local_name: LocalName,
59 prefix: Option<Prefix>,
60 document: &Document,
61 proto: Option<HandleObject>,
62 ) -> DomRoot<HTMLFieldSetElement> {
63 Node::reflect_node_with_proto(
64 cx,
65 Box::new(HTMLFieldSetElement::new_inherited(
66 local_name, prefix, document,
67 )),
68 document,
69 proto,
70 )
71 }
72
73 pub(crate) fn update_validity(&self, cx: &mut JSContext) {
74 let has_invalid_child = self
75 .upcast::<Node>()
76 .traverse_preorder(ShadowIncluding::No)
77 .flat_map(DomRoot::downcast::<Element>)
78 .any(|element| element.is_invalid(cx, false));
79
80 self.upcast::<Element>()
81 .set_state(cx.no_gc(), ElementState::VALID, !has_invalid_child);
82 self.upcast::<Element>()
83 .set_state(cx.no_gc(), ElementState::INVALID, has_invalid_child);
84 }
85}
86
87impl HTMLFieldSetElementMethods<crate::DomTypeHolder> for HTMLFieldSetElement {
88 fn Elements(&self, cx: &mut js::context::JSContext) -> DomRoot<HTMLCollection> {
90 HTMLCollection::new_with_filter_fn(cx, &self.owner_window(), self.upcast(), |element, _| {
91 element
92 .downcast::<HTMLElement>()
93 .is_some_and(HTMLElement::is_listed_element)
94 })
95 }
96
97 make_bool_getter!(Disabled, "disabled");
99
100 make_bool_setter!(SetDisabled, "disabled");
102
103 make_atomic_setter!(SetName, "name");
105
106 make_getter!(Name, "name");
108
109 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
111 self.form_owner()
112 }
113
114 fn WillValidate(&self, no_gc: &NoGC) -> bool {
116 self.is_instance_validatable(no_gc)
117 }
118
119 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
121 self.validity_state(cx)
122 }
123
124 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
126 self.check_validity(cx)
127 }
128
129 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
131 self.report_validity(cx)
132 }
133
134 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
136 self.validation_message(cx)
137 }
138
139 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
141 self.validity_state(cx).set_custom_error_message(cx, error);
142 }
143
144 fn Type(&self) -> DOMString {
146 DOMString::from_static("fieldset")
147 }
148}
149
150impl VirtualMethods for HTMLFieldSetElement {
151 fn super_type(&self) -> Option<&dyn VirtualMethods> {
152 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
153 }
154
155 fn attribute_mutated(
156 &self,
157 cx: &mut js::context::JSContext,
158 attr: AttrRef<'_>,
159 mutation: AttributeMutation,
160 ) {
161 self.super_type()
162 .unwrap()
163 .attribute_mutated(cx, attr, mutation);
164 match *attr.local_name() {
165 local_name!("disabled") => {
166 let disabled_state = match mutation {
167 AttributeMutation::Set(None) => true,
168 AttributeMutation::Set(Some(_)) => {
169 return;
171 },
172 AttributeMutation::Removed => false,
173 };
174 let node = self.upcast::<Node>();
175 let element = self.upcast::<Element>();
176 element.set_disabled_state(cx.no_gc(), disabled_state);
177 element.set_enabled_state(cx.no_gc(), !disabled_state);
178 let mut found_legend = false;
179 let children = node.children().filter(|node| {
180 if found_legend {
181 true
182 } else if node.is::<HTMLLegendElement>() {
183 found_legend = true;
184 false
185 } else {
186 true
187 }
188 });
189 let fields = children.flat_map(|child| {
190 child
191 .traverse_preorder(ShadowIncluding::No)
192 .filter_map(DomRoot::downcast::<HTMLElement>)
193 });
194 for field in fields {
195 if !field.is_disableable_form_control() {
196 continue;
197 }
198 if disabled_state {
199 field.set_disabled_state_and_notify(cx, disabled_state);
200 } else if field.as_element().disabled_state() {
201 field.maybe_update_form_control_disabled(cx);
202 }
203 }
204 },
205 local_name!("form") => {
206 self.form_attribute_mutated(cx, mutation);
207 },
208 _ => {},
209 }
210 }
211}
212
213impl FormControl for HTMLFieldSetElement {
214 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
215 self.form_owner.get()
216 }
217
218 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
219 self.form_owner.set(form);
220 }
221
222 fn to_html_element(&self) -> &HTMLElement {
223 self.upcast::<HTMLElement>()
224 }
225}
226
227impl Validatable for HTMLFieldSetElement {
228 fn as_element(&self) -> &Element {
229 self.upcast()
230 }
231
232 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
233 self.validity_state
234 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
235 }
236
237 fn is_instance_validatable(&self, _no_gc: &NoGC) -> bool {
238 false
240 }
241}