aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <metajack+bors@gmail.com>2014-10-14 10:00:38 -0600
committerbors-servo <metajack+bors@gmail.com>2014-10-14 10:00:38 -0600
commitd1685015559562a42cc440f4e3b7a97d38cc642c (patch)
tree8c90f71383bc929a1c4376286143ce2b4c7da68b
parent834df4e211e7dcac4369da4f5b8113f295869aa1 (diff)
parent79cb1af12ae5cab650ff6d72663da17812d267c2 (diff)
downloadservo-d1685015559562a42cc440f4e3b7a97d38cc642c.tar.gz
servo-d1685015559562a42cc440f4e3b7a97d38cc642c.zip
auto merge of #3652 : Manishearth/servo/form-button, r=jdm
Partially fixes #3647
-rw-r--r--components/script/dom/htmlformelement.rs114
-rw-r--r--components/script/dom/htmlinputelement.rs86
-rw-r--r--components/script/dom/macros.rs46
-rw-r--r--components/script/dom/webidls/HTMLInputElement.webidl8
-rw-r--r--tests/html/form_submission.html13
-rw-r--r--tests/wpt/metadata/html/dom/interfaces.html.ini24
-rw-r--r--tests/wpt/metadata/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address.html.ini9
-rw-r--r--tests/wpt/metadata/html/semantics/forms/attributes-common-to-form-controls/formaction.html.ini14
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