tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 0aae417ed88c19e70ba22bec6a3a160699ed4f86
parent 3248592a9f4ca679e5d5d9c4a44efca3ee11680e
Author: Pranav Modi <pranavmodi@microsoft.com>
Date:   Mon,  5 Jan 2026 10:40:22 +0000

Bug 2008495 [wpt PR 56981] - [Editing] Fix InputEvent type for deletion commands with non-collapsed selection, a=testonly

Automatic update from web-platform-tests
[Editing] Fix InputEvent type for deletion commands with non-collapsed
selection

When deletion commands (Ctrl+Backspace, Ctrl+Delete, etc.) are executed
with a non-collapsed selection, the InputEvent type should be
deleteContentBackward/Forward, not deleteWordBackward/Forward or other
granularity-based types, per the W3C Input Events specification [1].

This CL fixes both beforeinput and input event generation:
i) InputTypeFromCommandType() now checks selection state to return
   content-based InputType for beforeinput events when a selection
   exists
ii) DeleteWithDirection() updated to use deleteContent* InputType for
   non-collapsed selections, bypassing granularity-based deletion logic

[1] - https://w3c.github.io/input-events/#interface-InputEvent-Attributes

Bug: 41423062
Change-Id: I7e9f05a3e049472b2855341d571d6df350821e6a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7266851
Reviewed-by: Sambamurthy Bandaru <sambamurthy.bandaru@microsoft.com>
Reviewed-by: Kent Tamura <tkent@chromium.org>
Commit-Queue: Pranav Modi <pranavmodi@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1564205}

--

wpt-commits: 48866a98b0022b880d2221e2992fb606df817051
wpt-pr: 56981

Diffstat:
Atesting/web-platform/tests/input-events/input-events-delete-selection.html | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 240 insertions(+), 0 deletions(-)

