From c92d58980cd8eb6a1b267fe195213fc2354b1773 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 11 Oct 2014 16:44:48 +0530 Subject: [PATCH 1/6] Create an almost-correct form_owner() for convenience --- components/script/dom/htmlformelement.rs | 4 +++ components/script/dom/htmlinputelement.rs | 30 +++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 639d2296608..d8b0ad76695 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -410,3 +410,7 @@ impl<'a> FormSubmitter<'a> { } } } + +pub trait FormOwner<'a> { + fn form_owner(self) -> Option>; +} diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index b63fee7917b..20d8beb5390 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -8,16 +8,17 @@ 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}; use dom::attr::{AttrHelpers}; use dom::document::{Document, DocumentHelpers}; -use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId}; +use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId }; use dom::event::Event; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; +use dom::htmlformelement::{FormOwner, HTMLFormElement}; use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node}; use dom::virtualmethods::VirtualMethods; @@ -380,3 +381,28 @@ 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> { + // https://html.spec.whatwg.org/multipage/forms.html#reset-the-form-owner + let elem: JSRef = ElementCast::from_ref(self); + let owner = elem.get_string_attribute("owner"); + 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> = HTMLFormElementCast::to_ref(*o); + if maybe_form.is_some() { + return maybe_form; + } + }, + _ => () + } + } + let node: JSRef = NodeCast::from_ref(self); + node.ancestors().filter_map(|a| HTMLFormElementCast::to_ref(a)).next().clone() + } +} From 05134e6d1fa93c1068322b0cf3b548ba4b98e849 Mon Sep 17 00:00:00 2001 From: Rohan Prinja Date: Sat, 11 Oct 2014 17:29:15 +0530 Subject: [PATCH 2/6] Add form submission via input element --- components/script/dom/htmlformelement.rs | 65 ++++++++++++++----- components/script/dom/htmlinputelement.rs | 52 ++++++++++++++- .../dom/webidls/HTMLInputElement.webidl | 8 +-- 3 files changed, 103 insertions(+), 22 deletions(-) diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index d8b0ad76695..678a090e69b 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -367,46 +367,77 @@ 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) => { + let element: JSRef = ElementCast::from_ref(input_element); + if element.has_attribute("formaction") { + input_element.FormAction() + } else { + input_element.form_owner().map_or("".to_string(), |f_o| f_o.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) => { + let element: JSRef = ElementCast::from_ref(input_element); + if element.has_attribute("formenctype") { + input_element.FormEnctype() + } else { + input_element.form_owner().map_or("".to_string(), |f_o| f_o.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) => { + let element: JSRef = ElementCast::from_ref(input_element); + if element.has_attribute("formmethod") { + input_element.FormMethod() + } else { + input_element.form_owner().map_or("".to_string(), |f_o| f_o.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) => { + let element: JSRef = ElementCast::from_ref(input_element); + if element.has_attribute("formtarget") { + input_element.FormTarget() + } else { + input_element.form_owner().map_or("".to_string(), |f_o| f_o.Target()) + } + } } } } diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 20d8beb5390..14468721e0c 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -19,7 +19,7 @@ use dom::event::Event; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; use dom::htmlformelement::{FormOwner, HTMLFormElement}; -use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node}; +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}; @@ -169,6 +169,56 @@ 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 + fn FormAction(self) -> DOMString { + let element: JSRef = ElementCast::from_ref(self); + let url = element.get_url_attribute("formaction"); + match url.as_slice() { + "" => { + let window = window_from_node(self).root(); + window.get_url().serialize() + }, + _ => url + } + } + + // 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 + fn FormEnctype(self) -> DOMString { + let elem: JSRef = ElementCast::from_ref(self); + let enctype = elem.get_string_attribute("formenctype").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() + } + } + + // 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 + fn FormMethod(self) -> DOMString { + let elem: JSRef = ElementCast::from_ref(self); + let method = elem.get_string_attribute("formmethod").into_ascii_lower(); + // https://html.spec.whatwg.org/multipage/forms.html#attr-fs-method + match method.as_slice() { + "post" | "dialog" => method, + _ => "get".to_string() + } + } + + // 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 { 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; From 749e2394c5c8286734e1dd09312da5c33a7dcde4 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 11 Oct 2014 19:28:03 +0530 Subject: [PATCH 3/6] Add click handler for --- components/script/dom/htmlinputelement.rs | 9 +++++++-- tests/html/form_submission.html | 13 +++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 tests/html/form_submission.html diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 14468721e0c..870b5f7fe8d 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -14,11 +14,11 @@ use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, ResultRootable}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::attr::{AttrHelpers}; use dom::document::{Document, DocumentHelpers}; -use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId }; +use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId}; use dom::event::Event; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; -use dom::htmlformelement::{FormOwner, HTMLFormElement}; +use dom::htmlformelement::{InputElement, FormOwner, HTMLFormElement, HTMLFormElementHelpers}; use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; @@ -420,6 +420,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.submit(false, InputElement(self.clone())) + }); + } _ => {} } } 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 @@ + + + + +
+ + + + + +
+ + From d03120b3a480224d9553e9e229788cf990e9cf77 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 11 Oct 2014 19:28:54 +0530 Subject: [PATCH 4/6] Filter out buttons which aren't the submitter while constructing the dataset --- components/script/dom/htmlformelement.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 678a090e69b..813dc8beb95 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -222,7 +222,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> { script_chan.send(TriggerLoadMsg(win.page().id, load_data)); } - fn get_form_dataset(self, _submitter: Option) -> Vec { + fn get_form_dataset<'b>(self, submitter: Option>) -> Vec { fn clean_crlf(s: &str) -> DOMString { // https://html.spec.whatwg.org/multipage/forms.html#constructing-the-form-data-set // Step 4 @@ -286,6 +286,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 +304,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, From 941bd2dad6d10c2c82a4fa894e8344a65b5bb36d Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 11 Oct 2014 21:31:26 +0530 Subject: [PATCH 5/6] Update wpt expectations --- .../wpt/metadata/html/dom/interfaces.html.ini | 24 ---- .../relevant-mutations.html.ini | 121 +----------------- .../formAction_document_address.html.ini | 9 -- .../formaction.html.ini | 14 +- 4 files changed, 2 insertions(+), 166 deletions(-) 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/embedded-content/the-img-element/relevant-mutations.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini index 3903287d32a..15e62d041e3 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini @@ -1,122 +1,3 @@ [relevant-mutations.html] type: testharness - [src set] - expected: FAIL - - [src changed] - expected: FAIL - - [src removed] - expected: FAIL - - [srcset set] - expected: FAIL - - [srcset changed] - expected: FAIL - - [srcset removed] - expected: FAIL - - [sizes set] - expected: FAIL - - [sizes changed] - expected: FAIL - - [sizes removed] - expected: FAIL - - [src set to same value] - expected: FAIL - - [crossorigin absent to empty] - expected: FAIL - - [crossorigin absent to anonymous] - expected: FAIL - - [crossorigin absent to use-credentials] - expected: FAIL - - [crossorigin empty to absent] - expected: FAIL - - [crossorigin empty to use-credentials] - expected: FAIL - - [crossorigin anonymous to absent] - expected: FAIL - - [crossorigin anonymous to use-credentials] - expected: FAIL - - [crossorigin use-credentials to absent] - expected: FAIL - - [crossorigin use-credentials to empty] - expected: FAIL - - [crossorigin use-credentials to anonymous] - expected: FAIL - - [inserted into picture] - expected: FAIL - - [removed from picture] - expected: FAIL - - [parent is picture, previous source inserted] - expected: FAIL - - [parent is picture, previous source removed] - expected: FAIL - - [parent is picture, previous source has srcset set] - expected: FAIL - - [parent is picture, previous source has srcset changed] - expected: FAIL - - [parent is picture, previous source has srcset removed] - expected: FAIL - - [parent is picture, previous source has sizes set] - expected: FAIL - - [parent is picture, previous source has sizes changed] - expected: FAIL - - [parent is picture, previous source has sizes removed] - expected: FAIL - - [parent is picture, previous source has media set] - expected: FAIL - - [parent is picture, previous source has media changed] - expected: FAIL - - [parent is picture, previous source has media removed] - expected: FAIL - - [parent is picture, previous source has type set] - expected: FAIL - - [parent is picture, previous source has type changed] - expected: FAIL - - [parent is picture, previous source has type removed] - expected: FAIL - - [ancestor picture has a source removed] - expected: FAIL - - [ancestor picture; previous sibling source removed] - expected: FAIL - - [src on previous sibling source set] - expected: FAIL - - [class on previous sibling source set] - expected: FAIL - + expected: TIMEOUT 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 From 79cb1af12ae5cab650ff6d72663da17812d267c2 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 14 Oct 2014 20:44:38 +0530 Subject: [PATCH 6/6] Address review comments --- components/script/dom/htmlformelement.rs | 95 +++----------- components/script/dom/htmlinputelement.rs | 69 +++------- components/script/dom/macros.rs | 46 +++++++ .../relevant-mutations.html.ini | 121 +++++++++++++++++- 4 files changed, 207 insertions(+), 124 deletions(-) diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 813dc8beb95..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 = 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 = 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 = 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 = 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") @@ -384,28 +349,14 @@ impl<'a> FormSubmitter<'a> { fn action(&self) -> DOMString { match *self { FormElement(form) => form.Action(), - InputElement(input_element) => { - let element: JSRef = ElementCast::from_ref(input_element); - if element.has_attribute("formaction") { - input_element.FormAction() - } else { - input_element.form_owner().map_or("".to_string(), |f_o| f_o.Action()) - } - } + InputElement(input_element) => input_element.get_form_attribute("formaction", |i| i.FormAction(), |f| f.Action()) } } fn enctype(&self) -> FormEncType { let attr = match *self { FormElement(form) => form.Enctype(), - InputElement(input_element) => { - let element: JSRef = ElementCast::from_ref(input_element); - if element.has_attribute("formenctype") { - input_element.FormEnctype() - } else { - input_element.form_owner().map_or("".to_string(), |f_o| f_o.Enctype()) - } - } + InputElement(input_element) => input_element.get_form_attribute("formenctype", |i| i.FormEnctype(), |f| f.Enctype()) }; match attr.as_slice() { "multipart/form-data" => FormDataEncoded, @@ -419,14 +370,7 @@ impl<'a> FormSubmitter<'a> { fn method(&self) -> FormMethod { let attr = match *self { FormElement(form) => form.Method(), - InputElement(input_element) => { - let element: JSRef = ElementCast::from_ref(input_element); - if element.has_attribute("formmethod") { - input_element.FormMethod() - } else { - input_element.form_owner().map_or("".to_string(), |f_o| f_o.Method()) - } - } + InputElement(input_element) => input_element.get_form_attribute("formmethod", |i| i.FormMethod(), |f| f.Method()) }; match attr.as_slice() { "dialog" => FormDialog, @@ -438,18 +382,21 @@ impl<'a> FormSubmitter<'a> { fn target(&self) -> DOMString { match *self { FormElement(form) => form.Target(), - InputElement(input_element) => { - let element: JSRef = ElementCast::from_ref(input_element); - if element.has_attribute("formtarget") { - input_element.FormTarget() - } else { - input_element.form_owner().map_or("".to_string(), |f_o| f_o.Target()) - } - } + InputElement(input_element) => input_element.get_form_attribute("formtarget", |i| i.FormTarget(), |f| f.Target()) } } } -pub trait FormOwner<'a> { - fn form_owner(self) -> Option>; +pub trait FormOwner<'a> : Copy { + fn form_owner(self) -> Option>; + fn get_form_attribute(self, attr: &str, + input: |Self| -> DOMString, + owner: |JSRef| -> 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 870b5f7fe8d..52cb51eeaff 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -137,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 = 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") @@ -171,45 +163,19 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> { make_setter!(SetName, "name") // https://html.spec.whatwg.org/multipage/forms.html#dom-input-formaction - fn FormAction(self) -> DOMString { - let element: JSRef = ElementCast::from_ref(self); - let url = element.get_url_attribute("formaction"); - match url.as_slice() { - "" => { - let window = window_from_node(self).root(); - window.get_url().serialize() - }, - _ => url - } - } + 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 - fn FormEnctype(self) -> DOMString { - let elem: JSRef = ElementCast::from_ref(self); - let enctype = elem.get_string_attribute("formenctype").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!(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 - fn FormMethod(self) -> DOMString { - let elem: JSRef = ElementCast::from_ref(self); - let method = elem.get_string_attribute("formmethod").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!(FormMethod, "get", "post" | "dialog") // https://html.spec.whatwg.org/multipage/forms.html#dom-input-formmethod make_setter!(SetFormMethod, "formmethod") @@ -422,7 +388,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { InputRadio => self.SetChecked(true), InputButton(Some(DEFAULT_SUBMIT_VALUE)) => { self.form_owner().map(|o| { - o.submit(false, InputElement(self.clone())) + o.root().submit(false, InputElement(self.clone())) }); } _ => {} @@ -440,10 +406,10 @@ impl Reflectable for HTMLInputElement { 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> { + fn form_owner(self) -> Option> { // https://html.spec.whatwg.org/multipage/forms.html#reset-the-form-owner let elem: JSRef = ElementCast::from_ref(self); - let owner = elem.get_string_attribute("owner"); + let owner = elem.get_string_attribute("form"); if !owner.is_empty() { let doc = document_from_node(self).root(); let owner = doc.GetElementById(owner).root(); @@ -451,13 +417,18 @@ impl<'a> FormOwner<'a> for JSRef<'a, HTMLInputElement> { Some(o) => { let maybe_form: Option> = HTMLFormElementCast::to_ref(*o); if maybe_form.is_some() { - return maybe_form; + return maybe_form.map(Temporary::from_rooted); } }, _ => () } } let node: JSRef = NodeCast::from_ref(self); - node.ancestors().filter_map(|a| HTMLFormElementCast::to_ref(a)).next().clone() + 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 = 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 = 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/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini index 15e62d041e3..3903287d32a 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini @@ -1,3 +1,122 @@ [relevant-mutations.html] type: testharness - expected: TIMEOUT + [src set] + expected: FAIL + + [src changed] + expected: FAIL + + [src removed] + expected: FAIL + + [srcset set] + expected: FAIL + + [srcset changed] + expected: FAIL + + [srcset removed] + expected: FAIL + + [sizes set] + expected: FAIL + + [sizes changed] + expected: FAIL + + [sizes removed] + expected: FAIL + + [src set to same value] + expected: FAIL + + [crossorigin absent to empty] + expected: FAIL + + [crossorigin absent to anonymous] + expected: FAIL + + [crossorigin absent to use-credentials] + expected: FAIL + + [crossorigin empty to absent] + expected: FAIL + + [crossorigin empty to use-credentials] + expected: FAIL + + [crossorigin anonymous to absent] + expected: FAIL + + [crossorigin anonymous to use-credentials] + expected: FAIL + + [crossorigin use-credentials to absent] + expected: FAIL + + [crossorigin use-credentials to empty] + expected: FAIL + + [crossorigin use-credentials to anonymous] + expected: FAIL + + [inserted into picture] + expected: FAIL + + [removed from picture] + expected: FAIL + + [parent is picture, previous source inserted] + expected: FAIL + + [parent is picture, previous source removed] + expected: FAIL + + [parent is picture, previous source has srcset set] + expected: FAIL + + [parent is picture, previous source has srcset changed] + expected: FAIL + + [parent is picture, previous source has srcset removed] + expected: FAIL + + [parent is picture, previous source has sizes set] + expected: FAIL + + [parent is picture, previous source has sizes changed] + expected: FAIL + + [parent is picture, previous source has sizes removed] + expected: FAIL + + [parent is picture, previous source has media set] + expected: FAIL + + [parent is picture, previous source has media changed] + expected: FAIL + + [parent is picture, previous source has media removed] + expected: FAIL + + [parent is picture, previous source has type set] + expected: FAIL + + [parent is picture, previous source has type changed] + expected: FAIL + + [parent is picture, previous source has type removed] + expected: FAIL + + [ancestor picture has a source removed] + expected: FAIL + + [ancestor picture; previous sibling source removed] + expected: FAIL + + [src on previous sibling source set] + expected: FAIL + + [class on previous sibling source set] + expected: FAIL +