Skip to content

Commit

Permalink
The beforeinput event should fire before textInput
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=268988

Reviewed by Wenson Hsieh and Ryosuke Niwa.

This change makes WebKit fire the beforeinput & textInput events in the
order (beforeinput first, textInput after) conforming to UI Events spec
requirements at w3c/uievents#362 and in the spec at
https://w3c.github.io/uievents/event-algo.html#fire%20key%20input%20events
and consistent with the order in which the events are fired in Blink.

Otherwise, without this change, WebKit fires the events in an order
(textInput first, beforeinput after) that breaks conformance with
the spec requirements, and that breaks compatibility with Blink.

Note also that this change makes WebKit conform to the requirements in
https://w3c.github.io/uievents/event-algo.html#fire%20key%20input%20events,
https://w3c.github.io/uievents/event-algo.html#handle%20native%20paste, and
https://w3c.github.io/uievents/event-algo.html#end%20composition — limiting
textInput to being fired only when ending a composition or when the input
type is insertText, insertParagraph, insertLineBreak, or insertFromPaste.

* LayoutTests/editing/execCommand/break-out-of-empty-list-item.html:
* LayoutTests/editing/inserting/typing-space-to-trigger-smart-link.html:
* LayoutTests/editing/pasteboard/paste-text-events-expected.txt:
* LayoutTests/editing/pasteboard/paste-text-events.html:
* LayoutTests/editing/style/highlight-insert-paragraph.html:
* LayoutTests/fast/events/input-events-fired-when-typing-expected.txt:
* LayoutTests/fast/events/input-events-fired-when-typing.html:
* LayoutTests/fast/events/ios/submit-form-target-blank-using-return-key.html:
* LayoutTests/fast/events/onchange-passwordfield.html:
* LayoutTests/fast/events/onchange-searchfield.html:
* LayoutTests/fast/events/onchange-textfield.html:
* LayoutTests/fast/forms/onchange-change-type.html:
* LayoutTests/http/tests/navigation/keyboard-events-during-provisional-navigation-expected.txt:
* LayoutTests/http/tests/navigation/keyboard-events-during-provisional-subframe-navigation-expected.txt:
* LayoutTests/platform/mac-wk2/http/tests/navigation/keyboard-events-during-provisional-navigation-expected.txt:
* LayoutTests/platform/wpe/TestExpectations:
* Source/WebCore/dom/Node.cpp:
(WebCore::Node::defaultEventHandler):
* Source/WebCore/dom/TextEvent.cpp:
(WebCore::TextEvent::TextEvent):
(WebCore::TextEvent::initTextEvent):
(WebCore::TextEvent::createForPlainTextPaste): Deleted.
(WebCore::TextEvent::createForFragmentPaste): Deleted.
(WebCore::TextEvent::createForDictation): Deleted.
* Source/WebCore/dom/TextEvent.h:
* Source/WebCore/editing/AlternativeTextController.cpp:
(WebCore::AlternativeTextController::insertDictatedText):
* Source/WebCore/editing/Editor.cpp:
(WebCore::dispatchTextInputEvent):
(WebCore::Editor::selectionForCommand):
(WebCore::Editor::pasteAsPlainText):
(WebCore::Editor::pasteAsFragment):
(WebCore::dispatchTextInputEvents):
(WebCore::Editor::appliedEditing):
(WebCore::Editor::insertText):
(WebCore::Editor::insertTextForConfirmedComposition):
(WebCore::Editor::insertTextWithoutSendingTextEvent):
(WebCore::Editor::setComposition):
(WebCore::Editor::handleTextEvent): Deleted.
* Source/WebCore/editing/Editor.h:
* Source/WebCore/editing/EditorCommand.cpp:
(WebCore::executeInsertBacktab):
(WebCore::executeInsertLineBreak):
(WebCore::executeInsertNewline):
(WebCore::executeInsertTab):
(WebCore::executeYank):
(WebCore::executeYankAndSelect):
(WebCore::enabledVisibleSelection):
(WebCore::enabledVisibleSelectionAndMark):
(WebCore::enableCaretInEditableText):
(WebCore::enabledInEditableText):
* Source/WebCore/page/EventHandler.cpp:
(WebCore::EventHandler::handleTextInput):
(WebCore::EventHandler::handleTextInputEvent): Deleted.
(WebCore::EventHandler::defaultTextInputEventHandler): Deleted.
* Source/WebCore/page/EventHandler.h:

Canonical link: https://commits.webkit.org/278971@main
  • Loading branch information
sideshowbarker committed May 19, 2024
1 parent dbcbbe8 commit 7da094e
Show file tree
Hide file tree
Showing 25 changed files with 213 additions and 281 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,6 @@
testContainer.contentEditable = true;
document.body.appendChild(testContainer);

