Skip to content

Commit 7990e38

Browse files
Merge branch 'trunk' into deps/remove-check-node-version
2 parents 1cc6aad + b0dc430 commit 7990e38

7 files changed

Lines changed: 853 additions & 203 deletions

File tree

‎src/js/_enqueues/lib/emoji-loader.js‎

Lines changed: 112 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,30 @@
44

55
// Note: This is loaded as a script module, so there is no need for an IIFE to prevent pollution of the global scope.
66

7-
/**
8-
* Emoji Settings as exported in PHP via _print_emoji_detection_script().
9-
* @typedef WPEmojiSettings
10-
* @type {Object}
11-
* @property {?object} source The source object containing emoji-related URLs.
12-
* @property {?string} source.concatemoji The URL for the concatenated emoji script.
13-
* @property {?string} source.twemoji The URL for the Twemoji script.
14-
* @property {?string} source.wpemoji The URL for the WP Emoji script.
15-
*/
7+
// Note: The WPEmojiSettings and EmojiSupports types are declared in typings/wp-emoji, since wp-emoji.js reads them back.
168

179
const selector = 'script#wp-emoji-settings';
1810
const script = document.querySelector( selector );
1911
if ( ! ( script instanceof HTMLScriptElement ) ) {
20-
throw new Error( `Element missing: ${ selector }`);
12+
throw new Error( `Element missing: ${ selector }` );
2113
}
2214
const settings = /** @type {WPEmojiSettings} */ ( JSON.parse( script.text ) );
2315

2416
// For compatibility with other scripts that read from this global, in particular wp-includes/js/wp-emoji.js (source file: js/_enqueues/wp/emoji.js).
2517
window._wpemojiSettings = settings;
2618

2719
/**
28-
* Support tests.
20+
* Results of the emoji support tests.
21+
*
2922
* @typedef SupportTests
3023
* @type {Object}
31-
* @property {?boolean} flag Whether the browser supports flag emojis.
32-
* @property {?boolean} emoji Whether the browser supports general emojis.
24+
* @property {boolean} flag Whether the browser renders flag emoji.
25+
* @property {boolean} emoji Whether the browser renders emoji.
3326
*/
3427

3528
const sessionStorageKey = 'wpEmojiSettingsSupports';
29+
30+
/** @type {Array<keyof SupportTests>} */
3631
const tests = [ 'flag', 'emoji' ];
3732

3833
/**
@@ -49,16 +44,18 @@ function supportsWorkerOffloading() {
4944
typeof Worker !== 'undefined' &&
5045
typeof OffscreenCanvas !== 'undefined' &&
5146
typeof URL !== 'undefined' &&
52-
URL.createObjectURL &&
47+
typeof URL.createObjectURL === 'function' &&
5348
typeof Blob !== 'undefined'
5449
);
5550
}
5651

5752
/**
53+
* Support tests as they are stored in session storage.
54+
*
5855
* @typedef SessionSupportTests
5956
* @type {Object}
60-
* @property {number} timestamp The timestamp when the support tests were last updated.
61-
* @property {SupportTests} supportTests The support tests for the browser.
57+
* @property {number} timestamp When the tests were run, in milliseconds since the epoch.
58+
* @property {SupportTests} supportTests What the tests found.
6259
*/
6360

