script/dom/html/
htmlbuttonelement.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
98    make_bool_getter!(Disabled, "disabled");
99
100    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
101    make_bool_setter!(SetDisabled, "disabled");
102
103    // https://html.spec.whatwg.org/multipage/#dom-fae-form
104    fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
105        self.form_owner()
106    }
107
108    // <https://html.spec.whatwg.org/multipage/#dom-button-type>
109    make_enumerated_getter!(
110        Type,
111        "type",
112        "submit" | "reset" | "button",
113        missing => "submit",
114        invalid => "submit"
115    );
116
117    // https://html.spec.whatwg.org/multipage/#dom-button-type
118    make_setter!(SetType, "type");
119
120    // https://html.spec.whatwg.org/multipage/#dom-fs-formaction
121    make_form_action_getter!(FormAction, "formaction");
122
123    // https://html.spec.whatwg.org/multipage/#dom-fs-formaction
124    make_setter!(SetFormAction, "formaction");
125
126    // https://html.spec.whatwg.org/multipage/#dom-fs-formenctype
127    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    // https://html.spec.whatwg.org/multipage/#dom-fs-formenctype
135    make_setter!(SetFormEnctype, "formenctype");
136
137    // https://html.spec.whatwg.org/multipage/#dom-fs-formmethod
138    make_enumerated_getter!(
139        FormMethod,
140        "formmethod",
141        "get" | "post" | "dialog",
142        missing => "get",
143        invalid => "get"
144    );
145
146    // https://html.spec.whatwg.org/multipage/#dom-fs-formmethod
147    make_setter!(SetFormMethod, "formmethod");
148
149    // https://html.spec.whatwg.org/multipage/#dom-fs-formtarget
150    make_getter!(FormTarget, "formtarget");
151
152    // https://html.spec.whatwg.org/multipage/#dom-fs-formtarget
153    make_setter!(SetFormTarget, "formtarget");
154
155    // https://html.spec.whatwg.org/multipage/#attr-fs-formnovalidate
156    make_bool_getter!(FormNoValidate, "formnovalidate");
157
158    // https://html.spec.whatwg.org/multipage/#attr-fs-formnovalidate
159    make_bool_setter!(SetFormNoValidate, "formnovalidate");
160
161    // https://html.spec.whatwg.org/multipage/#dom-fe-name
162    make_getter!(Name, "name");
163
164    // https://html.spec.whatwg.org/multipage/#dom-fe-name
165    make_atomic_setter!(SetName, "name");
166
167    // https://html.spec.whatwg.org/multipage/#dom-button-value
168    make_getter!(Value, "value");
169
170    // https://html.spec.whatwg.org/multipage/#dom-button-value
171    make_setter!(SetValue, "value");
172
173    // https://html.spec.whatwg.org/multipage/#dom-lfe-labels
174    make_labels_getter!(Labels, labels_node_list);
175
176    // https://html.spec.whatwg.org/multipage/#dom-cva-willvalidate
177    fn WillValidate(&self) -> bool {
178        self.is_instance_validatable()
179    }
180
181    // https://html.spec.whatwg.org/multipage/#dom-cva-validity
182    fn Validity(&self) -> DomRoot<ValidityState> {
183        self.validity_state()
184    }
185
186    // https://html.spec.whatwg.org/multipage/#dom-cva-checkvalidity
187    fn CheckValidity(&self, can_gc: CanGc) -> bool {
188        self.check_validity(can_gc)
189    }
190
191    // https://html.spec.whatwg.org/multipage/#dom-cva-reportvalidity
192    fn ReportValidity(&self, can_gc: CanGc) -> bool {
193        self.report_validity(can_gc)
194    }
195
196    // https://html.spec.whatwg.org/multipage/#dom-cva-validationmessage
197    fn ValidationMessage(&self) -> DOMString {
198        self.validation_message()
199    }
200
201    // https://html.spec.whatwg.org/multipage/#dom-cva-setcustomvalidity
202    fn SetCustomValidity(&self, error: DOMString) {
203        self.validity_state().set_custom_error_message(error);
204    }
205}
206
207impl HTMLButtonElement {
208    /// <https://html.spec.whatwg.org/multipage/#constructing-the-form-data-set>
209    /// Steps range from 3.1 to 3.7 (specific to HTMLButtonElement)
210    pub(crate) fn form_datum(&self, submitter: Option<FormSubmitterElement>) -> Option<FormDatum> {
211        // Step 3.1: disabled state check is in get_unclean_dataset
212
213        // Step 3.1: only run steps if this is the submitter
214        if let Some(FormSubmitterElement::Button(submitter)) = submitter {
215            if submitter != self {
216                return None;
217            }
218        } else {
219            return None;
220        }
221        // Step 3.2
222        let ty = self.Type();
223        // Step 3.4
224        let name = self.Name();
225
226        if name.is_empty() {
227            // Step 3.1: Must have a name
228            return None;
229        }
230
231        // Step 3.9
232        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        // https://html.spec.whatwg.org/multipage/#the-button-element%3Abarred-from-constraint-validation
343        // https://html.spec.whatwg.org/multipage/#enabling-and-disabling-form-controls%3A-the-disabled-attribute%3Abarred-from-constraint-validation
344        // https://html.spec.whatwg.org/multipage/#the-datalist-element%3Abarred-from-constraint-validation
345        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        // https://html.spec.whatwg.org/multipage/#the-button-element
358        !self.upcast::<Element>().disabled_state()
359    }
360
361    // https://html.spec.whatwg.org/multipage/#run-post-click-activation-steps
362    fn activation_behavior(&self, _event: &Event, _target: &EventTarget, can_gc: CanGc) {
363        let ty = self.button_type.get();
364        match ty {
365            // https://html.spec.whatwg.org/multipage/#attr-button-type-submit-state
366            ButtonType::Submit => {
367                // TODO: is document owner fully active?
368                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                // TODO: is document owner fully active?
378                if let Some(owner) = self.form_owner() {
379                    owner.reset(ResetFrom::NotFromForm, can_gc);
380                }
381            },
382            _ => (),
383        }
384    }
385}