function pressKey(key)
{
if (window.KeyEvent) {
var ev = document.createEvent("KeyboardEvent");
ev.initKeyEvent("keypress", true, true, window, 0,0,0,0, 0, key.charCodeAt(0));
document.body.dispatchEvent(ev);
}
else {
var ev = document.createEvent("TextEvent");
ev.initTextEvent('textInput', true, true, null, key.charAt(0));
document.body.dispatchEvent(ev);
}
}

function enterAtTarget(initialContent)
{
testContainer.innerHTML = initialContent;
Expand All @@ -39,8 +25,7 @@
s.removeAllRanges();
s.addRange(r);

pressKey('\n');

document.execCommand("InsertParagraph");
return testContainer.innerHTML;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
var testTypeSpaceDiv = document.getElementById('testTypeSpace');
var targetText = testTypeSpaceDiv.firstChild;
window.getSelection().setPosition(targetText, 15);
pressKey(" ");
document.execCommand("InsertText", false, " ");
var expectedContents = "The <a href=\"http://www.foo.com\">www.foo.com</a> should be underlined and there is an anchor node created for it.";
if (expectedContents == testTypeSpaceDiv.innerHTML)
document.getElementById('log').textContent = "PASS: the anchor for 'www.foo.com' has been created.\n"
Expand All @@ -22,18 +22,18 @@
var testTypeLinkDiv = document.getElementById('testTypeLink');
targetText = testTypeLinkDiv.firstChild;
window.getSelection().setPosition(targetText, 4);
pressKey("w");
pressKey("w");
pressKey("w");
pressKey(".");
pressKey("b");
pressKey("a");
pressKey("r");
pressKey(".");
pressKey("c");
pressKey("o");
pressKey("m");
pressKey(" ");
document.execCommand("InsertText", false, "w");
document.execCommand("InsertText", false, "w");
document.execCommand("InsertText", false, "w");
document.execCommand("InsertText", false, ".");
document.execCommand("InsertText", false, "b");
document.execCommand("InsertText", false, "a");
document.execCommand("InsertText", false, "r");
document.execCommand("InsertText", false, ".");
document.execCommand("InsertText", false, "c");
document.execCommand("InsertText", false, "o");
document.execCommand("InsertText", false, "m");
document.execCommand("InsertText", false, " ");
expectedContents = "The <a href=\"http://www.bar.com\">www.bar.com</a> should be underlined and there is an anchor node created for it.";
if (expectedContents == testTypeLinkDiv.innerHTML)
document.getElementById('log').textContent += "PASS: the anchor for 'www.bar.com' has been created."
Expand All @@ -43,19 +43,6 @@
if (window.internals)
internals.setAutomaticLinkDetectionEnabled(false);
}

function pressKey(key)
{
if (window.KeyEvent) {
var ev = document.createEvent("KeyboardEvent");
ev.initKeyEvent("keypress", true, true, window, 0,0,0,0, 0, key.charCodeAt(0));
document.body.dispatchEvent(ev);
} else {
var ev = document.createEvent("TextEvent");
ev.initTextEvent('textInput', true, true, null, key.charAt(0));
document.body.dispatchEvent(ev);
}
}
</script>
</head>
<body>
Expand Down
13 changes: 0 additions & 13 deletions LayoutTests/editing/pasteboard/paste-text-events-expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,6 @@ PASS testTargetInput.value is 'RichHello'
PASS event.data is ''
PASS testTargetEditable.innerHTML is '<b>Rich</b>Hello'
PASS textInputCount is proceedingTestCases.length
PASS event.data is 'PlainHello'
PASS testTargetTextarea.value is ''
PASS event.data is 'PlainHello'
PASS testTargetInput.value is ''
PASS event.data is ''
PASS testTargetEditable.innerHTML is ''
PASS event.data is 'RichHello'
PASS testTargetTextarea.value is ''
PASS event.data is 'RichHello'
PASS testTargetInput.value is ''
PASS event.data is ''
PASS testTargetEditable.innerHTML is ''
PASS textInputCount is cancelingTestCases.length
PASS successfullyParsed is true

TEST COMPLETE
Expand Down
18 changes: 0 additions & 18 deletions LayoutTests/editing/pasteboard/paste-text-events.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
{
shouldBe("event.data", toStringLiteral(expectedTextEventData));
textInputCount++;
if (willCancelTextInput)
evt.preventDefault();
}

var testSourceRoot = document.createElement("div");
Expand Down Expand Up @@ -112,15 +110,6 @@
[copyRichText, pasteToTargetEditable, targetEditableShouldHave, "<b>Rich</b>Hello", ""],
];

