Opened 4 weeks ago
Closed 4 weeks ago
#66104 closed defect (bug) (fixed)
Emoji 17 support test uses a malformed string, so browsers that support Emoji 17 get every emoji replaced by images
| Reported by: | kimjiwoon | Owned by: | peterwilsoncc |
|---|---|---|---|
| Priority: | normal | Milestone: | 7.2 |
| Component: | Emoji | Version: | 6.9 |
| Severity: | normal | Keywords: | has-patch commit |
| Cc: | Focuses: |
Description
Since [61134] (#64184), browserSupportsEmoji() in src/js/_enqueues/lib/emoji-loader.js tests Emoji 17 support with:
const notSupported = emojiRendersEmptyCenterPoint( context, '\uD83Eᾬ8' );
\u takes exactly four hex digits, so this is three code points: a lone high surrogate U+D83E, U+1FAC, and the digit 8. It is not U+1FAC8 HAIRY CREATURE, whose UTF-16 form is . The same malformed string is in wp-emoji-loader.min.js shipped in 6.9, 7.0, and 7.1; 6.8 tested .
The malformed string draws black text glyphs with an empty center point, so the test reports Emoji 17 as unsupported even where it is supported. With emoji false, everythingExceptFlag is false too, and wp-emoji.js replaces every emoji with images rather than only flags.
Related: #64318 updated Twemoji after [61134], but does not cover this malformed support-test string.
Measured
Windows 11, Chromium 152, WordPress 7.1, drawing each string as emojiRendersEmptyCenterPoint() does (32px canvas, 600 32px Arial, textBaseline top, center pixel at 16,16):
| String | Painted pixels | Coloured pixels | Center pixel |
|---|---|---|---|
| (U+1FAC8) | 503 | 443 | coloured |
| \uD83Eᾬ8 (current test) | 478 | 0 | empty |
| (U+1F600, comparison) | 692 | 554 | coloured |
The browser renders U+1FAC8 as a colour emoji, but the stored support result is {"flag":false,"emoji":false}; both and were replaced with s.w.org/images/core/emoji/17.0.2/svg/ images. With the corrected string, the result is flag: false, emoji: true, and only flags are replaced.
I could only measure Chromium on Windows; whether the malformed string also leaves the center empty in other engines and fonts is not verified.
Steps to reproduce
- In a browser that supports Emoji 17, open any front-end page of a site on 6.9 or later.
- In the console run sessionStorage.getItem( 'wpEmojiSettingsSupports' ): emoji is false.
- Insert an emoji that the browser renders natively, for example document.body.insertAdjacentHTML( 'beforeend', '<p></p>' ): it is replaced by an img.emoji.
Suggested fix
- * 0xD83E 0x1FAC8 (\uD83Eᾬ8) == Hairy creature. + * U+1FAC8 () == Hairy creature. @@ - const notSupported = emojiRendersEmptyCenterPoint( context, '\uD83Eᾬ8' ); + const notSupported = emojiRendersEmptyCenterPoint( context, '' );
Since the support result is cached in sessionStorage, affected visitors pick up the fix on their next session.
Change History (7)
This ticket was mentioned in PR #13515 on WordPress/wordpress-develop by @kimjiwoon.
4 weeks ago
#2
- Keywords has-patch added
The Emoji 17 support test in emoji-loader.js draws '\uD83E\u1FAC8'. Because \u takes exactly four hex digits, that string is a lone high surrogate, U+1FAC and the digit 8, not U+1FAC8 HAIRY CREATURE. It renders with an empty center point, so the test reports Emoji 17 as unsupported even in browsers that support it; emoji is then false, everythingExceptFlag is false as well, and wp-emoji.js replaces every emoji with images instead of only flags.
This uses the correct UTF-16 pair, '\uD83E\uDEC8', and corrects the comment. The malformed string was introduced with the Twemoji 17.0.1 update in [61134] (#64184) and is present in 6.9, 7.0, 7.1 and trunk; 6.8 tested '\uD83E\uDEDF'.
Testing: there is no JS unit test for the loader, so Core's own support-test functions (testEmojiSupports() and the helpers it calls) were extracted from trunk and from this branch and run in the same browser (Windows 11, Chromium 152). Trunk returns { flag: false, emoji: false }, so every emoji is replaced; this branch returns { flag: false, emoji: true }, so only flags are replaced, which is what everythingExceptFlag is for. Both runs used the same OffscreenCanvas path. wp-scripts lint-js and jshint pass on the changed file.
## Use of AI Tools
This pull request was prepared with Claude Code (Anthropic). It located the defect, wrote the fix, and ran the browser comparison and lint checks locally.
#3
@
4 weeks ago
- Milestone Awaiting Review → 7.2
Thanks for the report @kimjiwoon.
I've moved this on to the WordPress 7.2 milestone.
The hairy creature code point matches the changes in your PR.
#4
@
4 weeks ago
Thanks, @peterwilsoncc. Since this is a regression introduced in 6.9, could it also be considered for the 7.1.2 maintenance release? I understand 7.1.1 is already at RC and am not asking to reopen that scope.
The patch only corrects the test literal and restores the intended distinction between unsupported flags and unsupported emoji. It would stop affected 6.9+ sites from replacing every emoji with an image while 7.2 is in development. If broader cross-browser testing means 7.2 is the right target, that makes sense too.
#5
@
4 weeks ago
- Keywords commit added
I've put an approval on the pull request.
@kimjiwoon As a rule, the only bug fixes included in minor releases are those for regressions introduced during the major release. As this is a relatively minor bug and wasn't introduced in the 7.1 release cycle, WordPress 7.2 is the best target.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
Correction: the source-code escape sequences in the original description were transformed while pasting. The relevant literals are:
Likewise, the previous Emoji 16 test used '\uD83E\uDEDF'. The transformed display in the original description is a paste error; the source and proposed fix should be read as the escaped forms above.