Skip to content

Commit db48475

Browse files
committed
docs.google.com: Emojis don't show properly
https://bugs.webkit.org/show_bug.cgi?id=305666 rdar://122678873 Reviewed by Wenson Hsieh. Suppress keypress events for supplementary characters to fix emoji input Emoji with codepoints > 0xFFFF (like 🥹 U+1F979) are encoded as UTF-16 surrogate pairs. The deprecated KeyboardEvent.charCode property returns the full 32-bit codepoint (e.g., 129401), but JavaScript's String.fromCharCode() only uses the lower 16 bits, causing truncation (129401 & 0xFFFF = 0xF979 = "凉", a Chinese character). This breaks sites like Google Docs that rely on charCode to insert text. The fix suppresses keypress events for supplementary characters (codepoint > 0xFFFF) and manually inserts the text via Editor::insertText, ensuring beforeinput/input events fire correctly with the proper emoji. This matches Blink's behavior: Android Chrome routes emoji through IME (keyCode=229) and suppresses keypress. macOS Safari's emoji picker bypasses keyboard events entirely. Regular characters and BMP emoji (codepoint ≤ 0xFFFF) continue to fire keypress normally, preserving backward compatibility. Test: Tools/TestWebKitAPI/Tests/ios/KeyboardInputTestsIOS.mm * Source/WebCore/page/EventHandler.cpp: (WebCore::EventHandler::internalKeyEvent): * Tools/TestWebKitAPI/Tests/ios/KeyboardInputTestsIOS.mm: (TestWebKitAPI::sendKeyEventWithCharacters): (TestWebKitAPI::TEST(KeyboardInputTests, SuppressKeypressForSupplementaryCharacterEmoji)): (TestWebKitAPI::TEST(KeyboardInputTests, AllowKeypressForRegularCharacters)): (TestWebKitAPI::TEST(KeyboardInputTests, AllowKeypressForBMPEmoji)): Canonical link: https://commits.webkit.org/305914@main
1 parent f463a6d commit db48475

File tree

2 files changed

+103
-1
lines changed

2 files changed

+103
-1
lines changed

