1use std::cell::Cell;
6use std::default::Default;
7
8use dom_struct::dom_struct;
9use html5ever::{LocalName, Prefix, local_name};
10use js::rust::HandleObject;
11use stylo_dom::ElementState;
12
13use crate::dom::activation::Activatable;
14use crate::dom::attr::Attr;
15use crate::dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods;
16use crate::dom::bindings::inheritance::Castable;
17use crate::dom::bindings::root::{DomRoot, MutNullableDom};
18use crate::dom::bindings::str::DOMString;
19use crate::dom::document::Document;
20use crate::dom::element::{AttributeMutation, Element};
21use crate::dom::event::Event;
22use crate::dom::eventtarget::EventTarget;
23use crate::dom::html::htmlelement::HTMLElement;
24use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
25use crate::dom::html::htmlformelement::{
26 FormControl, FormDatum, FormDatumValue, FormSubmitterElement, HTMLFormElement, ResetFrom,
27 SubmittedFrom,
28};
29use crate::dom::node::{BindContext, Node, NodeTraits, UnbindContext};
30use crate::dom::nodelist::NodeList;
31use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
32use crate::dom::validitystate::{ValidationFlags, ValidityState};
33use crate::dom::virtualmethods::VirtualMethods;
34use crate::script_runtime::CanGc;
35
36#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
37enum ButtonType {
38 Submit,
39 Reset,
40 Button,
41}
42
43#[dom_struct]
44pub(crate) struct HTMLButtonElement {
45 htmlelement: HTMLElement,
46 button_type: Cell<ButtonType>,
47 form_owner: MutNullableDom<HTMLFormElement>,
48 labels_node_list: MutNullableDom<NodeList>,
49 validity_state: MutNullableDom<ValidityState>,
50}
51
52impl HTMLButtonElement {
53 fn new_inherited(
54 local_name: LocalName,
55 prefix: Option<Prefix>,
56 document: &Document,
57 ) -> HTMLButtonElement {
58 HTMLButtonElement {
59 htmlelement: HTMLElement::new_inherited_with_state(
60 ElementState::ENABLED,
61 local_name,
62 prefix,
63 document,
64 ),
65 button_type: Cell::new(ButtonType::Submit),
66 form_owner: Default::default(),
67 labels_node_list: Default::default(),
68 validity_state: Default::default(),
69 }
70 }
71
72 pub(crate) fn new(
73 local_name: LocalName,
74 prefix: Option<Prefix>,
75 document: &Document,
76 proto: Option<HandleObject>,
77 can_gc: CanGc,
78 ) -> DomRoot<HTMLButtonElement> {
79 Node::reflect_node_with_proto(
80 Box::new(HTMLButtonElement::new_inherited(
81 local_name, prefix, document,
82 )),
83 document,
84 proto,
85 can_gc,
86 )
87 }
88
89 #[inline]
90 pub(crate) fn is_submit_button(&self) -> bool {
91 self.button_type.get() == ButtonType::Submit
92 }
93}
94
95impl HTMLButtonElementMethods<crate::DomTypeHolder> for HTMLButtonElement {
96 make_bool_getter!(Disabled, "disabled");
98
99 make_bool_setter!(SetDisabled, "disabled");
101
102 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
104 self.form_owner()
105 }
106
107 make_enumerated_getter!(
109 Type,
110 "type",
111 "submit" | "reset" | "button",
112 missing => "submit",
113 invalid => "submit"
114 );
115
116 make_setter!(SetType, "type");
118
119 make_form_action_getter!(FormAction, "formaction");
121
122 make_setter!(SetFormAction, "formaction");
124
125 make_enumerated_getter!(
127 FormEnctype,
128 "formenctype",
129 "application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain",
130 invalid => "application/x-www-form-urlencoded"
131 );
132
133 make_setter!(SetFormEnctype, "formenctype");
135
136 make_enumerated_getter!(
138 FormMethod,
139 "formmethod",
140 "get" | "post" | "dialog",
141 invalid => "get"
142 );
143
144 make_setter!(SetFormMethod, "formmethod");
146
147 make_getter!(FormTarget, "formtarget");
149
150 make_setter!(SetFormTarget, "formtarget");
152
153 make_bool_getter!(FormNoValidate, "formnovalidate");
155
156 make_bool_setter!(SetFormNoValidate, "formnovalidate");
158
159 make_getter!(Name, "name");
161
162 make_atomic_setter!(SetName, "name");
164
165 make_getter!(Value, "value");
167
168 make_setter!(SetValue, "value");
170
171 make_labels_getter!(Labels, labels_node_list);
173
174 fn WillValidate(&self) -> bool {
176 self.is_instance_validatable()
177 }
178
179 fn Validity(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
181 self.validity_state(can_gc)
182 }
183
184 fn CheckValidity(&self, can_gc: CanGc) -> bool {
186 self.check_validity(can_gc)
187 }
188
189 fn ReportValidity(&self, can_gc: CanGc) -> bool {
191 self.report_validity(can_gc)
192 }
193
194 fn ValidationMessage(&self) -> DOMString {
196 self.validation_message()
197 }
198
199 fn SetCustomValidity(&self, error: DOMString, can_gc: CanGc) {
201 self.validity_state(can_gc).set_custom_error_message(error);
202 }
203}
204
205impl HTMLButtonElement {
206 pub(crate) fn form_datum(&self, submitter: Option<FormSubmitterElement>) -> Option<FormDatum> {
209 if let Some(FormSubmitterElement::Button(submitter)) = submitter {
213 if submitter != self {
214 return None;
215 }
216 } else {
217 return None;
218 }
219 let ty = self.Type();
221 let name = self.Name();
223
224 if name.is_empty() {
225 return None;
227 }
228
229 Some(FormDatum {
231 ty,
232 name,
233 value: FormDatumValue::String(self.Value()),
234 })
235 }
236}
237
238impl VirtualMethods for HTMLButtonElement {
239 fn super_type(&self) -> Option<&dyn VirtualMethods> {
240 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
241 }
242
243 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
244 self.super_type()
245 .unwrap()
246 .attribute_mutated(attr, mutation, can_gc);
247 match *attr.local_name() {
248 local_name!("disabled") => {
249 let el = self.upcast::<Element>();
250 match mutation {
251 AttributeMutation::Set(Some(_), _) => {},
252 AttributeMutation::Set(None, _) => {
253 el.set_disabled_state(true);
254 el.set_enabled_state(false);
255 },
256 AttributeMutation::Removed => {
257 el.set_disabled_state(false);
258 el.set_enabled_state(true);
259 el.check_ancestors_disabled_state_for_form_control();
260 },
261 }
262 el.update_sequentially_focusable_status(can_gc);
263 self.validity_state(can_gc)
264 .perform_validation_and_update(ValidationFlags::all(), can_gc);
265 },
266 local_name!("type") => match mutation {
267 AttributeMutation::Set(..) => {
268 let value = match &**attr.value() {
269 "reset" => ButtonType::Reset,
270 "button" => ButtonType::Button,
271 _ => ButtonType::Submit,
272 };
273 self.button_type.set(value);
274 self.validity_state(can_gc)
275 .perform_validation_and_update(ValidationFlags::all(), can_gc);
276 },
277 AttributeMutation::Removed => {
278 self.button_type.set(ButtonType::Submit);
279 },
280 },
281 local_name!("form") => {
282 self.form_attribute_mutated(mutation, can_gc);
283 self.validity_state(can_gc)
284 .perform_validation_and_update(ValidationFlags::empty(), can_gc);
285 },
286 _ => {},
287 }
288 }
289
290 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
291 if let Some(s) = self.super_type() {
292 s.bind_to_tree(context, can_gc);
293 }
294
295 self.upcast::<Element>()
296 .check_ancestors_disabled_state_for_form_control();
297 }
298
299 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
300 self.super_type().unwrap().unbind_from_tree(context, can_gc);
301
302 let node = self.upcast::<Node>();
303 let el = self.upcast::<Element>();
304 if node
305 .ancestors()
306 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
307 {
308 el.check_ancestors_disabled_state_for_form_control();
309 } else {
310 el.check_disabled_attribute();
311 }
312 }
313}
314
315impl FormControl for HTMLButtonElement {
316 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
317 self.form_owner.get()
318 }
319
320 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
321 self.form_owner.set(form);
322 }
323
324 fn to_element(&self) -> &Element {
325 self.upcast::<Element>()
326 }
327}
328
329impl Validatable for HTMLButtonElement {
330 fn as_element(&self) -> &Element {
331 self.upcast()
332 }
333
334 fn validity_state(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
335 self.validity_state
336 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), can_gc))
337 }
338
339 fn is_instance_validatable(&self) -> bool {
340 self.button_type.get() == ButtonType::Submit &&
344 !self.upcast::<Element>().disabled_state() &&
345 !is_barred_by_datalist_ancestor(self.upcast())
346 }
347}
348
349impl Activatable for HTMLButtonElement {
350 fn as_element(&self) -> &Element {
351 self.upcast()
352 }
353
354 fn is_instance_activatable(&self) -> bool {
355 !self.upcast::<Element>().disabled_state()
357 }
358
359 fn activation_behavior(&self, _event: &Event, target: &EventTarget, can_gc: CanGc) {
361 let ty = self.button_type.get();
362 match ty {
363 ButtonType::Submit => {
365 if !target
367 .downcast::<Node>()
368 .is_none_or(|node| node.owner_document().is_fully_active())
369 {
370 return;
371 }
372 if let Some(owner) = self.form_owner() {
373 owner.submit(
374 SubmittedFrom::NotFromForm,
375 FormSubmitterElement::Button(self),
376 can_gc,
377 );
378 }
379 },
380 ButtonType::Reset => {
381 if !target
383 .downcast::<Node>()
384 .is_none_or(|node| node.owner_document().is_fully_active())
385 {
386 return;
387 }
388 if let Some(owner) = self.form_owner() {
389 owner.reset(ResetFrom::NotFromForm, can_gc);
390 }
391 },
392 _ => (),
393 }
394 }
395}