var cancelingTestCases = [
[copyPlainText, pasteToTargetTextarea, targetTextareaShouldHave, "", "PlainHello"],
[copyPlainText, pasteToTargetInput, targetInputShouldHave, "", "PlainHello"],
[copyPlainText, pasteToTargetEditable, targetEditableShouldHave, "", ""],
[copyRichText, pasteToTargetTextarea, targetTextareaShouldHave, "", "RichHello"],
[copyRichText, pasteToTargetInput, targetInputShouldHave, "", "RichHello"],
[copyRichText, pasteToTargetEditable, targetEditableShouldHave, "", ""],
];

function runSingleTest(caseData)
{
var copy = caseData[0];
Expand All @@ -136,17 +125,10 @@
}

textInputCount = 0;
willCancelTextInput = false;
for (var i = 0; i < proceedingTestCases.length; ++i)
runSingleTest(proceedingTestCases[i]);
shouldBe("textInputCount", "proceedingTestCases.length");

textInputCount = 0;
willCancelTextInput = true;
for (var i = 0; i < cancelingTestCases.length; ++i)
runSingleTest(cancelingTestCases[i]);
shouldBe("textInputCount", "cancelingTestCases.length");

// Hides dataset to make dump clean.
testTargetRoot.style.display = "none";
testSourceRoot.style.display = "none";
Expand Down
18 changes: 2 additions & 16 deletions LayoutTests/editing/style/highlight-insert-paragraph.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,6 @@
<div id="console"></div>

<script type="text/javascript">

function pressKey( key ) {
if (window.KeyEvent) {
var ev = document.createEvent("KeyboardEvent");
ev.initKeyEvent("keypress", true, true, window, 0,0,0,0, 0, key.charCodeAt(0));
document.body.dispatchEvent(ev);
}
else {
var ev = document.createEvent("TextEvent");
ev.initTextEvent('textInput', true, true, null, key.charAt(0));
document.body.dispatchEvent(ev);
}
}

if (window.testRunner)
testRunner.dumpAsText();

Expand All @@ -41,8 +27,8 @@
r.setEnd(e.firstChild.firstChild,5);
s.removeAllRanges();
s.addRange(r);
pressKey('\n');
pressKey('e');
document.execCommand("InsertParagraph");
document.execCommand('InsertText', false, 'e')

document.getElementById('console').appendChild(document.createTextNode(e.innerHTML));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
PASS successfullyParsed is true

TEST COMPLETE
Typing into contenteditable div element
Fired `onbeforeinput`!
PASS event.__lookupGetter__('inputType') is defined.
PASS event.__lookupGetter__('data') is defined.
Expand All @@ -11,6 +12,12 @@ PASS event.bubbles is true
PASS event.cancelable is true
PASS event.composed is true
PASS event.isComposing is false
Fired `textInput`!
PASS event.__lookupGetter__('data') is defined.
PASS event.target.id is expectedTargetID
PASS event.bubbles is true
PASS event.cancelable is true
PASS event.composed is true
Fired `oninput`!
PASS event.__lookupGetter__('inputType') is defined.
PASS event.__lookupGetter__('data') is defined.
Expand All @@ -21,6 +28,7 @@ PASS event.bubbles is true
PASS event.cancelable is false
PASS event.composed is true
PASS event.isComposing is false
Typing into input element
Fired `onbeforeinput`!
PASS event.__lookupGetter__('inputType') is defined.
PASS event.__lookupGetter__('data') is defined.
Expand All @@ -31,6 +39,39 @@ PASS event.bubbles is true
PASS event.cancelable is true
PASS event.composed is true
PASS event.isComposing is false
Fired `textInput`!
PASS event.__lookupGetter__('data') is defined.
PASS event.target.id is expectedTargetID
PASS event.bubbles is true
PASS event.cancelable is true
PASS event.composed is true
Fired `oninput`!
PASS event.__lookupGetter__('inputType') is defined.
PASS event.__lookupGetter__('data') is defined.
PASS event.__lookupGetter__('dataTransfer') is defined.
PASS event.getTargetRanges is defined.
PASS event.target.id is expectedTargetID
PASS event.bubbles is true
PASS event.cancelable is false
PASS event.composed is true
PASS event.isComposing is false
Typing into textarea element
Fired `onbeforeinput`!
PASS event.__lookupGetter__('inputType') is defined.
PASS event.__lookupGetter__('data') is defined.
PASS event.__lookupGetter__('dataTransfer') is defined.
PASS event.getTargetRanges is defined.
PASS event.target.id is expectedTargetID
PASS event.bubbles is true
PASS event.cancelable is true
PASS event.composed is true
PASS event.isComposing is false
Fired `textInput`!
PASS event.__lookupGetter__('data') is defined.
PASS event.target.id is expectedTargetID
PASS event.bubbles is true
PASS event.cancelable is true
PASS event.composed is true
Fired `oninput`!
PASS event.__lookupGetter__('inputType') is defined.
PASS event.__lookupGetter__('data') is defined.
Expand Down
34 changes: 32 additions & 2 deletions LayoutTests/fast/events/input-events-fired-when-typing.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,36 @@
return document.querySelector("#foo");
}