diff --git a/testing/web-platform/tests/input-events/input-events-delete-selection.html b/testing/web-platform/tests/input-events/input-events-delete-selection.html @@ -0,0 +1,240 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<title> + Input Event tests for deletion with non-collapsed selection +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<div id="rich" contenteditable></div> +<script> + let inputEventsLog = []; + const rich = document.getElementById("rich"); + const isMacOS = navigator.platform.indexOf("Mac") === 0; + const MODIFIER_KEY = isMacOS ? "\uE00A" : "\uE009"; // Alt on Mac, Ctrl on others + + function log(event) { + inputEventsLog.push({ + type: event.type, + inputType: event.inputType, + data: event.data, + }); + } + + function resetRich() { + inputEventsLog = []; + rich.innerHTML = ""; + } + + function selectText(startNode, startOffset, endNode, endOffset) { + const selection = window.getSelection(); + const range = document.createRange(); + range.setStart(startNode, startOffset); + range.setEnd(endNode, endOffset); + selection.removeAllRanges(); + selection.addRange(range); + } + + rich.addEventListener("beforeinput", log); + rich.addEventListener("input", log); + + // Test Ctrl+Backspace with non-collapsed selection + promise_test(async function () { + this.add_cleanup(resetRich); + rich.innerHTML = "hello world"; + rich.focus(); + + const textNode = rich.firstChild; + // Select "or" in "world" + selectText(textNode, 6, textNode, 8); + + // Simulate Ctrl+Backspace (Alt+Backspace on Mac) + await new test_driver.Actions() + .keyDown(MODIFIER_KEY) + .keyDown("\uE003") // Backspace + .keyUp("\uE003") + .keyUp(MODIFIER_KEY) + .send(); + + assert_equals(inputEventsLog.length, 2, "Should have 2 events"); + const [beforeInputEvent, inputEvent] = inputEventsLog; + assert_equals(beforeInputEvent.type, "beforeinput"); + assert_equals(inputEvent.type, "input"); + // When deleting a selection, inputType should be deleteContentBackward + assert_equals( + beforeInputEvent.inputType, + "deleteContentBackward", + "Ctrl+Backspace with selection should report deleteContentBackward" + ); + assert_equals( + inputEvent.inputType, + "deleteContentBackward", + "Ctrl+Backspace with selection should report deleteContentBackward" + ); + }, "Ctrl+Backspace with non-collapsed selection should report deleteContentBackward"); + + // Test Ctrl+Backspace with caret (collapsed selection) + promise_test(async function () { + this.add_cleanup(resetRich); + rich.innerHTML = "hello world"; + rich.focus(); + + const textNode = rich.firstChild; + // Place caret after "world" + selectText(textNode, 11, textNode, 11); + + // Simulate Ctrl+Backspace (Alt+Backspace on Mac) + await new test_driver.Actions() + .keyDown(MODIFIER_KEY) + .keyDown("\uE003") // Backspace + .keyUp("\uE003") + .keyUp(MODIFIER_KEY) + .send(); + + assert_equals(inputEventsLog.length, 2, "Should have 2 events"); + const [beforeInputEvent, inputEvent] = inputEventsLog; + assert_equals(beforeInputEvent.type, "beforeinput"); + assert_equals(inputEvent.type, "input"); + // With caret, should be deleteWordBackward + assert_equals( + beforeInputEvent.inputType, + "deleteWordBackward", + "Ctrl+Backspace with caret should report deleteWordBackward" + ); + assert_equals( + inputEvent.inputType, + "deleteWordBackward", + "Ctrl+Backspace with caret should report deleteWordBackward" + ); + }, "Ctrl+Backspace with caret should report deleteWordBackward"); + + // Test Ctrl+Delete with non-collapsed selection + promise_test(async function () { + this.add_cleanup(resetRich); + rich.innerHTML = "hello world"; + rich.focus(); + + const textNode = rich.firstChild; + // Select "ell" in "hello" + selectText(textNode, 1, textNode, 4); + + // Simulate Ctrl+Delete (Alt+Delete on Mac) + await new test_driver.Actions() + .keyDown(MODIFIER_KEY) + .keyDown("\uE017") // Delete + .keyUp("\uE017") + .keyUp(MODIFIER_KEY) + .send(); + + assert_equals(inputEventsLog.length, 2, "Should have 2 events"); + const [beforeInputEvent, inputEvent] = inputEventsLog; + assert_equals(beforeInputEvent.type, "beforeinput"); + assert_equals(inputEvent.type, "input"); + // When deleting a selection, inputType should be deleteContentForward + assert_equals( + beforeInputEvent.inputType, + "deleteContentForward", + "Ctrl+Delete with selection should report deleteContentForward" + ); + assert_equals( + inputEvent.inputType, + "deleteContentForward", + "Ctrl+Delete with selection should report deleteContentForward" + ); + }, "Ctrl+Delete with non-collapsed selection should report deleteContentForward"); + + // Test Ctrl+Delete with caret (collapsed selection) + promise_test(async function () { + this.add_cleanup(resetRich); + rich.innerHTML = "hello world"; + rich.focus(); + + const textNode = rich.firstChild; + // Place caret before "world" + selectText(textNode, 6, textNode, 6); + + // Simulate Ctrl+Delete (Alt+Delete on Mac) + await new test_driver.Actions() + .keyDown(MODIFIER_KEY) + .keyDown("\uE017") // Delete + .keyUp("\uE017") + .keyUp(MODIFIER_KEY) + .send(); + + assert_equals(inputEventsLog.length, 2, "Should have 2 events"); + const [beforeInputEvent, inputEvent] = inputEventsLog; + assert_equals(beforeInputEvent.type, "beforeinput"); + assert_equals(inputEvent.type, "input"); + // With caret, should be deleteWordForward + assert_equals( + beforeInputEvent.inputType, + "deleteWordForward", + "Ctrl+Delete with caret should report deleteWordForward" + ); + assert_equals( + inputEvent.inputType, + "deleteWordForward", + "Ctrl+Delete with caret should report deleteWordForward" + ); + }, "Ctrl+Delete with caret should report deleteWordForward"); + + // Test regular Backspace with non-collapsed selection (should still be deleteContentBackward) + promise_test(async function () { + this.add_cleanup(resetRich); + rich.innerHTML = "hello world"; + rich.focus(); + + const textNode = rich.firstChild; + // Select "lo wo" + selectText(textNode, 3, textNode, 8); + + // Simulate Backspace (without Ctrl) + await test_driver.send_keys(rich, "\uE003"); // Backspace + + assert_equals(inputEventsLog.length, 2, "Should have 2 events"); + const [beforeInputEvent, inputEvent] = inputEventsLog; + assert_equals(beforeInputEvent.type, "beforeinput"); + assert_equals(inputEvent.type, "input"); + assert_equals( + beforeInputEvent.inputType, + "deleteContentBackward", + "Regular Backspace with selection should report deleteContentBackward" + ); + assert_equals( + inputEvent.inputType, + "deleteContentBackward", + "Regular Backspace with selection should report deleteContentBackward" + ); + }, "Regular Backspace with non-collapsed selection should report deleteContentBackward"); + + // Test regular Delete with non-collapsed selection (should be deleteContentForward) + promise_test(async function () { + this.add_cleanup(resetRich); + rich.innerHTML = "hello world"; + rich.focus(); + + const textNode = rich.firstChild; + // Select "lo wo" + selectText(textNode, 3, textNode, 8); + + // Simulate Delete (without Ctrl) + await test_driver.send_keys(rich, "\uE017"); // Delete + + assert_equals(inputEventsLog.length, 2, "Should have 2 events"); + const [beforeInputEvent, inputEvent] = inputEventsLog; + assert_equals(beforeInputEvent.type, "beforeinput"); + assert_equals(inputEvent.type, "input"); + assert_equals( + beforeInputEvent.inputType, + "deleteContentForward", + "Regular Delete with selection should report deleteContentForward" + ); + assert_equals( + inputEvent.inputType, + "deleteContentForward", + "Regular Delete with selection should report deleteContentForward" + ); + }, "Regular Delete with non-collapsed selection should report deleteContentForward"); +</script>