diff --git a/servo/components/script/dom/htmlbuttonelement.rs b/servo/components/script/dom/htmlbuttonelement.rs index 04c827056b77..2f5496ee38bb 100644 --- a/servo/components/script/dom/htmlbuttonelement.rs +++ b/servo/components/script/dom/htmlbuttonelement.rs @@ -2,30 +2,45 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::activation::Activatable; use dom::attr::Attr; use dom::attr::AttrHelpers; use dom::bindings::codegen::Bindings::HTMLButtonElementBinding; use dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods; -use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast}; +use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLButtonElementCast, NodeCast}; use dom::bindings::codegen::InheritTypes::{HTMLButtonElementDerived, HTMLFieldSetElementDerived}; use dom::bindings::js::{JSRef, Temporary}; use dom::document::Document; -use dom::element::{AttributeHandlers, Element}; +use dom::element::{AttributeHandlers, Element, ElementTypeId}; +use dom::element::ActivationElementHelpers; use dom::eventtarget::{EventTarget, EventTargetTypeId}; -use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; -use dom::node::{DisabledStateHelpers, Node, NodeHelpers, NodeTypeId, window_from_node}; +use dom::htmlformelement::{FormSubmitter, FormControl, HTMLFormElementHelpers}; +use dom::htmlformelement::{SubmittedFrom}; +use dom::node::{DisabledStateHelpers, Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node}; use dom::validitystate::ValidityState; use dom::virtualmethods::VirtualMethods; use std::ascii::OwnedAsciiExt; use std::borrow::ToOwned; use util::str::DOMString; +use std::cell::Cell; use string_cache::Atom; +#[jstraceable] +#[derive(PartialEq, Copy)] +#[allow(dead_code)] +enum ButtonType { + ButtonSubmit, + ButtonReset, + ButtonButton, + ButtonMenu +} + #[dom_struct] pub struct HTMLButtonElement { - htmlelement: HTMLElement + htmlelement: HTMLElement, + button_type: Cell } impl HTMLButtonElementDerived for EventTarget { @@ -37,7 +52,9 @@ impl HTMLButtonElementDerived for EventTarget { impl HTMLButtonElement { fn new_inherited(localName: DOMString, prefix: Option, document: JSRef) -> HTMLButtonElement { HTMLButtonElement { - htmlelement: HTMLElement::new_inherited(HTMLElementTypeId::HTMLButtonElement, localName, prefix, document) + htmlelement: HTMLElement::new_inherited(HTMLElementTypeId::HTMLButtonElement, localName, prefix, document), + //TODO: implement button_type in after_set_attr + button_type: Cell::new(ButtonType::ButtonSubmit) } } @@ -73,6 +90,23 @@ impl<'a> HTMLButtonElementMethods for JSRef<'a, HTMLButtonElement> { // https://html.spec.whatwg.org/multipage/forms.html#dom-button-type make_setter!(SetType, "type"); + + // https://html.spec.whatwg.org/multipage/forms.html#htmlbuttonelement + make_url_or_base_getter!(FormAction); + + make_setter!(SetFormAction, "formaction"); + + make_enumerated_getter!(FormEnctype, "application/x-www-form-urlencoded", ("text/plain") | ("multipart/form-data")); + + make_setter!(SetFormEnctype, "formenctype"); + + make_enumerated_getter!(FormMethod, "get", ("post") | ("dialog")); + + make_setter!(SetFormMethod, "formmethod"); + + make_getter!(FormTarget); + + make_setter!(SetFormTarget, "formtarget"); } impl<'a> VirtualMethods for JSRef<'a, HTMLButtonElement> { @@ -139,3 +173,68 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLButtonElement> { } } +impl<'a> FormControl<'a> for JSRef<'a, HTMLButtonElement> { + fn to_element(self) -> JSRef<'a, Element> { + ElementCast::from_ref(self) + } +} + +impl<'a> Activatable for JSRef<'a, HTMLButtonElement> { + fn as_element(&self) -> Temporary { + Temporary::from_rooted(ElementCast::from_ref(*self)) + } + + fn is_instance_activatable(&self) -> bool { + //https://html.spec.whatwg.org/multipage/forms.html#the-button-element + let node: JSRef = NodeCast::from_ref(*self); + !(node.get_disabled_state()) + } + + // https://html.spec.whatwg.org/multipage/interaction.html#run-pre-click-activation-steps + // https://html.spec.whatwg.org/multipage/forms.html#the-button-element:activation-behavior + fn pre_click_activation(&self) { + } + + // https://html.spec.whatwg.org/multipage/interaction.html#run-canceled-activation-steps + fn canceled_activation(&self) { + } + + // https://html.spec.whatwg.org/multipage/interaction.html#run-post-click-activation-steps + fn activation_behavior(&self) { + let ty = self.button_type.get(); + match ty { + //https://html.spec.whatwg.org/multipage/forms.html#attr-button-type-submit-state + ButtonType::ButtonSubmit => { + self.form_owner().map(|o| { + o.root().r().submit(SubmittedFrom::NotFromFormSubmitMethod, + FormSubmitter::ButtonElement(self.clone())) + }); + } + _ => () + } + } + + // https://html.spec.whatwg.org/multipage/forms.html#implicit-submission + #[allow(unsafe_blocks)] + fn implicit_submission(&self, ctrlKey: bool, shiftKey: bool, altKey: bool, metaKey: bool) { + let doc = document_from_node(*self).root(); + let node: JSRef = NodeCast::from_ref(doc.r()); + let owner = self.form_owner(); + let elem: JSRef = ElementCast::from_ref(*self); + if owner.is_none() || elem.click_in_progress() { + return; + } + // This is safe because we are stopping after finding the first element + // and only then performing actions which may modify the DOM tree + unsafe { + node.query_selector_iter("button[type=submit]".to_owned()).unwrap() + .filter_map(|t| { + let h: Option> = HTMLButtonElementCast::to_ref(t); + h + }) + .find(|r| r.form_owner() == owner) + .map(|&:s| s.synthetic_click_activation(ctrlKey, shiftKey, altKey, metaKey)); + } + } +} + diff --git a/servo/components/script/dom/htmlformelement.rs b/servo/components/script/dom/htmlformelement.rs index 2bc35264f64a..be296d04c150 100644 --- a/servo/components/script/dom/htmlformelement.rs +++ b/servo/components/script/dom/htmlformelement.rs @@ -8,6 +8,7 @@ use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; use dom::bindings::codegen::Bindings::HTMLFormElementBinding; use dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; +use dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods; use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLFormElementDerived, NodeCast}; use dom::bindings::codegen::InheritTypes::{HTMLInputElementCast, HTMLTextAreaElementCast, HTMLFormElementCast}; use dom::bindings::global::GlobalRef; @@ -18,8 +19,9 @@ use dom::event::{Event, EventHelpers, EventBubbles, EventCancelable}; use dom::eventtarget::{EventTarget, EventTargetTypeId}; use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; -use dom::htmlinputelement::HTMLInputElement; -use dom::htmltextareaelement::HTMLTextAreaElement; +use dom::htmlinputelement::{HTMLInputElement, HTMLInputElementHelpers}; +use dom::htmlbuttonelement::{HTMLButtonElement}; +use dom::htmltextareaelement::{HTMLTextAreaElement, HTMLTextAreaElementHelpers}; use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node}; use hyper::method::Method; use servo_msg::constellation_msg::LoadData; @@ -416,8 +418,9 @@ pub enum FormMethod { #[derive(Copy)] pub enum FormSubmitter<'a> { FormElement(JSRef<'a, HTMLFormElement>), - InputElement(JSRef<'a, HTMLInputElement>) - // TODO: Submit buttons, image submit, etc etc + InputElement(JSRef<'a, HTMLInputElement>), + ButtonElement(JSRef<'a, HTMLButtonElement>) + // TODO: image submit, etc etc } impl<'a> FormSubmitter<'a> { @@ -429,6 +432,11 @@ impl<'a> FormSubmitter<'a> { input_element.get_form_attribute(&Atom::from_slice("formaction"), |i| i.FormAction(), |f| f.Action()) + }, + FormSubmitter::ButtonElement(button_element) => { + button_element.get_form_attribute(&Atom::from_slice("formaction"), + |i| i.FormAction(), + |f| f.Action()) } } } @@ -441,6 +449,11 @@ impl<'a> FormSubmitter<'a> { input_element.get_form_attribute(&Atom::from_slice("formenctype"), |i| i.FormEnctype(), |f| f.Enctype()) + }, + FormSubmitter::ButtonElement(button_element) => { + button_element.get_form_attribute(&Atom::from_slice("formenctype"), + |i| i.FormAction(), + |f| f.Action()) } }; match attr.as_slice() { @@ -460,6 +473,11 @@ impl<'a> FormSubmitter<'a> { input_element.get_form_attribute(&Atom::from_slice("formmethod"), |i| i.FormMethod(), |f| f.Method()) + }, + FormSubmitter::ButtonElement(button_element) => { + button_element.get_form_attribute(&Atom::from_slice("formmethod"), + |i| i.FormAction(), + |f| f.Action()) } }; match attr.as_slice() { @@ -477,6 +495,11 @@ impl<'a> FormSubmitter<'a> { input_element.get_form_attribute(&Atom::from_slice("formtarget"), |i| i.FormTarget(), |f| f.Target()) + }, + FormSubmitter::ButtonElement(button_element) => { + button_element.get_form_attribute(&Atom::from_slice("formtarget"), + |i| i.FormAction(), + |f| f.Action()) } } } @@ -523,7 +546,4 @@ pub trait FormControl<'a> : Copy + Sized { } fn to_element(self) -> JSRef<'a, Element>; - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-mutable - fn mutable(self) -> bool; - fn reset(self); } diff --git a/servo/components/script/dom/htmlinputelement.rs b/servo/components/script/dom/htmlinputelement.rs index ede6ee71fb01..a6622cd87f14 100644 --- a/servo/components/script/dom/htmlinputelement.rs +++ b/servo/components/script/dom/htmlinputelement.rs @@ -303,6 +303,8 @@ pub trait HTMLInputElementHelpers { fn update_checked_state(self, checked: bool, dirty: bool); fn get_size(&self) -> u32; fn get_indeterminate_state(self) -> bool; + fn mutable(self) -> bool; + fn reset(self); } #[allow(unsafe_blocks)] @@ -392,6 +394,29 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> { fn get_indeterminate_state(self) -> bool { self.indeterminate.get() } + + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-mutable + fn mutable(self) -> bool { + // https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-fe-mutable + // https://html.spec.whatwg.org/multipage/forms.html#the-readonly-attribute:concept-fe-mutable + let node: JSRef = NodeCast::from_ref(self); + !(node.get_disabled_state() || self.ReadOnly()) + } + + // https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-form-reset-control + fn reset(self) { + match self.input_type.get() { + InputType::InputRadio | InputType::InputCheckbox => { + self.update_checked_state(self.DefaultChecked(), false); + self.checked_changed.set(false); + }, + InputType::InputImage => (), + _ => () + } + + self.SetValue(self.DefaultValue()); + self.value_changed.set(false); + } } impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { @@ -583,32 +608,8 @@ impl<'a> FormControl<'a> for JSRef<'a, HTMLInputElement> { fn to_element(self) -> JSRef<'a, Element> { ElementCast::from_ref(self) } - - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-mutable - fn mutable(self) -> bool { - // https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-fe-mutable - // https://html.spec.whatwg.org/multipage/forms.html#the-readonly-attribute:concept-fe-mutable - let node: JSRef = NodeCast::from_ref(self); - !(node.get_disabled_state() || self.ReadOnly()) - } - - // https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-form-reset-control - fn reset(self) { - match self.input_type.get() { - InputType::InputRadio | InputType::InputCheckbox => { - self.update_checked_state(self.DefaultChecked(), false); - self.checked_changed.set(false); - }, - InputType::InputImage => (), - _ => () - } - - self.SetValue(self.DefaultValue()); - self.value_changed.set(false); - } } - impl<'a> Activatable for JSRef<'a, HTMLInputElement> { fn as_element(&self) -> Temporary { Temporary::from_rooted(ElementCast::from_ref(*self)) diff --git a/servo/components/script/dom/htmltextareaelement.rs b/servo/components/script/dom/htmltextareaelement.rs index 7196b4ff891f..ed7fb71a9546 100644 --- a/servo/components/script/dom/htmltextareaelement.rs +++ b/servo/components/script/dom/htmltextareaelement.rs @@ -187,6 +187,24 @@ impl<'a> HTMLTextAreaElementMethods for JSRef<'a, HTMLTextAreaElement> { } } +pub trait HTMLTextAreaElementHelpers { + fn mutable(self) -> bool; + fn reset(self); +} + +impl<'a> HTMLTextAreaElementHelpers for JSRef<'a, HTMLTextAreaElement> { + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-mutable + fn mutable(self) -> bool { + // https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-fe-mutable + !(self.Disabled() || self.ReadOnly()) + } + fn reset(self) { + // https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-form-reset-control + self.SetValue(self.DefaultValue()); + self.value_changed.set(false); + } +} + trait PrivateHTMLTextAreaElementHelpers { fn force_relayout(self); } @@ -335,16 +353,4 @@ impl<'a> FormControl<'a> for JSRef<'a, HTMLTextAreaElement> { fn to_element(self) -> JSRef<'a, Element> { ElementCast::from_ref(self) } - - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-mutable - fn mutable(self) -> bool { - // https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-fe-mutable - !(self.Disabled() || self.ReadOnly()) - } - - fn reset(self) { - // https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-form-reset-control - self.SetValue(self.DefaultValue()); - self.value_changed.set(false); - } } diff --git a/servo/components/script/dom/webidls/HTMLButtonElement.webidl b/servo/components/script/dom/webidls/HTMLButtonElement.webidl index 622abbd1cf5d..a04b9dd67374 100644 --- a/servo/components/script/dom/webidls/HTMLButtonElement.webidl +++ b/servo/components/script/dom/webidls/HTMLButtonElement.webidl @@ -8,11 +8,11 @@ interface HTMLButtonElement : HTMLElement { // attribute boolean autofocus; attribute boolean disabled; //readonly attribute HTMLFormElement? form; - // attribute DOMString formAction; - // attribute DOMString formEnctype; - // attribute DOMString formMethod; + attribute DOMString formAction; + attribute DOMString formEnctype; + attribute DOMString formMethod; // attribute boolean formNoValidate; - // attribute DOMString formTarget; + attribute DOMString formTarget; // attribute DOMString name; attribute DOMString type; // attribute DOMString value;