6461
/**
@@ -72,10 +69,13 @@ function supportsWorkerOffloading() {
7269
*/
7370
function getSessionSupportTests() {
7471
try {
72+
const itemJson = sessionStorage.getItem( sessionStorageKey );
73+
if ( null === itemJson ) {
74+
return null;
75+
}
76+
7577
/** @type {SessionSupportTests} */
76-
const item = JSON.parse(
77-
sessionStorage.getItem( sessionStorageKey )
78-
);
78+
const item = JSON.parse( itemJson );
7979
if (
8080
typeof item === 'object' &&
8181
typeof item.timestamp === 'number' &&
@@ -95,7 +95,7 @@ function getSessionSupportTests() {
9595
*
9696
* @private
9797
*
98-
* @param {SupportTests} supportTests Support tests.
98+
* @param {SupportTests} supportTests What the tests found.
9999
*/
100100
function setSessionSupportTests( supportTests ) {
101101
try {
@@ -112,6 +112,51 @@ function setSessionSupportTests( supportTests ) {
112112
} catch ( e ) {}
113113
}
114114

115+
/**
116+
* A 2D context for the support tests.
117+
*
118+
* Which of the two it is depends on the kind of canvas it came from, and the tests use only what
119+
* both provide.
120+
*
121+
* @typedef {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} EmojiTestContext
122+
*/
123+
124+
/**
125+
* Checks if two sets of Emoji characters render the same visually.
126+
*
127+
* @callback EmojiSetsRenderIdentically
128+
*
129+
* @param {EmojiTestContext} context 2D Context.
130+
* @param {string} set1 Set of Emoji to test.
131+
* @param {string} set2 Set of Emoji to test.
132+
*
133+
* @return {boolean} True if the two sets render the same.
134+
*/
135+
136+
/**
137+
* Checks if the center point of a single emoji is empty.
138+
*
139+
* @callback EmojiRendersEmptyCenterPoint
140+
*
141+
* @param {EmojiTestContext} context 2D Context.
142+
* @param {string} emoji Emoji to test.
143+
*
144+
* @return {boolean} True if the center point is empty.
145+
*/
146+
147+
/**
148+
* Determines if the browser properly renders Emoji that Twemoji can supplement.
149+
*
150+
* @callback BrowserSupportsEmoji
151+
*
152+
* @param {EmojiTestContext} context 2D Context.
153+
* @param {keyof SupportTests} type Which support test to run.
154+
* @param {EmojiSetsRenderIdentically} emojiSetsRenderIdentically Reference to emojiSetsRenderIdentically function, needed due to minification.
155+
* @param {EmojiRendersEmptyCenterPoint} emojiRendersEmptyCenterPoint Reference to emojiRendersEmptyCenterPoint function, needed due to minification.
156+
*
157+
* @return {boolean} True if the browser can render emoji, false if it cannot.
158+
*/
159+
115160
/**
116161
* Checks if two sets of Emoji characters render the same visually.
117162
*
@@ -127,9 +172,9 @@ function setSessionSupportTests( supportTests ) {
127172
*
128173
* @private
129174
*
130-
* @param {CanvasRenderingContext2D} context 2D Context.
131-
* @param {string} set1 Set of Emoji to test.
132-
* @param {string} set2 Set of Emoji to test.
175+
* @param {EmojiTestContext} context 2D Context.
176+
* @param {string} set1 Set of Emoji to test.
177+
* @param {string} set2 Set of Emoji to test.
133178
*
134179
* @return {boolean} True if the two sets render the same.
135180
*/
@@ -177,8 +222,8 @@ function emojiSetsRenderIdentically( context, set1, set2 ) {
177222
*
178223
* @private
179224
*
180-
* @param {CanvasRenderingContext2D} context 2D Context.
181-
* @param {string} emoji Emoji to test.
225+
* @param {EmojiTestContext} context 2D Context.
226+
* @param {string} emoji Emoji to test.
182227
*
183228
* @return {boolean} True if the center point is empty.
184229
*/
@@ -188,7 +233,7 @@ function emojiRendersEmptyCenterPoint( context, emoji ) {
188233
context.fillText( emoji, 0, 0 );
189234

190235
// Test if the center point (16, 16) is empty (0,0,0,0).
191-
const centerPoint = context.getImageData(16, 16, 1, 1);
236+
const centerPoint = context.getImageData( 16, 16, 1, 1 );
192237
for ( let i = 0; i < centerPoint.data.length; i++ ) {
193238
if ( centerPoint.data[ i ] !== 0 ) {
194239
// Stop checking the moment it's known not to be empty.
@@ -209,14 +254,15 @@ function emojiRendersEmptyCenterPoint( context, emoji ) {
209254
*
210255
* @private
211256
*
212-
* @param {CanvasRenderingContext2D} context 2D Context.
213-
* @param {string} type Whether to test for support of "flag" or "emoji".
214-
* @param {Function} emojiSetsRenderIdentically Reference to emojiSetsRenderIdentically function, needed due to minification.
215-
* @param {Function} emojiRendersEmptyCenterPoint Reference to emojiRendersEmptyCenterPoint function, needed due to minification.
257+
* @param {EmojiTestContext} context 2D Context.
258+
* @param {keyof SupportTests} type Which support test to run.
259+
* @param {EmojiSetsRenderIdentically} emojiSetsRenderIdentically Reference to emojiSetsRenderIdentically function, needed due to minification.
260+
* @param {EmojiRendersEmptyCenterPoint} emojiRendersEmptyCenterPoint Reference to emojiRendersEmptyCenterPoint function, needed due to minification.
216261
*
217262
* @return {boolean} True if the browser can render emoji, false if it cannot.
218263
*/
219264
function browserSupportsEmoji( context, type, emojiSetsRenderIdentically, emojiRendersEmptyCenterPoint ) {
265+
/** @type {boolean} */
220266
let isIdentical;
221267

222268
switch ( type ) {
@@ -256,7 +302,7 @@ function browserSupportsEmoji( context, type, emojiSetsRenderIdentically, emojiR
256302

257303
/*
258304
* Test for English flag compatibility. England is a country in the United Kingdom, it
259-
* does not have a two letter locale code but rather a five letter sub-division code.
305+
* does not have a two letter locale code but rather a five letter subdivision code.
260306
*
261307
* To test for support, we try to render it, and compare the rendering to how it would look if
262308
* the browser doesn't render it correctly (black flag emoji + [G] + [B] + [E] + [N] + [G]).
@@ -288,8 +334,6 @@ function browserSupportsEmoji( context, type, emojiSetsRenderIdentically, emojiR
288334
const notSupported = emojiRendersEmptyCenterPoint( context, '\uD83E\uDEC8' );
289335
return ! notSupported;
290336
}
291-
292-
return false;
293337
}
294338

295339
/**
@@ -302,25 +346,30 @@ function browserSupportsEmoji( context, type, emojiSetsRenderIdentically, emojiR
302346
*
303347
* @private
304348
*
305-
* @param {string[]} tests Tests.
306-
* @param {Function} browserSupportsEmoji Reference to browserSupportsEmoji function, needed due to minification.
307-
* @param {Function} emojiSetsRenderIdentically Reference to emojiSetsRenderIdentically function, needed due to minification.
308-
* @param {Function} emojiRendersEmptyCenterPoint Reference to emojiRendersEmptyCenterPoint function, needed due to minification.
349+
* @param {Array<keyof SupportTests>} tests Which support tests to run.
350+
* @param {BrowserSupportsEmoji} browserSupportsEmoji Reference to browserSupportsEmoji function, needed due to minification.
351+
* @param {EmojiSetsRenderIdentically} emojiSetsRenderIdentically Reference to emojiSetsRenderIdentically function, needed due to minification.
352+
* @param {EmojiRendersEmptyCenterPoint} emojiRendersEmptyCenterPoint Reference to emojiRendersEmptyCenterPoint function, needed due to minification.
309353
*
310354
* @return {SupportTests} Support tests.
311355
*/
312356
function testEmojiSupports( tests, browserSupportsEmoji, emojiSetsRenderIdentically, emojiRendersEmptyCenterPoint ) {
313-
let canvas;
357+
/** @type {?EmojiTestContext} */
358+
let context;
359+
314360
if (
315361
typeof WorkerGlobalScope !== 'undefined' &&
316362
self instanceof WorkerGlobalScope
317363
) {
318-
canvas = new OffscreenCanvas( 300, 150 ); // Dimensions are default for HTMLCanvasElement.
364+
// Dimensions are default for HTMLCanvasElement.
365+
context = new OffscreenCanvas( 300, 150 ).getContext( '2d', { willReadFrequently: true } );
319366
} else {
320-
canvas = document.createElement( 'canvas' );
367+
context = document.createElement( 'canvas' ).getContext( '2d', { willReadFrequently: true } );
321368
}
322369

323-
const context = canvas.getContext( '2d', { willReadFrequently: true } );
370+
if ( ! context ) {
371+
throw new Error( 'Unable to obtain a 2D context for the emoji support tests.' );
372+
}
324373

325374
/*
326375
* Chrome on OS X added native emoji rendering in M41. Unfortunately,
@@ -330,7 +379,7 @@ function testEmojiSupports( tests, browserSupportsEmoji, emojiSetsRenderIdentica
330379
context.textBaseline = 'top';
331380
context.font = '600 32px Arial';
332381

333-
const supports = {};
382+
const supports = /** @type {SupportTests} */ ( {} );
334383
tests.forEach( ( test ) => {
335384
supports[ test ] = browserSupportsEmoji( context, test, emojiSetsRenderIdentically, emojiRendersEmptyCenterPoint );
336385
} );
@@ -361,10 +410,11 @@ settings.supports = {
361410
};
362411

363412
// Obtain the emoji support from the browser, asynchronously when possible.
364-
new Promise( ( resolve ) => {
365-
let supportTests = getSessionSupportTests();
366-
if ( supportTests ) {
367-
resolve( supportTests );
413+
/** @type {Promise<SupportTests>} */
414+
const supportTestsPromise = new Promise( ( resolve ) => {
415+
const sessionSupportTests = getSessionSupportTests();
416+
if ( sessionSupportTests ) {
417+
resolve( sessionSupportTests );
368418
return;
369419
}
370420

@@ -386,36 +436,39 @@ new Promise( ( resolve ) => {
386436
type: 'text/javascript'
387437
} );
388438
const worker = new Worker( URL.createObjectURL( blob ), { name: 'wpTestEmojiSupports' } );
389-
worker.onmessage = ( event ) => {
390-
supportTests = event.data;
391-
setSessionSupportTests( supportTests );
439+
worker.onmessage = ( /** @type {MessageEvent<SupportTests>} */ event ) => {
440+
setSessionSupportTests( event.data );
392441
worker.terminate();
393-
resolve( supportTests );
442+
resolve( event.data );
394443
};
395444
return;
396445
} catch ( e ) {}
397446
}
398447

399-
supportTests = testEmojiSupports( tests, browserSupportsEmoji, emojiSetsRenderIdentically, emojiRendersEmptyCenterPoint );
400-
setSessionSupportTests( supportTests );
401-
resolve( supportTests );
402-
} )
448+
const testedSupportTests = testEmojiSupports( tests, browserSupportsEmoji, emojiSetsRenderIdentically, emojiRendersEmptyCenterPoint );
449+
setSessionSupportTests( testedSupportTests );
450+
resolve( testedSupportTests );
451+
} );
452+
453+
supportTestsPromise
403454
// Once the browser emoji support has been obtained from the session, finalize the settings.
404455
.then( ( supportTests ) => {
405456
/*
406457
* Tests the browser support for flag emojis and other emojis, and adjusts the
407458
* support settings accordingly.
408459
*/
409460
for ( const test in supportTests ) {
410-
settings.supports[ test ] = supportTests[ test ];
461+
const key = /** @type {keyof SupportTests} */ ( test );
462+
const supported = supportTests[ key ];
463+
464+
settings.supports[ key ] = supported;
411465

412466
settings.supports.everything =
413-
settings.supports.everything && settings.supports[ test ];
467+
settings.supports.everything && supported;
414468

415-
if ( 'flag' !== test ) {
469+
if ( 'flag' !== key ) {
416470
settings.supports.everythingExceptFlag =
417-
settings.supports.everythingExceptFlag &&
418-
settings.supports[ test ];
471+
settings.supports.everythingExceptFlag && supported;
419472
}
420473
}
421474

0 commit comments

Comments
 (0)