mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
auto merge of #3652 : Manishearth/servo/form-button, r=jdm
Partially fixes #3647
This commit is contained in:
commit
d168501555
8 changed files with 185 additions and 129 deletions
|
@ -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,21 +340,25 @@ 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() {
|
||||
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
|
||||
|
@ -389,24 +366,37 @@ impl<'a> FormSubmitter<'a> {
|
|||
_ => UrlEncoded
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn method(&self) -> FormMethod {
|
||||
match *self {
|
||||
FormElement(form) => {
|
||||
match form.Method().as_slice() {
|
||||
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>;
|
||||
}
|
||||
|
|
|
@ -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" |
|
||||
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" => ty,
|
||||
_ => "text".to_string()
|
||||
}
|
||||
}
|
||||
"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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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;
|
||||
|
|
13
tests/html/form_submission.html
Normal file
13
tests/html/form_submission.html
Normal file
|
@ -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>
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue