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 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
73 pub(crate) fn new(
74 local_name: LocalName,
75 prefix: Option<Prefix>,
76 document: &Document,
77 proto: Option<HandleObject>,
78 can_gc: CanGc,
79 ) -> DomRoot<HTMLButtonElement> {
80 Node::reflect_node_with_proto(
81 Box::new(HTMLButtonElement::new_inherited(
82 local_name, prefix, document,
83 )),
84 document,
85 proto,
86 can_gc,
87 )
88 }
89
90 #[inline]
91 pub(crate) fn is_submit_button(&self) -> bool {
92 self.button_type.get() == ButtonType::Submit
93 }
94}
95
96impl HTMLButtonElementMethods<crate::DomTypeHolder> for HTMLButtonElement {
97 make_bool_getter!(Disabled, "disabled");
99
100 make_bool_setter!(SetDisabled, "disabled");
102
103 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
105 self.form_owner()
106 }
107
108 make_enumerated_getter!(
110 Type,
111 "type",
112 "submit" | "reset" | "button",
113 missing => "submit",
114 invalid => "submit"
115 );
116
117 make_setter!(SetType, "type");
119
120 make_form_action_getter!(FormAction, "formaction");
122
123 make_setter!(SetFormAction, "formaction");
125
126 make_enumerated_getter!(
128 FormEnctype,
129 "formenctype",
130 "application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain",
131 invalid => "application/x-www-form-urlencoded"
132 );
133
134 make_setter!(SetFormEnctype, "formenctype");
136
137 make_enumerated_getter!(
139 FormMethod,
140 "formmethod",
141 "get" | "post" | "dialog",
142 invalid => "get"
143 );
144
145 make_setter!(SetFormMethod, "formmethod");
147
148 make_getter!(FormTarget, "formtarget");
150
151 make_setter!(SetFormTarget, "formtarget");
153
154 make_bool_getter!(FormNoValidate, "formnovalidate");
156
157 make_bool_setter!(SetFormNoValidate, "formnovalidate");
159
160 make_getter!(Name, "name");
162
163 make_atomic_setter!(SetName, "name");
165
166 make_getter!(Value, "value");
168
169 make_setter!(SetValue, "value");
171
172 make_labels_getter!(Labels, labels_node_list);
174
175 fn WillValidate(&self) -> bool {
177 self.is_instance_validatable()
178 }
179
180 fn Validity(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
182 self.validity_state(can_gc)
183 }
184
185 fn CheckValidity(&self, can_gc: CanGc) -> bool {
187 self.check_validity(can_gc)
188 }
189
190 fn ReportValidity(&self, can_gc: CanGc) -> bool {
192 self.report_validity(can_gc)
193 }
194
195 fn ValidationMessage(&self) -> DOMString {
197 self.validation_message()
198 }
199
200 fn SetCustomValidity(&self, error: DOMString, can_gc: CanGc) {
202 self.validity_state(can_gc).set_custom_error_message(error);
203 }
204}
205
206impl HTMLButtonElement {
207 pub(crate) fn form_datum(&self, submitter: Option<FormSubmitterElement>) -> Option<FormDatum> {
210 if let Some(FormSubmitterElement::Button(submitter)) = submitter {
214 if submitter != self {
215 return None;
216 }
217 } else {
218 return None;
219 }
220 let ty = self.Type();
222 let name = self.Name();
224
225 if name.is_empty() {
226 return None;
228 }
229
230 Some(FormDatum {
232 ty,
233 name,
234 value: FormDatumValue::String(self.Value()),
235 })
236 }
237}
238
239impl VirtualMethods for HTMLButtonElement {
240 fn super_type(&self) -> Option<&dyn VirtualMethods> {
241 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
242 }
243
244 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
245 self.super_type()
246 .unwrap()
247 .attribute_mutated(attr, mutation, can_gc);
248 match *attr.local_name() {
249 local_name!("disabled") => {
250 let el = self.upcast::<Element>();
251 match mutation {
252 AttributeMutation::Set(Some(_), _) => {},
253 AttributeMutation::Set(None, _) => {
254 el.set_disabled_state(true);
255 el.set_enabled_state(false);
256 },
257 AttributeMutation::Removed => {
258 el.set_disabled_state(false);
259 el.set_enabled_state(true);
260 el.check_ancestors_disabled_state_for_form_control();
261 },
262 }
263 el.update_sequentially_focusable_status(can_gc);
264 self.validity_state(can_gc)
265 .perform_validation_and_update(ValidationFlags::all(), can_gc);
266 },
267 local_name!("type") => match mutation {
268 AttributeMutation::Set(..) => {
269 let value = match &**attr.value() {
270 "reset" => ButtonType::Reset,
271 "button" => ButtonType::Button,
272 _ => ButtonType::Submit,
273 };
274 self.button_type.set(value);
275 self.validity_state(can_gc)
276 .perform_validation_and_update(ValidationFlags::all(), can_gc);
277 },
278 AttributeMutation::Removed => {
279 self.button_type.set(ButtonType::Submit);
280 },
281 },
282 local_name!("form") => {
283 self.form_attribute_mutated(mutation, can_gc);
284 self.validity_state(can_gc)
285 .perform_validation_and_update(ValidationFlags::empty(), can_gc);
286 },
287 _ => {},
288 }
289 }
290
291 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
292 if let Some(s) = self.super_type() {
293 s.bind_to_tree(context, can_gc);
294 }
295
296 self.upcast::<Element>()
297 .check_ancestors_disabled_state_for_form_control();
298 }
299
300 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
301 self.super_type().unwrap().unbind_from_tree(context, can_gc);
302
303 let node = self.upcast::<Node>();
304 let el = self.upcast::<Element>();
305 if node
306 .ancestors()
307 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
308 {
309 el.check_ancestors_disabled_state_for_form_control();
310 } else {
311 el.check_disabled_attribute();
312 }
313 }
314}
315
316impl FormControl for HTMLButtonElement {
317 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
318 self.form_owner.get()
319 }
320
321 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
322 self.form_owner.set(form);
323 }
324
325 fn to_element(&self) -> &Element {
326 self.upcast::<Element>()
327 }
328}
329
330impl Validatable for HTMLButtonElement {
331 fn as_element(&self) -> &Element {
332 self.upcast()
333 }
334
335 fn validity_state(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
336 self.validity_state
337 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), can_gc))
338 }
339
340 fn is_instance_validatable(&self) -> bool {
341 self.button_type.get() == ButtonType::Submit &&
345 !self.upcast::<Element>().disabled_state() &&
346 !is_barred_by_datalist_ancestor(self.upcast())
347 }
348}
349
350impl Activatable for HTMLButtonElement {
351 fn as_element(&self) -> &Element {
352 self.upcast()
353 }
354
355 fn is_instance_activatable(&self) -> bool {
356 !self.upcast::<Element>().disabled_state()
358 }
359
360 fn activation_behavior(&self, _event: &Event, target: &EventTarget, can_gc: CanGc) {
362 let ty = self.button_type.get();
363 match ty {
364 ButtonType::Submit => {
366 if !target
368 .downcast::<Node>()
369 .is_none_or(|node| node.owner_document().is_fully_active())
370 {
371 return;
372 }
373 if let Some(owner) = self.form_owner() {
374 owner.submit(
375 SubmittedFrom::NotFromForm,
376 FormSubmitterElement::Button(self),
377 can_gc,
378 );
379 }
380 },
381 ButtonType::Reset => {
382 if !target
384 .downcast::<Node>()
385 .is_none_or(|node| node.owner_document().is_fully_active())
386 {
387 return;
388 }
389 if let Some(owner) = self.form_owner() {
390 owner.reset(ResetFrom::NotFromForm, can_gc);
391 }
392 },
393 _ => (),
394 }
395 }
396}