Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upImplemented sanitize_value for time input #19379
Conversation
|
This is a good start! Could you move the validation methods to |
| let is_hour = |h: u8| h <= 23; | ||
| let is_minute = |m: u8| m <= 59; | ||
| let is_second = |s: u8| s <= 59; | ||
| let is_ms = |ms: u16| ms <= 999; |
This comment has been minimized.
This comment has been minimized.
KiChjang
Nov 27, 2017
Member
Why are these closures necessary? Comparing the characters directly should be sufficient.
This comment has been minimized.
This comment has been minimized.
SWW13
Nov 27, 2017
Author
Contributor
They are not necessary but I started with an version without parse and they made the code easy to read IMHO. The compiler should inline them anyway.
| len >= 5 && | ||
| // HH:mm | ||
| is_hour(content[0..2].parse().unwrap_or(99)) && | ||
| is_colon(&content[2..3]) && |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
SWW13
Nov 27, 2017
Author
Contributor
&content[2] used to return a char, but that was removed b/c it doesn't work as expected when dealing with utf8. It would be fine for use as the only valid content is ASCII, but that's an edge case we're hitting here.
Maybe working with the underlying byte slice would work, but that's a dirty solution IMHO.
|
|
||
| len >= 5 && | ||
| // HH:mm | ||
| is_hour(content[0..2].parse().unwrap_or(99)) && |
This comment has been minimized.
This comment has been minimized.
KiChjang
Nov 27, 2017
Member
I don't think it's necessary to parse the integer here (and below). We're simply trying to validate the string, not generate a numerical representation of it.
This comment has been minimized.
This comment has been minimized.
SWW13
Nov 27, 2017
Author
Contributor
I'll revert to my early try, but the string validation is a bit of a mess b/c of the utf8 encoding of the strings in rust.
|
It would also be great if you can link to the spec here and provide step annotations on your code, so that future readers of the code would not be too surprised by what's happening in the code. |
f1105c5
to
938b3f6
|
@bors-servo try |
Implemented sanitize_value for time input Implemented value sanitization for `<input type=time/>`. The value has the be valid time string (https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-time-string) or set to empty string. --- The following test results look expected to me, but I'm not sure: ```▶ Unexpected subtest result in /html/semantics/forms/the-input-element/type-change-state.html: │ FAIL [expected PASS] change state from time to text │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to search │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to tel │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to url │ → assert_equals: input.value should be foobar after change of state expected "foobar" but got "" │ FAIL [expected PASS] change state from time to password │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ │ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:53:15 │ Test.prototype.step@http://web-platform.test:8000/resources/testharness.js:1489:20 │ test@http://web-platform.test:8000/resources/testharness.js:511:9 └ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:37:9▶ Unexpected subtest result in /html/semantics/forms/the-input-element/type-change-state.html: │ FAIL [expected PASS] change state from color to time │ → assert_equals: input.value should be #000000 after change of state expected "#000000" but got "" │ │ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:55:15 │ Test.prototype.step@http://web-platform.test:8000/resources/testharness.js:1489:20 │ test@http://web-platform.test:8000/resources/testharness.js:511:9 └ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:37:9 ``` All other tests do now `PASS` instead of `FAIL`. --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix *part of* #19172 - [x] There are tests for these changes - [ ] All tests `PASS` <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19379) <!-- Reviewable:end -->
|
|
|
|
Are these test results expected or not? |
|
They were expected to fail, but after your changes, they now pass, which is a positive sign! All you need to do now is to update the test expectations and address my comments, and we can finally land this! |
| // Step 2 ":" | ||
| is_colon(bytes[2]) && | ||
| // Step 3 "mm" | ||
| is_minute(&bytes[3..5]) && |
This comment has been minimized.
This comment has been minimized.
KiChjang
Nov 29, 2017
•
Member
I can't shake the feeling that the code as it is written is brittle. At the very least though, I can safely say that this isn't as maintainable as it can be, since there's a lot of index calculations based on the positions of the characters.
What particularly makes me nervous is when we're indexing the arrays directly in such a manner, because array indexing can panic during runtime, and if we're not careful enough to validate our assumptions, we would encounter a bug and the thread would panic.
If we can instead iterate through the characters and returning early when we encounter a wrong character, that would be safer and I would feel a bit more relaxed since the language itself would guarantee some invariants that we hold in our assumptions.
This comment has been minimized.
This comment has been minimized.
SWW13
Nov 29, 2017
Author
Contributor
Any good idea how to avoid indexing without bloating the code?
This comment has been minimized.
This comment has been minimized.
KiChjang
Nov 30, 2017
Member
I propose using Iterator::fold and keeping a state machine with the accumulator.
|
New tests still work in progress. |
|
This is looking really good! I only have a couple of small comments. |
| } | ||
| let next_state = |valid: bool, next: State| -> State { if valid { next } else { State::Error } }; | ||
|
|
||
| let state = self.chars().fold(State::HourHigh, |state, c| { |
This comment has been minimized.
This comment has been minimized.
KiChjang
Nov 30, 2017
Member
One thing that we didn't check here is whether the character is ASCII. If at any point we run into a non-ASCII character, we would want to error out. In addition, I recall that iterating over chars is actually a bit slow, due to the fact that it needs to ensure UTF-8 well-formedness on each iteration.
So instead, maybe we could iterate over the underlying u8 data type by calling as_bytes, and importing AsciiExt so that we can use the .is_ascii() function to check whether the character is ASCII on each iteration.
This comment has been minimized.
This comment has been minimized.
SWW13
Dec 1, 2017
Author
Contributor
I don't know if Unicode has other digits that are valid for is_digit, if not we are already check for ASCII implicit.
When using bytes (u8) there should be no need to check for ASCII as we already check for specific values (0-9, :, .) which implies there are only ASCII values.
This comment has been minimized.
This comment has been minimized.
KiChjang
Dec 1, 2017
Member
Good point. I'll leave it up to you whether you want to use u8 or char in this PR.
| } | ||
|
|
||
| // type[j] sanitization | ||
| if ((types[i].type !== "color" && types[j].sanitizedValue) || types[j].sanitizedValue === "") { |
This comment has been minimized.
This comment has been minimized.
KiChjang
Nov 30, 2017
Member
The check for the color type is actually an incorrect change that I wrongly approved of. This should instead just check whether the sanitizedValue exists on the types object.
| assert_equals(input.value, " foo\rbar ", "input.value should be ' foo\\rbar ' after change of state"); | ||
|
|
||
| // type[i] sanitization | ||
| if (types[i].sanitizedValue || types[i].sanitizedValue === "") { |
This comment has been minimized.
This comment has been minimized.
KiChjang
Nov 30, 2017
Member
The ordering on this test looks wrong. If I'm reading correctly, j represents the destination input type that this input element is changing to, so the value sanitization algorithm should run for the input type described by types[j] first.
This comment has been minimized.
This comment has been minimized.
SWW13
Dec 1, 2017
Author
Contributor
I'm still not sure how to do this right. We have an input sanitization which may set the value to empty string, trims it somehow or replace it with a default value on source input type and then afterwards another that possibly do the same with that value on the destination input type.
This comment has been minimized.
This comment has been minimized.
KiChjang
Dec 1, 2017
Member
So the part of the spec that specifies this behaviour is located here https://html.spec.whatwg.org/multipage/input.html#input-type-change, and in the code it's this line:
servo/components/script/dom/htmlinputelement.rs
Line 1016 in b7bb6ff
... which will get called whenever the value content attribute is modified.
This comment has been minimized.
This comment has been minimized.
SWW13
Dec 1, 2017
Author
Contributor
I think we're getting near the expected test behavior, the only thing left is:
▶ Unexpected subtest result in /html/semantics/forms/the-input-element/type-change-state.html:
│ FAIL [expected PASS] change state from time to checkbox
│ → assert_equals: input.value should be '' after change of state expected "" but got "on"
│ FAIL [expected PASS] change state from time to radio
│ → assert_equals: input.value should be '' after change of state expected "" but got "on"
BTW: There are many tests that pass who should fail (many date / number related input type changes).
This comment has been minimized.
This comment has been minimized.
KiChjang
Dec 1, 2017
Member
Ick, this is because the default value for radio and checkbox is "on" instead of the empty string:
So, perhaps instead of having a default boolean field on the test object, we should have a defaultValue string.
This comment has been minimized.
This comment has been minimized.
SWW13
Dec 1, 2017
Author
Contributor
The more I try to find a nice solution the more I break :/ The additional defaultValue is ugly, but at least the tests work now.
| input.type = types[i].type; | ||
| if (types[i].type === "file") { | ||
| assert_throws("INVALID_STATE_ERR", function() { | ||
| input.value = " foo\rbar "; | ||
| input.value = expected; |
This comment has been minimized.
This comment has been minimized.
tigercosmos
Nov 30, 2017
Collaborator
why set input.value = expected; here?
I think keep expected = " foo\rbar " is fine.
This comment has been minimized.
This comment has been minimized.
KiChjang
Nov 30, 2017
•
Member
That's fine, it deduplicates the repeated construction of a new string.
This comment has been minimized.
This comment has been minimized.
tigercosmos
Dec 1, 2017
Collaborator
Moving input.value = " foo\rbar " is good.
But input.value = expected; seems unnecessary.
This comment has been minimized.
This comment has been minimized.
KiChjang
Dec 1, 2017
Member
I'm not following. This subtest in particular throws an exception when input.value is being assigned to. Are you saying that we should rather duplicate writing " foo\rbar " here?
This comment has been minimized.
This comment has been minimized.
tigercosmos
Dec 1, 2017
Collaborator
NO, we have moved input.value = " foo\rbar " to the front of if section.
input.value is set, and not need to set input.value = expected;
This comment has been minimized.
This comment has been minimized.
KiChjang
Dec 1, 2017
Member
You've lost me. The change that this patch has is var expected = " foo\rbar "; instead of input.value = " foo\rbar ";
| } | ||
| } | ||
|
|
||
| assert_equals(input.value, expected, "input.value should be '" + expected + "' after change of state"); |
This comment has been minimized.
This comment has been minimized.
tigercosmos
Dec 1, 2017
Collaborator
@KiChjang which mean this line is unnecessary to change to expected, just keep the origin " foo\rbar "
This comment has been minimized.
This comment has been minimized.
KiChjang
Dec 1, 2017
Member
No, this is clearly not the case, because the expected value may change depending on whether the destination type has a sanitizedValue as defined above in this test file.
This comment has been minimized.
This comment has been minimized.
SWW13
Dec 1, 2017
Author
Contributor
Keeping " foo\rbar " is wrong, we are not always expecting " foo\rbar ", sometimes it's "#000000", "foo\rbar" or "".
This comment has been minimized.
This comment has been minimized.
|
@tigercosmos Not necessary, whichever PR lands last would simply need to rebase against master. |
|
@bors-servo r+ Thanks! |
|
|
Implemented sanitize_value for time input Implemented value sanitization for `<input type=time/>`. The value has the be valid time string (https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-time-string) or set to empty string. --- The following test results look expected to me, but I'm not sure: ```▶ Unexpected subtest result in /html/semantics/forms/the-input-element/type-change-state.html: │ FAIL [expected PASS] change state from time to text │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to search │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to tel │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to url │ → assert_equals: input.value should be foobar after change of state expected "foobar" but got "" │ FAIL [expected PASS] change state from time to password │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ │ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:53:15 │ Test.prototype.step@http://web-platform.test:8000/resources/testharness.js:1489:20 │ test@http://web-platform.test:8000/resources/testharness.js:511:9 └ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:37:9▶ Unexpected subtest result in /html/semantics/forms/the-input-element/type-change-state.html: │ FAIL [expected PASS] change state from color to time │ → assert_equals: input.value should be #000000 after change of state expected "#000000" but got "" │ │ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:55:15 │ Test.prototype.step@http://web-platform.test:8000/resources/testharness.js:1489:20 │ test@http://web-platform.test:8000/resources/testharness.js:511:9 └ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:37:9 ``` All other tests do now `PASS` instead of `FAIL`. --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix *part of* #19172 - [x] There are tests for these changes - [x] All tests `PASS` <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19379) <!-- Reviewable:end -->
|
|
|
@bors-servo: retry |
Implemented sanitize_value for time input Implemented value sanitization for `<input type=time/>`. The value has the be valid time string (https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-time-string) or set to empty string. --- The following test results look expected to me, but I'm not sure: ```▶ Unexpected subtest result in /html/semantics/forms/the-input-element/type-change-state.html: │ FAIL [expected PASS] change state from time to text │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to search │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to tel │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to url │ → assert_equals: input.value should be foobar after change of state expected "foobar" but got "" │ FAIL [expected PASS] change state from time to password │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ │ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:53:15 │ Test.prototype.step@http://web-platform.test:8000/resources/testharness.js:1489:20 │ test@http://web-platform.test:8000/resources/testharness.js:511:9 └ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:37:9▶ Unexpected subtest result in /html/semantics/forms/the-input-element/type-change-state.html: │ FAIL [expected PASS] change state from color to time │ → assert_equals: input.value should be #000000 after change of state expected "#000000" but got "" │ │ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:55:15 │ Test.prototype.step@http://web-platform.test:8000/resources/testharness.js:1489:20 │ test@http://web-platform.test:8000/resources/testharness.js:511:9 └ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:37:9 ``` All other tests do now `PASS` instead of `FAIL`. --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix *part of* #19172 - [x] There are tests for these changes - [x] All tests `PASS` <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19379) <!-- Reviewable:end -->
|
|
|
@bors-servo retry |
Implemented sanitize_value for time input Implemented value sanitization for `<input type=time/>`. The value has the be valid time string (https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-time-string) or set to empty string. --- The following test results look expected to me, but I'm not sure: ```▶ Unexpected subtest result in /html/semantics/forms/the-input-element/type-change-state.html: │ FAIL [expected PASS] change state from time to text │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to search │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to tel │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ FAIL [expected PASS] change state from time to url │ → assert_equals: input.value should be foobar after change of state expected "foobar" but got "" │ FAIL [expected PASS] change state from time to password │ → assert_equals: input.value should be foobar after change of state expected " foobar " but got "" │ │ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:53:15 │ Test.prototype.step@http://web-platform.test:8000/resources/testharness.js:1489:20 │ test@http://web-platform.test:8000/resources/testharness.js:511:9 └ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:37:9▶ Unexpected subtest result in /html/semantics/forms/the-input-element/type-change-state.html: │ FAIL [expected PASS] change state from color to time │ → assert_equals: input.value should be #000000 after change of state expected "#000000" but got "" │ │ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:55:15 │ Test.prototype.step@http://web-platform.test:8000/resources/testharness.js:1489:20 │ test@http://web-platform.test:8000/resources/testharness.js:511:9 └ @http://web-platform.test:8000/html/semantics/forms/the-input-element/type-change-state.html:37:9 ``` All other tests do now `PASS` instead of `FAIL`. --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix *part of* #19172 - [x] There are tests for these changes - [x] All tests `PASS` <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19379) <!-- Reviewable:end -->
|
|
- Implemented is_valid_time_string for DOMString. - Use is_valid_time_string for sanitize_value with time input. - Improved input type change test Upstreamed from servo/servo#19379 [ci skip]
SWW13 commentedNov 26, 2017
•
edited
Implemented value sanitization for
<input type=time/>.The value has the be valid time string (https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-time-string) or set to empty string.
The following test results look expected to me, but I'm not sure:
All other tests do now
PASSinstead ofFAIL../mach build -ddoes not report any errors./mach test-tidydoes not report any errorsPASSThis change is