<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[53282] trunk: Themes: Add internal-only theme.json's webfonts handler (stopgap).</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/53282">53282</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/53282","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>hellofromTonya</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2022-04-26 14:46:37 +0000 (Tue, 26 Apr 2022)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Themes: Add internal-only theme.json's webfonts handler (stopgap).

Adds `_wp_theme_json_webfonts_handler()` for handling `fontFace` declarations in a theme's `theme.json` file to generate the `@font-face` styles for both the editor and front-end.

Design notes:
* It is not a public API, but rather an internal, Core-only handler.
* It is a stopgap implementation that will be replaced when the public Webfonts API is introduced in Core.
* The code design is intentional, albeit funky, with the purpose of avoiding backwards-compatibility issues when the public Webfonts API is introduced in Core.
   * It hides the inter-workings.
   * Does not exposing API ins and outs for external consumption.
   * Only works for `theme.json`.
   * Does not provide registration or enqueuing access for plugins.

For more context on the decision to include this stopgap and the Webfonts API, see:
* Core's PR 40493 https://github.com/WordPress/gutenberg/pull/40493
* Gutenberg's tracking issue 40472 https://github.com/WordPress/gutenberg/issues/40472

Props aristath, hellofromTonya, peterwilsoncc, costdev, jffng, zieladam, gziolo, bph, jonoaldersonwp, desrosj.

