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 missing => "get",
143 invalid => "get"
144 );
145
146 make_setter!(SetFormMethod, "formmethod");
148
149 make_getter!(FormTarget, "formtarget");
151
152 make_setter!(SetFormTarget, "formtarget");
154
155 make_bool_getter!(FormNoValidate, "formnovalidate");
157
158 make_bool_setter!(SetFormNoValidate, "formnovalidate");
160
161 make_getter!(Name, "name");
163
164 make_atomic_setter!(SetName, "name");
166
167 make_getter!(Value, "value");
169
170 make_setter!(SetValue, "value");
172
173 make_labels_getter!(Labels, labels_node_list);
175
176 fn WillValidate(&self) -> bool {
178 self.is_instance_validatable()
179 }
180
181 fn Validity(&self) -> DomRoot<ValidityState> {
183 self.validity_state()
184 }
185
186 fn CheckValidity(&self, can_gc: CanGc) -> bool {
188 self.check_validity(can_gc)
189 }
190
191 fn ReportValidity(&self, can_gc: CanGc) -> bool {
193 self.report_validity(can_gc)
194 }
195
196 fn ValidationMessage(&self) -> DOMString {
198 self.validation_message()
199 }
200
201 fn SetCustomValidity(&self, error: DOMString) {
203 self.validity_state().set_custom_error_message(error);
204 }
205}
206
207impl HTMLButtonElement {
208 pub(crate) fn form_datum(&self, submitter: Option<FormSubmitterElement>) -> Option<FormDatum> {
211 if let Some(FormSubmitterElement::Button(submitter)) = submitter {
215 if submitter != self {
216 return None;
217 }
218 } else {
219 return None;
220 }
221 let ty = self.Type();
223 let name = self.Name();
225
226 if name.is_empty() {
227 return None;
229 }
230
231 Some(FormDatum {
233 ty,
234 name,
235 value: FormDatumValue::String(self.Value()),
236 })
237 }
238}
239
240impl VirtualMethods for HTMLButtonElement {
241 fn super_type(&self) -> Option<&dyn VirtualMethods> {
242 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
243 }
244
245 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
246 self.super_type()
247 .unwrap()
248 .attribute_mutated(attr, mutation, can_gc);
249 match *attr.local_name() {
250 local_name!("disabled") => {
251 let el = self.upcast::<Element>();
252 match mutation {
253 AttributeMutation::Set(Some(_)) => {},
254 AttributeMutation::Set(None) => {
255 el.set_disabled_state(true);
256 el.set_enabled_state(false);
257 },
258 AttributeMutation::Removed => {
259 el.set_disabled_state(false);
260 el.set_enabled_state(true);
261 el.check_ancestors_disabled_state_for_form_control();
262 },
263 }
264 el.update_sequentially_focusable_status(can_gc);
265 self.validity_state()
266 .perform_validation_and_update(ValidationFlags::all(), can_gc);
267 },
268 local_name!("type") => match mutation {
269 AttributeMutation::Set(_) => {
270 let value = match &**attr.value() {
271 "reset" => ButtonType::Reset,
272 "button" => ButtonType::Button,
273 _ => ButtonType::Submit,
274 };
275 self.button_type.set(value);
276 self.validity_state()
277 .perform_validation_and_update(ValidationFlags::all(), can_gc);
278 },
279 AttributeMutation::Removed => {
280 self.button_type.set(ButtonType::Submit);
281 },
282 },
283 local_name!("form") => {
284 self.form_attribute_mutated(mutation, can_gc);
285 self.validity_state()
286 .perform_validation_and_update(ValidationFlags::empty(), can_gc);
287 },
288 _ => {},
289 }
290 }
291
292 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
293 if let Some(s) = self.super_type() {
294 s.bind_to_tree(context, can_gc);
295 }
296
297 self.upcast::<Element>()
298 .check_ancestors_disabled_state_for_form_control();
299 }
300
301 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
302 self.super_type().unwrap().unbind_from_tree(context, can_gc);
303
304 let node = self.upcast::<Node>();
305 let el = self.upcast::<Element>();
306 if node
307 .ancestors()
308 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
309 {
310 el.check_ancestors_disabled_state_for_form_control();
311 } else {
312 el.check_disabled_attribute();
313 }
314 }
315}
316
317impl FormControl for HTMLButtonElement {
318 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
319 self.form_owner.get()
320 }
321
322 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
323 self.form_owner.set(form);
324 }
325
326 fn to_element(&self) -> &Element {
327 self.upcast::<Element>()
328 }
329}
330
331impl Validatable for HTMLButtonElement {
332 fn as_element(&self) -> &Element {
333 self.upcast()
334 }
335
336 fn validity_state(&self) -> DomRoot<ValidityState> {
337 self.validity_state
338 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), CanGc::note()))
339 }
340
341 fn is_instance_validatable(&self) -> bool {
342 self.button_type.get() == ButtonType::Submit &&
346 !self.upcast::<Element>().disabled_state() &&
347 !is_barred_by_datalist_ancestor(self.upcast())
348 }
349}
350
351impl Activatable for HTMLButtonElement {
352 fn as_element(&self) -> &Element {
353 self.upcast()
354 }
355
356 fn is_instance_activatable(&self) -> bool {
357 !self.upcast::<Element>().disabled_state()
359 }
360
361 fn activation_behavior(&self, _event: &Event, _target: &EventTarget, can_gc: CanGc) {
363 let ty = self.button_type.get();
364 match ty {
365 ButtonType::Submit => {
367 if let Some(owner) = self.form_owner() {
369 owner.submit(
370 SubmittedFrom::NotFromForm,
371 FormSubmitterElement::Button(self),
372 can_gc,
373 );
374 }
375 },
376 ButtonType::Reset => {
377 if let Some(owner) = self.form_owner() {
379 owner.reset(ResetFrom::NotFromForm, can_gc);
380 }
381 },
382 _ => (),
383 }
384 }
385}