Make WordPress Core

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

  1. In a browser that supports Emoji 17, open any front-end page of a site on 6.9 or later.
  2. In the console run sessionStorage.getItem( 'wpEmojiSettingsSupports' ): emoji is false.
  3. 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)

#1 @kimjiwoon
4 weeks ago

Correction: the source-code escape sequences in the original description were transformed while pasting. The relevant literals are:

// Current source: a lone high surrogate, U+1FAC, then the digit 8.
const notSupported = emojiRendersEmptyCenterPoint( context, '\uD83E\u1FAC8' );

// Correct UTF-16 surrogate pair for U+1FAC8 HAIRY CREATURE.
const notSupported = emojiRendersEmptyCenterPoint( context, '\uD83E\uDEC8' );

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.

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 @peterwilsoncc
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 @kimjiwoon
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.

Last edited 4 weeks ago by kimjiwoon (previous) (diff)

#5 @peterwilsoncc
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.

#6 @westonruter
4 weeks ago

  • Owner set to peterwilsoncc
  • Status new → reviewing

#7 @peterwilsoncc
4 weeks ago

  • Resolution → fixed
  • Status reviewing → closed

In 63755:

Emoji: Fix test for Emoji 17 browser/OS support.

Correct code point used for testing the 🫈 hairy creature emoji to the correctly JSON encoded value, \uD83E\uDEC8.

Follow up to r61134.

Developed in ​https://github.com/WordPress/wordpress-develop/pull/13515

Props kimjiwoon.
Fixes #66104.

Note: See TracTickets for help on using tickets.