diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index aa728dc5a1c..b9a6ad3ff69 100755 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -34,7 +34,7 @@ use std::default::Default; use std::ops::Range; use style::attr::AttrValue; use style::element_state::ElementState; -use textinput::{KeyReaction, Lines, SelectionDirection, TextInput}; +use textinput::{Direction, KeyReaction, Lines, Selection, SelectionDirection, TextInput}; #[dom_struct] pub struct HTMLTextAreaElement { @@ -232,10 +232,25 @@ impl HTMLTextAreaElementMethods for HTMLTextAreaElement { // https://html.spec.whatwg.org/multipage/#dom-textarea-value fn SetValue(&self, value: DOMString) { - // TODO move the cursor to the end of the field - self.textinput.borrow_mut().set_content(value); + let mut textinput = self.textinput.borrow_mut(); + + // Step 1 + let old_value = textinput.get_content(); + let old_selection = textinput.selection_begin; + + // Step 2 + textinput.set_content(value); + + // Step 3 self.value_changed.set(true); + if old_value != textinput.get_content() { + // Step 4 + textinput.adjust_horizontal_to_limit(Direction::Forward, Selection::NotSelected); + } else { + textinput.selection_begin = old_selection; + } + self.upcast::().dirty(NodeDamage::OtherNodeDamage); } diff --git a/components/script/dom/textcontrol.rs b/components/script/dom/textcontrol.rs index 0b8870b1996..2b47fcd00a4 100644 --- a/components/script/dom/textcontrol.rs +++ b/components/script/dom/textcontrol.rs @@ -21,7 +21,16 @@ pub trait TextControl: DerivedFrom + DerivedFrom { // https://html.spec.whatwg.org/multipage/#dom-textarea/input-selectionstart fn set_dom_selection_start(&self, start: u32) { - self.set_selection_range(start, self.dom_selection_end(), self.selection_direction()); + // Step 2 + let mut end = self.dom_selection_end(); + + // Step 3 + if end < start { + end = start; + } + + // Step 4 + self.set_selection_range(start, end, self.selection_direction()); } // https://html.spec.whatwg.org/multipage/#dom-textarea/input-selectionend diff --git a/components/script/textinput.rs b/components/script/textinput.rs index c4d8b3747b6..310e0566453 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -770,7 +770,11 @@ impl TextInput { /// any \n encountered will be stripped and force a new logical line. pub fn set_content(&mut self, content: DOMString) { self.lines = if self.multiline { - content.split('\n').map(DOMString::from).collect() + // https://html.spec.whatwg.org/multipage/#textarea-line-break-normalisation-transformation + content.replace("\r\n", "\n") + .split(|c| c == '\n' || c == '\r') + .map(DOMString::from) + .collect() } else { vec!(content) }; diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index ff4391f73b0..32fd7e9e58e 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -542604,7 +542604,7 @@ "testharness" ], "html/semantics/forms/textfieldselection/selection-value-interactions.html": [ - "c568d7fe10cb4c2071b5d38530ad601988a789ea", + "39fe9646bbc2741e9bd3296d4e01513c99106151", "testharness" ], "html/semantics/forms/textfieldselection/selection.html": [ diff --git a/tests/wpt/metadata/html/semantics/forms/textfieldselection/selection-start-end.html.ini b/tests/wpt/metadata/html/semantics/forms/textfieldselection/selection-start-end.html.ini deleted file mode 100644 index cc755ee3c3a..00000000000 --- a/tests/wpt/metadata/html/semantics/forms/textfieldselection/selection-start-end.html.ini +++ /dev/null @@ -1,35 +0,0 @@ -[selection-start-end.html] - type: testharness - [Setting selectionStart to a value larger than selectionEnd should increase selectionEnd] - expected: FAIL - - [Setting selectionEnd to a value smaller than selectionStart should decrease selectionStart] - expected: FAIL - - [selectionStart edge-case values] - expected: FAIL - - [Initial .value set on textarea-appended should set selectionStart to end of value] - expected: FAIL - - [Initial .value set on textarea-not-appended should set selectionStart to end of value] - expected: FAIL - - [Initial .value set on textarea-appended-prefocused should set selectionStart to end of value] - expected: FAIL - - [Initial .value set on textarea-not-appended-prefocused should set selectionStart to end of value] - expected: FAIL - - [Initial .value set on textarea-appended should set selectionEnd to end of value] - expected: FAIL - - [Initial .value set on textarea-not-appended should set selectionEnd to end of value] - expected: FAIL - - [Initial .value set on textarea-appended-prefocused should set selectionEnd to end of value] - expected: FAIL - - [Initial .value set on textarea-not-appended-prefocused should set selectionEnd to end of value] - expected: FAIL - diff --git a/tests/wpt/metadata/html/semantics/forms/textfieldselection/selection.html.ini b/tests/wpt/metadata/html/semantics/forms/textfieldselection/selection.html.ini index ca71444a736..f9483ec5a35 100644 --- a/tests/wpt/metadata/html/semantics/forms/textfieldselection/selection.html.ini +++ b/tests/wpt/metadata/html/semantics/forms/textfieldselection/selection.html.ini @@ -36,15 +36,9 @@ [test SelectionStart offset for textarea that is appended] expected: FAIL - [test SelectionStart offset for textarea that is not appended] - expected: FAIL - [test SelectionEnd offset for input that is appended] expected: FAIL [test SelectionEnd offset for textarea that is appended] expected: FAIL - [test SelectionEnd offset for textarea that is not appended] - expected: FAIL - diff --git a/tests/wpt/metadata/html/semantics/forms/the-textarea-element/value-defaultValue-textContent.html.ini b/tests/wpt/metadata/html/semantics/forms/the-textarea-element/value-defaultValue-textContent.html.ini index 9377d122a79..70748406931 100644 --- a/tests/wpt/metadata/html/semantics/forms/the-textarea-element/value-defaultValue-textContent.html.ini +++ b/tests/wpt/metadata/html/semantics/forms/the-textarea-element/value-defaultValue-textContent.html.ini @@ -1,17 +1,8 @@ [value-defaultValue-textContent.html] type: testharness - [defaultValue and value treat CRLF differently] - expected: FAIL - - [tests for the value setter] - expected: FAIL - [defaultValue and value are affected by textContent in combination with appending a DocumentFragment] expected: FAIL [defaultValue and value reflect child text content, not textContent] expected: FAIL - [value normalizes CRLF even spread over multiple text nodes] - expected: FAIL - diff --git a/tests/wpt/web-platform-tests/html/semantics/forms/textfieldselection/selection-value-interactions.html b/tests/wpt/web-platform-tests/html/semantics/forms/textfieldselection/selection-value-interactions.html index c453efd8e0a..0fd8a5b182c 100644 --- a/tests/wpt/web-platform-tests/html/semantics/forms/textfieldselection/selection-value-interactions.html +++ b/tests/wpt/web-platform-tests/html/semantics/forms/textfieldselection/selection-value-interactions.html @@ -97,22 +97,31 @@ for (var tag of ['input', 'textarea']) { var el = document.createElement(tag); document.body.appendChild(el); this.add_cleanup(() => el.remove()); - el.value = ""; - assert_equals(el.selectionStart, el.value.length, - "element.selectionStart should be value.length"); - assert_equals(el.selectionEnd, el.value.length, - "element.selectionEnd should be value.length"); - el.value = "foo"; - assert_equals(el.selectionStart, el.value.length, - "element.selectionStart should be value.length"); - assert_equals(el.selectionEnd, el.value.length, - "element.selectionEnd should be value.length"); - el.value = "foobar"; - assert_equals(el.selectionStart, el.value.length, - "element.selectionStart should be value.length"); - assert_equals(el.selectionEnd, el.value.length, - "element.selectionEnd should be value.length"); - }, `selection is always collapsed to the end after setting values on ${tag}`); + + for (let val of ["", "foo", "foobar"]) { + el.value = val; + assert_equals(el.selectionStart, val.length, + "element.selectionStart should be value.length"); + assert_equals(el.selectionEnd, val.length, + "element.selectionEnd should be value.length"); + } + }, `selection is collapsed to the end after changing values on ${tag}`); + + test(function() { + var el = document.createElement(tag); + document.body.appendChild(el); + this.add_cleanup(() => el.remove()); + + el.value = "foobar" + el.selectionStart = 2 + el.selectionEnd = 4 + el.value = "foobar" + + assert_equals(el.selectionStart, 2, + "element.selectionStart should be unchanged"); + assert_equals(el.selectionEnd, 4, + "element.selectionEnd should be unchanged"); + }, `selection is not collapsed to the end when value is set to its existing value on ${tag}`); }