Implement HTMLFormElement.requestSubmit()#27100
Conversation
|
Thanks for the pull request, and welcome! The Servo team is excited to review your changes, and you should hear from @ferjm (or someone else) soon. |
|
Heads up! This PR modifies the following files:
|
| [form-requestsubmit.html] | ||
| [requestSubmit() doesn't run interactive validation reentrantly] | ||
| expected: FAIL | ||
| expected: PASS |
There was a problem hiding this comment.
These passing tests can be removed instead of being marked as PASS.
gterzian
left a comment
There was a problem hiding this comment.
Looks great! Couple of comments...
| let submitters_owner = submit_button.form_owner(); | ||
|
|
||
| // Step 1.2 | ||
| if submitters_owner.is_none() || |
There was a problem hiding this comment.
let owner = match submitters_owner {
Some(owner) => owner,
None => return Err(Error::NotFound),
};
if *owner != self {
return Err(Error::NotFound);
}There was a problem hiding this comment.
Done, with a small change: *owner != *self
| let submitter: FormSubmitter = match submitter { | ||
| Some(submitter_element) => { | ||
| // Step 1.1 | ||
| let submit_button = match submitter_element.upcast::<Node>().type_id() { |
There was a problem hiding this comment.
Sort of a nit really, however since you say it's your first hundred lines of Rust:
I think you can simplify this by limited the nesting inside the match, and instead doing a flat series of matches where you would do an early return in the None case, for example:
let element = match match submitter_element.upcast::<Node>().type_id() {
Some(element) => element,
None => {
return Err(Error::Type("submitter must be a submit button".to_string()));
}
};
let submit_button = match element {
HTMLElementTypeId::HTMLInputElement => //
HTMLElementTypeId::HTMLButtonElement => //
_ => {
return Err(Error::Type("submitter must be a submit button".to_string()));
}
};
if !submit_button.is_submit_button() {
return Err(Error::Type("submitter must be a submit button".to_string()));
}You do get some repetition with the error case, but it's much easier to read.
Also regarding the submitter_element.downcast::<HTMLInputElement>().unwrap(), I think it's better to do expect("Failed to downcast submitter elem to HTMLInputElement.") or something similar so that it's easier to figure out what when wrong when it does.
There was a problem hiding this comment.
:) As a matter of fact, I was doubting whether I should do this when I was first writing this. Wasn't sure what would be more "Rust-y". I suppose it comes down to a code style really, and I don't have a strong opinion on this, so I changed it as you suggested 👍
| }; | ||
| // Step 3 | ||
| self.submit(SubmittedFrom::NotFromForm, submitter); | ||
| return Ok(()); |
There was a problem hiding this comment.
nit: no need for the explicit return.
There was a problem hiding this comment.
changed it. Implicit returns are hard to get used to :)
| InputElement(&'a HTMLInputElement), | ||
| ButtonElement(&'a HTMLButtonElement), // TODO: image submit, etc etc | ||
| ButtonElement(&'a HTMLButtonElement), | ||
| // TODO: the spec doesn't seem to put hard constraints on submitter, so it can theoretically be any element |
There was a problem hiding this comment.
I think instead of here, we can add a doc to the enum with \\\ <https://html.spec.whatwg.org/multipage/#form-associated-element>, and then add a TODO that would simply state to implement the other types of form associated elements, including custom elements.
| 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")); |
There was a problem hiding this comment.
This looks un-necessary, as it is already done at step 6.1 of static validation(which is called into at step 1 of interactive validation).
There was a problem hiding this comment.
I didn't change this part, I merely added indentation. Looking at the code you mentioned, I got an impression that static validation handles only elements inside the form, whereas this line fires an event on the form itself. I might be wrong about this, would you mind double-checking?
There was a problem hiding this comment.
Ok. You're right about the different event targets, and I actually don't see where in the spec the event is fired on the element as a whole, however if this isn't an actual change, let's leave it for now...
There was a problem hiding this comment.
Actually I think I've found where the event should be fired on the element as a whole, and this line appears incorrect, so I've filed a follow-up at #27133
| 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 { |
There was a problem hiding this comment.
Please number the various Step 6 substeps, like you've already done with 6.4 below
There was a problem hiding this comment.
done, for the whole function (numbers were off, I suppose the spec changed at some point)
| let event = event.upcast::<Event>(); | ||
| event.fire(self.upcast::<EventTarget>()); | ||
|
|
||
| self.firing_submission_events.set(false); |
| } | ||
|
|
||
| #[inline] | ||
| pub fn button_type(&self) -> ButtonType { |
There was a problem hiding this comment.
I would rather have a is_submit method on HTMLButtonElement, since the button_type seems to only be used for that purpose(thay way the ButtonType doesn't have to become public either).
There was a problem hiding this comment.
Sounds reasonable, done.
| // https://html.spec.whatwg.org/multipage/#concept-submit-button | ||
| fn is_submit_button(&self) -> bool { | ||
| match *self { | ||
| FormSubmitter::InputElement(input_element) => { |
There was a problem hiding this comment.
I think here we can add a link to https://html.spec.whatwg.org/multipage/#image-button-state-(type=image)
There was a problem hiding this comment.
this line handles both type=image and type=submit, I added the links
| input_element.input_type() == InputType::Submit || | ||
| input_element.input_type() == InputType::Image | ||
| }, | ||
| FormSubmitter::ButtonElement(button_element) => { |
There was a problem hiding this comment.
There was a problem hiding this comment.
| } | ||
|
|
||
| #[inline] | ||
| pub fn button_type(&self) -> ButtonType { |
There was a problem hiding this comment.
Sounds reasonable, done.
| let submitter: FormSubmitter = match submitter { | ||
| Some(submitter_element) => { | ||
| // Step 1.1 | ||
| let submit_button = match submitter_element.upcast::<Node>().type_id() { |
There was a problem hiding this comment.
:) As a matter of fact, I was doubting whether I should do this when I was first writing this. Wasn't sure what would be more "Rust-y". I suppose it comes down to a code style really, and I don't have a strong opinion on this, so I changed it as you suggested 👍
| let submitters_owner = submit_button.form_owner(); | ||
|
|
||
| // Step 1.2 | ||
| if submitters_owner.is_none() || |
There was a problem hiding this comment.
Done, with a small change: *owner != *self
| }; | ||
| // Step 3 | ||
| self.submit(SubmittedFrom::NotFromForm, submitter); | ||
| return Ok(()); |
There was a problem hiding this comment.
changed it. Implicit returns are hard to get used to :)
| 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 { |
There was a problem hiding this comment.
done, for the whole function (numbers were off, I suppose the spec changed at some point)
| 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")); |
There was a problem hiding this comment.
I didn't change this part, I merely added indentation. Looking at the code you mentioned, I got an impression that static validation handles only elements inside the form, whereas this line fires an event on the form itself. I might be wrong about this, would you mind double-checking?
| let event = event.upcast::<Event>(); | ||
| event.fire(self.upcast::<EventTarget>()); | ||
|
|
||
| self.firing_submission_events.set(false); |
| InputElement(&'a HTMLInputElement), | ||
| ButtonElement(&'a HTMLButtonElement), // TODO: image submit, etc etc | ||
| ButtonElement(&'a HTMLButtonElement), | ||
| // TODO: the spec doesn't seem to put hard constraints on submitter, so it can theoretically be any element |
| // https://html.spec.whatwg.org/multipage/#concept-submit-button | ||
| fn is_submit_button(&self) -> bool { | ||
| match *self { | ||
| FormSubmitter::InputElement(input_element) => { |
There was a problem hiding this comment.
this line handles both type=image and type=submit, I added the links
| input_element.input_type() == InputType::Submit || | ||
| input_element.input_type() == InputType::Image | ||
| }, | ||
| FormSubmitter::ButtonElement(button_element) => { |
There was a problem hiding this comment.
gterzian
left a comment
There was a problem hiding this comment.
Awesome work! Please squash into one commit and then it should be ready to go...
gterzian
left a comment
There was a problem hiding this comment.
The event-firing does appear redundant at that line, and I think it's a good follow-up, since it's not an actual change of this PR.
| 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")); |
There was a problem hiding this comment.
Actually I think I've found where the event should be fired on the element as a whole, and this line appears incorrect, so I've filed a follow-up at #27133
|
@gterzian I squashed the commits 👍 I'm happy to work on #27133, it does look like a good follow-up indeed. |
If the PR includes different pieces of work, then it could have multiple commits, so I wouldn't say it's a requirement to always squash. The Github workflow, linked to from the contribution guide, goes into this a bit more I believe. |
|
Thanks! @bors-servo r+ |
|
📌 Commit 332ad1a has been approved by |
Sounds like a plan! |
Implement HTMLFormElement.requestSubmit() <!-- Please describe your changes on the following line: --> This PR contains an implementation of [HTMLFormElement.requestSubmit()](https://html.spec.whatwg.org/multipage/forms.html#dom-form-requestsubmit) This is literally my first hundred lines of Rust code, so if I crossed a few sacred lines here and there, please go easy on me :) --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #23417 <!-- Either: --> - [x] [WPT tests](https://github.com/servo/servo/blob/master/tests/wpt/web-platform-tests/html/semantics/forms/the-form-element/form-requestsubmit.html) for these changes There are two tests that still fail because we don't support `:invalid` CSS selector (see #10781). I verified that they pass if you change them to not use `:invalid`. Should be unlocked by #26729. <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
|
💔 Test failed - status-taskcluster |
Great, some more test that require updating to PASS. |
…trant form submission behavior
|
@bors-servo r=gterzian |
|
📌 Commit 8194da2 has been approved by |
|
☀️ Test successful - status-taskcluster |
Do not fire `invalid` event on form elements This is a follow-up to my [previous PR](#27100) suggested by @gterzian. `invalid` event on the `<form>` element has been [recently removed](whatwg/html#4626) from the spec. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #27133 <!-- Either: --> - [x] [WPT test](https://github.com/servo/servo/blob/master/tests/wpt/web-platform-tests/html/semantics/forms/form-submission-0/historical.window.js) marked as passing
Do not fire `invalid` event on form elements This is a follow-up to my [previous PR](#27100) suggested by @gterzian. `invalid` event on the `<form>` element has been [recently removed](whatwg/html#4626) from the spec. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #27133 <!-- Either: --> - [x] [WPT test](https://github.com/servo/servo/blob/master/tests/wpt/web-platform-tests/html/semantics/forms/form-submission-0/historical.window.js) marked as passing
This PR contains an implementation of HTMLFormElement.requestSubmit()
This is literally my first hundred lines of Rust code, so if I crossed a few sacred lines here and there, please go easy on me :)
./mach build -ddoes not report any errors./mach test-tidydoes not report any errorsThere are two tests that still fail because we don't support
:invalidCSS selector (see Implement the :valid and :invalid pseudo-classes #10781). I verified that they pass if you change them to not use:invalid. Should be unlocked by Implement :valid :invalid pseudo classes #26729.