From cb8702587f2635a23727b466902caf694fd20e11 Mon Sep 17 00:00:00 2001 From: Bastien Orivel Date: Mon, 15 Oct 2018 14:50:46 +0200 Subject: [PATCH 1/3] Add a way not to sanitize values in an input until its creation is done --- .../script/dom/bindings/htmlconstructor.rs | 2 ++ components/script/dom/element.rs | 26 +++++++++++++++++++ components/script/dom/htmlinputelement.rs | 10 +++++++ components/script/dom/servoparser/mod.rs | 4 ++- 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/components/script/dom/bindings/htmlconstructor.rs b/components/script/dom/bindings/htmlconstructor.rs index c8f7e63358ec..28f67bf5af68 100644 --- a/components/script/dom/bindings/htmlconstructor.rs +++ b/components/script/dom/bindings/htmlconstructor.rs @@ -166,6 +166,8 @@ where // Step 8.4 element.set_custom_element_definition(definition.clone()); + element.done_creating(); + // Step 8.5 DomRoot::downcast(element).ok_or(Error::InvalidState) }, diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 4144bb972ca3..f56836769d93 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -14,6 +14,7 @@ use dom::bindings::codegen::Bindings::ElementBinding; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::FunctionBinding::Function; +use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementBinding::HTMLInputElementMethods; use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions}; @@ -159,6 +160,7 @@ pub struct Element { custom_element_definition: DomRefCell>>, /// custom_element_state: Cell, + done_creating: Cell, } impl fmt::Debug for Element { @@ -243,6 +245,18 @@ impl Element { document: &Document, creator: ElementCreator, mode: CustomElementCreationMode, + ) -> DomRoot { + let el = Element::create_unfinished(name, is, document, creator, mode); + el.done_creating(); + el + } + + pub fn create_unfinished( + name: QualName, + is: Option, + document: &Document, + creator: ElementCreator, + mode: CustomElementCreationMode, ) -> DomRoot { create_element(name, is, document, creator, mode) } @@ -286,9 +300,21 @@ impl Element { custom_element_reaction_queue: Default::default(), custom_element_definition: Default::default(), custom_element_state: Cell::new(CustomElementState::Uncustomized), + done_creating: Cell::new(false), + } + } + + pub fn done_creating(&self) { + self.done_creating.set(true); + if let Some(el) = self.downcast::() { + el.refresh_value(el.Value()); } } + pub fn is_done_creating(&self) -> bool { + self.done_creating.get() + } + pub fn new( local_name: LocalName, namespace: Namespace, diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 163a85cbdb68..3e53a924462d 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -1122,8 +1122,18 @@ impl HTMLInputElement { } } + pub fn refresh_value(&self, mut value: DOMString) { + let mut textinput = self.textinput.borrow_mut(); + self.sanitize_value(&mut value); + textinput.set_content(value); + } + // https://html.spec.whatwg.org/multipage/#value-sanitization-algorithm fn sanitize_value(&self, value: &mut DOMString) { + let el = self.upcast::(); + if !el.is_done_creating() { + return + } match self.input_type() { InputType::Text | InputType::Search | InputType::Tel | InputType::Password => { value.strip_newlines(); diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index e64580f68c1c..e0794dd154bb 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -1120,13 +1120,15 @@ fn create_element_for_token( } else { CustomElementCreationMode::Asynchronous }; - let element = Element::create(name, is, document, creator, creation_mode); + let element = Element::create_unfinished(name, is, document, creator, creation_mode); // Step 8. for attr in attrs { element.set_attribute_from_parser(attr.name, attr.value, None); } + element.done_creating(); + // Step 9. if will_execute_script { // Steps 9.1 - 9.2. From e790002e6607baf9ef4f0015489acfb45275da82 Mon Sep 17 00:00:00 2001 From: Bastien Orivel Date: Mon, 15 Oct 2018 18:34:07 +0200 Subject: [PATCH 2/3] Implement range input sanitization Fixes #19773 --- components/script/dom/htmlinputelement.rs | 48 ++++++++- .../forms/the-input-element/range-2.html.ini | 14 --- .../forms/the-input-element/range.html.ini | 27 +---- .../type-change-state.html.ini | 101 +----------------- .../the-input-element/valueMode.html.ini | 6 -- 5 files changed, 51 insertions(+), 145 deletions(-) delete mode 100644 tests/wpt/metadata/html/semantics/forms/the-input-element/range-2.html.ini diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 3e53a924462d..7adcc8509c39 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -1193,7 +1193,47 @@ impl HTMLInputElement { }, // https://html.spec.whatwg.org/multipage/#range-state-(type=range):value-sanitization-algorithm InputType::Range => { - value.set_best_representation_of_the_floating_point_number(); + let minimum = self.Min().parse().unwrap_or(0f64); + let maximum = self.Max().parse().unwrap_or(100f64); + + let new_v = if let Ok(value) = value.trim().parse::() { + if value < minimum || maximum < minimum { + minimum + } else if value > maximum { + maximum + } else { + value + } + } else { + if maximum <= minimum { + minimum + } else { + minimum + (maximum - minimum) / 2f64 + } + }; + + let step = self.Step().parse::().unwrap_or(1f64); + let delta = (new_v - minimum) - step * ((new_v - minimum) / step).floor(); + let new_v = if delta != 0f64 { + let step_below = new_v - delta; + let step_above = new_v - delta + step; + let half_step = step / 2f64; + let step_above_is_closest = (step_above - new_v) <= half_step; + let step_above_in_range = step_above >= minimum && step_above <= maximum; + let step_below_in_range = step_below >= minimum && step_below <= maximum; + + if (step_above_is_closest || !step_below_in_range) && step_above_in_range { + step_above + } else if (!step_above_is_closest || !step_above_in_range) && step_below_in_range { + step_below + } else { + new_v + } + } else { + new_v + }; + + *value = DOMString::from_string(new_v.to_string()); }, _ => (), } @@ -1344,6 +1384,12 @@ impl VirtualMethods for HTMLInputElement { self.textinput.borrow_mut().set_content(value); self.update_placeholder_shown_state(); }, + &local_name!("max") | &local_name!("min") | &local_name!("step") => { + let mut textinput = self.textinput.borrow_mut(); + let mut value = textinput.single_line_content().clone(); + self.sanitize_value(&mut value); + textinput.set_content(value); + }, &local_name!("name") if self.input_type() == InputType::Radio => { self.radio_group_updated( mutation.new_value(attr).as_ref().map(|name| name.as_atom()), diff --git a/tests/wpt/metadata/html/semantics/forms/the-input-element/range-2.html.ini b/tests/wpt/metadata/html/semantics/forms/the-input-element/range-2.html.ini deleted file mode 100644 index 13c7333bf957..000000000000 --- a/tests/wpt/metadata/html/semantics/forms/the-input-element/range-2.html.ini +++ /dev/null @@ -1,14 +0,0 @@ -[range-2.html] - type: testharness - [range input value set to ''] - expected: FAIL - - [range input value equals 50] - expected: FAIL - - [range input value equals 100] - expected: FAIL - - [range input value set to an integer] - expected: FAIL - diff --git a/tests/wpt/metadata/html/semantics/forms/the-input-element/range.html.ini b/tests/wpt/metadata/html/semantics/forms/the-input-element/range.html.ini index 4cecdb935bbd..3fa417cb71b0 100644 --- a/tests/wpt/metadata/html/semantics/forms/the-input-element/range.html.ini +++ b/tests/wpt/metadata/html/semantics/forms/the-input-element/range.html.ini @@ -1,35 +1,11 @@ [range.html] type: testharness - [Converting an illegal string to the default value] - expected: FAIL - [Converting an illegal string to the default step] expected: FAIL - [the value is set to min when a smaller value than min attribute is given] - expected: FAIL - - [the value is set to max when a larger value than max attribute is given] - expected: FAIL - - [default value when min and max attributes are given (= min plus half the difference between min and max)] - expected: FAIL - - [default value with step control when both min and max attributes are given] - expected: FAIL - - [default value when both min and max attributes are given, while min > max] - expected: FAIL - - [Step scale factor behavior when min attribute has integer value but max attribute is non-integer ] - expected: FAIL - [The default scale factor is 1 even if step attribute is explicitly set to non-integer value, unless min attribute has non-integer value] expected: FAIL - [Solving the step mismatch] - expected: FAIL - [Performing stepUp()] expected: FAIL @@ -42,3 +18,6 @@ [Performing stepDown() beyond the value of the min attribute] expected: FAIL + [Skip ASCII whitespace within input] + expected: FAIL + diff --git a/tests/wpt/metadata/html/semantics/forms/the-input-element/type-change-state.html.ini b/tests/wpt/metadata/html/semantics/forms/the-input-element/type-change-state.html.ini index b4e046a284f3..6461455ff4a9 100644 --- a/tests/wpt/metadata/html/semantics/forms/the-input-element/type-change-state.html.ini +++ b/tests/wpt/metadata/html/semantics/forms/the-input-element/type-change-state.html.ini @@ -6,36 +6,24 @@ [change state from hidden to datetime] expected: FAIL - [change state from hidden to range] - expected: FAIL - [change state from text to email] expected: FAIL [change state from text to datetime] expected: FAIL - [change state from text to range] - expected: FAIL - [change state from search to email] expected: FAIL [change state from search to datetime] expected: FAIL - [change state from search to range] - expected: FAIL - [change state from tel to email] expected: FAIL [change state from tel to datetime] expected: FAIL - [change state from tel to range] - expected: FAIL - [change state from url to text] expected: FAIL @@ -51,9 +39,6 @@ [change state from url to datetime] expected: FAIL - [change state from url to range] - expected: FAIL - [change state from email to hidden] expected: FAIL @@ -78,18 +63,12 @@ [change state from email to datetime] expected: FAIL - [change state from email to range] - expected: FAIL - [change state from password to email] expected: FAIL [change state from password to datetime] expected: FAIL - [change state from password to range] - expected: FAIL - [change state from datetime to text] expected: FAIL @@ -117,126 +96,48 @@ [change state from date to datetime] expected: FAIL - [change state from date to range] - expected: FAIL - - [change state from date to range] - expected: FAIL - - [change state from month to range] - expected: FAIL - [change state from week to datetime] expected: FAIL - [change state from week to range] - expected: FAIL - [change state from time to datetime] expected: FAIL - [change state from time to range] - expected: FAIL - - [change state from number to range] - expected: FAIL - - [change state from range to hidden] - expected: FAIL - - [change state from range to checkbox] - expected: FAIL - - [change state from range to radio] - expected: FAIL - - [change state from range to submit] - expected: FAIL - - [change state from range to image] - expected: FAIL - - [change state from range to reset] - expected: FAIL - - [change state from range to button] - expected: FAIL - - [change state from range to email] - expected: FAIL - [change state from range to datetime] expected: FAIL [change state from checkbox to email] expected: FAIL - [change state from checkbox to range] - expected: FAIL - [change state from radio to email] expected: FAIL [change state from radio to datetime] expected: FAIL - [change state from radio to range] - expected: FAIL - [change state from submit to email] expected: FAIL [change state from submit to datetime] expected: FAIL - [change state from submit to range] - expected: FAIL - [change state from image to email] expected: FAIL [change state from image to datetime] expected: FAIL - [change state from image to range] - expected: FAIL - [change state from reset to email] expected: FAIL [change state from reset to datetime] expected: FAIL - [change state from reset to range] - expected: FAIL - [change state from button to email] expected: FAIL [change state from button to datetime] expected: FAIL - [change state from button to range] - expected: FAIL - - [change state from datetime-local to range] - expected: FAIL - - [change state from range to text] - expected: FAIL - - [change state from range to search] - expected: FAIL - - [change state from range to tel] - expected: FAIL - - [change state from range to url] - expected: FAIL - - [change state from range to password] - expected: FAIL - - [change state from color to range] + [change state from range to number] expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/forms/the-input-element/valueMode.html.ini b/tests/wpt/metadata/html/semantics/forms/the-input-element/valueMode.html.ini index 7577868bb52f..132a45463209 100644 --- a/tests/wpt/metadata/html/semantics/forms/the-input-element/valueMode.html.ini +++ b/tests/wpt/metadata/html/semantics/forms/the-input-element/valueMode.html.ini @@ -6,12 +6,6 @@ [value IDL attribute of input type datetime with value attribute] expected: FAIL - [value IDL attribute of input type range without value attribute] - expected: FAIL - - [value IDL attribute of input type range with value attribute] - expected: FAIL - [value IDL attribute of input type email without value attribute] expected: FAIL From e3e47712cae745b3754e15da208630f3cca58357 Mon Sep 17 00:00:00 2001 From: Bastien Orivel Date: Mon, 15 Oct 2018 18:45:23 +0200 Subject: [PATCH 3/3] Fix a range input test It was setting the value to " 123" without setting a maximum above. Since the specs specifies 100 to be the default max, the value we'd get after sanitization would be 100, not 123. This fixes it by setting the value to something that is between the default min/max. --- tests/wpt/metadata/MANIFEST.json | 2 +- .../html/semantics/forms/the-input-element/range.html.ini | 3 --- .../html/semantics/forms/the-input-element/range.html | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index 0c8f83804595..4f0c58aa1849 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -615929,7 +615929,7 @@ "testharness" ], "html/semantics/forms/the-input-element/range.html": [ - "209ce25306e02986238ae26fb6fdf38b0a26a500", + "f57c520b87ea8adceb7311ef8d642db5cc5fa6b8", "testharness" ], "html/semantics/forms/the-input-element/required_attribute.html": [ diff --git a/tests/wpt/metadata/html/semantics/forms/the-input-element/range.html.ini b/tests/wpt/metadata/html/semantics/forms/the-input-element/range.html.ini index 3fa417cb71b0..29ffed47e1e5 100644 --- a/tests/wpt/metadata/html/semantics/forms/the-input-element/range.html.ini +++ b/tests/wpt/metadata/html/semantics/forms/the-input-element/range.html.ini @@ -18,6 +18,3 @@ [Performing stepDown() beyond the value of the min attribute] expected: FAIL - [Skip ASCII whitespace within input] - expected: FAIL - diff --git a/tests/wpt/web-platform-tests/html/semantics/forms/the-input-element/range.html b/tests/wpt/web-platform-tests/html/semantics/forms/the-input-element/range.html index 209ce25306e0..f57c520b87ea 100644 --- a/tests/wpt/web-platform-tests/html/semantics/forms/the-input-element/range.html +++ b/tests/wpt/web-platform-tests/html/semantics/forms/the-input-element/range.html @@ -34,7 +34,7 @@

Input Range

- + @@ -286,7 +286,7 @@

Input Range

test( function() { var e = document.getElementById('should_skip_whitespace'); - assert_equals(e.value, "123") + assert_equals(e.value, "98") }, "Skip ASCII whitespace within input", { "help" : "https://html.spec.whatwg.org/multipage/#best-representation-of-the-number-as-a-floating-point-number" }