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}`);
}