Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 4951276cc765d82514a1a3d7d6ef10e363ea7d18
https://github.com/WebKit/WebKit/commit/4951276cc765d82514a1a3d7d6ef10e363ea7d18
Author: Ryosuke Niwa <[email protected]>
Date: 2026-05-14 (Thu, 14 May 2026)
Changed paths:
M Source/WebKit/UIProcess/mac/WebViewImpl.mm
M Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm
M Tools/Scripts/webkitpy/api_tests/allowlist.txt
M Tools/TestWebKitAPI/Tests/WebKit/WKWebView/mac/WKWebViewMacEditingTests.mm
Log Message:
-----------
REGRESSION(310826@main): Vietnamese & Korean keyboard gets out of modeless
input method in Mail Compose
https://bugs.webkit.org/show_bug.cgi?id=314823
rdar://176847897
Reviewed by Wenson Hsieh.
InputMethodUsesCorrectKeyEventOrder (turned on by default in 310826@main,
originally added in
297270@main to fix Devanagari typing on docs.google.com) defers setMarkedText:
and insertText: calls
from the input method's handleEventByInputMethod: callback into a queue
(m_collectedKeypressCommands).
The queue is replayed in the web process's keydown default handler, ensuring
keydown fires before
downstream events such as compositionstart and input.
There was an unintended consequence from this change: while the input method is
still inside its
callback, it polls selectedRange to verify that its insertText: took effect.
Because the queued command
hasn't reached the web process yet, the poll returns the pre-keystroke cursor,
which is logically stale
by the inserted text length. Modeless input methods such as Vietnamese Simple
Telex and 2-Set Korean
interpret that as "my insertion didn't stick" and abandon the modeless path,
switching back to the mode
which uses setMarkedText:-based for the rest of the session in applications
such as Mail and Notes.
There were also two other related issues of the same root cause:
- Once an input method is confident enough to keep going in modeless mode, it
commits via insertText:
with a non-NSNotFound replacementRange (e.g. insertText:"vi"
replacementRange:(0,1) to extend 'v'
into "vi"). The queue's KeypressCommand constructor ignored
replacementRange, and the queue path
asserted it was NSNotFound.
- When the IME returned handled=YES for a purely-insertText: keystroke with
no prior composition,
handleEditingKeyboardEvent's event.handledByInputMethod() branch (added by
313212@main) executed
the command during the keydown default handler. That dispatched a TextInput
event with
underlyingEvent set to keydown, which trips the assertion in
EventHandler::handleTextInputEvent
requiring a keypress as the underlying event instead.
This PR implements three narrow changes in
Source/WebKit/UIProcess/mac/WebViewImpl.mm:
1. Modeless commit routing in interpretKeyEvent's completion handler: when
the input method's
commands are purely insertText: (no setMarkedText:, no other) and
editorState().hasComposition is
false, force handled=NO. The keypress flow then runs and
executeKeypressCommandsInternal fires
insertText: during the Char phase with underlyingEvent=keypress — no
assertion.
The !hasComposition guard preserves 313212@main's Japanese Enter-commit
fix: insertText:"k" with
prior composition stays handled=YES, keydown gets keyCode 229, and
google.com doesn't misread it
as a real Enter.
2. selectedRange staging in selectedRangeWithCompletionHandler: when every
queued keypress command
is insertText:, add the cumulative text length to the cursor returned to
the input method.
The input method observes its own insertion reflected and stays in
modeless mode. If any
setMarkedText: is in the queue, the input method is already composing and
the unstaged value is
correct.
3. Replacement-range queueing in WebViewImpl::insertText: the queue path uses
the existing 6-arg
KeypressCommand constructor (already used by setMarkedText:) so it carries
replacementRange.
The IME's modeless continuation goes through the same queue as plain
insertText:, executed in
the web process's keydown default handler with the same event ordering.
In addition, this PR implements one fix in
Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm
4. replacementRange execution in executeKeypressCommandsInternal for
insertText:: when the queued
command has a non-NSNotFound replacementRange, set the selection to that
range first via
frame->selection().setSelection(VisibleSelection(replacementRange)) then
call editor->insertText.
This mirrors what WebPage::insertTextAsync already does for the same case.
Test:
TestWebKitAPI.WKWebViewMacEditingTests.KeyDownForModelessInputMethodInsertUsesRealKeyCode
TestWebKitAPI.WKWebViewMacEditingTests.ModelessInputMethodInsertTextWithReplacementRangeInsertsCorrectly
TestWebKitAPI.WKWebViewMacEditingTests.ModelessInputMethodEventOrderingMatchesNormalTyping
* Source/WebKit/UIProcess/mac/WebViewImpl.mm:
(WebKit::WebViewImpl::interpretKeyEvent):
(WebKit::WebViewImpl::insertText):
(WebKit::WebViewImpl::selectedRangeWithCompletionHandler):
* Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm:
(WebKit::WebPage::executeKeypressCommandsInternal):
* Tools/Scripts/webkitpy/api_tests/allowlist.txt:
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/mac/WKWebViewMacEditingTests.mm:
(TestWebKitAPI::TEST(WKWebViewMacEditingTests,
KeyDownForModelessInputMethodInsertUsesRealKeyCode)):
(TestWebKitAPI::TEST(WKWebViewMacEditingTests,
ModelessInputMethodInsertTextWithReplacementRangeInsertsCorrectly)):
(TestWebKitAPI::TEST(WKWebViewMacEditingTests,
ModelessInputMethodEventOrderingMatchesNormalTyping)):
Canonical link: https://commits.webkit.org/313286@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications