aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxim Tsoy <maks.tsoy@gmail.com>2020-06-27 02:01:01 +0200
committerMaxim Tsoy <maks.tsoy@gmail.com>2020-07-02 12:33:21 +0200
commit8194da2752feda8ae58de455ebe0ca6f5904feb8 (patch)
tree1668ad0396fd6e1a5867392c1d99c389a17bb43c
parent19b36bd7952630d2e4a749565c570d3cca217658 (diff)
downloadservo-8194da2752feda8ae58de455ebe0ca6f5904feb8.tar.gz
servo-8194da2752feda8ae58de455ebe0ca6f5904feb8.zip
Implement HTMLFormElement.requestSubmit()Also includes a fix for reentrant form submission behavior
-rwxr-xr-xcomponents/script/dom/htmlbuttonelement.rs5
-rw-r--r--components/script/dom/htmlformelement.rs161
-rwxr-xr-xcomponents/script/dom/htmlinputelement.rs6
-rw-r--r--components/script/dom/webidls/HTMLFormElement.webidl1
-rw-r--r--tests/wpt/metadata-layout-2020/html/dom/idlharness.https.html.ini6
-rw-r--r--tests/wpt/metadata/html/dom/idlharness.https.html.ini12
-rw-r--r--tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini16
-rw-r--r--tests/wpt/metadata/html/semantics/forms/the-form-element/form-requestsubmit.html.ini16
8 files changed, 143 insertions, 80 deletions
diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs
index 100d9fb92b7..9e9898f328c 100755
--- a/components/script/dom/htmlbuttonelement.rs
+++ b/components/script/dom/htmlbuttonelement.rs
@@ -77,6 +77,11 @@ impl HTMLButtonElement {
document,
)
}
+
+ #[inline]
+ pub fn is_submit_button(&self) -> bool {
+ self.button_type.get() == ButtonType::Submit
+ }
}
impl HTMLButtonElementMethods for HTMLButtonElement {
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index 76ae4a7ab27..4e2439b13dd 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -16,6 +16,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputE
use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
+use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
@@ -88,6 +89,7 @@ pub struct HTMLFormElement {
generation_id: Cell<GenerationId>,
controls: DomRefCell<Vec<Dom<Element>>>,
past_names_map: DomRefCell<HashMap<Atom, (Dom<Element>, Tm)>>,
+ firing_submission_events: Cell<bool>,
}
impl HTMLFormElement {
@@ -104,6 +106,7 @@ impl HTMLFormElement {
generation_id: Cell::new(GenerationId(0)),
controls: DomRefCell::new(Vec::new()),
past_names_map: DomRefCell::new(HashMap::new()),
+ firing_submission_events: Cell::new(false),
}
}
@@ -243,6 +246,67 @@ impl HTMLFormElementMethods for HTMLFormElement {
self.submit(SubmittedFrom::FromForm, FormSubmitter::FormElement(self));
}
+ // https://html.spec.whatwg.org/multipage/#dom-form-requestsubmit
+ fn RequestSubmit(&self, submitter: Option<&HTMLElement>) -> Fallible<()> {
+ let submitter: FormSubmitter = match submitter {
+ Some(submitter_element) => {
+ // Step 1.1
+ let error_not_a_submit_button =
+ Err(Error::Type("submitter must be a submit button".to_string()));
+
+ let element = match submitter_element.upcast::<Node>().type_id() {
+ NodeTypeId::Element(ElementTypeId::HTMLElement(element)) => element,
+ _ => {
+ return error_not_a_submit_button;
+ },
+ };
+
+ let submit_button = match element {
+ HTMLElementTypeId::HTMLInputElement => FormSubmitter::InputElement(
+ &submitter_element
+ .downcast::<HTMLInputElement>()
+ .expect("Failed to downcast submitter elem to HTMLInputElement."),
+ ),
+ HTMLElementTypeId::HTMLButtonElement => FormSubmitter::ButtonElement(
+ &submitter_element
+ .downcast::<HTMLButtonElement>()
+ .expect("Failed to downcast submitter elem to HTMLButtonElement."),
+ ),
+ _ => {
+ return error_not_a_submit_button;
+ },
+ };
+
+ if !submit_button.is_submit_button() {
+ return error_not_a_submit_button;
+ }
+
+ let submitters_owner = submit_button.form_owner();
+
+ // Step 1.2
+ let owner = match submitters_owner {
+ Some(owner) => owner,
+ None => {
+ return Err(Error::NotFound);
+ },
+ };
+
+ if *owner != *self {
+ return Err(Error::NotFound);
+ }
+
+ submit_button
+ },
+ None => {
+ // Step 2
+ FormSubmitter::FormElement(&self)
+ },
+ };
+ // Step 3
+ self.submit(SubmittedFrom::NotFromForm, submitter);
+ Ok(())
+ }
+
// https://html.spec.whatwg.org/multipage/#dom-form-reset
fn Reset(&self) {
self.reset(ResetFrom::FromForm);
@@ -599,28 +663,38 @@ impl HTMLFormElement {
let base = doc.base_url();
// TODO: Handle browsing contexts (Step 4, 5)
// Step 6
- if submit_method_flag == SubmittedFrom::NotFromForm && !submitter.no_validate(self) {
- if self.interactive_validation().is_err() {
- // TODO: Implement event handlers on all form control elements
- self.upcast::<EventTarget>().fire_event(atom!("invalid"));
+ if submit_method_flag == SubmittedFrom::NotFromForm {
+ // Step 6.1
+ if self.firing_submission_events.get() {
return;
}
- }
- // Step 7
- // spec calls this "submitterButton" but it doesn't have to be a button,
- // just not be the form itself
- let submitter_button = match submitter {
- FormSubmitter::FormElement(f) => {
- if f == self {
- None
- } else {
- Some(f.upcast::<HTMLElement>())
+ // Step 6.2
+ self.firing_submission_events.set(true);
+ // Step 6.3
+ if !submitter.no_validate(self) {
+ if self.interactive_validation().is_err() {
+ // TODO: Implement event handlers on all form control elements
+ self.upcast::<EventTarget>().fire_event(atom!("invalid"));
+ self.firing_submission_events.set(false);
+ return;
}
- },
- FormSubmitter::InputElement(i) => Some(i.upcast::<HTMLElement>()),
- FormSubmitter::ButtonElement(b) => Some(b.upcast::<HTMLElement>()),
- };
- if submit_method_flag == SubmittedFrom::NotFromForm {
+ }
+ // Step 6.4
+ // spec calls this "submitterButton" but it doesn't have to be a button,
+ // just not be the form itself
+ let submitter_button = match submitter {
+ FormSubmitter::FormElement(f) => {
+ if f == self {
+ None
+ } else {
+ Some(f.upcast::<HTMLElement>())
+ }
+ },
+ FormSubmitter::InputElement(i) => Some(i.upcast::<HTMLElement>()),
+ FormSubmitter::ButtonElement(b) => Some(b.upcast::<HTMLElement>()),
+ };
+
+ // Step 6.5
let event = SubmitEvent::new(
&self.global(),
atom!("submit"),
@@ -630,48 +704,51 @@ impl HTMLFormElement {
);
let event = event.upcast::<Event>();
event.fire(self.upcast::<EventTarget>());
+
+ // Step 6.6
+ self.firing_submission_events.set(false);
+ // Step 6.7
if event.DefaultPrevented() {
return;
}
-
- // Step 7-3
+ // Step 6.8
if self.upcast::<Element>().cannot_navigate() {
return;
}
}
- // Step 8
+ // Step 7
let encoding = self.pick_encoding();
- // Step 9
+ // Step 8
let mut form_data = match self.get_form_dataset(Some(submitter), Some(encoding)) {
Some(form_data) => form_data,
None => return,
};
- // Step 10
+ // Step 9
if self.upcast::<Element>().cannot_navigate() {
return;
}
- // Step 11
+ // Step 10
let mut action = submitter.action();
- // Step 12
+ // Step 11
if action.is_empty() {
action = DOMString::from(base.as_str());
}
- // Step 13-14
+ // Step 12-13
let action_components = match base.join(&action) {
Ok(url) => url,
Err(_) => return,
};
- // Step 15-17
+ // Step 14-16
let scheme = action_components.scheme().to_owned();
let enctype = submitter.enctype();
let method = submitter.method();
- // Step 18-21
+ // Step 17-21
let target_attribute_value = submitter.target();
let source = doc.browsing_context().unwrap();
let (maybe_chosen, _new) = source.choose_browsing_context(target_attribute_value, false);
@@ -1232,11 +1309,14 @@ pub enum FormMethod {
FormDialog,
}
+/// <https://html.spec.whatwg.org/multipage/#form-associated-element>
#[derive(Clone, Copy, MallocSizeOf)]
pub enum FormSubmitter<'a> {
FormElement(&'a HTMLFormElement),
InputElement(&'a HTMLInputElement),
- ButtonElement(&'a HTMLButtonElement), // TODO: image submit, etc etc
+ ButtonElement(&'a HTMLButtonElement),
+ // TODO: implement other types of form associated elements
+ // (including custom elements) that can be passed as submitter.
}
impl<'a> FormSubmitter<'a> {
@@ -1332,6 +1412,27 @@ impl<'a> FormSubmitter<'a> {
),
}
}
+
+ // https://html.spec.whatwg.org/multipage/#concept-submit-button
+ fn is_submit_button(&self) -> bool {
+ match *self {
+ // https://html.spec.whatwg.org/multipage/#image-button-state-(type=image)
+ // https://html.spec.whatwg.org/multipage/#submit-button-state-(type=submit)
+ FormSubmitter::InputElement(input_element) => input_element.is_submit_button(),
+ // https://html.spec.whatwg.org/multipage/#attr-button-type-submit-state
+ FormSubmitter::ButtonElement(button_element) => button_element.is_submit_button(),
+ _ => false,
+ }
+ }
+
+ // https://html.spec.whatwg.org/multipage/#form-owner
+ fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
+ match *self {
+ FormSubmitter::ButtonElement(button_el) => button_el.form_owner(),
+ FormSubmitter::InputElement(input_el) => input_el.form_owner(),
+ _ => None,
+ }
+ }
}
pub trait FormControl: DomObject {
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 1078f71d59e..a8ca388c172 100755
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -405,6 +405,12 @@ impl HTMLInputElement {
self.input_type.get()
}
+ #[inline]
+ pub fn is_submit_button(&self) -> bool {
+ let input_type = self.input_type.get();
+ input_type == InputType::Submit || input_type == InputType::Image
+ }
+
pub fn disable_sanitization(&self) {
self.sanitization_flag.set(false);
}
diff --git a/components/script/dom/webidls/HTMLFormElement.webidl b/components/script/dom/webidls/HTMLFormElement.webidl
index c7ea91a472c..78d7e96ec39 100644
--- a/components/script/dom/webidls/HTMLFormElement.webidl
+++ b/components/script/dom/webidls/HTMLFormElement.webidl
@@ -32,6 +32,7 @@ interface HTMLFormElement : HTMLElement {
getter (RadioNodeList or Element) (DOMString name);
void submit();
+ [Throws] void requestSubmit(optional HTMLElement? submitter = null);
[CEReactions]
void reset();
boolean checkValidity();
diff --git a/tests/wpt/metadata-layout-2020/html/dom/idlharness.https.html.ini b/tests/wpt/metadata-layout-2020/html/dom/idlharness.https.html.ini
index 3dabbc6d6a3..a3cf6d4bf2d 100644
--- a/tests/wpt/metadata-layout-2020/html/dom/idlharness.https.html.ini
+++ b/tests/wpt/metadata-layout-2020/html/dom/idlharness.https.html.ini
@@ -1650,9 +1650,6 @@
[idlharness.https.html?include=HTML.*]
- [HTMLFormElement interface: calling requestSubmit(optional HTMLElement?) on document.createElement("form") with too few arguments must throw TypeError]
- expected: FAIL
-
[HTMLTableSectionElement interface: document.createElement("tfoot") must inherit property "align" with the proper type]
expected: FAIL
@@ -2307,9 +2304,6 @@
[HTMLObjectElement interface: document.createElement("object") must inherit property "archive" with the proper type]
expected: FAIL
- [HTMLFormElement interface: operation requestSubmit(optional HTMLElement?)]
- expected: FAIL
-
[HTMLTableColElement interface: document.createElement("col") must inherit property "chOff" with the proper type]
expected: FAIL
diff --git a/tests/wpt/metadata/html/dom/idlharness.https.html.ini b/tests/wpt/metadata/html/dom/idlharness.https.html.ini
index bbc23fe7631..6f4b20ee287 100644
--- a/tests/wpt/metadata/html/dom/idlharness.https.html.ini
+++ b/tests/wpt/metadata/html/dom/idlharness.https.html.ini
@@ -3621,9 +3621,6 @@
[HTMLAreaElement interface: document.createElement("area") must inherit property "download" with the proper type]
expected: FAIL
- [HTMLFormElement interface: calling requestSubmit(HTMLElement) on document.createElement("form") with too few arguments must throw TypeError]
- expected: FAIL
-
[HTMLProgressElement interface: attribute value]
expected: FAIL
@@ -3711,9 +3708,6 @@
[HTMLImageElement interface: document.createElement("img") must inherit property "loading" with the proper type]
expected: FAIL
- [HTMLFormElement interface: calling requestSubmit(optional HTMLElement?) on document.createElement("form") with too few arguments must throw TypeError]
- expected: FAIL
-
[HTMLSlotElement interface: calling assignedNodes(optional AssignedNodesOptions) on document.createElement("slot") with too few arguments must throw TypeError]
expected: FAIL
@@ -3732,18 +3726,12 @@
[HTMLAllCollection interface: document.all must inherit property "item(optional DOMString)" with the proper type]
expected: FAIL
- [HTMLFormElement interface: operation requestSubmit(optional HTMLElement?)]
- expected: FAIL
-
[HTMLAllCollection interface: calling item(optional DOMString) on document.all with too few arguments must throw TypeError]
expected: FAIL
[HTMLAllCollection interface: operation item(optional DOMString)]
expected: FAIL
- [HTMLFormElement interface: document.createElement("form") must inherit property "requestSubmit(optional HTMLElement?)" with the proper type]
- expected: FAIL
-
[HTMLCanvasElement interface: document.createElement("canvas") must inherit property "toBlob(BlobCallback, optional DOMString, optional any)" with the proper type]
expected: FAIL
diff --git a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini b/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini
index 193868a895b..3d6a35c28e5 100644
--- a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini
+++ b/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini
@@ -1,19 +1,3 @@
[form-submission-algorithm.html]
- [If form's firing submission events is true, then return; 'invalid' event]
- expected: FAIL
-
- [firing an event named submit; form.requestSubmit(submitter)]
- expected: FAIL
-
- [firing an event named submit; form.requestSubmit(null)]
- expected: FAIL
-
- [firing an event named submit; form.requestSubmit()]
- expected: FAIL
-
[Submission URL should always have a non-null query part]
expected: FAIL
-
- [If firing submission events flag of form is true, then return]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/html/semantics/forms/the-form-element/form-requestsubmit.html.ini b/tests/wpt/metadata/html/semantics/forms/the-form-element/form-requestsubmit.html.ini
index a58b5dc12ca..4f05392b2e0 100644
--- a/tests/wpt/metadata/html/semantics/forms/the-form-element/form-requestsubmit.html.ini
+++ b/tests/wpt/metadata/html/semantics/forms/the-form-element/form-requestsubmit.html.ini
@@ -1,22 +1,6 @@
[form-requestsubmit.html]
- [requestSubmit() doesn't run interactive validation reentrantly]
- expected: FAIL
-
[The value of the submitter should be appended, and form* attributes of the submitter should be handled.]
expected: FAIL
- [Passing a submit button not owned by the context object should throw]
- expected: FAIL
-
- [requestSubmit() for a disconnected form should not submit the form]
- expected: FAIL
-
[requestSubmit() should trigger interactive form validation]
expected: FAIL
-
- [requestSubmit() doesn't run form submission reentrantly]
- expected: FAIL
-
- [requestSubmit() should accept button[type=submit\], input[type=submit\], and input[type=image\]]
- expected: FAIL
-