See <a href="https://core.trac.wordpress.org/ticket/55567">#55567</a>, <a href="https://core.trac.wordpress.org/ticket/46370">#46370</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesdefaultfiltersphp">trunk/src/wp-includes/default-filters.php</a></li>
<li><a href="#trunksrcwpincludesscriptloaderphp">trunk/src/wp-includes/script-loader.php</a></li>
<li><a href="#trunktestsphpunitteststhemethemeDirphp">trunk/tests/phpunit/tests/theme/themeDir.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/tests/phpunit/data/themedir1/empty-fontface-theme/</li>
<li><a href="#trunktestsphpunitdatathemedir1emptyfontfacethemefunctionsphp">trunk/tests/phpunit/data/themedir1/empty-fontface-theme/functions.php</a></li>
<li><a href="#trunktestsphpunitdatathemedir1emptyfontfacethemeindexphp">trunk/tests/phpunit/data/themedir1/empty-fontface-theme/index.php</a></li>
<li><a href="#trunktestsphpunitdatathemedir1emptyfontfacethemestylecss">trunk/tests/phpunit/data/themedir1/empty-fontface-theme/style.css</a></li>
<li>trunk/tests/phpunit/data/themedir1/empty-fontface-theme/templates/</li>
<li><a href="#trunktestsphpunitdatathemedir1emptyfontfacethemetemplatesindexhtml">trunk/tests/phpunit/data/themedir1/empty-fontface-theme/templates/index.html</a></li>
<li><a href="#trunktestsphpunitdatathemedir1emptyfontfacethemethemejson">trunk/tests/phpunit/data/themedir1/empty-fontface-theme/theme.json</a></li>
<li>trunk/tests/phpunit/data/themedir1/webfonts-theme/</li>
<li><a href="#trunktestsphpunitdatathemedir1webfontsthemefunctionsphp">trunk/tests/phpunit/data/themedir1/webfonts-theme/functions.php</a></li>
<li><a href="#trunktestsphpunitdatathemedir1webfontsthemeindexphp">trunk/tests/phpunit/data/themedir1/webfonts-theme/index.php</a></li>
<li><a href="#trunktestsphpunitdatathemedir1webfontsthemestylecss">trunk/tests/phpunit/data/themedir1/webfonts-theme/style.css</a></li>
<li>trunk/tests/phpunit/data/themedir1/webfonts-theme/templates/</li>
<li><a href="#trunktestsphpunitdatathemedir1webfontsthemetemplatesindexhtml">trunk/tests/phpunit/data/themedir1/webfonts-theme/templates/index.html</a></li>
<li><a href="#trunktestsphpunitdatathemedir1webfontsthemethemejson">trunk/tests/phpunit/data/themedir1/webfonts-theme/theme.json</a></li>
<li>trunk/tests/phpunit/tests/webfonts/</li>
<li><a href="#trunktestsphpunittestswebfontswpThemeJsonWebfontsHandlerphp">trunk/tests/phpunit/tests/webfonts/wpThemeJsonWebfontsHandler.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesdefaultfiltersphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/default-filters.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/default-filters.php 2022-04-26 14:26:38 UTC (rev 53281)
+++ trunk/src/wp-includes/default-filters.php   2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -351,6 +351,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'after_switch_theme', '_wp_menus_changed' );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'after_switch_theme', '_wp_sidebars_changed' );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'wp_print_styles', 'print_emoji_styles' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+add_action( 'plugins_loaded', '_wp_theme_json_webfonts_handler' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> if ( isset( $_GET['replytocom'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">        add_filter( 'wp_robots', 'wp_robots_no_robots' );
</span></span></pre></div>
<a id="trunksrcwpincludesscriptloaderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/script-loader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/script-loader.php   2022-04-26 14:26:38 UTC (rev 53281)
+++ trunk/src/wp-includes/script-loader.php     2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3027,3 +3027,499 @@
</span><span class="cx" style="display: block; padding: 0 10px">        // Enqueue assets in the editor.
</span><span class="cx" style="display: block; padding: 0 10px">        add_action( 'enqueue_block_assets', $callback );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * Runs the theme.json webfonts handler.
+ *
+ * Using `WP_Theme_JSON_Resolver`, it gets the fonts defined
+ * in the `theme.json` for the current selection and style
+ * variations, validates the font-face properties, generates
+ * the '@font-face' style declarations, and then enqueues the
+ * styles for both the editor and front-end.
+ *
+ * Design Notes:
+ * This is not a public API, but rather an internal handler.
+ * A future public Webfonts API will replace this stopgap code.
+ *
+ * This code design is intentional.
+ *    a. It hides the inner-workings.
+ *    b. It does not expose API ins or outs for consumption.
+ *    c. It only works with a theme's `theme.json`.
+ *
+ * Why?
+ *    a. To avoid backwards-compatibility issues when
+ *       the Webfonts API is introduced in Core.
+ *    b. To make `fontFace` declarations in `theme.json` work.
+ *
+ * @link  https://github.com/WordPress/gutenberg/issues/40472
+ *
+ * @since 6.0.0
+ * @access private
+ */
+function _wp_theme_json_webfonts_handler() {
+       // Webfonts to be processed.
+       $registered_webfonts = array();
+
+       /**
+        * Gets the webfonts from theme.json.
+        *
+        * @since 6.0.0
+        *
+        * @return array Array of defined webfonts.
+        */
+       $fn_get_webfonts_from_theme_json = static function() {
+               // Get settings from theme.json.
+               $settings = WP_Theme_JSON_Resolver::get_merged_data()->get_settings();
+
+               // If in the editor, add webfonts defined in variations.
+               if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
+                       $variations = WP_Theme_JSON_Resolver::get_style_variations();
+                       foreach ( $variations as $variation ) {
+                               // Skip if fontFamilies are not defined in the variation.
+                               if ( empty( $variation['settings']['typography']['fontFamilies'] ) ) {
+                                       continue;
+                               }
+
+                               // Initialize the array structure.
+                               if ( empty( $settings['typography'] ) ) {
+                                       $settings['typography'] = array();
+                               }
+                               if ( empty( $settings['typography']['fontFamilies'] ) ) {
+                                       $settings['typography']['fontFamilies'] = array();
+                               }
+                               if ( empty( $settings['typography']['fontFamilies']['theme'] ) ) {
+                                       $settings['typography']['fontFamilies']['theme'] = array();
+                               }
+
+                               // Combine variations with settings. Remove duplicates.
+                               $settings['typography']['fontFamilies']['theme'] = array_merge( $settings['typography']['fontFamilies']['theme'], $variation['settings']['typography']['fontFamilies']['theme'] );
+                               $settings['typography']['fontFamilies']          = array_unique( $settings['typography']['fontFamilies'] );
+                       }
+               }
+
+               // Bail out early if there are no settings for webfonts.
+               if ( empty( $settings['typography']['fontFamilies'] ) ) {
+                       return array();
+               }
+
+               $webfonts = array();
+
+               // Look for fontFamilies.
+               foreach ( $settings['typography']['fontFamilies'] as $font_families ) {
+                       foreach ( $font_families as $font_family ) {
+
+                               // Skip if fontFace is not defined.
+                               if ( empty( $font_family['fontFace'] ) ) {
+                                       continue;
+                               }
+
+                               // Skip if fontFace is not an array of webfonts.
+                               if ( ! is_array( $font_family['fontFace'] ) ) {
+                                       continue;
+                               }
+
+                               $webfonts = array_merge( $webfonts, $font_family['fontFace'] );
+                       }
+               }
+
+               return $webfonts;
+       };
+
+       /**
+        * Transforms each 'src' into an URI by replacing 'file:./'
+        * placeholder from theme.json.
+        *
+        * The absolute path to the webfont file(s) cannot be defined in
+        * theme.json. `file:./` is the placeholder which is replaced by
+        * the theme's URL path to the theme's root.
+        *
+        * @since 6.0.0
+        *
+        * @param array $src Webfont file(s) `src`.
+        * @return array Webfont's `src` in URI.
+        */
+       $fn_transform_src_into_uri = static function( array $src ) {
+               foreach ( $src as $key => $url ) {
+                       // Tweak the URL to be relative to the theme root.
+                       if ( ! str_starts_with( $url, 'file:./' ) ) {
+                               continue;
+                       }
+
+                       $src[ $key ] = get_theme_file_uri( str_replace( 'file:./', '', $url ) );
+               }
+
+               return $src;
+       };
+
+       /**
+        * Converts the font-face properties (i.e. keys) into kebab-case.
+        *
+        * @since 6.0.0
+        *
+        * @param array $font_face Font face to convert.
+        * @return array Font faces with each property in kebab-case format.
+        */
+       $fn_convert_keys_to_kebab_case = static function( array $font_face ) {
+               foreach ( $font_face as $property => $value ) {
+                       $kebab_case               = _wp_to_kebab_case( $property );
+                       $font_face[ $kebab_case ] = $value;
+                       if ( $kebab_case !== $property ) {
+                               unset( $font_face[ $property ] );
+                       }
+               }
+
+               return $font_face;
+       };
+
+       /**
+        * Validates a webfont.
+        *
+        * @since 6.0.0
+        *
+        * @param array $webfont The webfont arguments.
+        * @return array|false The validated webfont arguments, or false if the webfont is invalid.
+        */
+       $fn_validate_webfont = static function( $webfont ) {
+               $webfont = wp_parse_args(
+                       $webfont,
+                       array(
+                               'font-family'  => '',
+                               'font-style'   => 'normal',
+                               'font-weight'  => '400',
+                               'font-display' => 'fallback',
+                               'src'          => array(),
+                       )
+               );
+
+               // Check the font-family.
+               if ( empty( $webfont['font-family'] ) || ! is_string( $webfont['font-family'] ) ) {
+                       trigger_error( __( 'Webfont font family must be a non-empty string.', 'gutenberg' ) );
+
+                       return false;
+               }
+
+               // Check that the `src` property is defined and a valid type.
+               if ( empty( $webfont['src'] ) || ( ! is_string( $webfont['src'] ) && ! is_array( $webfont['src'] ) ) ) {
+                       trigger_error( __( 'Webfont src must be a non-empty string or an array of strings.', 'gutenberg' ) );
+
+                       return false;
+               }
+
+               // Validate the `src` property.
+               foreach ( (array) $webfont['src'] as $src ) {
+                       if ( ! is_string( $src ) || '' === trim( $src ) ) {
+                               trigger_error( __( 'Each webfont src must be a non-empty string.', 'gutenberg' ) );
+
+                               return false;
+                       }
+               }
+
+               // Check the font-weight.
+               if ( ! is_string( $webfont['font-weight'] ) && ! is_int( $webfont['font-weight'] ) ) {
+                       trigger_error( __( 'Webfont font weight must be a properly formatted string or integer.', 'gutenberg' ) );
+
+                       return false;
+               }
+
+               // Check the font-display.
+               if ( ! in_array( $webfont['font-display'], array( 'auto', 'block', 'fallback', 'swap' ), true ) ) {
+                       $webfont['font-display'] = 'fallback';
+               }
+
+               $valid_props = array(
+                       'ascend-override',
+                       'descend-override',
+                       'font-display',
+                       'font-family',
+                       'font-stretch',
+                       'font-style',
+                       'font-weight',
+                       'font-variant',
+                       'font-feature-settings',
+                       'font-variation-settings',
+                       'line-gap-override',
+                       'size-adjust',
+                       'src',
+                       'unicode-range',
+               );
+
+               foreach ( $webfont as $prop => $value ) {
+                       if ( ! in_array( $prop, $valid_props, true ) ) {
+                               unset( $webfont[ $prop ] );
+                       }
+               }
+
+               return $webfont;
+       };
+
+       /**
+        * Registers webfonts declared in theme.json.
+        *
+        * @since 6.0.0
+        *
+        * @uses $registered_webfonts To access and update the registered webfonts registry (passed by reference).
+        * @uses $fn_get_webfonts_from_theme_json To run the function that gets the webfonts from theme.json.
+        * @uses $fn_convert_keys_to_kebab_case To run the function that converts keys into kebab-case.
+        * @uses $fn_validate_webfont To run the function that validates each font-face (webfont) from theme.json.
+        */
+       $fn_register_webfonts = static function() use ( &$registered_webfonts, $fn_get_webfonts_from_theme_json, $fn_convert_keys_to_kebab_case, $fn_validate_webfont, $fn_transform_src_into_uri ) {
+               $registered_webfonts = array();
+
+               foreach ( $fn_get_webfonts_from_theme_json() as $webfont ) {
+                       if ( ! is_array( $webfont ) ) {
+                               continue;
+                       }
+
+                       $webfont = $fn_convert_keys_to_kebab_case( $webfont );
+
+                       $webfont = $fn_validate_webfont( $webfont );
+
+                       $webfont['src'] = $fn_transform_src_into_uri( (array) $webfont['src'] );
+
+                       // Skip if not valid.
+                       if ( empty( $webfont ) ) {
+                               continue;
+                       }
+
+                       $registered_webfonts[] = $webfont;
+               }
+       };
+
+       /**
+        * Orders 'src' items to optimize for browser support.
+        *
+        * @since 6.0.0
+        *
+        * @param array $webfont Webfont to process.
+        * @return array Ordered `src` items.
+        */
+       $fn_order_src = static function( array $webfont ) {
+               $src         = array();
+               $src_ordered = array();
+
+               foreach ( $webfont['src'] as $url ) {
+                       // Add data URIs first.
+                       if ( str_starts_with( trim( $url ), 'data:' ) ) {
+                               $src_ordered[] = array(
+                                       'url'    => $url,
+                                       'format' => 'data',
+                               );
+                               continue;
+                       }
+                       $format         = pathinfo( $url, PATHINFO_EXTENSION );
+                       $src[ $format ] = $url;
+               }
+
+               // Add woff2.
+               if ( ! empty( $src['woff2'] ) ) {
+                       $src_ordered[] = array(
+                               'url'    => sanitize_url( $src['woff2'] ),
+                               'format' => 'woff2',
+                       );
+               }
+
+               // Add woff.
+               if ( ! empty( $src['woff'] ) ) {
+                       $src_ordered[] = array(
+                               'url'    => sanitize_url( $src['woff'] ),
+                               'format' => 'woff',
+                       );
+               }
+
+               // Add ttf.
+               if ( ! empty( $src['ttf'] ) ) {
+                       $src_ordered[] = array(
+                               'url'    => sanitize_url( $src['ttf'] ),
+                               'format' => 'truetype',
+                       );
+               }
+
+               // Add eot.
+               if ( ! empty( $src['eot'] ) ) {
+                       $src_ordered[] = array(
+                               'url'    => sanitize_url( $src['eot'] ),
+                               'format' => 'embedded-opentype',
+                       );
+               }
+
+               // Add otf.
+               if ( ! empty( $src['otf'] ) ) {
+                       $src_ordered[] = array(
+                               'url'    => sanitize_url( $src['otf'] ),
+                               'format' => 'opentype',
+                       );
+               }
+               $webfont['src'] = $src_ordered;
+
+               return $webfont;
+       };
+
+       /**
+        * Compiles the 'src' into valid CSS.
+        *
+        * @since 6.0.0
+        *
+        * @param string $font_family Font family.
+        * @param array  $value       Value to process.
+        * @return string The CSS.
+        */
+       $fn_compile_src = static function( $font_family, array $value ) {
+               $src = "local($font_family)";
+
+               foreach ( $value as $item ) {
+
+                       if (
+                               str_starts_with( $item['url'], site_url() ) ||
+                               str_starts_with( $item['url'], home_url() )
+                       ) {
+                               $item['url'] = wp_make_link_relative( $item['url'] );
+                       }
+
+                       $src .= ( 'data' === $item['format'] )
+                               ? ", url({$item['url']})"
+                               : ", url('{$item['url']}') format('{$item['format']}')";
+               }
+
+               return $src;
+       };
+
+       /**
+        * Compiles the font variation settings.
+        *
+        * @since 6.0.0
+        *
+        * @param array $font_variation_settings Array of font variation settings.
+        * @return string The CSS.
+        */
+       $fn_compile_variations = static function( array $font_variation_settings ) {
+               $variations = '';
+
+               foreach ( $font_variation_settings as $key => $value ) {
+                       $variations .= "$key $value";
+               }
+
+               return $variations;
+       };
+
+       /**
+        * Builds the font-family's CSS.
+        *
+        * @since 6.0.0
+        *
+        * @uses $fn_compile_src To run the function that compiles the src.
+        * @uses $fn_compile_variations To run the function that compiles the variations.
+        *
+        * @param array $webfont Webfont to process.
+        * @return string This font-family's CSS.
+        */
+       $fn_build_font_face_css = static function( array $webfont ) use ( $fn_compile_src, $fn_compile_variations ) {
+               $css = '';
+
+               // Wrap font-family in quotes if it contains spaces.
+               if (
+                       str_contains( $webfont['font-family'], ' ' ) &&
+                       ! str_contains( $webfont['font-family'], '"' ) &&
+                       ! str_contains( $webfont['font-family'], "'" )
+               ) {
+                       $webfont['font-family'] = '"' . $webfont['font-family'] . '"';
+               }
+
+               foreach ( $webfont as $key => $value ) {
+                       /*
+                        * Skip "provider", since it's for internal API use,
+                        * and not a valid CSS property.
+                        */
+                       if ( 'provider' === $key ) {
+                               continue;
+                       }
+
+                       // Compile the "src" parameter.
+                       if ( 'src' === $key ) {
+                               $value = $fn_compile_src( $webfont['font-family'], $value );
+                       }
+
+                       // If font-variation-settings is an array, convert it to a string.
+                       if ( 'font-variation-settings' === $key && is_array( $value ) ) {
+                               $value = $fn_compile_variations( $value );
+                       }
+
+                       if ( ! empty( $value ) ) {
+                               $css .= "$key:$value;";
+                       }
+               }
+
+               return $css;
+       };
+
+       /**
+        * Gets the '@font-face' CSS styles for locally-hosted font files.
+        *
+        * @since 6.0.0
+        *
+        * @uses $registered_webfonts To access and update the registered webfonts registry (passed by reference).
+        * @uses $fn_order_src To run the function that orders the src.
+        * @uses $fn_build_font_face_css To run the function that builds the font-face CSS.
+        *
+        * @return string The `@font-face` CSS.
+        */
+       $fn_get_css = static function() use ( &$registered_webfonts, $fn_order_src, $fn_build_font_face_css ) {
+               $css = '';
+
+               foreach ( $registered_webfonts as $webfont ) {
+                       // Order the webfont's `src` items to optimize for browser support.
+                       $webfont = $fn_order_src( $webfont );
+
+                       // Build the @font-face CSS for this webfont.
+                       $css .= '@font-face{' . $fn_build_font_face_css( $webfont ) . '}';
+               }
+
+               return $css;
+       };
+
+       /**
+        * Generates and enqueues webfonts styles.
+        *
+        * @since 6.0.0
+        *
+        * @uses $fn_get_css To run the function that gets the CSS.
+        */
+       $fn_generate_and_enqueue_styles = static function() use ( $fn_get_css ) {
+               // Generate the styles.
+               $styles = $fn_get_css();
+
+               // Bail out if there are no styles to enqueue.
+               if ( '' === $styles ) {
+                       return;
+               }
+
+               // Enqueue the stylesheet.
+               wp_register_style( 'wp-webfonts', '' );
+               wp_enqueue_style( 'wp-webfonts' );
+
+               // Add the styles to the stylesheet.
+               wp_add_inline_style( 'wp-webfonts', $styles );
+       };
+
+       /**
+        * Generates and enqueues editor styles.
+        *
+        * @since 6.0.0
+        *
+        * @uses $fn_get_css To run the function that gets the CSS.
+        */
+       $fn_generate_and_enqueue_editor_styles = static function() use ( $fn_get_css ) {
+               // Generate the styles.
+               $styles = $fn_get_css();
+
+               // Bail out if there are no styles to enqueue.
+               if ( '' === $styles ) {
+                       return;
+               }
+
+               wp_add_inline_style( 'wp-block-library', $styles );
+       };
+
+       add_action( 'wp_loaded', $fn_register_webfonts );
+       add_action( 'wp_enqueue_scripts', $fn_generate_and_enqueue_styles );
+       add_action( 'admin_init', $fn_generate_and_enqueue_editor_styles );
+}
</ins></span></pre></div>
<a id="trunktestsphpunitdatathemedir1emptyfontfacethemefunctionsphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/themedir1/empty-fontface-theme/functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/empty-fontface-theme/functions.php                             (rev 0)
+++ trunk/tests/phpunit/data/themedir1/empty-fontface-theme/functions.php       2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/themedir1/empty-fontface-theme/functions.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitdatathemedir1emptyfontfacethemeindexphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/themedir1/empty-fontface-theme/index.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/empty-fontface-theme/index.php                         (rev 0)
+++ trunk/tests/phpunit/data/themedir1/empty-fontface-theme/index.php   2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,4 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Block theme.
+ */
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/themedir1/empty-fontface-theme/index.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitdatathemedir1emptyfontfacethemestylecss"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/themedir1/empty-fontface-theme/style.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/empty-fontface-theme/style.css                         (rev 0)
+++ trunk/tests/phpunit/data/themedir1/empty-fontface-theme/style.css   2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,7 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/*
+Theme Name: Empty `fontFace` in theme.json - no webfonts defined
+Theme URI: https://wordpress.org/
+Description: For testing purposes only.
+Version: 1.0.0
+Text Domain: empty-fontface-theme
+*/
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/themedir1/empty-fontface-theme/style.css
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitdatathemedir1emptyfontfacethemetemplatesindexhtml"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/themedir1/empty-fontface-theme/templates/index.html</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/empty-fontface-theme/templates/index.html                              (rev 0)
+++ trunk/tests/phpunit/data/themedir1/empty-fontface-theme/templates/index.html        2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,3 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<!-- wp:paragraph -->
+<p>Index Template</p>
+<!-- /wp:paragraph -->
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/themedir1/empty-fontface-theme/templates/index.html
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitdatathemedir1emptyfontfacethemethemejson"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/themedir1/empty-fontface-theme/theme.json</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/empty-fontface-theme/theme.json                                (rev 0)
+++ trunk/tests/phpunit/data/themedir1/empty-fontface-theme/theme.json  2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,88 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+{
+  "version": 2,
+  "customTemplates": [
+       {
+         "name": "blank",
+         "title": "Blank",
+         "postTypes": [
+               "page",
+               "post"
+         ]
+       }
+  ],
+  "settings": {
+       "appearanceTools": true,
+       "color": {
+         "duotone": [],
+         "gradients": [],
+         "palette": []
+       },
+       "custom": {},
+       "spacing": {
+         "units": [
+               "%",
+               "px",
+               "em",
+               "rem",
+               "vh",
+               "vw"
+         ]
+       },
+       "typography": {
+         "dropCap": false,
+         "fontFamilies": [
+               {
+                 "fontFamily": "Roboto",
+                 "name": "Roboto",
+                 "slug": "roboto",
+                 "fontFace": []
+               }
+         ],
+         "fontSizes": [
+               {
+                 "size": "1rem",
+                 "slug": "small"
+               },
+               {
+                 "size": "1.125rem",
+                 "slug": "medium"
+               },
+               {
+                 "size": "1.75rem",
+                 "slug": "large"
+               },
+               {
+                 "size": "clamp(1.75rem, 3vw, 2.25rem)",
+                 "slug": "x-large"
+               }
+         ]
+       },
+       "layout": {
+         "contentSize": "650px",
+         "wideSize": "1000px"
+       }
+  },
+  "styles": {
+       "blocks": {},
+       "color": {
+         "background": "var(--wp--preset--color--background)",
+         "text": "var(--wp--preset--color--foreground)"
+       },
+       "elements": {},
+       "spacing": {
+         "blockGap": "1.5rem"
+       },
+       "typography": {
+         "fontFamily": "var(--wp--preset--font-family--system-font)",
+         "lineHeight": "var(--wp--custom--typography--line-height--normal)",
+         "fontSize": "var(--wp--preset--font-size--medium)"
+       }
+  },
+  "templateParts": [
+       {
+         "name": "header",
+         "title": "Header",
+         "area": "header"
+       }
+  ]
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/themedir1/empty-fontface-theme/theme.json
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitdatathemedir1webfontsthemefunctionsphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/themedir1/webfonts-theme/functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/webfonts-theme/functions.php                           (rev 0)
+++ trunk/tests/phpunit/data/themedir1/webfonts-theme/functions.php     2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/themedir1/webfonts-theme/functions.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitdatathemedir1webfontsthemeindexphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/themedir1/webfonts-theme/index.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/webfonts-theme/index.php                               (rev 0)
+++ trunk/tests/phpunit/data/themedir1/webfonts-theme/index.php 2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,4 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Block theme.
+ */
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/themedir1/webfonts-theme/index.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitdatathemedir1webfontsthemestylecss"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/themedir1/webfonts-theme/style.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/webfonts-theme/style.css                               (rev 0)
+++ trunk/tests/phpunit/data/themedir1/webfonts-theme/style.css 2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,7 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/*
+Theme Name: Webfonts theme
+Theme URI: https://wordpress.org/
+Description: For testing purposes only.
+Version: 1.0.0
+Text Domain: webfonts-theme
+*/
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/themedir1/webfonts-theme/style.css
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitdatathemedir1webfontsthemetemplatesindexhtml"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/themedir1/webfonts-theme/templates/index.html</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/webfonts-theme/templates/index.html                            (rev 0)
+++ trunk/tests/phpunit/data/themedir1/webfonts-theme/templates/index.html      2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,3 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<!-- wp:paragraph -->
+<p>Index Template</p>
+<!-- /wp:paragraph -->
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/themedir1/webfonts-theme/templates/index.html
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitdatathemedir1webfontsthemethemejson"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/themedir1/webfonts-theme/theme.json</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/webfonts-theme/theme.json                              (rev 0)
+++ trunk/tests/phpunit/data/themedir1/webfonts-theme/theme.json        2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,103 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+{
+  "version": 2,
+  "customTemplates": [
+       {
+         "name": "blank",
+         "title": "Blank",
+         "postTypes": [
+               "page",
+               "post"
+         ]
+       }
+  ],
+  "settings": {
+       "appearanceTools": true,
+       "color": {
+         "duotone": [],
+         "gradients": [],
+         "palette": []
+       },
+       "custom": {},
+       "spacing": {
+         "units": [
+               "%",
+               "px",
+               "em",
+               "rem",
+               "vh",
+               "vw"
+         ]
+       },
+       "typography": {
+         "dropCap": false,
+         "fontFamilies": [
+               {
+                 "fontFamily": "\"Source Serif Pro\", serif",
+                 "name": "Source Serif Pro",
+                 "slug": "source-serif-pro",
+                 "fontFace": [
+                       {
+                         "fontFamily": "Source Serif Pro",
+                         "fontWeight": "200 900",
+                         "fontStyle": "normal",
+                         "fontStretch": "normal",
+                         "src": [ "file:./assets/fonts/SourceSerif4Variable-Roman.ttf.woff2" ]
+                       },
+                       {
+                         "fontFamily": "Source Serif Pro",
+                         "fontWeight": "200 900",
+                         "fontStyle": "italic",
+                         "fontStretch": "normal",
+                         "src": [ "file:./assets/fonts/SourceSerif4Variable-Italic.ttf.woff2" ]
+                       }
+                 ]
+               }
+         ],
+         "fontSizes": [
+               {
+                 "size": "1rem",
+                 "slug": "small"
+               },
+               {
+                 "size": "1.125rem",
+                 "slug": "medium"
+               },
+               {
+                 "size": "1.75rem",
+                 "slug": "large"
+               },
+               {
+                 "size": "clamp(1.75rem, 3vw, 2.25rem)",
+                 "slug": "x-large"
+               }
+         ]
+       },
+       "layout": {
+         "contentSize": "650px",
+         "wideSize": "1000px"
+       }
+  },
+  "styles": {
+       "blocks": {},
+       "color": {
+         "background": "var(--wp--preset--color--background)",
+         "text": "var(--wp--preset--color--foreground)"
+       },
+       "elements": {},
+       "spacing": {
+         "blockGap": "1.5rem"
+       },
+       "typography": {
+         "fontFamily": "var(--wp--preset--font-family--system-font)",
+         "lineHeight": "var(--wp--custom--typography--line-height--normal)",
+         "fontSize": "var(--wp--preset--font-size--medium)"
+       }
+  },
+  "templateParts": [
+       {
+         "name": "header",
+         "title": "Header",
+         "area": "header"
+       }
+  ]
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/themedir1/webfonts-theme/theme.json
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitteststhemethemeDirphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/theme/themeDir.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/theme/themeDir.php      2022-04-26 14:26:38 UTC (rev 53281)
+++ trunk/tests/phpunit/tests/theme/themeDir.php        2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -165,6 +165,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'Block Theme Child Theme',
</span><span class="cx" style="display: block; padding: 0 10px">                        'Block Theme [0.4.0]',
</span><span class="cx" style="display: block; padding: 0 10px">                        'Block Theme [1.0.0] in subdirectory',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'Webfonts theme',
+                       'Empty `fontFace` in theme.json - no webfonts defined',
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                sort( $theme_names );
</span></span></pre></div>
<a id="trunktestsphpunittestswebfontswpThemeJsonWebfontsHandlerphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/tests/webfonts/wpThemeJsonWebfontsHandler.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/webfonts/wpThemeJsonWebfontsHandler.php                         (rev 0)
+++ trunk/tests/phpunit/tests/webfonts/wpThemeJsonWebfontsHandler.php   2022-04-26 14:46:37 UTC (rev 53282)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,135 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Enqueue only webfonts listed in theme.json
+ *
+ * @package  WordPress
+ */
+
+/**
+ * Integration tests for the theme.json webfonts handler.
+ *
+ * @group webfonts
+ * @group themes
+ * @covers _wp_theme_json_webfonts_handler
+ */
+class Test_WebfontsApi_WpThemeJsonWebfontsHandler extends WP_UnitTestCase {
+
+       /**
+        * WP_Styles instance reference
+        *
+        * @var WP_Styles
+        */
+       private $orig_wp_styles;
+
+       /**
+        * Theme root path.
+        *
+        * @var string
+        */
+       private $theme_root;
+
+       /**
+        * The old theme root path.
+        *
+        * @var string
+        */
+       private $orig_theme_dir;
+
+       public function set_up() {
+               parent::set_up();
+
+               global $wp_styles;
+               $this->orig_wp_styles = $wp_styles;
+               $wp_styles            = null;
+
+               $this->theme_root     = realpath( DIR_TESTDATA . '/themedir1' );
+               $this->orig_theme_dir = $GLOBALS['wp_theme_directories'];
+
+               // /themes is necessary as theme.php functions assume /themes is the root if there is only one root.
+               $GLOBALS['wp_theme_directories'] = array( WP_CONTENT_DIR . '/themes', $this->theme_root );
+
+               $theme_root_callback = function () {
+                       return $this->theme_root;
+               };
+
+               add_filter( 'theme_root', $theme_root_callback );
+               add_filter( 'stylesheet_root', $theme_root_callback );
+               add_filter( 'template_root', $theme_root_callback );
+
+               // Clear caches.
+               wp_clean_themes_cache();
+               unset( $GLOBALS['wp_themes'] );
+       }
+
+       public function tear_down() {
+               global $wp_styles;
+               $wp_styles = $this->orig_wp_styles;
+
+               // Restore the original theme directory setup.
+               $GLOBALS['wp_theme_directories'] = $this->orig_theme_dir;
+               wp_clean_themes_cache();
+               unset( $GLOBALS['wp_themes'] );
+
+               parent::tear_down();
+       }
+
+       /**
+        * @ticket 55567
+        * @ticket 46370
+        */
+       public function test_font_face_generated_from_themejson() {
+               $this->setup_theme_and_test( 'webfonts-theme' );
+
+               $expected = <<<EOF
+<style id='wp-webfonts-inline-css' type='text/css'>
+@font-face{font-family:"Source Serif Pro";font-style:normal;font-weight:200 900;font-display:fallback;src:local("Source Serif Pro"), url('THEME_ROOT_URL/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2') format('woff2');font-stretch:normal;}@font-face{font-family:"Source Serif Pro";font-style:italic;font-weight:200 900;font-display:fallback;src:local("Source Serif Pro"), url('THEME_ROOT_URL/assets/fonts/SourceSerif4Variable-Italic.ttf.woff2') format('woff2');font-stretch:normal;}
+</style>
+EOF;
+               $expected = str_replace( 'THEME_ROOT_URL', get_stylesheet_directory_uri(), $expected );
+
+               $this->assertStringContainsString(
+                       $expected,
+                       get_echo( 'wp_print_styles' )
+               );
+       }
+
+       /**
+        * @dataProvider data_font_face_not_generated
+        *
+        * @ticket 55567
+        * @ticket 46370
+        */
+       public function test_font_face_not_generated( $theme_name ) {
+               $this->setup_theme_and_test( $theme_name );
+
+               $actual = get_echo( 'wp_print_styles' );
+               $this->assertStringNotContainsString( "<style id='wp-webfonts-inline-css", $actual );
+               $this->assertStringNotContainsString( '@font-face', $actual );
+       }
+
+       /**
+        * Data provider for unhappy paths.
+        *
+        * @return string[][]
+        */
+       public function data_font_face_not_generated() {
+               return array(
+                       'classic theme with no theme.json' => array( 'default' ),
+                       'no "fontFace" in theme.json'      => array( 'block-theme' ),
+                       'empty "fontFace" in theme.json'   => array( 'empty-fontface-theme' ),
+               );
+       }
+
+       /**
+        * Sets up the theme and test.
+        *
+        * @param string $theme_name Name of the theme to switch to for the test.
+        */
+       private function setup_theme_and_test( $theme_name ) {
+               switch_theme( $theme_name );
+               do_action( 'after_setup_theme' );
+               WP_Theme_JSON_Resolver::clean_cached_data();
+               do_action( 'wp_loaded' );
+               do_action( 'wp_enqueue_scripts' );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/webfonts/wpThemeJsonWebfontsHandler.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span></div>

</body>
</html>