Source/WebCore/page/EventHandler.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4315,9 +4315,16 @@ bool EventHandler::internalKeyEvent(const PlatformKeyboardEvent& initialKeyEvent
43154315
keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Type::Char, backwardCompatibilityMode);
43164316
if (keyPressEvent.text().isEmpty())
43174317
return keydownResult;
4318+
4319+
// webkit.org/b/305666: Emojis appear as Chinese characters in Google Docs
4320+
auto shouldAvoidDispatchingKeyPressEvent = [&] {
4321+
auto text = keyPressEvent.text();
4322+
return !text.isEmpty() && !U_IS_BMP(text.characterStartingAt(0));
4323+
};
4324+
43184325
auto keypress = KeyboardEvent::create(keyPressEvent, &frame->windowProxy());
43194326
keypress->setTarget(element.copyRef());
4320-
if (keypress->isComposing()) {
4327+
if (keypress->isComposing() || shouldAvoidDispatchingKeyPressEvent()) {
43214328
frame->editor().handleKeyboardEvent(keypress);
43224329
return keydownResult;
43234330
}

Tools/TestWebKitAPI/Tests/ios/KeyboardInputTestsIOS.mm

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,101 @@ HTTPServer server({
15621562
[webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.getElementById('input').focus()"];
15631563
}
15641564

1565+
static void sendKeyEventWithCharacters(TestWKWebView *webView, NSString *characters)
1566+
{
1567+
RetainPtr keyDownEvent = adoptNS([[WebEvent alloc]
1568+
initWithKeyEventType:WebEventKeyDown
1569+
timeStamp:CFAbsoluteTimeGetCurrent()
1570+
characters:characters
1571+
charactersIgnoringModifiers:characters
1572+
modifiers:0
1573+
isRepeating:NO
1574+
withFlags:0
1575+
withInputManagerHint:nil
1576+
keyCode:0
1577+
isTabKey:NO]);
1578+
1579+
RetainPtr keyUpEvent = adoptNS([[WebEvent alloc]
1580+
initWithKeyEventType:WebEventKeyUp
1581+
timeStamp:CFAbsoluteTimeGetCurrent()
1582+
characters:characters
1583+
charactersIgnoringModifiers:characters
1584+
modifiers:0
1585+
isRepeating:NO
1586+
withFlags:0
1587+
withInputManagerHint:nil
1588+
keyCode:0
1589+
isTabKey:NO]);
1590+
1591+
__block bool doneWaiting = false;
1592+
[webView handleKeyEvent:keyDownEvent.get() completion:^(WebEvent *, BOOL) {
1593+
[(id<UITextInput>)[webView firstResponder] insertText:characters];
1594+
[webView handleKeyEvent:keyUpEvent.get() completion:^(WebEvent *, BOOL) {
1595+
doneWaiting = true;
1596+
}];
1597+
}];
1598+
1599+
Util::run(&doneWaiting);
1600+
[webView waitForNextPresentationUpdate];
1601+
}
1602+
1603+
static constexpr auto keyEventTrackingTestMarkup = @"<input id='test' autofocus><script>"
1604+
"window.events = [];"
1605+
"test.addEventListener('keydown', e => events.push('keydown'));"
1606+
"test.addEventListener('keypress', e => events.push('keypress'));"
1607+
"test.addEventListener('beforeinput', e => events.push('beforeinput'));"
1608+
"test.addEventListener('input', e => events.push('input'));"
1609+
"test.addEventListener('keyup', e => events.push('keyup'));"
1610+
"</script>";
1611+
1612+
TEST(KeyboardInputTests, SuppressKeypressForSupplementaryCharacterEmoji)
1613+
{
1614+
auto [webView, inputDelegate] = webViewAndInputDelegateWithAutofocusedInput();
1615+
[webView synchronouslyLoadHTMLString:keyEventTrackingTestMarkup];
1616+
1617+
sendKeyEventWithCharacters(webView.get(), @"🥹");
1618+
1619+
NSArray *firedEvents = [webView objectByEvaluatingJavaScript:@"events"];
1620+
1621+
EXPECT_EQ(4U, firedEvents.count);
1622+
EXPECT_FALSE([firedEvents containsObject:@"keypress"]);
1623+
EXPECT_WK_STREQ("keydown", firedEvents[0]);
1624+
EXPECT_WK_STREQ("beforeinput", firedEvents[1]);
1625+
EXPECT_WK_STREQ("input", firedEvents[2]);
1626+
EXPECT_WK_STREQ("keyup", firedEvents[3]);
1627+
1628+
EXPECT_WK_STREQ("🥹", [webView stringByEvaluatingJavaScript:@"test.value"]);
1629+
}
1630+
1631+
static void expectStandardKeyEventSequenceForCharacter(NSString *character)
1632+
{
1633+
auto [webView, inputDelegate] = webViewAndInputDelegateWithAutofocusedInput();
1634+
[webView synchronouslyLoadHTMLString:keyEventTrackingTestMarkup];
1635+
1636+
sendKeyEventWithCharacters(webView.get(), character);
1637+
1638+
NSArray *firedEvents = [webView objectByEvaluatingJavaScript:@"events"];
1639+
1640+
EXPECT_EQ(5U, firedEvents.count);
1641+
EXPECT_WK_STREQ("keydown", firedEvents[0]);
1642+
EXPECT_WK_STREQ("keypress", firedEvents[1]);
1643+
EXPECT_WK_STREQ("beforeinput", firedEvents[2]);
1644+
EXPECT_WK_STREQ("input", firedEvents[3]);
1645+
EXPECT_WK_STREQ("keyup", firedEvents[4]);
1646+
1647+
EXPECT_WK_STREQ(character, [webView stringByEvaluatingJavaScript:@"test.value"]);
1648+
}
1649+
1650+
TEST(KeyboardInputTests, AllowKeypressForRegularCharacters)
1651+
{
1652+
expectStandardKeyEventSequenceForCharacter(@"a");
1653+
}
1654+
1655+
TEST(KeyboardInputTests, AllowKeypressForBMPEmoji)
1656+
{
1657+
expectStandardKeyEventSequenceForCharacter(@"");
1658+
}
1659+
15651660
} // namespace TestWebKitAPI
15661661

15671662
#endif // PLATFORM(IOS_FAMILY)

0 commit comments

Comments
 (0)