Fix Backspace deleting entire previous line in <textarea> (#36112)

* test(textinput): Add test for backspace at beginning of line in textarea

Introduce a test to reproduce and verify the fix for backspacing at the
beginning of a line in a multiline textarea. This ensures that pressing
Backspace when the cursor is at the start of a line correctly removes the
newline without deleting the entire previous line’s content.

Related to: #27523

Signed-off-by: Emmanuel Elom <elomemmanuel007@gmail.com>

* fix(textinput): Preserve selection origin when adjusting vertical position

Fixes an issue where pressing Backspace at the beginning of a line in a
textarea incorrectly deleted the entire previous line's content. This happened
because `self.adjust_vertical(-1, select)` modified `selection_origin` and
`edit_point`, but `selection_origin` was not restored before performing the
horizontal adjustment. As a result, `self.selection_start()` and
`self.selection_end()` were inconsistent, leading to `replace_operation`
erasing the entire line.

Now, we temporarily store `selection_origin` before adjusting vertical
position and restore it afterward to ensure proper cursor and selection
behavior.

Fixes: #27523
Signed-off-by: Emmanuel Elom <elomemmanuel007@gmail.com>

---------

Signed-off-by: Emmanuel Elom <elomemmanuel007@gmail.com>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
elomscansio 2025-03-26 19:37:48 +01:00 committed by GitHub
parent 4f33f31c86
commit 30a2a89d16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 14 additions and 0 deletions

View file

@ -671,8 +671,14 @@ impl<T: ClipboardProvider> TextInput<T> {
Direction::Backward => {
let remaining = self.edit_point.index;
if adjust > remaining && self.edit_point.line > 0 {
// Preserve the current selection origin because `adjust_vertical`
// modifies `selection_origin`. Since we are moving backward instead of
// highlighting vertically, we need to restore it after adjusting the line.
let selection_origin_temp = self.selection_origin;
self.adjust_vertical(-1, select);
self.edit_point.index = self.current_line_length();
// Restore the original selection origin to maintain expected behavior.
self.selection_origin = selection_origin_temp;
// one shift is consumed by the change of line, hence the -1
self.adjust_horizontal(
adjust.saturating_sub(remaining + UTF8Bytes::one()),

View file

@ -856,3 +856,11 @@ fn test_select_all() {
textinput.selection_end()
);
}
#[test]
fn test_backspace_in_textarea_at_beginning_of_line() {
let mut textinput = text_input(Lines::Multiple, "first line\n");
textinput.handle_keydown_aux(Key::ArrowDown, Modifiers::empty(), false);
textinput.handle_keydown_aux(Key::Backspace, Modifiers::empty(), false);
assert_eq!(textinput.get_content(), DOMString::from("first line"));
}