<!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>[52049] trunk: Update theme.json classes for WordPress 5.9.</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/52049">52049</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/52049","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>jorgefilipecosta</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2021-11-08 19:18:39 +0000 (Mon, 08 Nov 2021)</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'>Update theme.json classes for WordPress 5.9.

This commit ports to core the changes to the classes that deal with theme.json code.

See <a href="https://core.trac.wordpress.org/ticket/54336">#54336</a>.
Props oandregal, spacedmonkey, noisysocks, hellofromtonya, youknowriad.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesblockeditorphp">trunk/src/wp-includes/block-editor.php</a></li>
<li><a href="#trunksrcwpincludesblocksupportsduotonephp">trunk/src/wp-includes/block-supports/duotone.php</a></li>
<li><a href="#trunksrcwpincludesclasswpthemejsonresolverphp">trunk/src/wp-includes/class-wp-theme-json-resolver.php</a></li>
<li><a href="#trunksrcwpincludesclasswpthemejsonphp">trunk/src/wp-includes/class-wp-theme-json.php</a></li>
<li><a href="#trunksrcwpincludesdefaultfiltersphp">trunk/src/wp-includes/default-filters.php</a></li>
<li><a href="#trunksrcwpincludesksesphp">trunk/src/wp-includes/kses.php</a></li>
<li><a href="#trunksrcwpincludesscriptloaderphp">trunk/src/wp-includes/script-loader.php</a></li>
<li><a href="#trunksrcwpincludesthemei18njson">trunk/src/wp-includes/theme-i18n.json</a></li>
<li><a href="#trunksrcwpincludesthemejson">trunk/src/wp-includes/theme.json</a></li>
<li><a href="#trunksrcwpsettingsphp">trunk/src/wp-settings.php</a></li>
<li><a href="#trunktestsphpunitdatalanguagesthemesblockthemepl_PLpo">trunk/tests/phpunit/data/languages/themes/block-theme-pl_PL.po</a></li>
<li><a href="#trunktestsphpunitdatathemedir1blockthemethemejson">trunk/tests/phpunit/data/themedir1/block-theme/theme.json</a></li>
<li><a href="#trunktestsphpunitteststhemethemeDirphp">trunk/tests/phpunit/tests/theme/themeDir.php</a></li>
<li><a href="#trunktestsphpunitteststhemewpThemeJsonphp">trunk/tests/phpunit/tests/theme/wpThemeJson.php</a></li>
<li><a href="#trunktestsphpunitteststhemewpThemeJsonResolverphp">trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesclasswpthemejsonschemaphp">trunk/src/wp-includes/class-wp-theme-json-schema.php</a></li>
<li>trunk/tests/phpunit/data/themedir1/block-theme-child/</li>
<li><a href="#trunktestsphpunitdatathemedir1blockthemechildstylecss">trunk/tests/phpunit/data/themedir1/block-theme-child/style.css</a></li>
<li><a href="#trunktestsphpunitdatathemedir1blockthemechildthemejson">trunk/tests/phpunit/data/themedir1/block-theme-child/theme.json</a></li>
<li><a href="#trunktestsphpunitteststhemewpThemeJsonSchemaphp">trunk/tests/phpunit/tests/theme/wpThemeJsonSchema.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesblockeditorphp"></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/block-editor.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/block-editor.php    2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/src/wp-includes/block-editor.php      2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -303,15 +303,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $custom_settings
</span><span class="cx" style="display: block; padding: 0 10px">        );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $theme_json = WP_Theme_JSON_Resolver::get_merged_data( $editor_settings );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $theme_json = WP_Theme_JSON_Resolver::get_merged_data();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        if ( WP_Theme_JSON_Resolver::theme_has_support() ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $editor_settings['styles'][] = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'css'            => $theme_json->get_stylesheet( 'block_styles' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'css'            => $theme_json->get_stylesheet( array( 'styles', 'presets' ) ),
</ins><span class="cx" style="display: block; padding: 0 10px">                         '__unstableType' => 'globalStyles',
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">                $editor_settings['styles'][] = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'css'                     => $theme_json->get_stylesheet( 'css_variables' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'css'                     => $theme_json->get_stylesheet( array( 'variables' ) ),
</ins><span class="cx" style="display: block; padding: 0 10px">                         '__experimentalNoWrapper' => true,
</span><span class="cx" style="display: block; padding: 0 10px">                        '__unstableType'          => 'globalStyles',
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -358,17 +358,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $editor_settings['disableCustomFontSizes'] = ! $editor_settings['__experimentalFeatures']['typography']['customFontSize'];
</span><span class="cx" style="display: block; padding: 0 10px">                unset( $editor_settings['__experimentalFeatures']['typography']['customFontSize'] );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( isset( $editor_settings['__experimentalFeatures']['typography']['customLineHeight'] ) ) {
-               $editor_settings['enableCustomLineHeight'] = $editor_settings['__experimentalFeatures']['typography']['customLineHeight'];
-               unset( $editor_settings['__experimentalFeatures']['typography']['customLineHeight'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( isset( $editor_settings['__experimentalFeatures']['typography']['lineHeight'] ) ) {
+               $editor_settings['enableCustomLineHeight'] = $editor_settings['__experimentalFeatures']['typography']['lineHeight'];
+               unset( $editor_settings['__experimentalFeatures']['typography']['lineHeight'] );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px">        if ( isset( $editor_settings['__experimentalFeatures']['spacing']['units'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $editor_settings['enableCustomUnits'] = $editor_settings['__experimentalFeatures']['spacing']['units'];
</span><span class="cx" style="display: block; padding: 0 10px">                unset( $editor_settings['__experimentalFeatures']['spacing']['units'] );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( isset( $editor_settings['__experimentalFeatures']['spacing']['customPadding'] ) ) {
-               $editor_settings['enableCustomSpacing'] = $editor_settings['__experimentalFeatures']['spacing']['customPadding'];
-               unset( $editor_settings['__experimentalFeatures']['spacing']['customPadding'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( isset( $editor_settings['__experimentalFeatures']['spacing']['padding'] ) ) {
+               $editor_settings['enableCustomSpacing'] = $editor_settings['__experimentalFeatures']['spacing']['padding'];
+               unset( $editor_settings['__experimentalFeatures']['spacing']['padding'] );
</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">        /**
</span></span></pre></div>
<a id="trunksrcwpincludesblocksupportsduotonephp"></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/block-supports/duotone.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/block-supports/duotone.php  2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/src/wp-includes/block-supports/duotone.php    2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -70,6 +70,28 @@
</span><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"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Direct port of tinycolor's boundAlpha function to maintain consistency with
+ * how tinycolor works.
+ *
+ * @see https://github.com/bgrins/TinyColor
+ *
+ * @since 5.9.0
+ * @access private
+ *
+ * @param  mixed $n   Number of unknown type.
+ * @return float      Value in the range [0,1].
+ */
+function _wp_tinycolor_bound_alpha( $n ) {
+       if ( is_numeric( $n ) ) {
+               $n = (float) $n;
+               if ( $n >= 0 && $n <= 1 ) {
+                       return $n;
+               }
+       }
+       return 1;
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * Round and convert values of an RGB object.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * Direct port of TinyColor's function, lightly simplified to maintain
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -170,8 +192,7 @@
</span><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">  * Parses hex, hsl, and rgb CSS strings using the same regex as TinyColor v1.4.2
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * used in the JavaScript. Only colors output from react-color are implemented
- * and the alpha value is ignored as it is not used in duotone.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * used in the JavaScript. Only colors output from react-color are implemented.
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * Direct port of TinyColor's function, lightly simplified to maintain
</span><span class="cx" style="display: block; padding: 0 10px">  * consistency with TinyColor.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -180,6 +201,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @see https://github.com/casesandberg/react-color/
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 5.9.0 Added alpha processing.
</ins><span class="cx" style="display: block; padding: 0 10px">  * @access private
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @param string $color_str CSS color string.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -199,7 +221,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        $rgb_regexp = '/^rgb' . $permissive_match3 . '$/';
</span><span class="cx" style="display: block; padding: 0 10px">        if ( preg_match( $rgb_regexp, $color_str, $match ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return wp_tinycolor_rgb_to_rgb(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $rgb = wp_tinycolor_rgb_to_rgb(
</ins><span class="cx" style="display: block; padding: 0 10px">                         array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'r' => $match[1],
</span><span class="cx" style="display: block; padding: 0 10px">                                'g' => $match[2],
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -206,11 +228,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'b' => $match[3],
</span><span class="cx" style="display: block; padding: 0 10px">                        )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $rgb['a'] = 1;
+
+               return $rgb;
</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">        $rgba_regexp = '/^rgba' . $permissive_match4 . '$/';
</span><span class="cx" style="display: block; padding: 0 10px">        if ( preg_match( $rgba_regexp, $color_str, $match ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return wp_tinycolor_rgb_to_rgb(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $rgb = wp_tinycolor_rgb_to_rgb(
</ins><span class="cx" style="display: block; padding: 0 10px">                         array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'r' => $match[1],
</span><span class="cx" style="display: block; padding: 0 10px">                                'g' => $match[2],
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -217,11 +243,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'b' => $match[3],
</span><span class="cx" style="display: block; padding: 0 10px">                        )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $rgb['a'] = _wp_tinycolor_bound_alpha( $match[4] );
+
+               return $rgb;
</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">        $hsl_regexp = '/^hsl' . $permissive_match3 . '$/';
</span><span class="cx" style="display: block; padding: 0 10px">        if ( preg_match( $hsl_regexp, $color_str, $match ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return wp_tinycolor_hsl_to_rgb(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $rgb = wp_tinycolor_hsl_to_rgb(
</ins><span class="cx" style="display: block; padding: 0 10px">                         array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'h' => $match[1],
</span><span class="cx" style="display: block; padding: 0 10px">                                's' => $match[2],
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -228,6 +258,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'l' => $match[3],
</span><span class="cx" style="display: block; padding: 0 10px">                        )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $rgb['a'] = 1;
+
+               return $rgb;
</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">        $hsla_regexp = '/^hsla' . $permissive_match4 . '$/';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -239,11 +273,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'l' => $match[3],
</span><span class="cx" style="display: block; padding: 0 10px">                        )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $rgb['a'] = _wp_tinycolor_bound_alpha( $match[4] );
+
+               return $rgb;
</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">        $hex8_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/';
</span><span class="cx" style="display: block; padding: 0 10px">        if ( preg_match( $hex8_regexp, $color_str, $match ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return wp_tinycolor_rgb_to_rgb(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $rgb = wp_tinycolor_rgb_to_rgb(
</ins><span class="cx" style="display: block; padding: 0 10px">                         array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'r' => base_convert( $match[1], 16, 10 ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'g' => base_convert( $match[2], 16, 10 ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -250,11 +288,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'b' => base_convert( $match[3], 16, 10 ),
</span><span class="cx" style="display: block; padding: 0 10px">                        )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $rgb['a'] = _wp_tinycolor_bound_alpha(
+                       base_convert( $match[4], 16, 10 ) / 255
+               );
+
+               return $rgb;
</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">        $hex6_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/';
</span><span class="cx" style="display: block; padding: 0 10px">        if ( preg_match( $hex6_regexp, $color_str, $match ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return wp_tinycolor_rgb_to_rgb(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $rgb = wp_tinycolor_rgb_to_rgb(
</ins><span class="cx" style="display: block; padding: 0 10px">                         array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'r' => base_convert( $match[1], 16, 10 ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'g' => base_convert( $match[2], 16, 10 ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -261,11 +305,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'b' => base_convert( $match[3], 16, 10 ),
</span><span class="cx" style="display: block; padding: 0 10px">                        )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $rgb['a'] = 1;
+
+               return $rgb;
</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">        $hex4_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/';
</span><span class="cx" style="display: block; padding: 0 10px">        if ( preg_match( $hex4_regexp, $color_str, $match ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return wp_tinycolor_rgb_to_rgb(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $rgb = wp_tinycolor_rgb_to_rgb(
</ins><span class="cx" style="display: block; padding: 0 10px">                         array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'r' => base_convert( $match[1] . $match[1], 16, 10 ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'g' => base_convert( $match[2] . $match[2], 16, 10 ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -272,11 +320,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'b' => base_convert( $match[3] . $match[3], 16, 10 ),
</span><span class="cx" style="display: block; padding: 0 10px">                        )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $rgb['a'] = _wp_tinycolor_bound_alpha(
+                       base_convert( $match[4] . $match[4], 16, 10 ) / 255
+               );
+
+               return $rgb;
</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">        $hex3_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/';
</span><span class="cx" style="display: block; padding: 0 10px">        if ( preg_match( $hex3_regexp, $color_str, $match ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return wp_tinycolor_rgb_to_rgb(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $rgb = wp_tinycolor_rgb_to_rgb(
</ins><span class="cx" style="display: block; padding: 0 10px">                         array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'r' => base_convert( $match[1] . $match[1], 16, 10 ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'g' => base_convert( $match[2] . $match[2], 16, 10 ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -283,7 +337,24 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'b' => base_convert( $match[3] . $match[3], 16, 10 ),
</span><span class="cx" style="display: block; padding: 0 10px">                        )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $rgb['a'] = 1;
+
+               return $rgb;
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /*
+        * The JS color picker considers the string "transparent" to be a hex value,
+        * so we need to handle it here as a special case.
+        */
+       if ( 'transparent' === $color_str ) {
+               return array(
+                       'r' => 0,
+                       'g' => 0,
+                       'b' => 0,
+                       'a' => 0,
+               );
+       }
</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"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -313,7 +384,96 @@
</span><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"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * Renders the duotone filter SVG and returns the CSS filter property to
+ * reference the rendered SVG.
+ *
+ * @since 5.9.0
+ *
+ * @param array $preset Duotone preset value as seen in theme.json.
+ * @return string Duotone CSS filter property.
+ */
+function wp_render_duotone_filter_preset( $preset ) {
+       $duotone_id     = $preset['slug'];
+       $duotone_colors = $preset['colors'];
+       $filter_id      = 'wp-duotone-' . $duotone_id;
+       $duotone_values = array(
+               'r' => array(),
+               'g' => array(),
+               'b' => array(),
+               'a' => array(),
+       );
+       foreach ( $duotone_colors as $color_str ) {
+               $color = wp_tinycolor_string_to_rgb( $color_str );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $duotone_values['r'][] = $color['r'] / 255;
+               $duotone_values['g'][] = $color['g'] / 255;
+               $duotone_values['b'][] = $color['b'] / 255;
+               $duotone_values['a'][] = $color['a'];
+       }
+
+       ob_start();
+
+       ?>
+
+       <svg
+               xmlns="http://www.w3.org/2000/svg"
+               viewBox="0 0 0 0"
+               width="0"
+               height="0"
+               focusable="false"
+               role="none"
+               style="visibility: hidden; position: absolute; left: -9999px; overflow: hidden;"
+       >
+               <defs>
+                       <filter id="<?php echo esc_attr( $filter_id ); ?>">
+                               <feColorMatrix
+                                       color-interpolation-filters="sRGB"
+                                       type="matrix"
+                                       values="
+                                               .299 .587 .114 0 0
+                                               .299 .587 .114 0 0
+                                               .299 .587 .114 0 0
+                                               .299 .587 .114 0 0
+                                       "
+                               />
+                               <feComponentTransfer color-interpolation-filters="sRGB" >
+                                       <feFuncR type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['r'] ) ); ?>" />
+                                       <feFuncG type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['g'] ) ); ?>" />
+                                       <feFuncB type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['b'] ) ); ?>" />
+                                       <feFuncA type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['a'] ) ); ?>" />
+                               </feComponentTransfer>
+                               <feComposite in2="SourceGraphic" operator="in" />
+                       </filter>
+               </defs>
+       </svg>
+
+       <?php
+
+       $svg = ob_get_clean();
+
+       if ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) {
+               // Clean up the whitespace.
+               $svg = preg_replace( "/[\r\n\t ]+/", ' ', $svg );
+               $svg = preg_replace( '/> </', '><', $svg );
+               $svg = trim( $svg );
+       }
+
+       add_action(
+               /*
+                * Safari doesn't render SVG filters defined in data URIs,
+                * and SVG filters won't render in the head of a document,
+                * so the next best place to put the SVG is in the footer.
+                */
+               is_admin() ? 'admin_footer' : 'wp_footer',
+               static function () use ( $svg ) {
+                       echo $svg;
+               }
+       );
+
+       return "url('#" . $filter_id . "')";
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Render out the duotone stylesheet and SVG.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpthemejsonresolverphp"></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/class-wp-theme-json-resolver.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-theme-json-resolver.php    2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/src/wp-includes/class-wp-theme-json-resolver.php      2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -11,6 +11,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * Class that abstracts the processing of the different data sources
</span><span class="cx" style="display: block; padding: 0 10px">  * for site-level config and offers an API to work with them.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * This class is for internal core usage and is not supposed to be used by extenders (plugins and/or themes).
+ * This is a low-level API that may need to do breaking changes. Please,
+ * use get_global_settings, get_global_styles, and get_global_stylesheet instead.
+ *
</ins><span class="cx" style="display: block; padding: 0 10px">  * @access private
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> class WP_Theme_JSON_Resolver {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -40,9 +44,27 @@
</span><span class="cx" style="display: block; padding: 0 10px">        private static $theme_has_support = null;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Container for data coming from the user.
+        *
+        * @since 5.9.0
+        * @var WP_Theme_JSON
+        */
+       private static $user = null;
+
+       /**
+        * Stores the ID of the custom post type
+        * that holds the user data.
+        *
+        * @since 5.9.0
+        * @var integer
+        */
+       private static $user_custom_post_type_id = null;
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Container to keep loaded i18n schema for `theme.json`.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @since 5.9.0
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @since 5.8.0
+        * @since 5.9.0 Renamed from $theme_json_i18n
</ins><span class="cx" style="display: block; padding: 0 10px">          * @var array
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        private static $i18n_schema = null;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -122,34 +144,45 @@
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Returns the theme's data.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Data from theme.json can be augmented via the $theme_support_data variable.
-        * This is useful, for example, to backfill the gaps in theme.json that a theme
-        * has declared via add_theme_supports.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Data from theme.json will be backfilled from existing
+        * theme supports, if any. Note that if the same data
+        * is present in theme.json and in theme supports,
+        * the theme.json takes precendence.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Note that if the same data is present in theme.json and in $theme_support_data,
-        * the theme.json's is not overwritten.
-        *
</del><span class="cx" style="display: block; padding: 0 10px">          * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Theme supports have been inlined and the argument removed.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param array $theme_support_data Optional. Theme support data in theme.json format.
-        *                                  Default empty array.
</del><span class="cx" style="display: block; padding: 0 10px">          * @return WP_Theme_JSON Entity that holds theme data.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public static function get_theme_data( $theme_support_data = array() ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public static function get_theme_data( $deprecated = array() ) {
+               if ( ! empty( $deprecated ) ) {
+                       _deprecated_argument( __METHOD__, '5.9' );
+               }
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( null === self::$theme ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $theme_json_data = self::read_json_file( self::get_file_path_from_theme( 'theme.json' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                        $theme_json_data = self::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                        self::$theme     = new WP_Theme_JSON( $theme_json_data );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                }
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( empty( $theme_support_data ) ) {
-                       return self::$theme;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( wp_get_theme()->parent() ) {
+                               // Get parent theme.json.
+                               $parent_theme_json_data = self::read_json_file( self::get_file_path_from_theme( 'theme.json', true ) );
+                               $parent_theme_json_data = self::translate( $parent_theme_json_data, wp_get_theme()->parent()->get( 'TextDomain' ) );
+                               $parent_theme           = new WP_Theme_JSON( $parent_theme_json_data );
+
+                               // Merge the child theme.json into the parent theme.json.
+                               // The child theme takes precedence over the parent.
+                               $parent_theme->merge( self::$theme );
+                               self::$theme = $parent_theme;
+                       }
</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">                /*
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * We want the presets and settings declared in theme.json
-                * to override the ones declared via add_theme_support.
-                */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * We want the presets and settings declared in theme.json
+               * to override the ones declared via theme supports.
+               * So we take theme supports, transform it to theme.json shape
+               * and merge the self::$theme upon that.
+               */
+               $theme_support_data  = WP_Theme_JSON::get_from_editor_settings( get_default_block_editor_settings() );
</ins><span class="cx" style="display: block; padding: 0 10px">                 $with_theme_supports = new WP_Theme_JSON( $theme_support_data );
</span><span class="cx" style="display: block; padding: 0 10px">                $with_theme_supports->merge( self::$theme );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -157,40 +190,180 @@
</span><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">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * There are different sources of data for a site: core and theme.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Returns the CPT that contains the user's origin config
+        * for the current theme or a void array if none found.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * While the getters {@link get_core_data}, {@link get_theme_data} return the raw data
-        * from the respective origins, this method merges them all together.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * It can also create and return a new draft CPT.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * If the same piece of data is declared in different origins (core and theme),
-        * the last origin overrides the previous. For example, if core disables custom colors
-        * but a theme enables them, the theme config wins.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @since 5.9.0
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @param bool  $should_create_cpt  Optional. Whether a new CPT should be created if no one was found.
+        *                                  False by default.
+        * @param array $post_status_filter Filter Optional. CPT by post status.
+        *                                   ['publish'] by default, so it only fetches published posts.
+        *
+        * @return array Custom Post Type for the user's origin config.
+        */
+       private static function get_user_data_from_custom_post_type( $should_create_cpt = false, $post_status_filter = array( 'publish' ) ) {
+               $user_cpt         = array();
+               $post_type_filter = 'wp_global_styles';
+               $query            = new WP_Query(
+                       array(
+                               'posts_per_page' => 1,
+                               'orderby'        => 'date',
+                               'order'          => 'desc',
+                               'post_type'      => $post_type_filter,
+                               'post_status'    => $post_status_filter,
+                               'tax_query'      => array(
+                                       array(
+                                               'taxonomy' => 'wp_theme',
+                                               'field'    => 'name',
+                                               'terms'    => wp_get_theme()->get_stylesheet(),
+                                       ),
+                               ),
+                       )
+               );
+
+               if ( is_array( $query->posts ) && ( 1 === $query->post_count ) ) {
+                       $user_cpt = $query->posts[0]->to_array();
+               } elseif ( $should_create_cpt ) {
+                       $cpt_post_id = wp_insert_post(
+                               array(
+                                       'post_content' => '{"version": ' . WP_Theme_JSON::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }',
+                                       'post_status'  => 'publish',
+                                       'post_title'   => __( 'Custom Styles', 'default' ),
+                                       'post_type'    => $post_type_filter,
+                                       'post_name'    => 'wp-global-styles-' . urlencode( wp_get_theme()->get_stylesheet() ),
+                                       'tax_input'    => array(
+                                               'wp_theme' => array( wp_get_theme()->get_stylesheet() ),
+                                       ),
+                               ),
+                               true
+                       );
+
+                       if ( is_wp_error( $cpt_post_id ) ) {
+                               $user_cpt = array();
+                       } else {
+                               $user_cpt = get_post( $cpt_post_id, ARRAY_A );
+                       }
+               }
+
+               return $user_cpt;
+       }
+
+       /**
+        * Returns the user's origin config.
+        *
+        * @since 5.9.0
+        *
+        * @return WP_Theme_JSON Entity that holds user data.
+        */
+       public static function get_user_data() {
+               if ( null !== self::$user ) {
+                       return self::$user;
+               }
+
+               $config   = array();
+               $user_cpt = self::get_user_data_from_custom_post_type();
+
+               if ( array_key_exists( 'post_content', $user_cpt ) ) {
+                       $decoded_data = json_decode( $user_cpt['post_content'], true );
+
+                       $json_decoding_error = json_last_error();
+                       if ( JSON_ERROR_NONE !== $json_decoding_error ) {
+                               trigger_error( 'Error when decoding a theme.json schema for user data. ' . json_last_error_msg() );
+                               return new WP_Theme_JSON( $config, 'user' );
+                       }
+
+                       // Very important to verify if the flag isGlobalStylesUserThemeJSON is true.
+                       // If is not true the content was not escaped and is not safe.
+                       if (
+                               is_array( $decoded_data ) &&
+                               isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) &&
+                               $decoded_data['isGlobalStylesUserThemeJSON']
+                       ) {
+                               unset( $decoded_data['isGlobalStylesUserThemeJSON'] );
+                               $config = $decoded_data;
+                       }
+               }
+               self::$user = new WP_Theme_JSON( $config, 'user' );
+
+               return self::$user;
+       }
+
+       /**
+        * There are three sources of data (origins) for a site:
+        * core, theme, and user. The user's has higher priority
+        * than the theme's, and the theme's higher than core's.
+        *
+        * Unlike the getters {@link get_core_data},
+        * {@link get_theme_data}, and {@link get_user_data},
+        * this method returns data after it has been merged
+        * with the previous origins. This means that if the same piece of data
+        * is declared in different origins (user, theme, and core),
+        * the last origin overrides the previous.
+        *
+        * For example, if the user has set a background color
+        * for the paragraph block, and the theme has done it as well,
+        * the user preference wins.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Add user data and change the arguments.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param array $settings Optional. Existing block editor settings. Default empty array.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param string $origin Optional. To what level should we merge data.
+        *                       Valid values are 'theme' or 'user'.
+        *                       Default is 'user'.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return WP_Theme_JSON
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public static function get_merged_data( $settings = array() ) {
-               $theme_support_data = WP_Theme_JSON::get_from_editor_settings( $settings );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public static function get_merged_data( $origin = 'user' ) {
+               if ( is_array( $origin ) ) {
+                       _deprecated_argument( __FUNCTION__, '5.9' );
+               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $result = new WP_Theme_JSON();
</span><span class="cx" style="display: block; padding: 0 10px">                $result->merge( self::get_core_data() );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $result->merge( self::get_theme_data( $theme_support_data ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $result->merge( self::get_theme_data() );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( 'user' === $origin ) {
+                       $result->merge( self::get_user_data() );
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 return $result;
</span><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">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Returns the ID of the custom post type
+        * that stores user data.
+        *
+        * @since 5.9.0
+        *
+        * @return integer|null
+        */
+       public static function get_user_custom_post_type_id() {
+               if ( null !== self::$user_custom_post_type_id ) {
+                       return self::$user_custom_post_type_id;
+               }
+
+               $user_cpt = self::get_user_data_from_custom_post_type( true );
+
+               if ( array_key_exists( 'ID', $user_cpt ) ) {
+                       self::$user_custom_post_type_id = $user_cpt['ID'];
+               }
+
+               return self::$user_custom_post_type_id;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Whether the current theme has a theme.json file.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Also check in the parent theme.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @return bool
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public static function theme_has_support() {
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! isset( self::$theme_has_support ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        self::$theme_has_support = (bool) self::get_file_path_from_theme( 'theme.json' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 self::$theme_has_support = is_readable( get_theme_file_path( 'theme.json' ) );
</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">                return self::$theme_has_support;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -202,24 +375,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * If it isn't, returns an empty string, otherwise returns the whole file path.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Adapt to work with child themes.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param string $file_name Name of the file.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @param bool   $template  Optional. Use template theme directory. Default false.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return string The whole file path or empty if the file doesn't exist.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        private static function get_file_path_from_theme( $file_name ) {
-               /*
-                * This used to be a locate_template call. However, that method proved problematic
-                * due to its use of constants (STYLESHEETPATH) that threw errors in some scenarios.
-                *
-                * When the theme.json merge algorithm properly supports child themes,
-                * this should also fall back to the template path, as locate_template did.
-                */
-               $located   = '';
-               $candidate = get_stylesheet_directory() . '/' . $file_name;
-               if ( is_readable( $candidate ) ) {
-                       $located = $candidate;
-               }
-               return $located;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private static function get_file_path_from_theme( $file_name, $template = false ) {
+               $path      = $template ? get_template_directory() : get_stylesheet_directory();
+               $candidate = $path . '/' . $file_name;
+
+               return is_readable( $candidate ) ? $candidate : '';
</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">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -226,11 +392,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * Cleans the cached data so it can be recalculated.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Added new variables to reset.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public static function clean_cached_data() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                self::$core              = null;
-               self::$theme             = null;
-               self::$theme_has_support = null;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         self::$core                     = null;
+               self::$theme                    = null;
+               self::$user                     = null;
+               self::$user_custom_post_type_id = null;
+               self::$theme_has_support        = null;
+               self::$i18n_schema              = null;
</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"> }
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpthemejsonschemaphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/src/wp-includes/class-wp-theme-json-schema.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-theme-json-schema.php                              (rev 0)
+++ trunk/src/wp-includes/class-wp-theme-json-schema.php        2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,141 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * WP_Theme_JSON_Schema class
+ *
+ * @package WordPress
+ * @subpackage Theme
+ * @since 5.9.0
+ */
+
+/**
+ * Class that migrates a given theme.json structure to the latest schema.
+ *
+ * This class is for internal core usage and is not supposed to be used by extenders (plugins and/or themes).
+ * This is a low-level API that may need to do breaking changes. Please,
+ * use get_global_settings, get_global_styles, and get_global_stylesheet instead.
+ *
+ * @access private
+ */
+class WP_Theme_JSON_Schema {
+
+       /**
+        * Maps old properties to their new location within the schema's settings.
+        * This will be applied at both the defaults and individual block levels.
+        */
+       const V1_TO_V2_RENAMED_PATHS = array(
+               'border.customRadius'         => 'border.radius',
+               'spacing.customMargin'        => 'spacing.margin',
+               'spacing.customPadding'       => 'spacing.padding',
+               'typography.customLineHeight' => 'typography.lineHeight',
+       );
+
+       /**
+        * Function that migrates a given theme.json structure to the last version.
+        *
+        * @param array $theme_json The structure to migrate.
+        *
+        * @return array The structure in the last version.
+        */
+       public static function migrate( $theme_json ) {
+               if ( ! isset( $theme_json['version'] ) ) {
+                       $theme_json = array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                       );
+               }
+
+               if ( 1 === $theme_json['version'] ) {
+                       $theme_json = self::migrate_v1_to_v2( $theme_json );
+               }
+
+               return $theme_json;
+       }
+
+       /**
+        * Removes the custom prefixes for a few properties
+        * that were part of v1:
+        *
+        * 'border.customRadius'         => 'border.radius',
+        * 'spacing.customMargin'        => 'spacing.margin',
+        * 'spacing.customPadding'       => 'spacing.padding',
+        * 'typography.customLineHeight' => 'typography.lineHeight',
+        *
+        * @param array $old Data to migrate.
+        *
+        * @return array Data without the custom prefixes.
+        */
+       private static function migrate_v1_to_v2( $old ) {
+               // Copy everything.
+               $new = $old;
+
+               // Overwrite the things that changed.
+               if ( isset( $old['settings'] ) ) {
+                       $new['settings'] = self::rename_paths( $old['settings'], self::V1_TO_V2_RENAMED_PATHS );
+               }
+
+               // Set the new version.
+               $new['version'] = 2;
+
+               return $new;
+       }
+
+       /**
+        * Processes the settings subtree.
+        *
+        * @param array $settings        Array to process.
+        * @param array $paths_to_rename Paths to rename.
+        *
+        * @return array The settings in the new format.
+        */
+       private static function rename_paths( $settings, $paths_to_rename ) {
+               $new_settings = $settings;
+
+               // Process any renamed/moved paths within default settings.
+               self::rename_settings( $new_settings, $paths_to_rename );
+
+               // Process individual block settings.
+               if ( isset( $new_settings['blocks'] ) && is_array( $new_settings['blocks'] ) ) {
+                       foreach ( $new_settings['blocks'] as &$block_settings ) {
+                               self::rename_settings( $block_settings, $paths_to_rename );
+                       }
+               }
+
+               return $new_settings;
+       }
+
+       /**
+        * Processes a settings array, renaming or moving properties.
+        *
+        * @param array $settings        Reference to settings either defaults or an individual block's.
+        * @param arary $paths_to_rename Paths to rename.
+        */
+       private static function rename_settings( &$settings, $paths_to_rename ) {
+               foreach ( $paths_to_rename as $original => $renamed ) {
+                       $original_path = explode( '.', $original );
+                       $renamed_path  = explode( '.', $renamed );
+                       $current_value = _wp_array_get( $settings, $original_path, null );
+
+                       if ( null !== $current_value ) {
+                               _wp_array_set( $settings, $renamed_path, $current_value );
+                               self::unset_setting_by_path( $settings, $original_path );
+                       }
+               }
+       }
+
+       /**
+        * Removes a property from within the provided settings by its path.
+        *
+        * @param array $settings Reference to the current settings array.
+        * @param array $path Path to the property to be removed.
+        *
+        * @return void
+        */
+       private static function unset_setting_by_path( &$settings, $path ) {
+               $tmp_settings = &$settings; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+               $last_key     = array_pop( $path );
+               foreach ( $path as $key ) {
+                       $tmp_settings = &$tmp_settings[ $key ];
+               }
+
+               unset( $tmp_settings[ $last_key ] );
+       }
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesclasswpthemejsonphp"></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/class-wp-theme-json.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-theme-json.php     2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/src/wp-includes/class-wp-theme-json.php       2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -10,6 +10,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Class that encapsulates the processing of structures that adhere to the theme.json spec.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * This class is for internal core usage and is not supposed to be used by extenders (plugins and/or themes).
+ * This is a low-level API that may need to do breaking changes. Please,
+ * use get_global_settings, get_global_styles, and get_global_stylesheet instead.
+ *
</ins><span class="cx" style="display: block; padding: 0 10px">  * @access private
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> class WP_Theme_JSON {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -70,119 +74,153 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * This contains the necessary metadata to process them:
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * - path          => where to find the preset within the settings section
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * - path       => where to find the preset within the settings section
+        * - value_key  => the key that represents the value
+        * - value_func => the callback to render the value (either value_key or value_func should be present)
+        * - css_vars   => template string to use in generating the CSS Custom Property.
+        *                 Example output: "--wp--preset--duotone--blue: <value>" will generate as many CSS Custom Properties as presets defined
+        *                 substituting the $slug for the slug's value for each preset value.
+        * - classes    => array containing a structure with the classes to generate for the presets.
+        *                 Each key is a template string to resolve similarly to the css_vars and each value is the CSS property to use for that class.
+        *                 Example output: ".has-blue-color { color: <value> }"
+        * - properties  => a list of CSS properties to be used by kses to check the preset value is safe.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * - value_key     => the key that represents the value
-        *
-        * - css_var_infix => infix to use in generating the CSS Custom Property. Example:
-        *                   --wp--preset--<preset_infix>--<slug>: <preset_value>
-        *
-        * - classes      => array containing a structure with the classes to
-        *                   generate for the presets. Each class should have
-        *                   the class suffix and the property name. Example:
-        *
-        *                   .has-<slug>-<class_suffix> {
-        *                       <property_name>: <preset_value>
-        *                   }
-        *
</del><span class="cx" style="display: block; padding: 0 10px">          * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Added new presets and simplified the metadata structure.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @var array
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        const PRESETS_METADATA = array(
</span><span class="cx" style="display: block; padding: 0 10px">                array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'path'          => array( 'color', 'palette' ),
-                       'value_key'     => 'color',
-                       'css_var_infix' => 'color',
-                       'classes'       => array(
-                               array(
-                                       'class_suffix'  => 'color',
-                                       'property_name' => 'color',
-                               ),
-                               array(
-                                       'class_suffix'  => 'background-color',
-                                       'property_name' => 'background-color',
-                               ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'path'       => array( 'color', 'palette' ),
+                       'value_key'  => 'color',
+                       'css_vars'   => '--wp--preset--color--$slug',
+                       'classes'    => array(
+                               '.has-$slug-color'            => 'color',
+                               '.has-$slug-background-color' => 'background-color',
+                               '.has-$slug-border-color'     => 'border-color',
</ins><span class="cx" style="display: block; padding: 0 10px">                         ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'properties' => array( 'color', 'background-color', 'border-color' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'path'          => array( 'color', 'gradients' ),
-                       'value_key'     => 'gradient',
-                       'css_var_infix' => 'gradient',
-                       'classes'       => array(
-                               array(
-                                       'class_suffix'  => 'gradient-background',
-                                       'property_name' => 'background',
-                               ),
-                       ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'path'       => array( 'color', 'gradients' ),
+                       'value_key'  => 'gradient',
+                       'css_vars'   => '--wp--preset--gradient--$slug',
+                       'classes'    => array( '.has-$slug-gradient-background' => 'background' ),
+                       'properties' => array( 'background' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'path'          => array( 'typography', 'fontSizes' ),
-                       'value_key'     => 'size',
-                       'css_var_infix' => 'font-size',
-                       'classes'       => array(
-                               array(
-                                       'class_suffix'  => 'font-size',
-                                       'property_name' => 'font-size',
-                               ),
-                       ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'path'       => array( 'color', 'duotone' ),
+                       'value_func' => 'wp_render_duotone_filter_preset',
+                       'css_vars'   => '--wp--preset--duotone--$slug',
+                       'classes'    => array(),
+                       'properties' => array( 'filter' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                array(
+                       'path'       => array( 'typography', 'fontSizes' ),
+                       'value_key'  => 'size',
+                       'css_vars'   => '--wp--preset--font-size--$slug',
+                       'classes'    => array( '.has-$slug-font-size' => 'font-size' ),
+                       'properties' => array( 'font-size' ),
+               ),
+               array(
+                       'path'       => array( 'typography', 'fontFamilies' ),
+                       'value_key'  => 'fontFamily',
+                       'css_vars'   => '--wp--preset--font-family--$slug',
+                       'classes'    => array( '.has-$slug-font-family' => 'font-family' ),
+                       'properties' => array( 'font-family' ),
+               ),
</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">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Metadata for style properties.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Each property declares:
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Each element is a direct mapping from the CSS property name to the
+        * path to the value in theme.json & block attributes.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * - 'value': path to the value in theme.json and block attributes.
-        *
</del><span class="cx" style="display: block; padding: 0 10px">          * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Added new properties and simplified the metadata structure.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @var array
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        const PROPERTIES_METADATA = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'background'       => array(
-                       'value' => array( 'color', 'gradient' ),
-               ),
-               'background-color' => array(
-                       'value' => array( 'color', 'background' ),
-               ),
-               'color'            => array(
-                       'value' => array( 'color', 'text' ),
-               ),
-               'font-size'        => array(
-                       'value' => array( 'typography', 'fontSize' ),
-               ),
-               'line-height'      => array(
-                       'value' => array( 'typography', 'lineHeight' ),
-               ),
-               'margin'           => array(
-                       'value'      => array( 'spacing', 'margin' ),
-                       'properties' => array( 'top', 'right', 'bottom', 'left' ),
-               ),
-               'padding'          => array(
-                       'value'      => array( 'spacing', 'padding' ),
-                       'properties' => array( 'top', 'right', 'bottom', 'left' ),
-               ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'background'                 => array( 'color', 'gradient' ),
+               'background-color'           => array( 'color', 'background' ),
+               'border-radius'              => array( 'border', 'radius' ),
+               'border-top-left-radius'     => array( 'border', 'radius', 'topLeft' ),
+               'border-top-right-radius'    => array( 'border', 'radius', 'topRight' ),
+               'border-bottom-left-radius'  => array( 'border', 'radius', 'bottomLeft' ),
+               'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ),
+               'border-color'               => array( 'border', 'color' ),
+               'border-width'               => array( 'border', 'width' ),
+               'border-style'               => array( 'border', 'style' ),
+               'color'                      => array( 'color', 'text' ),
+               'font-family'                => array( 'typography', 'fontFamily' ),
+               'font-size'                  => array( 'typography', 'fontSize' ),
+               'font-style'                 => array( 'typography', 'fontStyle' ),
+               'font-weight'                => array( 'typography', 'fontWeight' ),
+               'letter-spacing'             => array( 'typography', 'letterSpacing' ),
+               'line-height'                => array( 'typography', 'lineHeight' ),
+               'margin'                     => array( 'spacing', 'margin' ),
+               'margin-top'                 => array( 'spacing', 'margin', 'top' ),
+               'margin-right'               => array( 'spacing', 'margin', 'right' ),
+               'margin-bottom'              => array( 'spacing', 'margin', 'bottom' ),
+               'margin-left'                => array( 'spacing', 'margin', 'left' ),
+               'padding'                    => array( 'spacing', 'padding' ),
+               'padding-top'                => array( 'spacing', 'padding', 'top' ),
+               'padding-right'              => array( 'spacing', 'padding', 'right' ),
+               'padding-bottom'             => array( 'spacing', 'padding', 'bottom' ),
+               'padding-left'               => array( 'spacing', 'padding', 'left' ),
+               '--wp--style--block-gap'     => array( 'spacing', 'blockGap' ),
+               'text-decoration'            => array( 'typography', 'textDecoration' ),
+               'text-transform'             => array( 'typography', 'textTransform' ),
+               'filter'                     => array( 'filter', 'duotone' ),
</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">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Protected style properties.
+        *
+        * These style properties are only rendered if a setting enables it
+        * via a value other than `null`.
+        *
+        * Each element maps the style property to the corresponding theme.json
+        * setting key.
+        *
+        * @since 5.9.0
+        */
+       const PROTECTED_PROPERTIES = array(
+               'spacing.blockGap' => array( 'spacing', 'blockGap' ),
+       );
+
+       /**
+        * The top-level keys a theme.json can have.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Renamed from ALLOWED_TOP_LEVEL_KEYS and added new values.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @var string[]
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        const ALLOWED_TOP_LEVEL_KEYS = array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ const VALID_TOP_LEVEL_KEYS = array(
+               'customTemplates',
</ins><span class="cx" style="display: block; padding: 0 10px">                 'settings',
</span><span class="cx" style="display: block; padding: 0 10px">                'styles',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                'templateParts',
</ins><span class="cx" style="display: block; padding: 0 10px">                 'version',
</span><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">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * The valid properties under the settings key.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Renamed from ALLOWED_SETTINGS, gained new properties, and renamed others according to the new schema.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @var array
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        const ALLOWED_SETTINGS = array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ const VALID_SETTINGS = array(
</ins><span class="cx" style="display: block; padding: 0 10px">                 'border'     => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'customRadius' => null,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'color'  => null,
+                       'radius' => null,
+                       'style'  => null,
+                       'width'  => null,
</ins><span class="cx" style="display: block; padding: 0 10px">                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                'color'      => array(
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'background'     => null,
</ins><span class="cx" style="display: block; padding: 0 10px">                         'custom'         => null,
</span><span class="cx" style="display: block; padding: 0 10px">                        'customDuotone'  => null,
</span><span class="cx" style="display: block; padding: 0 10px">                        'customGradient' => null,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -190,6 +228,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'gradients'      => null,
</span><span class="cx" style="display: block; padding: 0 10px">                        'link'           => null,
</span><span class="cx" style="display: block; padding: 0 10px">                        'palette'        => null,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'text'           => null,
</ins><span class="cx" style="display: block; padding: 0 10px">                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                'custom'     => null,
</span><span class="cx" style="display: block; padding: 0 10px">                'layout'     => array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -197,25 +236,38 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'wideSize'    => null,
</span><span class="cx" style="display: block; padding: 0 10px">                ),
</span><span class="cx" style="display: block; padding: 0 10px">                'spacing'    => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'customMargin'  => null,
-                       'customPadding' => null,
-                       'units'         => null,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'blockGap' => null,
+                       'margin'   => null,
+                       'padding'  => null,
+                       'units'    => null,
</ins><span class="cx" style="display: block; padding: 0 10px">                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                'typography' => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'customFontSize'   => null,
-                       'customLineHeight' => null,
-                       'dropCap'          => null,
-                       'fontSizes'        => null,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'customFontSize' => null,
+                       'dropCap'        => null,
+                       'fontFamilies'   => null,
+                       'fontSizes'      => null,
+                       'fontStyle'      => null,
+                       'fontWeight'     => null,
+                       'letterSpacing'  => null,
+                       'lineHeight'     => null,
+                       'textDecoration' => null,
+                       'textTransform'  => null,
</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"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * The valid properties under the styles key.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Renamed from ALLOWED_SETTINGS, gained new properties.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @var array
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        const ALLOWED_STYLES = array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ const VALID_STYLES = array(
</ins><span class="cx" style="display: block; padding: 0 10px">                 'border'     => array(
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'color'  => null,
</ins><span class="cx" style="display: block; padding: 0 10px">                         'radius' => null,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'style'  => null,
+                       'width'  => null,
</ins><span class="cx" style="display: block; padding: 0 10px">                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                'color'      => array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'background' => null,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -222,23 +274,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'gradient'   => null,
</span><span class="cx" style="display: block; padding: 0 10px">                        'text'       => null,
</span><span class="cx" style="display: block; padding: 0 10px">                ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                'filter'     => array(
+                       'duotone' => null,
+               ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 'spacing'    => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'margin'  => array(
-                               'top'    => null,
-                               'right'  => null,
-                               'bottom' => null,
-                               'left'   => null,
-                       ),
-                       'padding' => array(
-                               'bottom' => null,
-                               'left'   => null,
-                               'right'  => null,
-                               'top'    => null,
-                       ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'margin'   => null,
+                       'padding'  => null,
+                       'blockGap' => null,
</ins><span class="cx" style="display: block; padding: 0 10px">                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                'typography' => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'fontSize'   => null,
-                       'lineHeight' => null,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'fontFamily'     => null,
+                       'fontSize'       => null,
+                       'fontStyle'      => null,
+                       'fontWeight'     => null,
+                       'letterSpacing'  => null,
+                       'lineHeight'     => null,
+                       'textDecoration' => null,
+                       'textTransform'  => null,
</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"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -257,10 +309,13 @@
</span><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">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * The latest version of the schema in use.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Changed value.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @var int
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        const LATEST_SCHEMA = 1;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ const LATEST_SCHEMA = 2;
</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">         * Constructor.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -276,18 +331,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $origin = 'theme';
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( ! isset( $theme_json['version'] ) || self::LATEST_SCHEMA !== $theme_json['version'] ) {
-                       $this->theme_json = array();
-                       return;
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->theme_json    = WP_Theme_JSON_Schema::migrate( $theme_json );
+               $valid_block_names   = array_keys( self::get_blocks_metadata() );
+               $valid_element_names = array_keys( self::ELEMENTS );
+               $this->theme_json    = self::sanitize( $this->theme_json, $valid_block_names, $valid_element_names );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->theme_json = self::sanitize( $theme_json );
-
</del><span class="cx" style="display: block; padding: 0 10px">                 // Internally, presets are keyed by origin.
</span><span class="cx" style="display: block; padding: 0 10px">                $nodes = self::get_setting_nodes( $this->theme_json );
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $nodes as $node ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        foreach ( self::PRESETS_METADATA as $preset ) {
-                               $path   = array_merge( $node['path'], $preset['path'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 foreach ( self::PRESETS_METADATA as $preset_metadata ) {
+                               $path   = array_merge( $node['path'], $preset_metadata['path'] );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $preset = _wp_array_get( $this->theme_json, $path, null );
</span><span class="cx" style="display: block; padding: 0 10px">                                if ( null !== $preset ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                        _wp_array_set( $this->theme_json, $path, array( $origin => $preset ) );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -300,11 +353,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * Sanitizes the input according to the schemas.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Has new parameters.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array $input Structure to sanitize.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @param array $valid_block_names List of valid block names.
+        * @param array $valid_element_names List of valid element names.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return array The sanitized output.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        private static function sanitize( $input ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private static function sanitize( $input, $valid_block_names, $valid_element_names ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $output = array();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! is_array( $input ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -311,31 +367,25 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return $output;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $allowed_top_level_keys = self::ALLOWED_TOP_LEVEL_KEYS;
-               $allowed_settings       = self::ALLOWED_SETTINGS;
-               $allowed_styles         = self::ALLOWED_STYLES;
-               $allowed_blocks         = array_keys( self::get_blocks_metadata() );
-               $allowed_elements       = array_keys( self::ELEMENTS );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $output = array_intersect_key( $input, array_flip( self::VALID_TOP_LEVEL_KEYS ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $output = array_intersect_key( $input, array_flip( $allowed_top_level_keys ) );
-
-               // Build the schema.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Build the schema based on valid block & element names.
</ins><span class="cx" style="display: block; padding: 0 10px">                 $schema                 = array();
</span><span class="cx" style="display: block; padding: 0 10px">                $schema_styles_elements = array();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                foreach ( $allowed_elements as $element ) {
-                       $schema_styles_elements[ $element ] = $allowed_styles;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         foreach ( $valid_element_names as $element ) {
+                       $schema_styles_elements[ $element ] = self::VALID_STYLES;
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">                $schema_styles_blocks   = array();
</span><span class="cx" style="display: block; padding: 0 10px">                $schema_settings_blocks = array();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                foreach ( $allowed_blocks as $block ) {
-                       $schema_settings_blocks[ $block ]           = $allowed_settings;
-                       $schema_styles_blocks[ $block ]             = $allowed_styles;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         foreach ( $valid_block_names as $block ) {
+                       $schema_settings_blocks[ $block ]           = self::VALID_SETTINGS;
+                       $schema_styles_blocks[ $block ]             = self::VALID_STYLES;
</ins><span class="cx" style="display: block; padding: 0 10px">                         $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $schema['styles']             = $allowed_styles;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $schema['styles']             = self::VALID_STYLES;
</ins><span class="cx" style="display: block; padding: 0 10px">                 $schema['styles']['blocks']   = $schema_styles_blocks;
</span><span class="cx" style="display: block; padding: 0 10px">                $schema['styles']['elements'] = $schema_styles_elements;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $schema['settings']           = $allowed_settings;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $schema['settings']           = self::VALID_SETTINGS;
</ins><span class="cx" style="display: block; padding: 0 10px">                 $schema['settings']['blocks'] = $schema_settings_blocks;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Remove anything that's not present in the schema.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -360,7 +410,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                return $output;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">         /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Returns the metadata for each block.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -377,14 +426,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *       'core/heading': {
</span><span class="cx" style="display: block; padding: 0 10px">         *         'selector': 'h1',
</span><span class="cx" style="display: block; padding: 0 10px">         *         'elements': {}
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         *       }
-        *       'core/group': {
-        *         'selector': '.wp-block-group',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  *       },
+        *       'core/image': {
+        *         'selector': '.wp-block-image',
+        *         'duotone': 'img',
</ins><span class="cx" style="display: block; padding: 0 10px">          *         'elements': {}
</span><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">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Added duotone key with CSS selector.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @return array Block metadata.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -407,11 +458,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                self::$blocks_metadata[ $block_name ]['selector'] = '.wp-block-' . str_replace( '/', '-', str_replace( 'core/', '', $block_name ) );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        /*
-                        * Assign defaults, then overwrite those that the block sets by itself.
-                        * If the block selector is compounded, will append the element to each
-                        * individual block selector.
-                        */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if (
+                               isset( $block_type->supports['color']['__experimentalDuotone'] ) &&
+                               is_string( $block_type->supports['color']['__experimentalDuotone'] )
+                       ) {
+                               self::$blocks_metadata[ $block_name ]['duotone'] = $block_type->supports['color']['__experimentalDuotone'];
+                       }
+
+                       // Assign defaults, then overwrite those that the block sets by itself.
+                       // If the block selector is compounded, will append the element to each
+                       // individual block selector.
</ins><span class="cx" style="display: block; padding: 0 10px">                         $block_selectors = explode( ',', self::$blocks_metadata[ $block_name ]['selector'] );
</span><span class="cx" style="display: block; padding: 0 10px">                        foreach ( self::ELEMENTS as $el_name => $el_selector ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $element_selector = array();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -493,28 +549,98 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * the theme.json structure this object represents.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Changed the arguments passed to the function.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param string $type Optional. Type of stylesheet we want. Accepts 'all',
-        *                     'block_styles', and 'css_variables'. Default 'all'.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param array $types    Types of styles to load. Will load all by default. It accepts:
+        *                         'variables': only the CSS Custom Properties for presets & custom ones.
+        *                         'styles': only the styles section in theme.json.
+        *                         'presets': only the classes for the presets.
+        * @param array $origins A list of origins to include. By default it includes self::VALID_ORIGINS.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return string Stylesheet.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public function get_stylesheet( $type = 'all' ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' ), $origins = self::VALID_ORIGINS ) {
+               if ( is_string( $types ) ) {
+                       // Dispatch error and map old arguments to new ones.
+                       _deprecated_argument( __FUNCTION__, '5.9' );
+                       if ( 'block_styles' === $types ) {
+                               $types = array( 'styles', 'presets' );
+                       } elseif ( 'css_variables' === $types ) {
+                               $types = array( 'variables' );
+                       } else {
+                               $types = array( 'variables', 'styles', 'presets' );
+                       }
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $blocks_metadata = self::get_blocks_metadata();
</span><span class="cx" style="display: block; padding: 0 10px">                $style_nodes     = self::get_style_nodes( $this->theme_json, $blocks_metadata );
</span><span class="cx" style="display: block; padding: 0 10px">                $setting_nodes   = self::get_setting_nodes( $this->theme_json, $blocks_metadata );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                switch ( $type ) {
-                       case 'block_styles':
-                               return $this->get_block_styles( $style_nodes, $setting_nodes );
-                       case 'css_variables':
-                               return $this->get_css_variables( $setting_nodes );
-                       default:
-                               return $this->get_css_variables( $setting_nodes ) . $this->get_block_styles( $style_nodes, $setting_nodes );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $stylesheet = '';
+
+               if ( in_array( 'variables', $types, true ) ) {
+                       $stylesheet .= $this->get_css_variables( $setting_nodes, $origins );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( in_array( 'styles', $types, true ) ) {
+                       $stylesheet .= $this->get_block_classes( $style_nodes );
+               }
+
+               if ( in_array( 'presets', $types, true ) ) {
+                       $stylesheet .= $this->get_preset_classes( $setting_nodes, $origins );
+               }
+
+               return $stylesheet;
</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">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Returns the page templates of the current theme.
+        *
+        * @since 5.9.0
+        *
+        * @return array
+        */
+       public function get_custom_templates() {
+               $custom_templates = array();
+               if ( ! isset( $this->theme_json['customTemplates'] ) ) {
+                       return $custom_templates;
+               }
+
+               foreach ( $this->theme_json['customTemplates'] as $item ) {
+                       if ( isset( $item['name'] ) ) {
+                               $custom_templates[ $item['name'] ] = array(
+                                       'title'     => isset( $item['title'] ) ? $item['title'] : '',
+                                       'postTypes' => isset( $item['postTypes'] ) ? $item['postTypes'] : array( 'page' ),
+                               );
+                       }
+               }
+               return $custom_templates;
+       }
+
+       /**
+        * Returns the template part data of current theme.
+        *
+        * @since 5.9.0
+        *
+        * @return array
+        */
+       public function get_template_parts() {
+               $template_parts = array();
+               if ( ! isset( $this->theme_json['templateParts'] ) ) {
+                       return $template_parts;
+               }
+
+               foreach ( $this->theme_json['templateParts'] as $item ) {
+                       if ( isset( $item['name'] ) ) {
+                               $template_parts[ $item['name'] ] = array(
+                                       'title' => isset( $item['title'] ) ? $item['title'] : '',
+                                       'area'  => isset( $item['area'] ) ? $item['area'] : '',
+                               );
+                       }
+               }
+               return $template_parts;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Converts each style section into a list of rulesets
</span><span class="cx" style="display: block; padding: 0 10px">         * containing the block styles to be appended to the stylesheet.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -526,37 +652,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *     style-property-one: value;
</span><span class="cx" style="display: block; padding: 0 10px">         *   }
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Additionally, it'll also create new rulesets
-        * as classes for each preset value such as:
-        *
-        *     .has-value-color {
-        *       color: value;
-        *     }
-        *
-        *     .has-value-background-color {
-        *       background-color: value;
-        *     }
-        *
-        *     .has-value-font-size {
-        *       font-size: value;
-        *     }
-        *
-        *     .has-value-gradient-background {
-        *       background: value;
-        *     }
-        *
-        *     p.has-value-gradient-background {
-        *       background: value;
-        *     }
-        *
</del><span class="cx" style="display: block; padding: 0 10px">          * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Renamed to get_block_classes and no longer returns preset classes.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param array $style_nodes   Nodes with styles.
-        * @param array $setting_nodes Nodes with settings.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param array $style_nodes Nodes with styles.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return string The new stylesheet.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        private function get_block_styles( $style_nodes, $setting_nodes ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private function get_block_classes( $style_nodes ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $block_rules = '';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 foreach ( $style_nodes as $metadata ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( null === $metadata['selector'] ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                continue;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -564,11 +668,77 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $node         = _wp_array_get( $this->theme_json, $metadata['path'], array() );
</span><span class="cx" style="display: block; padding: 0 10px">                        $selector     = $metadata['selector'];
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $declarations = self::compute_style_properties( $node );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $settings     = _wp_array_get( $this->theme_json, array( 'settings' ) );
+                       $declarations = self::compute_style_properties( $node, $settings );
+
+                       // 1. Separate the ones who use the general selector
+                       // and the ones who use the duotone selector.
+                       $declarations_duotone = array();
+                       foreach ( $declarations as $index => $declaration ) {
+                               if ( 'filter' === $declaration['name'] ) {
+                                       unset( $declarations[ $index ] );
+                                       $declarations_duotone[] = $declaration;
+                               }
+                       }
+
+                       // 2. Generate the rules that use the general selector.
</ins><span class="cx" style="display: block; padding: 0 10px">                         $block_rules .= self::to_ruleset( $selector, $declarations );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       // 3. Generate the rules that use the duotone selector.
+                       if ( isset( $metadata['duotone'] ) && ! empty( $declarations_duotone ) ) {
+                               $selector_duotone = self::scope_selector( $metadata['selector'], $metadata['duotone'] );
+                               $block_rules     .= self::to_ruleset( $selector_duotone, $declarations_duotone );
+                       }
+
+                       if ( self::ROOT_BLOCK_SELECTOR === $selector ) {
+                               $block_rules .= 'body { margin: 0; }';
+                               $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }';
+                               $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }';
+                               $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
+
+                               $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
+                               if ( $has_block_gap_support ) {
+                                       $block_rules .= '.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }';
+                                       $block_rules .= '.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }';
+                               }
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                return $block_rules;
+       }
+
+       /**
+        * Creates new rulesets as classes for each preset value such as:
+        *
+        *   .has-value-color {
+        *     color: value;
+        *   }
+        *
+        *   .has-value-background-color {
+        *     background-color: value;
+        *   }
+        *
+        *   .has-value-font-size {
+        *     font-size: value;
+        *   }
+        *
+        *   .has-value-gradient-background {
+        *     background: value;
+        *   }
+        *
+        *   p.has-value-gradient-background {
+        *     background: value;
+        *   }
+        *
+        * @since 5.9.0
+        *
+        * @param array $setting_nodes Nodes with settings.
+        * @param array $origins       List of origins to process presets from.
+        * @return string The new stylesheet.
+        */
+       private function get_preset_classes( $setting_nodes, $origins ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $preset_rules = '';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 foreach ( $setting_nodes as $metadata ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( null === $metadata['selector'] ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                continue;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -576,10 +746,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $selector      = $metadata['selector'];
</span><span class="cx" style="display: block; padding: 0 10px">                        $node          = _wp_array_get( $this->theme_json, $metadata['path'], array() );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $preset_rules .= self::compute_preset_classes( $node, $selector );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $preset_rules .= self::compute_preset_classes( $node, $selector, $origins );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return $block_rules . $preset_rules;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return $preset_rules;
</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">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -597,11 +767,13 @@
</span><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">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Added origins parameter.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array $nodes Nodes with settings.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @param array $origins List of origins to process.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return string The new stylesheet.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        private function get_css_variables( $nodes ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private function get_css_variables( $nodes, $origins ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $stylesheet = '';
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $nodes as $metadata ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( null === $metadata['selector'] ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -611,7 +783,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $selector = $metadata['selector'];
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $node         = _wp_array_get( $this->theme_json, $metadata['path'], array() );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $declarations = array_merge( self::compute_preset_vars( $node ), self::compute_theme_vars( $node ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $declarations = array_merge( self::compute_preset_vars( $node, $origins ), self::compute_theme_vars( $node ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $stylesheet .= self::to_ruleset( $selector, $declarations );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -668,45 +840,18 @@
</span><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">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Given an array of presets keyed by origin and the value key of the preset,
-        * it returns an array where each key is the preset slug and each value the preset value.
-        *
-        * @since 5.8.0
-        *
-        * @param array  $preset_per_origin Array of presets keyed by origin.
-        * @param string $value_key         The property of the preset that contains its value.
-        * @return array Array of presets where each key is a slug and each value is the preset value.
-        */
-       private static function get_merged_preset_by_slug( $preset_per_origin, $value_key ) {
-               $result = array();
-               foreach ( self::VALID_ORIGINS as $origin ) {
-                       if ( ! isset( $preset_per_origin[ $origin ] ) ) {
-                               continue;
-                       }
-                       foreach ( $preset_per_origin[ $origin ] as $preset ) {
-                               /*
-                                * We don't want to use kebabCase here,
-                                * see https://github.com/WordPress/gutenberg/issues/32347
-                                * However, we need to make sure the generated class or CSS variable
-                                * doesn't contain spaces.
-                                */
-                               $result[ preg_replace( '/\s+/', '-', $preset['slug'] ) ] = $preset[ $value_key ];
-                       }
-               }
-               return $result;
-       }
-
-       /**
</del><span class="cx" style="display: block; padding: 0 10px">          * Given a settings array, it returns the generated rulesets
</span><span class="cx" style="display: block; padding: 0 10px">         * for the preset classes.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Added origins parameter.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array  $settings Settings to process.
</span><span class="cx" style="display: block; padding: 0 10px">         * @param string $selector Selector wrapping the classes.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @param array  $origins  List of origins to process.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return string The result of processing the presets.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        private static function compute_preset_classes( $settings, $selector ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private static function compute_preset_classes( $settings, $selector, $origins ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( self::ROOT_BLOCK_SELECTOR === $selector ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        // Classes at the global level do not need any CSS prefixed,
</span><span class="cx" style="display: block; padding: 0 10px">                        // and we don't want to increase its specificity.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -714,17 +859,18 @@
</span><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">                $stylesheet = '';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                foreach ( self::PRESETS_METADATA as $preset ) {
-                       $preset_per_origin = _wp_array_get( $settings, $preset['path'], array() );
-                       $preset_by_slug    = self::get_merged_preset_by_slug( $preset_per_origin, $preset['value_key'] );
-                       foreach ( $preset['classes'] as $class ) {
-                               foreach ( $preset_by_slug as $slug => $value ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         foreach ( self::PRESETS_METADATA as $preset_metadata ) {
+                       $slugs = self::get_settings_slugs( $settings, $preset_metadata, $origins );
+                       foreach ( $preset_metadata['classes'] as $class => $property ) {
+                               foreach ( $slugs as $slug ) {
+                                       $css_var     = self::replace_slug_in_string( $preset_metadata['css_vars'], $slug );
+                                       $class_name  = self::replace_slug_in_string( $class, $slug );
</ins><span class="cx" style="display: block; padding: 0 10px">                                         $stylesheet .= self::to_ruleset(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                self::append_to_selector( $selector, '.has-' . _wp_to_kebab_case( $slug ) . '-' . $class['class_suffix'] ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         self::append_to_selector( $selector, $class_name ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                        array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                                'name'  => $class['property_name'],
-                                                               'value' => 'var(--wp--preset--' . $preset['css_var_infix'] . '--' . _wp_to_kebab_case( $slug ) . ') !important',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                         'name'  => $property,
+                                                               'value' => 'var(' . $css_var . ') !important',
</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">                                        );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -736,6 +882,147 @@
</span><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">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Function that scopes a selector with another one. This works a bit like
+        * SCSS nesting except the `&` operator isn't supported.
+        *
+        * <code>
+        * $scope = '.a, .b .c';
+        * $selector = '> .x, .y';
+        * $merged = scope_selector( $scope, $selector );
+        * // $merged is '.a > .x, .a .y, .b .c > .x, .b .c .y'
+        * </code>
+        *
+        * @since 5.9.0
+        *
+        * @param string $scope    Selector to scope to.
+        * @param string $selector Original selector.
+        *
+        * @return string Scoped selector.
+        */
+       private static function scope_selector( $scope, $selector ) {
+               $scopes    = explode( ',', $scope );
+               $selectors = explode( ',', $selector );
+
+               $selectors_scoped = array();
+               foreach ( $scopes as $outer ) {
+                       foreach ( $selectors as $inner ) {
+                               $selectors_scoped[] = trim( $outer ) . ' ' . trim( $inner );
+                       }
+               }
+
+               return implode( ', ', $selectors_scoped );
+       }
+
+       /**
+        * Gets preset values keyed by slugs based on settings and metadata.
+        *
+        * <code>
+        * $settings = array(
+        *     'typography' => array(
+        *         'fontFamilies' => array(
+        *             array(
+        *                 'slug'       => 'sansSerif',
+        *                 'fontFamily' => '"Helvetica Neue", sans-serif',
+        *             ),
+        *             array(
+        *                 'slug'   => 'serif',
+        *                 'colors' => 'Georgia, serif',
+        *             )
+        *         ),
+        *     ),
+        * );
+        * $meta = array(
+        *    'path'      => array( 'typography', 'fontFamilies' ),
+        *    'value_key' => 'fontFamily',
+        * );
+        * $values_by_slug = get_settings_values_by_slug();
+        * // $values_by_slug === array(
+        * //   'sans-serif' => '"Helvetica Neue", sans-serif',
+        * //   'serif'      => 'Georgia, serif',
+        * // );
+        * </code>
+        *
+        * @since 5.9.0
+        *
+        * @param array $settings Settings to process.
+        * @param array $preset_metadata One of the PRESETS_METADATA values.
+        * @param array $origins List of origins to process.
+        * @return array Array of presets where each key is a slug and each value is the preset value.
+        */
+       private static function get_settings_values_by_slug( $settings, $preset_metadata, $origins ) {
+               $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() );
+
+               $result = array();
+               foreach ( $origins as $origin ) {
+                       if ( ! isset( $preset_per_origin[ $origin ] ) ) {
+                               continue;
+                       }
+                       foreach ( $preset_per_origin[ $origin ] as $preset ) {
+                               $slug = _wp_to_kebab_case( $preset['slug'] );
+
+                               $value = '';
+                               if ( isset( $preset_metadata['value_key'] ) ) {
+                                       $value_key = $preset_metadata['value_key'];
+                                       $value     = $preset[ $value_key ];
+                               } elseif (
+                                       isset( $preset_metadata['value_func'] ) &&
+                                       is_callable( $preset_metadata['value_func'] )
+                               ) {
+                                       $value_func = $preset_metadata['value_func'];
+                                       $value      = call_user_func( $value_func, $preset );
+                               } else {
+                                       // If we don't have a value, then don't add it to the result.
+                                       continue;
+                               }
+
+                               $result[ $slug ] = $value;
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Similar to get_settings_values_by_slug, but doesn't compute the value.
+        *
+        * @since 5.9.0
+        *
+        * @param array $settings Settings to process.
+        * @param array $preset_metadata One of the PRESETS_METADATA values.
+        * @param array $origins List of origins to process.
+        * @return array Array of presets where the key and value are both the slug.
+        */
+       private static function get_settings_slugs( $settings, $preset_metadata, $origins = self::VALID_ORIGINS ) {
+               $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() );
+
+               $result = array();
+               foreach ( $origins as $origin ) {
+                       if ( ! isset( $preset_per_origin[ $origin ] ) ) {
+                               continue;
+                       }
+                       foreach ( $preset_per_origin[ $origin ] as $preset ) {
+                               $slug = _wp_to_kebab_case( $preset['slug'] );
+
+                               // Use the array as a set so we don't get duplicates.
+                               $result[ $slug ] = $slug;
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Transform a slug into a CSS Custom Property.
+        *
+        * @since 5.9.0
+        *
+        * @param string $input String to replace.
+        * @param string $slug The slug value to use to generate the custom property.
+        * @return string The CSS Custom Property. Something along the lines of --wp--preset--color--black.
+        */
+       private static function replace_slug_in_string( $input, $slug ) {
+               return strtr( $input, array( '$slug' => $slug ) );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Given the block settings, it extracts the CSS Custom Properties
</span><span class="cx" style="display: block; padding: 0 10px">         * for the presets and adds them to the $declarations array
</span><span class="cx" style="display: block; padding: 0 10px">         * following the format:
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -748,16 +1035,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array $settings Settings to process.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @param array $origins  List of origins to process.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return array Returns the modified $declarations.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        private static function compute_preset_vars( $settings ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private static function compute_preset_vars( $settings, $origins ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $declarations = array();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                foreach ( self::PRESETS_METADATA as $preset ) {
-                       $preset_per_origin = _wp_array_get( $settings, $preset['path'], array() );
-                       $preset_by_slug    = self::get_merged_preset_by_slug( $preset_per_origin, $preset['value_key'] );
-                       foreach ( $preset_by_slug as $slug => $value ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         foreach ( self::PRESETS_METADATA as $preset_metadata ) {
+                       $values_by_slug = self::get_settings_values_by_slug( $settings, $preset_metadata, $origins );
+                       foreach ( $values_by_slug as $slug => $value ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $declarations[] = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'name'  => '--wp--preset--' . $preset['css_var_infix'] . '--' . _wp_to_kebab_case( $slug ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'name'  => self::replace_slug_in_string( $preset_metadata['css_vars'], $slug ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                         'value' => $value,
</span><span class="cx" style="display: block; padding: 0 10px">                                );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -864,46 +1151,42 @@
</span><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">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Added theme setting and properties parameters.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array $styles Styles to process.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @param array $settings Theme settings.
+        * @param array $properties Properties metadata.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return array Returns the modified $declarations.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        private static function compute_style_properties( $styles ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private static function compute_style_properties( $styles, $settings = array(), $properties = self::PROPERTIES_METADATA ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $declarations = array();
</span><span class="cx" style="display: block; padding: 0 10px">                if ( empty( $styles ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        return $declarations;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $properties = array();
-               foreach ( self::PROPERTIES_METADATA as $name => $metadata ) {
-                       /*
-                        * Some properties can be shorthand properties, meaning that
-                        * they contain multiple values instead of a single one.
-                        * An example of this is the padding property.
-                        */
-                       if ( self::has_properties( $metadata ) ) {
-                               foreach ( $metadata['properties'] as $property ) {
-                                       $properties[] = array(
-                                               'name'  => $name . '-' . $property,
-                                               'value' => array_merge( $metadata['value'], array( $property ) ),
-                                       );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         foreach ( $properties as $css_property => $value_path ) {
+                       $value = self::get_property_value( $styles, $value_path );
+
+                       // Look up protected properties, keyed by value path.
+                       // Skip protected properties that are explicitly set to `null`.
+                       if ( is_array( $value_path ) ) {
+                               $path_string = implode( '.', $value_path );
+                               if (
+                                       array_key_exists( $path_string, self::PROTECTED_PROPERTIES ) &&
+                                       _wp_array_get( $settings, self::PROTECTED_PROPERTIES[ $path_string ], null ) === null
+                               ) {
+                                       continue;
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        } else {
-                               $properties[] = array(
-                                       'name'  => $name,
-                                       'value' => $metadata['value'],
-                               );
</del><span class="cx" style="display: block; padding: 0 10px">                         }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                }
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                foreach ( $properties as $prop ) {
-                       $value = self::get_property_value( $styles, $prop['value'] );
-                       if ( empty( $value ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 // Skip if empty and not "0" or value represents array of longhand values.
+                       $has_missing_value = empty( $value ) && ! is_numeric( $value );
+                       if ( $has_missing_value || is_array( $value ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 continue;
</span><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">                        $declarations[] = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'name'  => $prop['name'],
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'name'  => $css_property,
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'value' => $value,
</span><span class="cx" style="display: block; padding: 0 10px">                        );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -912,22 +1195,6 @@
</span><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">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Whether the metadata contains a key named properties.
-        *
-        * @since 5.8.0
-        *
-        * @param array $metadata Description of the style property.
-        * @return bool True if properties exists, false otherwise.
-        */
-       private static function has_properties( $metadata ) {
-               if ( array_key_exists( 'properties', $metadata ) ) {
-                       return true;
-               }
-
-               return false;
-       }
-
-       /**
</del><span class="cx" style="display: block; padding: 0 10px">          * Returns the style property for the given path.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * It also converts CSS Custom Property stored as
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -935,6 +1202,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * "--wp--preset--color--secondary".
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Consider $value that are arrays as well.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array $styles Styles subtree.
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array $path   Which property to process.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -943,7 +1211,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        private static function get_property_value( $styles, $path ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $value = _wp_array_get( $styles, $path, '' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( '' === $value ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( '' === $value || is_array( $value ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return $value;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1015,7 +1283,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                return $nodes;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">         /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Builds metadata for the style nodes, which returns in the form of:
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1022,11 +1289,13 @@
</span><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">         *         'path'     => [ 'path', 'to', 'some', 'node' ],
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         *         'selector' => 'CSS selector for some node'
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  *         'selector' => 'CSS selector for some node',
+        *         'duotone'  => 'CSS selector for duotone for some node'
</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">         *         'path'     => ['path', 'to', 'other', 'node' ],
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         *         'selector' => 'CSS selector for other node'
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  *         'selector' => 'CSS selector for other node',
+        *         'duotone'  => null
</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">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1068,9 +1337,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $selector = $selectors[ $name ]['selector'];
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        $duotone_selector = null;
+                       if ( isset( $selectors[ $name ]['duotone'] ) ) {
+                               $duotone_selector = $selectors[ $name ]['duotone'];
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $nodes[] = array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'path'     => array( 'styles', 'blocks', $name ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'selector' => $selector,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                'duotone'  => $duotone_selector,
</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">                        if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1090,6 +1365,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * Merge new incoming data.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.9.0 Duotone preset also has origins.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param WP_Theme_JSON $incoming Data to merge.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1098,14 +1374,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->theme_json = array_replace_recursive( $this->theme_json, $incoming_data );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /*
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * The array_replace_recursive() algorithm merges at the leaf level.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+          * The array_replace_recursive algorithm merges at the leaf level.
</ins><span class="cx" style="display: block; padding: 0 10px">                  * For leaf values that are arrays it will use the numeric indexes for replacement.
</span><span class="cx" style="display: block; padding: 0 10px">                 * In those cases, we want to replace the existing with the incoming value, if it exists.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                $to_replace   = array();
</span><span class="cx" style="display: block; padding: 0 10px">                $to_replace[] = array( 'spacing', 'units' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $to_replace[] = array( 'color', 'duotone' );
</del><span class="cx" style="display: block; padding: 0 10px">                 foreach ( self::VALID_ORIGINS as $origin ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        $to_replace[] = array( 'color', 'duotone', $origin );
</ins><span class="cx" style="display: block; padding: 0 10px">                         $to_replace[] = array( 'color', 'palette', $origin );
</span><span class="cx" style="display: block; padding: 0 10px">                        $to_replace[] = array( 'color', 'gradients', $origin );
</span><span class="cx" style="display: block; padding: 0 10px">                        $to_replace[] = array( 'typography', 'fontSizes', $origin );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1122,9 +1398,167 @@
</span><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">                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</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">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Removes insecure data from theme.json.
+        *
+        * @since 5.9.0
+        *
+        * @param array $theme_json Structure to sanitize.
+        * @return array Sanitized structure.
+        */
+       public static function remove_insecure_properties( $theme_json ) {
+               $sanitized = array();
+
+               $theme_json = WP_Theme_JSON_Schema::migrate( $theme_json );
+
+               $valid_block_names   = array_keys( self::get_blocks_metadata() );
+               $valid_element_names = array_keys( self::ELEMENTS );
+               $theme_json          = self::sanitize( $theme_json, $valid_block_names, $valid_element_names );
+
+               $blocks_metadata = self::get_blocks_metadata();
+               $style_nodes     = self::get_style_nodes( $theme_json, $blocks_metadata );
+               foreach ( $style_nodes as $metadata ) {
+                       $input = _wp_array_get( $theme_json, $metadata['path'], array() );
+                       if ( empty( $input ) ) {
+                               continue;
+                       }
+
+                       $output = self::remove_insecure_styles( $input );
+                       if ( ! empty( $output ) ) {
+                               _wp_array_set( $sanitized, $metadata['path'], $output );
+                       }
+               }
+
+               $setting_nodes = self::get_setting_nodes( $theme_json );
+               foreach ( $setting_nodes as $metadata ) {
+                       $input = _wp_array_get( $theme_json, $metadata['path'], array() );
+                       if ( empty( $input ) ) {
+                               continue;
+                       }
+
+                       $output = self::remove_insecure_settings( $input );
+                       if ( ! empty( $output ) ) {
+                               _wp_array_set( $sanitized, $metadata['path'], $output );
+                       }
+               }
+
+               if ( empty( $sanitized['styles'] ) ) {
+                       unset( $theme_json['styles'] );
+               } else {
+                       $theme_json['styles'] = $sanitized['styles'];
+               }
+
+               if ( empty( $sanitized['settings'] ) ) {
+                       unset( $theme_json['settings'] );
+               } else {
+                       $theme_json['settings'] = $sanitized['settings'];
+               }
+
+               return $theme_json;
+       }
+
+       /**
+        * Processes a setting node and returns the same node
+        * without the insecure settings.
+        *
+        * @since 5.9.0
+        *
+        * @param array $input Node to process.
+        * @return array
+        */
+       private static function remove_insecure_settings( $input ) {
+               $output = array();
+               foreach ( self::PRESETS_METADATA as $preset_metadata ) {
+                       $presets = _wp_array_get( $input, $preset_metadata['path'], null );
+                       if ( null === $presets ) {
+                               continue;
+                       }
+
+                       $escaped_preset = array();
+                       foreach ( $presets as $preset ) {
+                               if (
+                                       esc_attr( esc_html( $preset['name'] ) ) === $preset['name'] &&
+                                       sanitize_html_class( $preset['slug'] ) === $preset['slug']
+                               ) {
+                                       $value = null;
+                                       if ( isset( $preset_metadata['value_key'] ) ) {
+                                               $value = $preset[ $preset_metadata['value_key'] ];
+                                       } elseif (
+                                               isset( $preset_metadata['value_func'] ) &&
+                                               is_callable( $preset_metadata['value_func'] )
+                                       ) {
+                                               $value = call_user_func( $preset_metadata['value_func'], $preset );
+                                       }
+
+                                       $preset_is_valid = true;
+                                       foreach ( $preset_metadata['properties'] as $property ) {
+                                               if ( ! self::is_safe_css_declaration( $property, $value ) ) {
+                                                       $preset_is_valid = false;
+                                                       break;
+                                               }
+                                       }
+
+                                       if ( $preset_is_valid ) {
+                                               $escaped_preset[] = $preset;
+                                       }
+                               }
+                       }
+
+                       if ( ! empty( $escaped_preset ) ) {
+                               _wp_array_set( $output, $preset_metadata['path'], $escaped_preset );
+                       }
+               }
+
+               return $output;
+       }
+
+       /**
+        * Processes a style node and returns the same node
+        * without the insecure styles.
+        *
+        * @since 5.9.0
+        *
+        * @param array $input Node to process.
+        * @return array
+        */
+       private static function remove_insecure_styles( $input ) {
+               $output       = array();
+               $declarations = self::compute_style_properties( $input );
+
+               foreach ( $declarations as $declaration ) {
+                       if ( self::is_safe_css_declaration( $declaration['name'], $declaration['value'] ) ) {
+                               $path = self::PROPERTIES_METADATA[ $declaration['name'] ];
+
+                               // Check the value isn't an array before adding so as to not
+                               // double up shorthand and longhand styles.
+                               $value = _wp_array_get( $input, $path, array() );
+                               if ( ! is_array( $value ) ) {
+                                       _wp_array_set( $output, $path, $value );
+                               }
+                       }
+               }
+               return $output;
+       }
+
+       /**
+        * Checks that a declaration provided by the user is safe.
+        *
+        * @since 5.9.0
+        *
+        * @param string $property_name Property name in a CSS declaration, i.e. the `color` in `color: red`.
+        * @param string $property_value Value in a CSS declaration, i.e. the `red` in `color: red`.
+        * @return boolean
+        */
+       private static function is_safe_css_declaration( $property_name, $property_value ) {
+               $style_to_validate = $property_name . ': ' . $property_value;
+               $filtered          = esc_html( safecss_filter_attr( $style_to_validate ) );
+               return ! empty( trim( $filtered ) );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Returns the raw data.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1176,7 +1610,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! isset( $theme_settings['settings']['typography'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $theme_settings['settings']['typography'] = array();
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $theme_settings['settings']['typography']['customLineHeight'] = $settings['enableCustomLineHeight'];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $theme_settings['settings']['typography']['lineHeight'] = $settings['enableCustomLineHeight'];
</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">                if ( isset( $settings['enableCustomUnits'] ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1220,7 +1654,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! isset( $theme_settings['settings']['spacing'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $theme_settings['settings']['spacing'] = array();
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $theme_settings['settings']['spacing']['customPadding'] = $settings['enableCustomSpacing'];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $theme_settings['settings']['spacing']['padding'] = $settings['enableCustomSpacing'];
</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">                return $theme_settings;
</span></span></pre></div>
<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 2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/src/wp-includes/default-filters.php   2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -336,6 +336,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'init', 'check_theme_switched', 99 );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'init', array( 'WP_Block_Supports', 'init' ), 22 );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'switch_theme', array( 'WP_Theme_JSON_Resolver', 'clean_cached_data' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+add_action( 'start_previewing_theme', array( 'WP_Theme_JSON_Resolver', 'clean_cached_data' ) );
</ins><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></span></pre></div>
<a id="trunksrcwpincludesksesphp"></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/kses.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/kses.php    2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/src/wp-includes/kses.php      2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2260,6 +2260,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'border-bottom-color',
</span><span class="cx" style="display: block; padding: 0 10px">                        'border-bottom-style',
</span><span class="cx" style="display: block; padding: 0 10px">                        'border-bottom-width',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'border-bottom-right-radius',
+                       'border-bottom-left-radius',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'border-left',
</span><span class="cx" style="display: block; padding: 0 10px">                        'border-left-color',
</span><span class="cx" style="display: block; padding: 0 10px">                        'border-left-style',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2268,6 +2270,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'border-top-color',
</span><span class="cx" style="display: block; padding: 0 10px">                        'border-top-style',
</span><span class="cx" style="display: block; padding: 0 10px">                        'border-top-width',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'border-top-left-radius',
+                       'border-top-right-radius',
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        'border-spacing',
</span><span class="cx" style="display: block; padding: 0 10px">                        'border-collapse',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2282,6 +2286,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'column-width',
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        'color',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'filter',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'font',
</span><span class="cx" style="display: block; padding: 0 10px">                        'font-family',
</span><span class="cx" style="display: block; padding: 0 10px">                        'font-size',
</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   2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/src/wp-includes/script-loader.php     2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2321,8 +2321,7 @@
</span><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">        if ( null === $stylesheet ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $settings   = get_default_block_editor_settings();
-               $theme_json = WP_Theme_JSON_Resolver::get_merged_data( $settings );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $theme_json = WP_Theme_JSON_Resolver::get_merged_data();
</ins><span class="cx" style="display: block; padding: 0 10px">                 $stylesheet = $theme_json->get_stylesheet();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $can_use_cache ) {
</span></span></pre></div>
<a id="trunksrcwpincludesthemei18njson"></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/theme-i18n.json</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/theme-i18n.json     2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/src/wp-includes/theme-i18n.json       2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5,6 +5,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        {
</span><span class="cx" style="display: block; padding: 0 10px">                                                "name": "Font size name"
</span><span class="cx" style="display: block; padding: 0 10px">                                        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                ],
+                               "fontFamilies": [
+                                       {
+                                               "name": "Font family name"
+                                       }
</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">                "color": {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -31,6 +36,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                {
</span><span class="cx" style="display: block; padding: 0 10px">                                                        "name": "Font size name"
</span><span class="cx" style="display: block; padding: 0 10px">                                                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        ],
+                                       "fontFamilies": [
+                                               {
+                                                       "name": "Font family name"
+                                               }
</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">                                "color": {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -47,5 +57,15 @@
</span><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">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ },
+       "customTemplates": [
+               {
+                       "title": "Custom template name"
+               }
+       ],
+       "templateParts": [
+               {
+                       "title": "Template part name"
+               }
+       ]
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunksrcwpincludesthemejson"></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/theme.json</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/theme.json  2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/src/wp-includes/theme.json    2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,8 +1,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "version": 1,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ "version": 2,
</ins><span class="cx" style="display: block; padding: 0 10px">         "settings": {
</span><span class="cx" style="display: block; padding: 0 10px">                "border": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        "customRadius": false
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 "color": false,
+                       "radius": false,
+                       "style": false,
+                       "width": false
</ins><span class="cx" style="display: block; padding: 0 10px">                 },
</span><span class="cx" style="display: block; padding: 0 10px">                "color": {
</span><span class="cx" style="display: block; padding: 0 10px">                        "custom": true,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -9,6 +12,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        "customDuotone": true,
</span><span class="cx" style="display: block; padding: 0 10px">                        "customGradient": true,
</span><span class="cx" style="display: block; padding: 0 10px">                        "link": false,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        "background": true,
+                       "text": true,
</ins><span class="cx" style="display: block; padding: 0 10px">                         "duotone": [
</span><span class="cx" style="display: block; padding: 0 10px">                                {
</span><span class="cx" style="display: block; padding: 0 10px">                                        "name":  "Dark grayscale" ,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -177,14 +182,20 @@
</span><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">                "spacing": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        "customMargin": false,
-                       "customPadding": false,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 "blockGap": null,
+                       "margin": false,
+                       "padding": false,
</ins><span class="cx" style="display: block; padding: 0 10px">                         "units": [ "px", "em", "rem", "vh", "vw", "%" ]
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="cx" style="display: block; padding: 0 10px">                "typography": {
</span><span class="cx" style="display: block; padding: 0 10px">                        "customFontSize": true,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        "customLineHeight": false,
</del><span class="cx" style="display: block; padding: 0 10px">                         "dropCap": true,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        "fontStyle": true,
+                       "fontWeight": true,
+                       "letterSpacing": true,
+                       "lineHeight": false,
+                       "textDecoration": true,
+                       "textTransform": true,
</ins><span class="cx" style="display: block; padding: 0 10px">                         "fontSizes": [
</span><span class="cx" style="display: block; padding: 0 10px">                                {
</span><span class="cx" style="display: block; padding: 0 10px">                                        "name": "Small",
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -216,9 +227,20 @@
</span><span class="cx" style="display: block; padding: 0 10px">                "blocks": {
</span><span class="cx" style="display: block; padding: 0 10px">                        "core/button": {
</span><span class="cx" style="display: block; padding: 0 10px">                                "border": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        "customRadius": true
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 "radius": true
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        },
+                       "core/pullquote": {
+                               "border": {
+                                       "color": true,
+                                       "radius": true,
+                                       "style": true,
+                                       "width": true
+                               }
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        },
+       "styles": {
+               "spacing": { "blockGap": "24px" }
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunksrcwpsettingsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-settings.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-settings.php 2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/src/wp-settings.php   2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -170,6 +170,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/class-wp-date-query.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/theme.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/class-wp-theme.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require ABSPATH . WPINC . '/class-wp-theme-json-schema.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/class-wp-theme-json.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/class-wp-theme-json-resolver.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/class-wp-block-template.php';
</span></span></pre></div>
<a id="trunktestsphpunitdatalanguagesthemesblockthemepl_PLpo"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/data/languages/themes/block-theme-pl_PL.po</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/languages/themes/block-theme-pl_PL.po    2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/tests/phpunit/data/languages/themes/block-theme-pl_PL.po      2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -22,6 +22,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> msgid "Homepage template"
</span><span class="cx" style="display: block; padding: 0 10px"> msgstr "Szablon strony głównej"
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+msgctxt "Template part name"
+msgid "Small Header"
+msgstr "MaÅ‚y nagłówek"
+
</ins><span class="cx" style="display: block; padding: 0 10px"> msgctxt "Color name"
</span><span class="cx" style="display: block; padding: 0 10px"> msgid "Light"
</span><span class="cx" style="display: block; padding: 0 10px"> msgstr "Jasny"
</span></span></pre></div>
<a id="trunktestsphpunitdatathemedir1blockthemethemejson"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/data/themedir1/block-theme/theme.json</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/block-theme/theme.json 2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/tests/phpunit/data/themedir1/block-theme/theme.json   2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -64,6 +64,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        "templateParts": [
</span><span class="cx" style="display: block; padding: 0 10px">                {
</span><span class="cx" style="display: block; padding: 0 10px">                        "name": "small-header",
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        "title": "Small Header",
</ins><span class="cx" style="display: block; padding: 0 10px">                         "area": "header"
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">        ]
</span></span></pre></div>
<a id="trunktestsphpunitdatathemedir1blockthemechildstylecss"></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/block-theme-child/style.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/block-theme-child/style.css                            (rev 0)
+++ trunk/tests/phpunit/data/themedir1/block-theme-child/style.css      2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,8 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/*
+Theme Name: Block Theme Child Theme
+Theme URI: https://wordpress.org/
+Description: For testing purposes only.
+Template: block-theme
+Version: 1.0.0
+Text Domain: block-theme-child
+*/
</ins></span></pre></div>
<a id="trunktestsphpunitdatathemedir1blockthemechildthemejson"></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/block-theme-child/theme.json</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/data/themedir1/block-theme-child/theme.json                           (rev 0)
+++ trunk/tests/phpunit/data/themedir1/block-theme-child/theme.json     2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,51 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+{
+       "version": 1,
+       "settings": {
+               "color": {
+                       "palette": [
+                               {
+                                       "slug": "light",
+                                       "name": "Light",
+                                       "color": "#f3f4f6"
+                               },
+                               {
+                                       "slug": "primary",
+                                       "name": "Primary",
+                                       "color": "#3858e9"
+                               },
+                               {
+                                       "slug": "dark",
+                                       "name": "Dark",
+                                       "color": "#111827"
+                               }
+                       ],
+                       "link": true
+               },
+               "blocks": {
+                       "core/post-title": {
+                               "color": {
+                                       "palette": [
+                                               {
+                                                       "slug": "light",
+                                                       "name": "Light",
+                                                       "color": "#f3f4f6"
+                                               }
+                                       ]
+                               }
+                       }
+               }
+       },
+       "customTemplates": [
+               {
+                       "name": "page-home",
+                       "title": "Homepage"
+               }
+       ],
+       "templateParts": [
+               {
+                       "name": "small-header",
+                       "title": "Small Header",
+                       "area": "header"
+               }
+       ]
+}
</ins></span></pre></div>
<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      2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/tests/phpunit/tests/theme/themeDir.php        2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -162,6 +162,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'camelCase',
</span><span class="cx" style="display: block; padding: 0 10px">                        'REST Theme',
</span><span class="cx" style="display: block; padding: 0 10px">                        'Block Theme',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'Block Theme Child Theme',
</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="trunktestsphpunitteststhemewpThemeJsonphp"></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/wpThemeJson.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/theme/wpThemeJson.php   2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/tests/phpunit/tests/theme/wpThemeJson.php     2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -15,6 +15,7 @@
</span><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">         * @ticket 52991
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_settings() {
</span><span class="cx" style="display: block; padding: 0 10px">                $theme_json = new WP_Theme_JSON(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -24,6 +25,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        'color'       => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                'custom' => false,
</span><span class="cx" style="display: block; padding: 0 10px">                                        ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        'layout'      => array(
+                                               'contentSize' => 'value',
+                                               'invalid/key' => 'value',
+                                       ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                         'invalid/key' => 'value',
</span><span class="cx" style="display: block; padding: 0 10px">                                        'blocks'      => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                'core/group' => array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -52,6 +57,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'color'  => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'custom' => false,
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'layout' => array(
+                               'contentSize' => 'value',
+                       ),
</ins><span class="cx" style="display: block; padding: 0 10px">                         'blocks' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'core/group' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                        'color' => array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -61,7 +69,7 @@
</span><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"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertSameSetsWithIndex( $expected, $actual );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertEqualSetsWithIndex( $expected, $actual );
</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">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -193,7 +201,116 @@
</span><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">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
+        */
+       public function test_get_stylesheet_support_for_shorthand_and_longhand_values() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'blocks' => array(
+                                               'core/group' => array(
+                                                       'border'  => array(
+                                                               'radius' => '10px',
+                                                       ),
+                                                       'spacing' => array(
+                                                               'padding' => '24px',
+                                                               'margin'  => '1em',
+                                                       ),
+                                               ),
+                                               'core/image' => array(
+                                                       'border'  => array(
+                                                               'radius' => array(
+                                                                       'topLeft'     => '10px',
+                                                                       'bottomRight' => '1em',
+                                                               ),
+                                                       ),
+                                                       'spacing' => array(
+                                                               'padding' => array(
+                                                                       'top' => '15px',
+                                                               ),
+                                                               'margin'  => array(
+                                                                       'bottom' => '30px',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $styles = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-block-group{border-radius: 10px;margin: 1em;padding: 24px;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;padding-top: 15px;}';
+               $this->assertEquals( $styles, $theme_json->get_stylesheet() );
+               $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
+       }
+
+       /**
+        * @ticket 54336
+        */
+       public function test_get_stylesheet_skips_disabled_protected_properties() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+                               'settings' => array(
+                                       'spacing' => array(
+                                               'blockGap' => null,
+                                       ),
+                               ),
+                               'styles'   => array(
+                                       'spacing' => array(
+                                               'blockGap' => '1em',
+                                       ),
+                                       'blocks'  => array(
+                                               'core/columns' => array(
+                                                       'spacing' => array(
+                                                               'blockGap' => '24px',
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $expected = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
+               $this->assertEquals( $expected, $theme_json->get_stylesheet() );
+               $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
+       }
+
+       /**
+        * @ticket 54336
+        */
+       public function test_get_stylesheet_renders_enabled_protected_properties() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+                               'settings' => array(
+                                       'spacing' => array(
+                                               'blockGap' => true,
+                                       ),
+                               ),
+                               'styles'   => array(
+                                       'spacing' => array(
+                                               'blockGap' => '1em',
+                                       ),
+                                       'blocks'  => array(
+                                               'core/columns' => array(
+                                                       'spacing' => array(
+                                                               'blockGap' => '24px',
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $expected = 'body{--wp--style--block-gap: 1em;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }.wp-block-columns{--wp--style--block-gap: 24px;}';
+               $this->assertEquals( $expected, $theme_json->get_stylesheet() );
+               $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @ticket 53175
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_stylesheet() {
</span><span class="cx" style="display: block; padding: 0 10px">                $theme_json = new WP_Theme_JSON(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -221,6 +338,9 @@
</span><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">                                        ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        'spacing'    => array(
+                                               'blockGap' => false,
+                                       ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                         'misc'       => 'value',
</span><span class="cx" style="display: block; padding: 0 10px">                                        'blocks'     => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                'core/group' => array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -250,6 +370,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        ),
</span><span class="cx" style="display: block; padding: 0 10px">                                        'blocks'   => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                'core/group'     => array(
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                        'border'   => array(
+                                                               'radius' => '10px',
+                                                       ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                                         'elements' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                                'link' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                                        'color' => array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -258,10 +381,7 @@
</span><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">                                                        'spacing'  => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                                'padding' => array(
-                                                                       'top'    => '12px',
-                                                                       'bottom' => '24px',
-                                                               ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                         'padding' => '24px',
</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">                                                'core/heading'   => array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -293,6 +413,19 @@
</span><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">                                                ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                'core/image'     => array(
+                                                       'border'  => array(
+                                                               'radius' => array(
+                                                                       'topLeft'     => '10px',
+                                                                       'bottomRight' => '1em',
+                                                               ),
+                                                       ),
+                                                       'spacing' => array(
+                                                               'margin' => array(
+                                                                       'bottom' => '30px',
+                                                               ),
+                                                       ),
+                                               ),
</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">                                'misc'     => 'value',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -299,22 +432,19 @@
</span><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"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertSame(
-                       'body{--wp--preset--color--grey: grey;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}',
-                       $theme_json->get_stylesheet()
-               );
-               $this->assertSame(
-                       'body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}',
-                       $theme_json->get_stylesheet( 'block_styles' )
-               );
-               $this->assertSame(
-                       'body{--wp--preset--color--grey: grey;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}',
-                       $theme_json->get_stylesheet( 'css_variables' )
-               );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}';
+               $styles    = 'body{color: var(--wp--preset--color--grey);}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}';
+               $presets   = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}';
+               $all       = $variables . $styles . $presets;
+               $this->assertEquals( $all, $theme_json->get_stylesheet() );
+               $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
+               $this->assertEquals( $presets, $theme_json->get_stylesheet( array( 'presets' ) ) );
+               $this->assertEquals( $variables, $theme_json->get_stylesheet( array( 'variables' ) ) );
</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">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * @ticket 52991
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_stylesheet_preset_classes_work_with_compounded_selectors() {
</span><span class="cx" style="display: block; padding: 0 10px">                $theme_json = new WP_Theme_JSON(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -337,14 +467,15 @@
</span><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"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertSame(
-                       'h1.has-white-color,h2.has-white-color,h3.has-white-color,h4.has-white-color,h5.has-white-color,h6.has-white-color{color: var(--wp--preset--color--white) !important;}h1.has-white-background-color,h2.has-white-background-color,h3.has-white-background-color,h4.has-white-background-color,h5.has-white-background-color,h6.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}',
-                       $theme_json->get_stylesheet( 'block_styles' )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertEquals(
+                       'h1.has-white-color,h2.has-white-color,h3.has-white-color,h4.has-white-color,h5.has-white-color,h6.has-white-color{color: var(--wp--preset--color--white) !important;}h1.has-white-background-color,h2.has-white-background-color,h3.has-white-background-color,h4.has-white-background-color,h5.has-white-background-color,h6.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}h1.has-white-border-color,h2.has-white-border-color,h3.has-white-border-color,h4.has-white-border-color,h5.has-white-border-color,h6.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}',
+                       $theme_json->get_stylesheet( array( 'presets' ) )
</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"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * @ticket 53175
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_stylesheet_preset_rules_come_after_block_rules() {
</span><span class="cx" style="display: block; padding: 0 10px">                $theme_json = new WP_Theme_JSON(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -376,18 +507,62 @@
</span><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"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertSame(
-                       '.wp-block-group{--wp--preset--color--grey: grey;}.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}',
-                       $theme_json->get_stylesheet()
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $styles    = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-block-group{color: red;}';
+               $presets   = '.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}';
+               $variables = '.wp-block-group{--wp--preset--color--grey: grey;}';
+               $all       = $variables . $styles . $presets;
+               $this->assertEquals( $all, $theme_json->get_stylesheet() );
+               $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
+               $this->assertEquals( $presets, $theme_json->get_stylesheet( array( 'presets' ) ) );
+               $this->assertEquals( $variables, $theme_json->get_stylesheet( array( 'variables' ) ) );
+       }
+
+       /**
+        * @ticket 54336
+        */
+       public function test_get_stylesheet_generates_proper_classes_from_slugs() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+                               'settings' => array(
+                                       'color' => array(
+                                               'palette' => array(
+                                                       array(
+                                                               'slug'  => 'grey',
+                                                               'color' => 'grey',
+                                                       ),
+                                                       array(
+                                                               'slug'  => 'dark grey',
+                                                               'color' => 'grey',
+                                                       ),
+                                                       array(
+                                                               'slug'  => 'light-grey',
+                                                               'color' => 'grey',
+                                                       ),
+                                                       array(
+                                                               'slug'  => 'white2black',
+                                                               'color' => 'grey',
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertSame(
-                       '.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}',
-                       $theme_json->get_stylesheet( 'block_styles' )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $this->assertEquals(
+                       '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-dark-grey-color{color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-color{color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-color{color: var(--wp--preset--color--white-2-black) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-background-color{background-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-background-color{background-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-background-color{background-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-color{border-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-color{border-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-color{bor
 der-color: var(--wp--preset--color--white-2-black) !important;}',
+                       $theme_json->get_stylesheet( array( 'presets' ) )
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $this->assertEquals(
+                       'body{--wp--preset--color--grey: grey;--wp--preset--color--dark-grey: grey;--wp--preset--color--light-grey: grey;--wp--preset--color--white-2-black: grey;}',
+                       $theme_json->get_stylesheet( array( 'variables' ) )
+               );
+
</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">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * @ticket 53175
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_stylesheet_preset_values_are_marked_as_important() {
</span><span class="cx" style="display: block; padding: 0 10px">                $theme_json = new WP_Theme_JSON(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -421,8 +596,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'core'
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertSame(
-                       'body{--wp--preset--color--grey: grey;}p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertEquals(
+                       'body{--wp--preset--color--grey: grey;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}',
</ins><span class="cx" style="display: block; padding: 0 10px">                         $theme_json->get_stylesheet()
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -429,6 +604,7 @@
</span><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">         * @ticket 52991
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_merge_incoming_data() {
</span><span class="cx" style="display: block; padding: 0 10px">                $theme_json = new WP_Theme_JSON(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -608,7 +784,7 @@
</span><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">                                'typography' => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'fontSizes' => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'fontSizes'    => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 'theme' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                        array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                                'slug' => 'fontSize',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -616,6 +792,14 @@
</span><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">                                        ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        'fontFamilies' => array(
+                                               'theme' => array(
+                                                       array(
+                                                               'slug'       => 'fontFamily',
+                                                               'fontFamily' => 'fontFamily',
+                                                       ),
+                                               ),
+                                       ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'blocks'     => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                        'core/paragraph' => array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -669,6 +853,7 @@
</span><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">         * @ticket 53175
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_merge_incoming_data_empty_presets() {
</span><span class="cx" style="display: block; padding: 0 10px">                $theme_json = new WP_Theme_JSON(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -736,7 +921,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'version'  => WP_Theme_JSON::LATEST_SCHEMA,
</span><span class="cx" style="display: block; padding: 0 10px">                        'settings' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'color'      => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'duotone'   => array(),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'duotone'   => array(
+                                               'theme' => array(),
+                                       ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                         'gradients' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                'theme' => array(),
</span><span class="cx" style="display: block; padding: 0 10px">                                        ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -760,6 +947,7 @@
</span><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">         * @ticket 53175
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_merge_incoming_data_null_presets() {
</span><span class="cx" style="display: block; padding: 0 10px">                $theme_json = new WP_Theme_JSON(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -810,10 +998,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                        'custom' => false,
</span><span class="cx" style="display: block; padding: 0 10px">                                                ),
</span><span class="cx" style="display: block; padding: 0 10px">                                                'spacing'    => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        'customMargin' => false,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 'margin' => false,
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                                                'typography' => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        'customLineHeight' => false,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 'lineHeight' => false,
</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">                                )
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -827,9 +1015,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'color'      => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                        'custom'    => false,
</span><span class="cx" style="display: block; padding: 0 10px">                                        'duotone'   => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                array(
-                                                       'slug'   => 'value',
-                                                       'colors' => array( 'red', 'green' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         'theme' => array(
+                                                       array(
+                                                               'slug'   => 'value',
+                                                               'colors' => array( 'red', 'green' ),
+                                                       ),
</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">                                        'gradients' => array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -850,12 +1040,12 @@
</span><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">                                'spacing'    => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'customMargin' => false,
-                                       'units'        => array( 'px', 'em' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'margin' => false,
+                                       'units'  => array( 'px', 'em' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'typography' => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'customLineHeight' => false,
-                                       'fontSizes'        => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'lineHeight' => false,
+                                       'fontSizes'  => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 'theme' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                        array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                                'slug'  => 'size',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -871,6 +1061,558 @@
</span><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">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
+        */
+       public function test_remove_insecure_properties_removes_unsafe_styles() {
+               $actual = WP_Theme_JSON::remove_insecure_properties(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'color'    => array(
+                                               'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')',
+                                               'text'     => 'var:preset|color|dark-red',
+                                       ),
+                                       'elements' => array(
+                                               'link' => array(
+                                                       'color' => array(
+                                                               'gradient'   => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')',
+                                                               'text'       => 'var:preset|color|dark-pink',
+                                                               'background' => 'var:preset|color|dark-red',
+                                                       ),
+                                               ),
+                                       ),
+                                       'blocks'   => array(
+                                               'core/image'  => array(
+                                                       'filter' => array(
+                                                               'duotone' => 'var:preset|duotone|blue-red',
+                                                       ),
+                                               ),
+                                               'core/cover'  => array(
+                                                       'filter' => array(
+                                                               'duotone' => 'var(--wp--preset--duotone--blue-red, var(--fallback-unsafe))',
+                                                       ),
+                                               ),
+                                               'core/group'  => array(
+                                                       'color'    => array(
+                                                               'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')',
+                                                               'text'     => 'var:preset|color|dark-gray',
+                                                       ),
+                                                       'elements' => array(
+                                                               'link' => array(
+                                                                       'color' => array(
+                                                                               'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')',
+                                                                               'text'     => 'var:preset|color|dark-pink',
+                                                                       ),
+                                                               ),
+                                                       ),
+                                               ),
+                                               'invalid/key' => array(
+                                                       'background' => 'green',
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $expected = array(
+                       'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                       'styles'  => array(
+                               'color'    => array(
+                                       'text' => 'var:preset|color|dark-red',
+                               ),
+                               'elements' => array(
+                                       'link' => array(
+                                               'color' => array(
+                                                       'text'       => 'var:preset|color|dark-pink',
+                                                       'background' => 'var:preset|color|dark-red',
+                                               ),
+                                       ),
+                               ),
+                               'blocks'   => array(
+                                       'core/image' => array(
+                                               'filter' => array(
+                                                       'duotone' => 'var:preset|duotone|blue-red',
+                                               ),
+                                       ),
+                                       'core/group' => array(
+                                               'color'    => array(
+                                                       'text' => 'var:preset|color|dark-gray',
+                                               ),
+                                               'elements' => array(
+                                                       'link' => array(
+                                                               'color' => array(
+                                                                       'text' => 'var:preset|color|dark-pink',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+               );
+               $this->assertEqualSetsWithIndex( $expected, $actual );
+       }
+
+       /**
+        * @ticket 54336
+        */
+       public function test_remove_insecure_properties_removes_unsafe_styles_sub_properties() {
+               $actual = WP_Theme_JSON::remove_insecure_properties(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'border'   => array(
+                                               'radius' => array(
+                                                       'topLeft'     => '6px',
+                                                       'topRight'    => 'var(--top-right, var(--unsafe-fallback))',
+                                                       'bottomRight' => '6px',
+                                                       'bottomLeft'  => '6px',
+                                               ),
+                                       ),
+                                       'spacing'  => array(
+                                               'padding' => array(
+                                                       'top'    => '1px',
+                                                       'right'  => '1px',
+                                                       'bottom' => 'var(--bottom, var(--unsafe-fallback))',
+                                                       'left'   => '1px',
+                                               ),
+                                       ),
+                                       'elements' => array(
+                                               'link' => array(
+                                                       'spacing' => array(
+                                                               'padding' => array(
+                                                                       'top'    => '2px',
+                                                                       'right'  => '2px',
+                                                                       'bottom' => 'var(--bottom, var(--unsafe-fallback))',
+                                                                       'left'   => '2px',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                                       'blocks'   => array(
+                                               'core/group' => array(
+                                                       'border'   => array(
+                                                               'radius' => array(
+                                                                       'topLeft'     => '5px',
+                                                                       'topRight'    => 'var(--top-right, var(--unsafe-fallback))',
+                                                                       'bottomRight' => '5px',
+                                                                       'bottomLeft'  => '5px',
+                                                               ),
+                                                       ),
+                                                       'spacing'  => array(
+                                                               'padding' => array(
+                                                                       'top'    => '3px',
+                                                                       'right'  => '3px',
+                                                                       'bottom' => 'var(bottom, var(--unsafe-fallback))',
+                                                                       'left'   => '3px',
+                                                               ),
+                                                       ),
+                                                       'elements' => array(
+                                                               'link' => array(
+                                                                       'spacing' => array(
+                                                                               'padding' => array(
+                                                                                       'top'    => '4px',
+                                                                                       'right'  => '4px',
+                                                                                       'bottom' => 'var(--bottom, var(--unsafe-fallback))',
+                                                                                       'left'   => '4px',
+                                                                               ),
+                                                                       ),
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       true
+               );
+
+               $expected = array(
+                       'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                       'styles'  => array(
+                               'border'   => array(
+                                       'radius' => array(
+                                               'topLeft'     => '6px',
+                                               'bottomRight' => '6px',
+                                               'bottomLeft'  => '6px',
+                                       ),
+                               ),
+                               'spacing'  => array(
+                                       'padding' => array(
+                                               'top'   => '1px',
+                                               'right' => '1px',
+                                               'left'  => '1px',
+                                       ),
+                               ),
+                               'elements' => array(
+                                       'link' => array(
+                                               'spacing' => array(
+                                                       'padding' => array(
+                                                               'top'   => '2px',
+                                                               'right' => '2px',
+                                                               'left'  => '2px',
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                               'blocks'   => array(
+                                       'core/group' => array(
+                                               'border'   => array(
+                                                       'radius' => array(
+                                                               'topLeft'     => '5px',
+                                                               'bottomRight' => '5px',
+                                                               'bottomLeft'  => '5px',
+                                                       ),
+                                               ),
+                                               'spacing'  => array(
+                                                       'padding' => array(
+                                                               'top'   => '3px',
+                                                               'right' => '3px',
+                                                               'left'  => '3px',
+                                                       ),
+                                               ),
+                                               'elements' => array(
+                                                       'link' => array(
+                                                               'spacing' => array(
+                                                                       'padding' => array(
+                                                                               'top'   => '4px',
+                                                                               'right' => '4px',
+                                                                               'left'  => '4px',
+                                                                       ),
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+               );
+               $this->assertEqualSetsWithIndex( $expected, $actual );
+       }
+
+       /**
+        * @ticket 54336
+        */
+       public function test_remove_insecure_properties_removes_non_preset_settings() {
+               $actual = WP_Theme_JSON::remove_insecure_properties(
+                       array(
+                               'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+                               'settings' => array(
+                                       'color'   => array(
+                                               'custom'  => true,
+                                               'palette' => array(
+                                                       array(
+                                                               'name'  => 'Red',
+                                                               'slug'  => 'red',
+                                                               'color' => '#ff0000',
+                                                       ),
+                                                       array(
+                                                               'name'  => 'Green',
+                                                               'slug'  => 'green',
+                                                               'color' => '#00ff00',
+                                                       ),
+                                                       array(
+                                                               'name'  => 'Blue',
+                                                               'slug'  => 'blue',
+                                                               'color' => '#0000ff',
+                                                       ),
+                                               ),
+                                       ),
+                                       'spacing' => array(
+                                               'padding' => false,
+                                       ),
+                                       'blocks'  => array(
+                                               'core/group' => array(
+                                                       'color'   => array(
+                                                               'custom'  => true,
+                                                               'palette' => array(
+                                                                       array(
+                                                                               'name'  => 'Yellow',
+                                                                               'slug'  => 'yellow',
+                                                                               'color' => '#ff0000',
+                                                                       ),
+                                                                       array(
+                                                                               'name'  => 'Pink',
+                                                                               'slug'  => 'pink',
+                                                                               'color' => '#00ff00',
+                                                                       ),
+                                                                       array(
+                                                                               'name'  => 'Orange',
+                                                                               'slug'  => 'orange',
+                                                                               'color' => '#0000ff',
+                                                                       ),
+                                                               ),
+                                                       ),
+                                                       'spacing' => array(
+                                                               'padding' => false,
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $expected = array(
+                       'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+                       'settings' => array(
+                               'color'  => array(
+                                       'palette' => array(
+                                               array(
+                                                       'name'  => 'Red',
+                                                       'slug'  => 'red',
+                                                       'color' => '#ff0000',
+                                               ),
+                                               array(
+                                                       'name'  => 'Green',
+                                                       'slug'  => 'green',
+                                                       'color' => '#00ff00',
+                                               ),
+                                               array(
+                                                       'name'  => 'Blue',
+                                                       'slug'  => 'blue',
+                                                       'color' => '#0000ff',
+                                               ),
+                                       ),
+                               ),
+                               'blocks' => array(
+                                       'core/group' => array(
+                                               'color' => array(
+                                                       'palette' => array(
+                                                               array(
+                                                                       'name'  => 'Yellow',
+                                                                       'slug'  => 'yellow',
+                                                                       'color' => '#ff0000',
+                                                               ),
+                                                               array(
+                                                                       'name'  => 'Pink',
+                                                                       'slug'  => 'pink',
+                                                                       'color' => '#00ff00',
+                                                               ),
+                                                               array(
+                                                                       'name'  => 'Orange',
+                                                                       'slug'  => 'orange',
+                                                                       'color' => '#0000ff',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+               );
+               $this->assertEqualSetsWithIndex( $expected, $actual );
+       }
+
+       /**
+        * @ticket 54336
+        */
+       public function test_remove_insecure_properties_removes_unsafe_preset_settings() {
+               $actual = WP_Theme_JSON::remove_insecure_properties(
+                       array(
+                               'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+                               'settings' => array(
+                                       'color'      => array(
+                                               'palette' => array(
+                                                       array(
+                                                               'name'  => 'Red/><b>ok</ok>',
+                                                               'slug'  => 'red',
+                                                               'color' => '#ff0000',
+                                                       ),
+                                                       array(
+                                                               'name'  => 'Green',
+                                                               'slug'  => 'a" attr',
+                                                               'color' => '#00ff00',
+                                                       ),
+                                                       array(
+                                                               'name'  => 'Blue',
+                                                               'slug'  => 'blue',
+                                                               'color' => 'var(--color, var(--unsafe-fallback))',
+                                                       ),
+                                                       array(
+                                                               'name'  => 'Pink',
+                                                               'slug'  => 'pink',
+                                                               'color' => '#FFC0CB',
+                                                       ),
+                                               ),
+                                       ),
+                                       'typography' => array(
+                                               'fontFamilies' => array(
+                                                       array(
+                                                               'name'       => 'Helvetica Arial/><b>test</b>',
+                                                               'slug'       => 'helvetica-arial',
+                                                               'fontFamily' => 'Helvetica Neue, Helvetica, Arial, sans-serif',
+                                                       ),
+                                                       array(
+                                                               'name'       => 'Geneva',
+                                                               'slug'       => 'geneva#asa',
+                                                               'fontFamily' => 'Geneva, Tahoma, Verdana, sans-serif',
+                                                       ),
+                                                       array(
+                                                               'name'       => 'Cambria',
+                                                               'slug'       => 'cambria',
+                                                               'fontFamily' => 'Cambria, Georgia, serif',
+                                                       ),
+                                                       array(
+                                                               'name'       => 'Helvetica Arial',
+                                                               'slug'       => 'helvetica-arial',
+                                                               'fontFamily' => 'var(--fontFamily, var(--unsafe-fallback))',
+                                                       ),
+                                               ),
+                                       ),
+                                       'blocks'     => array(
+                                               'core/group' => array(
+                                                       'color' => array(
+                                                               'palette' => array(
+                                                                       array(
+                                                                               'name'  => 'Red/><b>ok</ok>',
+                                                                               'slug'  => 'red',
+                                                                               'color' => '#ff0000',
+                                                                       ),
+                                                                       array(
+                                                                               'name'  => 'Green',
+                                                                               'slug'  => 'a" attr',
+                                                                               'color' => '#00ff00',
+                                                                       ),
+                                                                       array(
+                                                                               'name'  => 'Blue',
+                                                                               'slug'  => 'blue',
+                                                                               'color' => 'var(--color, var(--unsafe--fallback))',
+                                                                       ),
+                                                                       array(
+                                                                               'name'  => 'Pink',
+                                                                               'slug'  => 'pink',
+                                                                               'color' => '#FFC0CB',
+                                                                       ),
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $expected = array(
+                       'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+                       'settings' => array(
+                               'color'      => array(
+                                       'palette' => array(
+                                               array(
+                                                       'name'  => 'Pink',
+                                                       'slug'  => 'pink',
+                                                       'color' => '#FFC0CB',
+                                               ),
+                                       ),
+                               ),
+                               'typography' => array(
+                                       'fontFamilies' => array(
+                                               array(
+                                                       'name'       => 'Cambria',
+                                                       'slug'       => 'cambria',
+                                                       'fontFamily' => 'Cambria, Georgia, serif',
+                                               ),
+                                       ),
+                               ),
+                               'blocks'     => array(
+                                       'core/group' => array(
+                                               'color' => array(
+                                                       'palette' => array(
+                                                               array(
+                                                                       'name'  => 'Pink',
+                                                                       'slug'  => 'pink',
+                                                                       'color' => '#FFC0CB',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+               );
+               $this->assertEqualSetsWithIndex( $expected, $actual );
+       }
+
+       /**
+        * @ticket 54336
+        */
+       public function test_remove_insecure_properties_applies_safe_styles() {
+               $actual = WP_Theme_JSON::remove_insecure_properties(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'color' => array(
+                                               'text' => '#abcabc ', // Trailing space.
+                                       ),
+                               ),
+                       ),
+                       true
+               );
+
+               $expected = array(
+                       'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                       'styles'  => array(
+                               'color' => array(
+                                       'text' => '#abcabc ',
+                               ),
+                       ),
+               );
+               $this->assertEqualSetsWithIndex( $expected, $actual );
+       }
+
+       /**
+        * @ticket 54336
+        */
+       public function test_get_custom_templates() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version'         => 1,
+                               'customTemplates' => array(
+                                       array(
+                                               'name'  => 'page-home',
+                                               'title' => 'Homepage template',
+                                       ),
+                               ),
+                       )
+               );
+
+               $page_templates = $theme_json->get_custom_templates();
+
+               $this->assertEqualSetsWithIndex(
+                       $page_templates,
+                       array(
+                               'page-home' => array(
+                                       'title'     => 'Homepage template',
+                                       'postTypes' => array( 'page' ),
+                               ),
+                       )
+               );
+       }
+
+       /**
+        * @ticket 54336
+        */
+       public function test_get_template_parts() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version'       => 1,
+                               'templateParts' => array(
+                                       array(
+                                               'name'  => 'small-header',
+                                               'title' => 'Small Header',
+                                               'area'  => 'header',
+                                       ),
+                               ),
+                       )
+               );
+
+               $template_parts = $theme_json->get_template_parts();
+
+               $this->assertEqualSetsWithIndex(
+                       $template_parts,
+                       array(
+                               'small-header' => array(
+                                       'title' => 'Small Header',
+                                       'area'  => 'header',
+                               ),
+                       )
+               );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @ticket 52991
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_from_editor_settings() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -928,9 +1670,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        'units' => array( 'px', 'em', 'rem', 'vh', 'vw', '%' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'typography' => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'customFontSize'   => false,
-                                       'customLineHeight' => true,
-                                       'fontSizes'        => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'customFontSize' => false,
+                                       'lineHeight'     => true,
+                                       'fontSizes'      => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                        'slug' => 'size-slug',
</span><span class="cx" style="display: block; padding: 0 10px">                                                        'name' => 'Size Name',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -948,6 +1690,7 @@
</span><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">         * @ticket 52991
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_editor_settings_no_theme_support() {
</span><span class="cx" style="display: block; padding: 0 10px">                $input = array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -990,8 +1733,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        'units' => false,
</span><span class="cx" style="display: block; padding: 0 10px">                                ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'typography' => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'customFontSize'   => true,
-                                       'customLineHeight' => false,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'customFontSize' => true,
+                                       'lineHeight'     => false,
</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">                );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1003,6 +1746,7 @@
</span><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">         * @ticket 52991
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_editor_settings_blank() {
</span><span class="cx" style="display: block; padding: 0 10px">                $expected = array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1011,58 +1755,57 @@
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">                $actual   = WP_Theme_JSON::get_from_editor_settings( array() );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertSameSetsWithIndex( $expected, $actual );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertEqualSetsWithIndex( $expected, $actual );
</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">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * @ticket 52991
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_editor_settings_custom_units_can_be_disabled() {
</span><span class="cx" style="display: block; padding: 0 10px">                add_theme_support( 'custom-units', array() );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $input = get_default_block_editor_settings();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $actual = WP_Theme_JSON::get_from_editor_settings( get_default_block_editor_settings() );
+               remove_theme_support( 'custom-units' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $expected = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'units'         => array( array() ),
-                       'customPadding' => false,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'units'   => array( array() ),
+                       'padding' => false,
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $actual = WP_Theme_JSON::get_from_editor_settings( $input );
-
-               $this->assertSameSetsWithIndex( $expected, $actual['settings']['spacing'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] );
</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">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * @ticket 52991
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_editor_settings_custom_units_can_be_enabled() {
</span><span class="cx" style="display: block; padding: 0 10px">                add_theme_support( 'custom-units' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $input = get_default_block_editor_settings();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $actual = WP_Theme_JSON::get_from_editor_settings( get_default_block_editor_settings() );
+               remove_theme_support( 'custom-units' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $expected = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'units'         => array( 'px', 'em', 'rem', 'vh', 'vw', '%' ),
-                       'customPadding' => false,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'units'   => array( 'px', 'em', 'rem', 'vh', 'vw', '%' ),
+                       'padding' => false,
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $actual = WP_Theme_JSON::get_from_editor_settings( $input );
-
-               $this->assertSameSetsWithIndex( $expected, $actual['settings']['spacing'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] );
</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">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * @ticket 52991
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_editor_settings_custom_units_can_be_filtered() {
</span><span class="cx" style="display: block; padding: 0 10px">                add_theme_support( 'custom-units', 'rem', 'em' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $input = get_default_block_editor_settings();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $actual = WP_Theme_JSON::get_from_editor_settings( get_default_block_editor_settings() );
+               remove_theme_support( 'custom-units' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $expected = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'units'         => array( 'rem', 'em' ),
-                       'customPadding' => false,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'units'   => array( 'rem', 'em' ),
+                       'padding' => false,
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-               $actual = WP_Theme_JSON::get_from_editor_settings( $input );
-
-               $this->assertSameSetsWithIndex( $expected, $actual['settings']['spacing'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] );
</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"> }
</span></span></pre></div>
<a id="trunktestsphpunitteststhemewpThemeJsonResolverphp"></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/wpThemeJsonResolver.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php   2021-11-08 17:07:49 UTC (rev 52048)
+++ trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php     2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -46,6 +46,7 @@
</span><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">         * @ticket 52991
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket 54336
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_translations_are_applied() {
</span><span class="cx" style="display: block; padding: 0 10px">                add_filter( 'locale', array( $this, 'filter_set_locale_to_polish' ) );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -52,7 +53,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                load_textdomain( 'block-theme', realpath( DIR_TESTDATA . '/languages/themes/block-theme-pl_PL.mo' ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                switch_theme( 'block-theme' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">                 $actual = WP_Theme_JSON_Resolver::get_theme_data();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                unload_textdomain( 'block-theme' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -62,6 +62,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertSame(
</span><span class="cx" style="display: block; padding: 0 10px">                        array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'color'      => array(
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        'custom'         => false,
+                                       'customGradient' => false,
</ins><span class="cx" style="display: block; padding: 0 10px">                                         'palette'        => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                'theme' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                        array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -85,11 +87,11 @@
</span><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">                                        ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'custom'         => false,
-                                       'customGradient' => false,
</del><span class="cx" style="display: block; padding: 0 10px">                                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'typography' => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'fontSizes'        => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'customFontSize' => false,
+                                       'lineHeight'     => true,
+                                       'fontSizes'      => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 'theme' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                        array(
</span><span class="cx" style="display: block; padding: 0 10px">                                                                'name' => 'Custom',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -98,14 +100,10 @@
</span><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">                                        ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'customFontSize'   => false,
-                                       'customLineHeight' => true,
</del><span class="cx" style="display: block; padding: 0 10px">                                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'spacing'    => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'units'         => array(
-                                               'rem',
-                                       ),
-                                       'customPadding' => true,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'units'   => array( 'rem' ),
+                                       'padding' => true,
</ins><span class="cx" style="display: block; padding: 0 10px">                                 ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'blocks'     => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                        'core/paragraph' => array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -125,6 +123,24 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><span class="cx" style="display: block; padding: 0 10px">                        $actual->get_settings()
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $this->assertSame(
+                       $actual->get_custom_templates(),
+                       array(
+                               'page-home' => array(
+                                       'title'     => 'Szablon strony głównej',
+                                       'postTypes' => array( 'page' ),
+                               ),
+                       )
+               );
+               $this->assertSame(
+                       $actual->get_template_parts(),
+                       array(
+                               'small-header' => array(
+                                       'title' => 'MaÅ‚y nagłówek',
+                                       'area'  => 'header',
+                               ),
+                       )
+               );
</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">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -143,4 +159,157 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertTrue( $has_theme_json_support );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * @ticket 54336
+        */
+       function test_add_theme_supports_are_loaded_for_themes_without_theme_json() {
+               switch_theme( 'default' );
+               $color_palette = array(
+                       array(
+                               'name'  => 'Primary',
+                               'slug'  => 'primary',
+                               'color' => '#F00',
+                       ),
+                       array(
+                               'name'  => 'Secondary',
+                               'slug'  => 'secondary',
+                               'color' => '#0F0',
+                       ),
+                       array(
+                               'name'  => 'Tertiary',
+                               'slug'  => 'tertiary',
+                               'color' => '#00F',
+                       ),
+               );
+               add_theme_support( 'editor-color-palette', $color_palette );
+               add_theme_support( 'custom-line-height' );
+
+               $settings = WP_Theme_JSON_Resolver::get_theme_data()->get_settings();
+
+               remove_theme_support( 'custom-line-height' );
+               remove_theme_support( 'editor-color-palette' );
+
+               $this->assertFalse( WP_Theme_JSON_Resolver::theme_has_support() );
+               $this->assertTrue( $settings['typography']['lineHeight'] );
+               $this->assertSame( $color_palette, $settings['color']['palette']['theme'] );
+       }
+
+       /**
+        * Recursively applies ksort to an array.
+        */
+       private static function recursive_ksort( &$array ) {
+               foreach ( $array as &$value ) {
+                       if ( is_array( $value ) ) {
+                               self::recursive_ksort( $value );
+                       }
+               }
+               ksort( $array );
+       }
+
+       /**
+        * @ticket 54336
+        */
+       function test_merges_child_theme_json_into_parent_theme_json() {
+               switch_theme( 'block-theme-child' );
+
+               $actual_settings   = WP_Theme_JSON_Resolver::get_theme_data()->get_settings();
+               $expected_settings = array(
+                       'color'      => array(
+                               'custom'         => false,
+                               'customGradient' => false,
+                               'gradients'      => array(
+                                       'theme' => array(
+                                               array(
+                                                       'name'     => 'Custom gradient',
+                                                       'gradient' => 'linear-gradient(135deg,rgba(0,0,0) 0%,rgb(0,0,0) 100%)',
+                                                       'slug'     => 'custom-gradient',
+                                               ),
+                                       ),
+                               ),
+                               'palette'        => array(
+                                       'theme' => array(
+                                               array(
+                                                       'slug'  => 'light',
+                                                       'name'  => 'Light',
+                                                       'color' => '#f3f4f6',
+                                               ),
+                                               array(
+                                                       'slug'  => 'primary',
+                                                       'name'  => 'Primary',
+                                                       'color' => '#3858e9',
+                                               ),
+                                               array(
+                                                       'slug'  => 'dark',
+                                                       'name'  => 'Dark',
+                                                       'color' => '#111827',
+                                               ),
+                                       ),
+                               ),
+                               'link'           => true,
+                       ),
+                       'typography' => array(
+                               'customFontSize' => false,
+                               'lineHeight'     => true,
+                               'fontSizes'      => array(
+                                       'theme' => array(
+                                               array(
+                                                       'name' => 'Custom',
+                                                       'slug' => 'custom',
+                                                       'size' => '100px',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'spacing'    => array(
+                               'units'   => array( 'rem' ),
+                               'padding' => true,
+                       ),
+                       'blocks'     => array(
+                               'core/paragraph'  => array(
+                                       'color' => array(
+                                               'palette' => array(
+                                                       'theme' => array(
+                                                               array(
+                                                                       'slug'  => 'light',
+                                                                       'name'  => 'Light',
+                                                                       'color' => '#f5f7f9',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                               'core/post-title' => array(
+                                       'color' => array(
+                                               'palette' => array(
+                                                       'theme' => array(
+                                                               array(
+                                                                       'slug'  => 'light',
+                                                                       'name'  => 'Light',
+                                                                       'color' => '#f3f4f6',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+               );
+               self::recursive_ksort( $actual_settings );
+               self::recursive_ksort( $expected_settings );
+
+               // Should merge settings.
+               $this->assertSame(
+                       $expected_settings,
+                       $actual_settings
+               );
+
+               $this->assertSame(
+                       WP_Theme_JSON_Resolver::get_theme_data()->get_custom_templates(),
+                       array(
+                               'page-home' => array(
+                                       'title'     => 'Homepage',
+                                       'postTypes' => array( 'page' ),
+                               ),
+                       )
+               );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunktestsphpunitteststhemewpThemeJsonSchemaphp"></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/theme/wpThemeJsonSchema.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/theme/wpThemeJsonSchema.php                             (rev 0)
+++ trunk/tests/phpunit/tests/theme/wpThemeJsonSchema.php       2021-11-08 19:18:39 UTC (rev 52049)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,193 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Test WP_Theme_JSON_Schema class.
+ *
+ * @package WordPress
+ * @subpackage Theme
+ *
+ * @since 5.9.0
+ *
+ * @group themes
+ */
+class Tests_Theme_wpThemeJsonSchema extends WP_UnitTestCase {
+       /**
+        * The current theme.json schema version.
+        */
+       const LATEST_SCHEMA_VERSION = WP_Theme_JSON::LATEST_SCHEMA;
+
+       /**
+        * @ticket 54336
+        */
+       function test_migrate_v1_to_v2() {
+               $theme_json_v1 = array(
+                       'version'  => 1,
+                       'settings' => array(
+                               'color'      => array(
+                                       'palette' => array(
+                                               array(
+                                                       'name'  => 'Pale Pink',
+                                                       'slug'  => 'pale-pink',
+                                                       'color' => '#f78da7',
+                                               ),
+                                               array(
+                                                       'name'  => 'Vivid Red',
+                                                       'slug'  => 'vivid-red',
+                                                       'color' => '#cf2e2e',
+                                               ),
+                                       ),
+                                       'custom'  => false,
+                                       'link'    => true,
+                               ),
+                               'border'     => array(
+                                       'color'        => false,
+                                       'customRadius' => false,
+                                       'style'        => false,
+                                       'width'        => false,
+                               ),
+                               'typography' => array(
+                                       'fontStyle'      => false,
+                                       'fontWeight'     => false,
+                                       'letterSpacing'  => false,
+                                       'textDecoration' => false,
+                                       'textTransform'  => false,
+                               ),
+                               'blocks'     => array(
+                                       'core/group' => array(
+                                               'border'     => array(
+                                                       'color'        => true,
+                                                       'customRadius' => true,
+                                                       'style'        => true,
+                                                       'width'        => true,
+                                               ),
+                                               'typography' => array(
+                                                       'fontStyle'      => true,
+                                                       'fontWeight'     => true,
+                                                       'letterSpacing'  => true,
+                                                       'textDecoration' => true,
+                                                       'textTransform'  => true,
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'styles'   => array(
+                               'color'    => array(
+                                       'background' => 'purple',
+                               ),
+                               'blocks'   => array(
+                                       'core/group' => array(
+                                               'color'    => array(
+                                                       'background' => 'red',
+                                               ),
+                                               'spacing'  => array(
+                                                       'padding' => array(
+                                                               'top' => '10px',
+                                                       ),
+                                               ),
+                                               'elements' => array(
+                                                       'link' => array(
+                                                               'color' => array(
+                                                                       'text' => 'yellow',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                               'elements' => array(
+                                       'link' => array(
+                                               'color' => array(
+                                                       'text' => 'red',
+                                               ),
+                                       ),
+                               ),
+                       ),
+               );
+
+               $actual = WP_Theme_JSON_Schema::migrate( $theme_json_v1 );
+
+               $expected = array(
+                       'version'  => self::LATEST_SCHEMA_VERSION,
+                       'settings' => array(
+                               'color'      => array(
+                                       'palette' => array(
+                                               array(
+                                                       'name'  => 'Pale Pink',
+                                                       'slug'  => 'pale-pink',
+                                                       'color' => '#f78da7',
+                                               ),
+                                               array(
+                                                       'name'  => 'Vivid Red',
+                                                       'slug'  => 'vivid-red',
+                                                       'color' => '#cf2e2e',
+                                               ),
+                                       ),
+                                       'custom'  => false,
+                                       'link'    => true,
+                               ),
+                               'border'     => array(
+                                       'color'  => false,
+                                       'radius' => false,
+                                       'style'  => false,
+                                       'width'  => false,
+                               ),
+                               'typography' => array(
+                                       'fontStyle'      => false,
+                                       'fontWeight'     => false,
+                                       'letterSpacing'  => false,
+                                       'textDecoration' => false,
+                                       'textTransform'  => false,
+                               ),
+                               'blocks'     => array(
+                                       'core/group' => array(
+                                               'border'     => array(
+                                                       'color'  => true,
+                                                       'radius' => true,
+                                                       'style'  => true,
+                                                       'width'  => true,
+                                               ),
+                                               'typography' => array(
+                                                       'fontStyle'      => true,
+                                                       'fontWeight'     => true,
+                                                       'letterSpacing'  => true,
+                                                       'textDecoration' => true,
+                                                       'textTransform'  => true,
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'styles'   => array(
+                               'color'    => array(
+                                       'background' => 'purple',
+                               ),
+                               'blocks'   => array(
+                                       'core/group' => array(
+                                               'color'    => array(
+                                                       'background' => 'red',
+                                               ),
+                                               'spacing'  => array(
+                                                       'padding' => array(
+                                                               'top' => '10px',
+                                                       ),
+                                               ),
+                                               'elements' => array(
+                                                       'link' => array(
+                                                               'color' => array(
+                                                                       'text' => 'yellow',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                               'elements' => array(
+                                       'link' => array(
+                                               'color' => array(
+                                                       'text' => 'red',
+                                               ),
+                                       ),
+                               ),
+                       ),
+               );
+
+               $this->assertEqualSetsWithIndex( $expected, $actual );
+       }
+}
</ins></span></pre>
</div>
</div>

</body>
</html>