function plainText()
function inputElement()
{
return document.querySelector("#bar");
}

function textareaElement()
{
return document.querySelector("#baz");
}

function beginTest()
{
if (!window.eventSender || !window.internals || !window.testRunner)
return;

testRunner.dumpAsText();

debug("Typing into contenteditable div element");
contentEditable().focus();
eventSender.keyDown("a", []);

debug("Typing into input element");
expectedTargetID = "bar";
plainText().focus();
inputElement().focus();
eventSender.keyDown("b", []);

debug("Typing into textarea element");
expectedTargetID = "baz";
textareaElement().focus();
eventSender.keyDown("c", []);
}

function checkInputEvent(event)
Expand Down Expand Up @@ -57,12 +70,29 @@
shouldBe("event.composed", "true");
shouldBe("event.isComposing", "false");
}

function checkTextInputEvent(event)
{
debug("Fired `textInput`!");
shouldBeDefined("event.__lookupGetter__('data')");
shouldBe("event.target.id", "expectedTargetID");
shouldBe("event.bubbles", "true");
shouldBe("event.cancelable", "true");
shouldBe("event.composed", "true");
}

</script>
</head>

<body onload=beginTest()>
<div id="foo" contenteditable oninput=checkInputEvent(event) onbeforeinput=checkBeforeInputEvent(event)></div>
<input id="bar" oninput=checkInputEvent(event) onbeforeinput=checkBeforeInputEvent(event)></input>
<textarea id="baz" oninput=checkInputEvent(event) onbeforeinput=checkBeforeInputEvent(event)></textarea>
<script>
document.querySelector("#foo").addEventListener("textInput", checkTextInputEvent, false);
document.querySelector("#bar").addEventListener("textInput", checkTextInputEvent, false);
document.querySelector("#baz").addEventListener("textInput", checkTextInputEvent, false);
</script>
<script src="../../resources/js-test-post.js"></script>
</body>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
function checkKeypressAndDone()
{
shouldBeEqualToString("event.key", "Enter");
done();
}

function runTest()
Expand Down
8 changes: 6 additions & 2 deletions LayoutTests/fast/events/onchange-passwordfield.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@

// hit enter
input.focus();
if (window.eventSender)
eventSender.keyDown("\r", []);
const enterKeyEvent = new KeyboardEvent('keypress', { bubbles: true, cancelable: true, view: window, detail: 0,
key: 'Enter', code: 'Enter', keyIdentifier: '', keyCode: 13, charCode: 13, which: 13,
location: 0, ctrlKey: false, altKey: false, shiftKey: false, metaKey: false });
// We use dispatchEvent() here because if eventSender.keyDown() is used
// instead, this test will fail when run with the WPE port.
input.dispatchEvent(enterKeyEvent);

</script>
8 changes: 6 additions & 2 deletions LayoutTests/fast/events/onchange-searchfield.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@

// hit enter
input.focus();
if (window.eventSender)
eventSender.keyDown("\r", []);
const enterKeyEvent = new KeyboardEvent('keypress', { bubbles: true, cancelable: true, view: window, detail: 0,
key: 'Enter', code: 'Enter', keyIdentifier: '', keyCode: 13, charCode: 13, which: 13,
location: 0, ctrlKey: false, altKey: false, shiftKey: false, metaKey: false });
// We use dispatchEvent() here because if eventSender.keyDown() is used
// instead, this test will fail when run with the WPE port.
input.dispatchEvent(enterKeyEvent);

</script>
8 changes: 6 additions & 2 deletions LayoutTests/fast/events/onchange-textfield.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@

// hit enter
input.focus();
if (window.eventSender)
eventSender.keyDown("\r", []);
const enterKeyEvent = new KeyboardEvent('keypress', { bubbles: true, cancelable: true, view: window, detail: 0,
key: 'Enter', code: 'Enter', keyIdentifier: '', keyCode: 13, charCode: 13, which: 13,
location: 0, ctrlKey: false, altKey: false, shiftKey: false, metaKey: false });
// We use dispatchEvent() here because if eventSender.keyDown() is used
// instead, this test will fail when run with the WPE port.
input.dispatchEvent(enterKeyEvent);

</script>
Loading

0 comments on commit 7da094e

Please sign in to comment.