diff options
author | bors-servo <metajack+bors@gmail.com> | 2014-10-14 10:00:38 -0600 |
---|---|---|
committer | bors-servo <metajack+bors@gmail.com> | 2014-10-14 10:00:38 -0600 |
commit | d1685015559562a42cc440f4e3b7a97d38cc642c (patch) | |
tree | 8c90f71383bc929a1c4376286143ce2b4c7da68b | |
parent | 834df4e211e7dcac4369da4f5b8113f295869aa1 (diff) | |
parent | 79cb1af12ae5cab650ff6d72663da17812d267c2 (diff) | |
download | servo-d1685015559562a42cc440f4e3b7a97d38cc642c.tar.gz servo-d1685015559562a42cc440f4e3b7a97d38cc642c.zip |
auto merge of #3652 : Manishearth/servo/form-button, r=jdm
Partially fixes #3647
8 files changed, 185 insertions, 129 deletions
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 639d2296608..363e90fa596 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -7,7 +7,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::InheritTypes::{ElementCast, EventTargetCast, HTMLFormElementDerived, NodeCast}; +use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLFormElementDerived, NodeCast}; use dom::bindings::codegen::InheritTypes::HTMLInputElementCast; use dom::bindings::global::Window; use dom::bindings::js::{JSRef, Temporary}; @@ -64,46 +64,19 @@ impl<'a> HTMLFormElementMethods for JSRef<'a, HTMLFormElement> { make_setter!(SetAcceptCharset, "accept-charset") // https://html.spec.whatwg.org/multipage/forms.html#dom-fs-action - fn Action(self) -> DOMString { - let element: JSRef<Element> = ElementCast::from_ref(self); - let url = element.get_url_attribute("action"); - match url.as_slice() { - "" => { - let window = window_from_node(self).root(); - window.get_url().serialize() - }, - _ => url - } - } + make_url_or_base_getter!(Action) // https://html.spec.whatwg.org/multipage/forms.html#dom-fs-action make_setter!(SetAction, "action") // https://html.spec.whatwg.org/multipage/forms.html#dom-form-autocomplete - fn Autocomplete(self) -> DOMString { - let elem: JSRef<Element> = ElementCast::from_ref(self); - let ac = elem.get_string_attribute("autocomplete").into_ascii_lower(); - // https://html.spec.whatwg.org/multipage/forms.html#attr-form-autocomplete - match ac.as_slice() { - "off" => ac, - _ => "on".to_string() - } - } + make_enumerated_getter!(Autocomplete, "on", "off") // https://html.spec.whatwg.org/multipage/forms.html#dom-form-autocomplete make_setter!(SetAutocomplete, "autocomplete") // https://html.spec.whatwg.org/multipage/forms.html#dom-fs-enctype - fn Enctype(self) -> DOMString { - let elem: JSRef<Element> = ElementCast::from_ref(self); - let enctype = elem.get_string_attribute("enctype").into_ascii_lower(); - // https://html.spec.whatwg.org/multipage/forms.html#attr-fs-enctype - match enctype.as_slice() { - "text/plain" | "multipart/form-data" => enctype, - _ => "application/x-www-form-urlencoded".to_string() - } - } - + make_enumerated_getter!(Enctype, "application/x-www-form-urlencoded", "text/plain" | "multipart/form-data") // https://html.spec.whatwg.org/multipage/forms.html#dom-fs-enctype make_setter!(SetEnctype, "enctype") @@ -119,15 +92,7 @@ impl<'a> HTMLFormElementMethods for JSRef<'a, HTMLFormElement> { } // https://html.spec.whatwg.org/multipage/forms.html#dom-fs-method - fn Method(self) -> DOMString { - let elem: JSRef<Element> = ElementCast::from_ref(self); - let method = elem.get_string_attribute("method").into_ascii_lower(); - // https://html.spec.whatwg.org/multipage/forms.html#attr-fs-method - match method.as_slice() { - "post" | "dialog" => method, - _ => "get".to_string() - } - } + make_enumerated_getter!(Method, "get", "post" | "dialog") // https://html.spec.whatwg.org/multipage/forms.html#dom-fs-method make_setter!(SetMethod, "method") @@ -222,7 +187,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> { script_chan.send(TriggerLoadMsg(win.page().id, load_data)); } - fn get_form_dataset(self, _submitter: Option<FormSubmitter>) -> Vec<FormDatum> { + fn get_form_dataset<'b>(self, submitter: Option<FormSubmitter<'b>>) -> Vec<FormDatum> { fn clean_crlf(s: &str) -> DOMString { // https://html.spec.whatwg.org/multipage/forms.html#constructing-the-form-data-set // Step 4 @@ -286,6 +251,12 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> { } let mut value = input.Value(); + let is_submitter = match submitter { + Some(InputElement(s)) => { + input == s + }, + _ => false + }; match ty.as_slice() { "image" => None, // Unimplemented "radio" | "checkbox" => { @@ -298,6 +269,8 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> { value: value }) }, + // Discard buttons which are not the submitter + "submit" | "button" | "reset" if !is_submitter => None, "file" => None, // Unimplemented _ => Some(FormDatum { ty: ty, @@ -367,46 +340,63 @@ pub enum FormMethod { } pub enum FormSubmitter<'a> { - FormElement(JSRef<'a, HTMLFormElement>) + FormElement(JSRef<'a, HTMLFormElement>), + InputElement(JSRef<'a, HTMLInputElement>) // TODO: Submit buttons, image submit, etc etc } impl<'a> FormSubmitter<'a> { fn action(&self) -> DOMString { match *self { - FormElement(form) => form.Action() + FormElement(form) => form.Action(), + InputElement(input_element) => input_element.get_form_attribute("formaction", |i| i.FormAction(), |f| f.Action()) } } fn enctype(&self) -> FormEncType { - match *self { - FormElement(form) => { - match form.Enctype().as_slice() { - "multipart/form-data" => FormDataEncoded, - "text/plain" => TextPlainEncoded, - // https://html.spec.whatwg.org/multipage/forms.html#attr-fs-enctype - // urlencoded is the default - _ => UrlEncoded - } - } + let attr = match *self { + FormElement(form) => form.Enctype(), + InputElement(input_element) => input_element.get_form_attribute("formenctype", |i| i.FormEnctype(), |f| f.Enctype()) + }; + match attr.as_slice() { + "multipart/form-data" => FormDataEncoded, + "text/plain" => TextPlainEncoded, + // https://html.spec.whatwg.org/multipage/forms.html#attr-fs-enctype + // urlencoded is the default + _ => UrlEncoded } } fn method(&self) -> FormMethod { - match *self { - FormElement(form) => { - match form.Method().as_slice() { - "dialog" => FormDialog, - "post" => FormPost, - _ => FormGet - } - } + let attr = match *self { + FormElement(form) => form.Method(), + InputElement(input_element) => input_element.get_form_attribute("formmethod", |i| i.FormMethod(), |f| f.Method()) + }; + match attr.as_slice() { + "dialog" => FormDialog, + "post" => FormPost, + _ => FormGet } } fn target(&self) -> DOMString { match *self { - FormElement(form) => form.Target() + FormElement(form) => form.Target(), + InputElement(input_element) => input_element.get_form_attribute("formtarget", |i| i.FormTarget(), |f| f.Target()) + } + } +} + +pub trait FormOwner<'a> : Copy { + fn form_owner(self) -> Option<Temporary<HTMLFormElement>>; + fn get_form_attribute(self, attr: &str, + input: |Self| -> DOMString, + owner: |JSRef<HTMLFormElement>| -> DOMString) -> DOMString { + if self.to_element().has_attribute(attr) { + input(self) + } else { + self.form_owner().map_or("".to_string(), |t| owner(*t.root())) } } + fn to_element(self) -> JSRef<'a, Element>; } diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index b63fee7917b..52cb51eeaff 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -8,7 +8,7 @@ use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::HTMLInputElementBinding; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; -use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLInputElementCast, NodeCast}; +use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLFormElementCast, HTMLInputElementCast, NodeCast}; use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived}; use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, ResultRootable}; use dom::bindings::utils::{Reflectable, Reflector}; @@ -18,7 +18,8 @@ use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId}; use dom::event::Event; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; -use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node}; +use dom::htmlformelement::{InputElement, FormOwner, HTMLFormElement, HTMLFormElementHelpers}; +use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; use servo_util::str::{DOMString, parse_unsigned_integer}; @@ -136,21 +137,13 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> { make_uint_setter!(SetSize, "size") // https://html.spec.whatwg.org/multipage/forms.html#dom-input-type - fn Type(self) -> DOMString { - let elem: JSRef<Element> = ElementCast::from_ref(self); - let ty = elem.get_string_attribute("type").into_ascii_lower(); - // https://html.spec.whatwg.org/multipage/forms.html#attr-input-type - match ty.as_slice() { - "hidden" | "search" | "tel" | - "url" | "email" | "password" | - "datetime" | "date" | "month" | - "week" | "time" | "datetime-local" | - "number" | "range" | "color" | - "checkbox" | "radio" | "file" | - "submit" | "image" | "reset" | "button" => ty, - _ => "text".to_string() - } - } + make_enumerated_getter!(Type, "text", "hidden" | "search" | "tel" | + "url" | "email" | "password" | + "datetime" | "date" | "month" | + "week" | "time" | "datetime-local" | + "number" | "range" | "color" | + "checkbox" | "radio" | "file" | + "submit" | "image" | "reset" | "button") // https://html.spec.whatwg.org/multipage/forms.html#dom-input-type make_setter!(SetType, "type") @@ -168,6 +161,30 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> { // https://html.spec.whatwg.org/multipage/forms.html#attr-fe-name make_setter!(SetName, "name") + + // https://html.spec.whatwg.org/multipage/forms.html#dom-input-formaction + make_url_or_base_getter!(FormAction) + + // https://html.spec.whatwg.org/multipage/forms.html#dom-input-formaction + make_setter!(SetFormAction, "formaction") + + // https://html.spec.whatwg.org/multipage/forms.html#dom-input-formenctype + make_enumerated_getter!(FormEnctype, "application/x-www-form-urlencoded", "text/plain" | "multipart/form-data") + + // https://html.spec.whatwg.org/multipage/forms.html#dom-input-formenctype + make_setter!(SetFormEnctype, "formenctype") + + // https://html.spec.whatwg.org/multipage/forms.html#dom-input-formmethod + make_enumerated_getter!(FormMethod, "get", "post" | "dialog") + + // https://html.spec.whatwg.org/multipage/forms.html#dom-input-formmethod + make_setter!(SetFormMethod, "formmethod") + + // https://html.spec.whatwg.org/multipage/forms.html#dom-input-formtarget + make_getter!(FormTarget) + + // https://html.spec.whatwg.org/multipage/forms.html#dom-input-formtarget + make_setter!(SetFormTarget, "formtarget") } trait HTMLInputElementHelpers { @@ -369,6 +386,11 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { match self.input_type.get() { InputCheckbox => self.SetChecked(!self.checked.get()), InputRadio => self.SetChecked(true), + InputButton(Some(DEFAULT_SUBMIT_VALUE)) => { + self.form_owner().map(|o| { + o.root().submit(false, InputElement(self.clone())) + }); + } _ => {} } } @@ -380,3 +402,33 @@ impl Reflectable for HTMLInputElement { self.htmlelement.reflector() } } + +impl<'a> FormOwner<'a> for JSRef<'a, HTMLInputElement> { + // FIXME: This is wrong (https://github.com/servo/servo/issues/3553) + // but we need html5ever to do it correctly + fn form_owner(self) -> Option<Temporary<HTMLFormElement>> { + // https://html.spec.whatwg.org/multipage/forms.html#reset-the-form-owner + let elem: JSRef<Element> = ElementCast::from_ref(self); + let owner = elem.get_string_attribute("form"); + if !owner.is_empty() { + let doc = document_from_node(self).root(); + let owner = doc.GetElementById(owner).root(); + match owner { + Some(o) => { + let maybe_form: Option<JSRef<HTMLFormElement>> = HTMLFormElementCast::to_ref(*o); + if maybe_form.is_some() { + return maybe_form.map(Temporary::from_rooted); + } + }, + _ => () + } + } + let node: JSRef<Node> = NodeCast::from_ref(self); + node.ancestors().filter_map(|a| HTMLFormElementCast::to_ref(a)).next() + .map(Temporary::from_rooted) + } + + fn to_element(self) -> JSRef<'a, Element> { + ElementCast::from_ref(self) + } +} diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index 719d5e93c94..b4b0d4d2ef7 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -70,6 +70,52 @@ macro_rules! make_url_getter( } ) +#[macro_export] +macro_rules! make_url_or_base_getter( + ( $attr:ident, $htmlname:expr ) => ( + fn $attr(self) -> DOMString { + use dom::element::{Element, AttributeHandlers}; + use dom::bindings::codegen::InheritTypes::ElementCast; + #[allow(unused_imports)] + use std::ascii::StrAsciiExt; + let element: JSRef<Element> = ElementCast::from_ref(self); + let url = element.get_url_attribute($htmlname); + match url.as_slice() { + "" => { + let window = window_from_node(self).root(); + window.get_url().serialize() + }, + _ => url + } + } + ); + ($attr:ident) => { + make_url_or_base_getter!($attr, stringify!($attr).to_ascii_lower().as_slice()) + } +) + +#[macro_export] +macro_rules! make_enumerated_getter( + ( $attr:ident, $htmlname:expr, $default:expr, $($choices: pat)|+) => ( + fn $attr(self) -> DOMString { + use dom::element::{Element, AttributeHandlers}; + use dom::bindings::codegen::InheritTypes::ElementCast; + #[allow(unused_imports)] + use std::ascii::StrAsciiExt; + let element: JSRef<Element> = ElementCast::from_ref(self); + let val = element.get_string_attribute($htmlname).into_ascii_lower(); + // https://html.spec.whatwg.org/multipage/forms.html#attr-fs-method + match val.as_slice() { + $($choices)|+ => val, + _ => $default.to_string() + } + } + ); + ($attr:ident, $default:expr, $($choices: pat)|+) => { + make_enumerated_getter!($attr, stringify!($attr).to_ascii_lower().as_slice(), $default, $($choices)|+) + } +) + // concat_idents! doesn't work for function name positions, so // we have to specify both the content name and the HTML name here #[macro_export] diff --git a/components/script/dom/webidls/HTMLInputElement.webidl b/components/script/dom/webidls/HTMLInputElement.webidl index f2b888dca47..b69d8fcf4ff 100644 --- a/components/script/dom/webidls/HTMLInputElement.webidl +++ b/components/script/dom/webidls/HTMLInputElement.webidl @@ -15,11 +15,11 @@ interface HTMLInputElement : HTMLElement { attribute boolean disabled; //readonly attribute HTMLFormElement? form; //readonly attribute FileList? files; - // 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 unsigned long height; // attribute boolean indeterminate; // attribute DOMString inputMode; diff --git a/tests/html/form_submission.html b/tests/html/form_submission.html new file mode 100644 index 00000000000..e00abe5e87b --- /dev/null +++ b/tests/html/form_submission.html @@ -0,0 +1,13 @@ +<html> +<head></head> +<body> +<!-- Run with nc -l 8000 --> +<form action="http://localhost:8000" method=get id="foo"> +<input name=bar type=checkbox checked> +<input name=baz value="baz1" type=radio checked> +<input name=baz value="baz2" type=radio> +<input type=text name=bye value="hi!"> +<input type=submit> +</form> +</body> +</html> diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index 2b46a216624..e302217b6c4 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -5472,21 +5472,9 @@ [HTMLInputElement interface: attribute files] expected: FAIL - [HTMLInputElement interface: attribute formAction] - expected: FAIL - - [HTMLInputElement interface: attribute formEnctype] - expected: FAIL - - [HTMLInputElement interface: attribute formMethod] - expected: FAIL - [HTMLInputElement interface: attribute formNoValidate] expected: FAIL - [HTMLInputElement interface: attribute formTarget] - expected: FAIL - [HTMLInputElement interface: attribute height] expected: FAIL @@ -5628,21 +5616,9 @@ [HTMLInputElement interface: document.createElement("input") must inherit property "files" with the proper type (9)] expected: FAIL - [HTMLInputElement interface: document.createElement("input") must inherit property "formAction" with the proper type (10)] - expected: FAIL - - [HTMLInputElement interface: document.createElement("input") must inherit property "formEnctype" with the proper type (11)] - expected: FAIL - - [HTMLInputElement interface: document.createElement("input") must inherit property "formMethod" with the proper type (12)] - expected: FAIL - [HTMLInputElement interface: document.createElement("input") must inherit property "formNoValidate" with the proper type (13)] expected: FAIL - [HTMLInputElement interface: document.createElement("input") must inherit property "formTarget" with the proper type (14)] - expected: FAIL - [HTMLInputElement interface: document.createElement("input") must inherit property "height" with the proper type (15)] expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address.html.ini b/tests/wpt/metadata/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address.html.ini index 26110a33b2d..14fa8007d75 100644 --- a/tests/wpt/metadata/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address.html.ini +++ b/tests/wpt/metadata/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address.html.ini @@ -3,18 +3,9 @@ [Check if button.formAction is the document\'s address when formaction content attribute is missing] expected: FAIL - [Check if input.formAction is the document\'s address when formaction content attribute is missing] - expected: FAIL - [Check if button.formAction is the document\'s address when formaction content attribute value is empty string] expected: FAIL - [Check if input.formAction is the document\'s address when formaction content attribute value is empty string] - expected: FAIL - [Check if button.formAction is the document\'s address when formaction content attribute value is not assigned] expected: FAIL - [Check if input.formAction is the document\'s address when formaction content attribute value is not assigned] - expected: FAIL - diff --git a/tests/wpt/metadata/html/semantics/forms/attributes-common-to-form-controls/formaction.html.ini b/tests/wpt/metadata/html/semantics/forms/attributes-common-to-form-controls/formaction.html.ini index e8578462282..d41cfa9499b 100644 --- a/tests/wpt/metadata/html/semantics/forms/attributes-common-to-form-controls/formaction.html.ini +++ b/tests/wpt/metadata/html/semantics/forms/attributes-common-to-form-controls/formaction.html.ini @@ -3,18 +3,6 @@ [formAction on button support] expected: FAIL - [formAction on input support] - expected: FAIL - - [formAction absolute URL value is correct using getAttribute] - expected: FAIL - - [formAction relative URL value is correct using getAttribute] - expected: FAIL - - [On getting, when formaction is missing, the document\'s address must be returned] - expected: FAIL - - [On getting, when formaction value is the empty string, the document\'s address must be returned] + [formAction relative URL value on input reflects correct value after being updated by the DOM] expected: FAIL |