script/dom/html/
htmlfieldsetelement.rs1use std::default::Default;
6
7use dom_struct::dom_struct;
8use html5ever::{LocalName, Prefix, local_name};
9use js::rust::HandleObject;
10use stylo_dom::ElementState;
11
12use crate::dom::attr::Attr;
13use crate::dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding::HTMLFieldSetElementMethods;
14use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
15use crate::dom::bindings::root::{DomRoot, MutNullableDom};
16use crate::dom::bindings::str::DOMString;
17use crate::dom::customelementregistry::CallbackReaction;
18use crate::dom::document::Document;
19use crate::dom::element::{AttributeMutation, Element};
20use crate::dom::html::htmlcollection::HTMLCollection;
21use crate::dom::html::htmlelement::HTMLElement;
22use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
23use crate::dom::html::htmllegendelement::HTMLLegendElement;
24use crate::dom::node::{Node, NodeTraits, ShadowIncluding};
25use crate::dom::validation::Validatable;
26use crate::dom::validitystate::ValidityState;
27use crate::dom::virtualmethods::VirtualMethods;
28use crate::script_runtime::CanGc;
29use crate::script_thread::ScriptThread;
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 local_name: LocalName,
58 prefix: Option<Prefix>,
59 document: &Document,
60 proto: Option<HandleObject>,
61 can_gc: CanGc,
62 ) -> DomRoot<HTMLFieldSetElement> {
63 Node::reflect_node_with_proto(
64 Box::new(HTMLFieldSetElement::new_inherited(
65 local_name, prefix, document,
66 )),
67 document,
68 proto,
69 can_gc,
70 )
71 }
72
73 pub(crate) fn update_validity(&self, can_gc: CanGc) {
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(false, can_gc));
79
80 self.upcast::<Element>()
81 .set_state(ElementState::VALID, !has_invalid_child);
82 self.upcast::<Element>()
83 .set_state(ElementState::INVALID, has_invalid_child);
84 }
85}
86
87impl HTMLFieldSetElementMethods<crate::DomTypeHolder> for HTMLFieldSetElement {
88 fn Elements(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
90 HTMLCollection::new_with_filter_fn(
91 &self.owner_window(),
92 self.upcast(),
93 |element, _| {
94 element
95 .downcast::<HTMLElement>()
96 .is_some_and(HTMLElement::is_listed_element)
97 },
98 can_gc,
99 )
100 }
101
102 make_bool_getter!(Disabled, "disabled");
104
105 make_bool_setter!(SetDisabled, "disabled");
107
108 make_atomic_setter!(SetName, "name");
110
111 make_getter!(Name, "name");
113
114 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
116 self.form_owner()
117 }
118
119 fn WillValidate(&self) -> bool {
121 self.is_instance_validatable()
122 }
123
124 fn Validity(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
126 self.validity_state(can_gc)
127 }
128
129 fn CheckValidity(&self, can_gc: CanGc) -> bool {
131 self.check_validity(can_gc)
132 }
133
134 fn ReportValidity(&self, can_gc: CanGc) -> bool {
136 self.report_validity(can_gc)
137 }
138
139 fn ValidationMessage(&self) -> DOMString {
141 self.validation_message()
142 }
143
144 fn SetCustomValidity(&self, error: DOMString, can_gc: CanGc) {
146 self.validity_state(can_gc).set_custom_error_message(error);
147 }
148
149 fn Type(&self) -> DOMString {
151 DOMString::from_string(String::from("fieldset"))
152 }
153}
154
155impl VirtualMethods for HTMLFieldSetElement {
156 fn super_type(&self) -> Option<&dyn VirtualMethods> {
157 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
158 }
159
160 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
161 self.super_type()
162 .unwrap()
163 .attribute_mutated(attr, mutation, can_gc);
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(disabled_state);
177 element.set_enabled_state(!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(|descendant| match descendant.type_id() {
193 NodeTypeId::Element(ElementTypeId::HTMLElement(
194 HTMLElementTypeId::HTMLButtonElement |
195 HTMLElementTypeId::HTMLInputElement |
196 HTMLElementTypeId::HTMLSelectElement |
197 HTMLElementTypeId::HTMLTextAreaElement,
198 )) => true,
199 NodeTypeId::Element(ElementTypeId::HTMLElement(
200 HTMLElementTypeId::HTMLElement,
201 )) => descendant
202 .downcast::<HTMLElement>()
203 .unwrap()
204 .is_form_associated_custom_element(),
205 _ => false,
206 })
207 });
208 if disabled_state {
209 for field in fields {
210 let element = field.downcast::<Element>().unwrap();
211 if element.enabled_state() {
212 element.set_disabled_state(true);
213 element.set_enabled_state(false);
214 if element
215 .downcast::<HTMLElement>()
216 .is_some_and(|h| h.is_form_associated_custom_element())
217 {
218 ScriptThread::enqueue_callback_reaction(
219 element,
220 CallbackReaction::FormDisabled(true),
221 None,
222 );
223 }
224 }
225 element.update_sequentially_focusable_status(can_gc);
226 }
227 } else {
228 for field in fields {
229 let element = field.downcast::<Element>().unwrap();
230 if element.disabled_state() {
231 element.check_disabled_attribute();
232 element.check_ancestors_disabled_state_for_form_control();
233 if element.enabled_state() &&
235 element
236 .downcast::<HTMLElement>()
237 .is_some_and(|h| h.is_form_associated_custom_element())
238 {
239 ScriptThread::enqueue_callback_reaction(
240 element,
241 CallbackReaction::FormDisabled(false),
242 None,
243 );
244 }
245 }
246 element.update_sequentially_focusable_status(can_gc);
247 }
248 }
249 element.update_sequentially_focusable_status(can_gc);
250 },
251 local_name!("form") => {
252 self.form_attribute_mutated(mutation, can_gc);
253 },
254 _ => {},
255 }
256 }
257}
258
259impl FormControl for HTMLFieldSetElement {
260 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
261 self.form_owner.get()
262 }
263
264 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
265 self.form_owner.set(form);
266 }
267
268 fn to_element(&self) -> &Element {
269 self.upcast::<Element>()
270 }
271}
272
273impl Validatable for HTMLFieldSetElement {
274 fn as_element(&self) -> &Element {
275 self.upcast()
276 }
277
278 fn validity_state(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
279 self.validity_state
280 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), can_gc))
281 }
282
283 fn is_instance_validatable(&self) -> bool {
284 false
286 }
287}