Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 2e47ba3f800b7e2edbdb24cce348bfd086c8ef2e
      
https://github.com/WebKit/WebKit/commit/2e47ba3f800b7e2edbdb24cce348bfd086c8ef2e
  Author: Ryosuke Niwa <[email protected]>
  Date:   2026-05-23 (Sat, 23 May 2026)

  Changed paths:
    M Source/WebCore/editing/Editor.cpp
    M Tools/Scripts/webkitpy/api_tests/allowlist.txt
    M Tools/TestWebKitAPI/Tests/WebKit/WKWebView/mac/WKWebViewMacEditingTests.mm

  Log Message:
  -----------
  REGRESSION(310113@main): On ChatGPT, typing a long text in Japanese IM 
results in a partially confirmed composition to be displaced after the caret
https://bugs.webkit.org/show_bug.cgi?id=315419
rdar://177691343

Reviewed by Wenson Hsieh.

Editor::setComposition dispatches compositionstart/compositionupdate to JS, 
then calls
TypingCommand::insertText to install the marked text — without flushing layout 
in between.
If the JS handler mutates the DOM around the caret (any insertion/removal that 
touches
the selected text node), two things could go wrong together:

  1. The selection's start position can end up parent-anchored on a non-text 
container
     (e.g. <p>:1) instead of inside a text node either because the JS 
explicitly set
     a Selection range, or because FrameSelection::nodeWillBeRemoved 
auto-adjusted the
     position via setWithoutValidation (which bypasses canonicalPosition, the 
one place
     that would have flushed layout).
  2. Any text nodes the handler added are in the DOM but may not have a 
RenderText yet
     as render-tree attachment only happens in the next layout.

When TypingCommand::insertText then runs, Position::upstream() walks from <p>:1 
looking
for a text-node candidate. It reaches the pending text node, but at 
Position.cpp:728 it
sees renderer() is null and continues past it. It falls back to <p>:0, and
positionInsideTextNode creates a new text node there. The marked text is 
installed into
that fresh node, ending up on the wrong side of the existing text.

The ProseMirror-specific insertBefore + removeChild sequence is one way to land 
in this
state, but it's not what the bug is about. It's about setComposition proceeding 
to
TypingCommand::insertText while the render tree is stale relative to the DOM 
the JS
handler just produced.

This PR fixes the issue by forcing a layout flush in Editor::setComposition 
after the
composition events fire and before TypingCommand::insertText. This attaches the 
pending
renderers and Position::upstream() can walk into the new text nodes and resolve 
the start
position correctly.

Co-authored with Claude and Simon Fraser.

Test: 
TestWebKitAPI.WKWebViewMacEditingTests.JapaneseAutoCommitWithDOMRebuildInCompositionStartLandsTextAtCursor

* Source/WebCore/editing/Editor.cpp:
(WebCore::Editor::setComposition):
* Tools/Scripts/webkitpy/api_tests/allowlist.txt:
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/mac/WKWebViewMacEditingTests.mm:
(TestWebKitAPI::TEST(WKWebViewMacEditingTests, 
JapaneseAutoCommitWithDOMRebuildInCompositionStartLandsTextAtCursor)):

Canonical link: https://commits.webkit.org/313806@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to