<!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>[54118] trunk: Editor: Backport Elements API updates.</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/54118">54118</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/54118","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>SergeyBiryukov</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2022-09-10 12:37:00 +0000 (Sat, 10 Sep 2022)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Editor: Backport Elements API updates.

This commit backports the original PRs from Gutenberg repository:

* [https://github.com/WordPress/gutenberg/pull/40260 <a href="https://core.trac.wordpress.org/ticket/40260">#40260</a> Add support for button elements to theme.json]
* [https://github.com/WordPress/gutenberg/pull/40889 <a href="https://core.trac.wordpress.org/ticket/40889">#40889</a> Theme Json: Don't output double selectors for elements inside blocks]
* [https://github.com/WordPress/gutenberg/pull/41140 <a href="https://core.trac.wordpress.org/ticket/41140">#41140</a> Global Styles: Add support for caption elements]
* [https://github.com/WordPress/gutenberg/pull/41160 <a href="https://core.trac.wordpress.org/ticket/41160">#41160</a> Global Styles: Load block CSS conditionally]
* [https://github.com/WordPress/gutenberg/pull/41240 <a href="https://core.trac.wordpress.org/ticket/41240">#41240</a> Global Styles: Button Element: update button element selector]
* [https://github.com/WordPress/gutenberg/pull/41335 <a href="https://core.trac.wordpress.org/ticket/41335">#41335</a> Duotone: Fix CSS Selectors rendered by theme.json duotone/filter settings for blocks on public pages]
* [https://github.com/WordPress/gutenberg/pull/41446 <a href="https://core.trac.wordpress.org/ticket/41446">#41446</a> Block styles: Account for style block nodes that have no name]
* [https://github.com/WordPress/gutenberg/pull/41696 <a href="https://core.trac.wordpress.org/ticket/41696">#41696</a> Global Styles: Allow references to values in other locations in the tree]
* [https://github.com/WordPress/gutenberg/pull/41753 <a href="https://core.trac.wordpress.org/ticket/41753">#41753</a> Elements: Add an API make it easier to get class names]
* [https://github.com/WordPress/gutenberg/pull/41786 <a href="https://core.trac.wordpress.org/ticket/41786">#41786</a> Support pseudo selectors on elements in theme json]
* [https://github.com/WordPress/gutenberg/pull/41822 <a href="https://core.trac.wordpress.org/ticket/41822">#41822</a> Elements: Button - Fix element selectors]
* [https://github.com/WordPress/gutenberg/pull/41981 <a href="https://core.trac.wordpress.org/ticket/41981">#41981</a> Global Styles: Add support for heading elements]
* [https://github.com/WordPress/gutenberg/pull/42072 <a href="https://core.trac.wordpress.org/ticket/42072">#42072</a> Fix link element hover bleeding into button element default styles]
* [https://github.com/WordPress/gutenberg/pull/42096 <a href="https://core.trac.wordpress.org/ticket/42096">#42096</a> Add visited to link element allowed pseudo selector list]
* [https://github.com/WordPress/gutenberg/pull/42669 <a href="https://core.trac.wordpress.org/ticket/42669">#42669</a> Link elements: Add a :where selector to the :not to lower specificity]
* [https://github.com/WordPress/gutenberg/pull/42776 <a href="https://core.trac.wordpress.org/ticket/42776">#42776</a> Theme JSON: Add a static $blocks_metadata data definition to the Gutenberg instance of WP_Theme_JSON]
* [https://github.com/WordPress/gutenberg/pull/43088 <a href="https://core.trac.wordpress.org/ticket/43088">#43088</a> Pseudo elements supports on button elements]
* [https://github.com/WordPress/gutenberg/pull/43167 <a href="https://core.trac.wordpress.org/ticket/43167">#43167</a> Theme_JSON: Use existing append_to_selector for pseudo elements]
* [https://github.com/WordPress/gutenberg/pull/43988 <a href="https://core.trac.wordpress.org/ticket/43988">#43988</a> Styles API: Fixed selectors for nested elements]

Props onemaggie, bernhard-reiter, cbravobernal, mmaattiiaass, scruffian, andraganescu, dpcalhoun, get_dave, Mamaduka, SergeyBiryukov.
See <a href="https://core.trac.wordpress.org/ticket/56467">#56467</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesclasswpthemejsonphp">trunk/src/wp-includes/class-wp-theme-json.php</a></li>
<li><a href="#trunksrcwpincludesglobalstylesandsettingsphp">trunk/src/wp-includes/global-styles-and-settings.php</a></li>
<li><a href="#trunksrcwpincludesscriptloaderphp">trunk/src/wp-includes/script-loader.php</a></li>
<li><a href="#trunktestsphpunitteststhemewpThemeJsonphp">trunk/tests/phpunit/tests/theme/wpThemeJson.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<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     2022-09-09 12:37:47 UTC (rev 54117)
+++ trunk/src/wp-includes/class-wp-theme-json.php       2022-09-10 12:37:00 UTC (rev 54118)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -344,14 +344,26 @@
</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">+         * Defines which pseudo selectors are enabled for which elements.
+        *
+        * Note: this will affect both top-level and block-level elements.
+        *
+        * @since 6.1.0
+        */
+       const VALID_ELEMENT_PSEUDO_SELECTORS = array(
+               'link'   => array( ':hover', ':focus', ':active', ':visited' ),
+               'button' => array( ':hover', ':focus', ':active', ':visited' ),
+       );
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * The valid elements that can be found under styles.
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @since 6.1.0 Added `heading`, `button`, and `caption` to the elements.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @since 6.1.0 Added `heading`, `button`. and `caption` elements.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @var string[]
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        const ELEMENTS = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'link'    => 'a',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'link'    => 'a:where(:not(.wp-element-button))', // The `where` is needed to lower the specificity.
</ins><span class="cx" style="display: block; padding: 0 10px">                 'heading' => 'h1, h2, h3, h4, h5, h6',
</span><span class="cx" style="display: block; padding: 0 10px">                'h1'      => 'h1',
</span><span class="cx" style="display: block; padding: 0 10px">                'h2'      => 'h2',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -365,7 +377,30 @@
</span><span class="cx" style="display: block; padding: 0 10px">                'caption' => '.wp-element-caption, .wp-block-audio figcaption, .wp-block-embed figcaption, .wp-block-gallery figcaption, .wp-block-image figcaption, .wp-block-table figcaption, .wp-block-video figcaption',
</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">+        const __EXPERIMENTAL_ELEMENT_CLASS_NAMES = array(
+               'button'  => 'wp-element-button',
+               'caption' => 'wp-element-caption',
+       );
+
</ins><span class="cx" style="display: block; padding: 0 10px">         /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Returns a class name by an element name.
+        *
+        * @since 6.1.0
+        *
+        * @param string $element The name of the element.
+        * @return string The name of the class.
+        */
+       public static function get_element_class_name( $element ) {
+               $class_name = '';
+
+               if ( array_key_exists( $element, static::__EXPERIMENTAL_ELEMENT_CLASS_NAMES ) ) {
+                       $class_name = static::__EXPERIMENTAL_ELEMENT_CLASS_NAMES[ $element ];
+               }
+
+               return $class_name;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Options that settings.appearanceTools enables.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 6.0.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -488,6 +523,7 @@
</span><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><span class="cx" style="display: block; padding: 0 10px">        protected static function sanitize( $input, $valid_block_names, $valid_element_names ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</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">@@ -494,10 +530,14 @@
</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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Preserve only the top most level keys.
</ins><span class="cx" style="display: block; padding: 0 10px">                 $output = array_intersect_key( $input, array_flip( static::VALID_TOP_LEVEL_KEYS ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Some styles are only meant to be available at the top-level (e.g.: blockGap),
-               // hence, the schema for blocks & elements should not have them.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /*
+                * Remove any rules that are annotated as "top" in VALID_STYLES constant.
+                * Some styles are only meant to be available at the top-level (e.g.: blockGap),
+                * hence, the schema for blocks & elements should not have them.
+                */
</ins><span class="cx" style="display: block; padding: 0 10px">                 $styles_non_top_level = static::VALID_STYLES;
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( array_keys( $styles_non_top_level ) as $section ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        foreach ( array_keys( $styles_non_top_level[ $section ] ) as $prop ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -510,9 +550,24 @@
</span><span class="cx" style="display: block; padding: 0 10px">                // Build the schema based on valid block & element names.
</span><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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               /*
+                * Set allowed element pseudo selectors based on per element allow list.
+                * Target data structure in schema:
+                * e.g.
+                * - top level elements: `$schema['styles']['elements']['link'][':hover']`.
+                * - block level elements: `$schema['styles']['blocks']['core/button']['elements']['link'][':hover']`.
+                */
</ins><span class="cx" style="display: block; padding: 0 10px">                 foreach ( $valid_element_names as $element ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $schema_styles_elements[ $element ] = $styles_non_top_level;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       if ( array_key_exists( $element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
+                               foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) {
+                                       $schema_styles_elements[ $element ][ $pseudo_selector ] = $styles_non_top_level;
+                               }
+                       }
</ins><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">                 $schema_styles_blocks   = array();
</span><span class="cx" style="display: block; padding: 0 10px">                $schema_settings_blocks = array();
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $valid_block_names as $block ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -520,6 +575,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $schema_styles_blocks[ $block ]             = $styles_non_top_level;
</span><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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $schema['styles']             = static::VALID_STYLES;
</span><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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -550,6 +606,30 @@
</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">+         * Appends a sub-selector to an existing one.
+        *
+        * Given the compounded $selector "h1, h2, h3"
+        * and the $to_append selector ".some-class" the result will be
+        * "h1.some-class, h2.some-class, h3.some-class".
+        *
+        * @since 5.8.0
+        * @since 6.1.0 Added append position.
+        *
+        * @param string $selector  Original selector.
+        * @param string $to_append Selector to append.
+        * @param string $position  A position sub-selector should be appended. Default 'right'.
+        * @return string
+        */
+       protected static function append_to_selector( $selector, $to_append, $position = 'right' ) {
+               $new_selectors = array();
+               $selectors     = explode( ',', $selector );
+               foreach ( $selectors as $sel ) {
+                       $new_selectors[] = 'right' === $position ? $sel . $to_append : $to_append . $sel;
+               }
+               return implode( ',', $new_selectors );
+       }
+
+       /**
</ins><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="cx" style="display: block; padding: 0 10px">         * Example:
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -611,7 +691,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        foreach ( static::ELEMENTS as $el_name => $el_selector ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $element_selector = array();
</span><span class="cx" style="display: block; padding: 0 10px">                                foreach ( $block_selectors as $selector ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        $element_selector[] = $selector . ' ' . $el_selector;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 if ( $selector === $el_selector ) {
+                                               $element_selector = array( $el_selector );
+                                               break;
+                                       }
+                                       $element_selector[] = static::append_to_selector( $el_selector, $selector . ' ', 'left' );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><span class="cx" style="display: block; padding: 0 10px">                                static::$blocks_metadata[ $block_name ]['elements'][ $el_name ] = implode( ',', $element_selector );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -810,54 +894,7 @@
</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="cx" style="display: block; padding: 0 10px">                        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-                       $node         = _wp_array_get( $this->theme_json, $metadata['path'], array() );
-                       $selector     = $metadata['selector'];
-                       $settings     = _wp_array_get( $this->theme_json, array( 'settings' ) );
-                       $declarations = static::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;
-                               }
-                       }
-
-                       /*
-                        * Reset default browser margin on the root body element.
-                        * This is set on the root selector **before** generating the ruleset
-                        * from the `theme.json`. This is to ensure that if the `theme.json` declares
-                        * `margin` in its `spacing` declaration for the `body` element then these
-                        * user-generated values take precedence in the CSS cascade.
-                        * @link https://github.com/WordPress/gutenberg/issues/36147.
-                        */
-                       if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
-                               $block_rules .= 'body { margin: 0; }';
-                       }
-
-                       // 2. Generate the rules that use the general selector.
-                       $block_rules .= static::to_ruleset( $selector, $declarations );
-
-                       // 3. Generate the rules that use the duotone selector.
-                       if ( isset( $metadata['duotone'] ) && ! empty( $declarations_duotone ) ) {
-                               $selector_duotone = static::scope_selector( $metadata['selector'], $metadata['duotone'] );
-                               $block_rules     .= static::to_ruleset( $selector_duotone, $declarations_duotone );
-                       }
-
-                       if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
-                               $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-block-start: 0; margin-block-end: 0; }';
-                                       $block_rules .= '.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }';
-                               }
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $block_rules .= static::get_styles_for_block( $metadata );
</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 $block_rules;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -973,29 +1010,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">-         * Function that appends a sub-selector to a existing one.
-        *
-        * Given the compounded $selector "h1, h2, h3"
-        * and the $to_append selector ".some-class" the result will be
-        * "h1.some-class, h2.some-class, h3.some-class".
-        *
-        * @since 5.8.0
-        *
-        * @param string $selector  Original selector.
-        * @param string $to_append Selector to append.
-        * @return string
-        */
-       protected static function append_to_selector( $selector, $to_append ) {
-               $new_selectors = array();
-               $selectors     = explode( ',', $selector );
-               foreach ( $selectors as $sel ) {
-                       $new_selectors[] = $sel . $to_append;
-               }
-
-               return implode( ',', $new_selectors );
-       }
-
-       /**
</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="lines" style="display: block; padding: 0 10px; color: #888">@@ -1312,13 +1326,15 @@
</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="cx" style="display: block; padding: 0 10px">         * @since 5.9.0 Added the `$settings` and `$properties` parameters.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 6.1.0 Added the `$theme_json` parameter.
</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><span class="cx" style="display: block; padding: 0 10px">         * @param array $settings  Theme settings.
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array $properties Properties metadata.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @param array $theme_json Theme JSON array.
</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">-        protected static function compute_style_properties( $styles, $settings = array(), $properties = null ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( null === $properties ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $properties = static::PROPERTIES_METADATA;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1329,7 +1345,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">                foreach ( $properties as $css_property => $value_path ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $value = static::get_property_value( $styles, $value_path );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $value = static::get_property_value( $styles, $value_path, $theme_json );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Look up protected properties, keyed by value path.
</span><span class="cx" style="display: block; padding: 0 10px">                        // Skip protected properties that are explicitly set to `null`.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1365,20 +1381,58 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * "var:preset|color|secondary" to the form
</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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * It also converts references to a path to the value
+        * stored at that location, e.g.
+        * { "ref": "style.color.background" } => "#fff".
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 5.8.0
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.9.0 Added support for values of array type, which are returned as is.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 6.1.0 Added the `$theme_json` parameter.
</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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @param array $theme_json Theme JSON array.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return string|array Style property value.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        protected static function get_property_value( $styles, $path ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ protected static function get_property_value( $styles, $path, $theme_json = null ) {
</ins><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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                /*
+                * This converts references to a path to the value at that path
+                * where the values is an array with a "ref" key, pointing to a path.
+                * For example: { "ref": "style.color.background" } => "#fff".
+                */
+               if ( is_array( $value ) && array_key_exists( 'ref', $value ) ) {
+                       $value_path = explode( '.', $value['ref'] );
+                       $ref_value  = _wp_array_get( $theme_json, $value_path );
+                       // Only use the ref value if we find anything.
+                       if ( ! empty( $ref_value ) && is_string( $ref_value ) ) {
+                               $value = $ref_value;
+                       }
+
+                       if ( is_array( $ref_value ) && array_key_exists( 'ref', $ref_value ) ) {
+                               $path_string      = json_encode( $path );
+                               $ref_value_string = json_encode( $ref_value );
+                               _doing_it_wrong(
+                                       'get_property_value',
+                                       sprintf(
+                                               /* translators: 1: theme.json, 2: Value name, 3: Value path, 4: Another value name. */
+                                               __( 'Your %1$s file uses a dynamic value (%2$s) for the path at %3$s. However, the value at %3$s is also a dynamic value (pointing to %4$s) and pointing to another dynamic value is not supported. Please update %3$s to point directly to %4$s.' ),
+                                               'theme.json',
+                                               $ref_value_string,
+                                               $path_string,
+                                               $ref_value['ref']
+                                       ),
+                                       '6.1.0'
+                               );
+                       }
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( '' === $value || is_array( $value ) ) {
</span><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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Convert custom CSS properties.
</ins><span class="cx" style="display: block; padding: 0 10px">                 $prefix     = 'var:';
</span><span class="cx" style="display: block; padding: 0 10px">                $prefix_len = strlen( $prefix );
</span><span class="cx" style="display: block; padding: 0 10px">                $token_in   = '|';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1490,6 +1544,19 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        'path'     => array( 'styles', 'elements', $element ),
</span><span class="cx" style="display: block; padding: 0 10px">                                        'selector' => static::ELEMENTS[ $element ],
</span><span class="cx" style="display: block; padding: 0 10px">                                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                               // Handle any pseudo selectors for the element.
+                               if ( array_key_exists( $element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
+                                       foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) {
+
+                                               if ( isset( $theme_json['styles']['elements'][ $element ][ $pseudo_selector ] ) ) {
+                                                       $nodes[] = array(
+                                                               'path'     => array( 'styles', 'elements', $element ),
+                                                               'selector' => static::append_to_selector( static::ELEMENTS[ $element ], $pseudo_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"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1498,6 +1565,51 @@
</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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $nodes = array_merge( $nodes, static::get_block_nodes( $theme_json ) );
+
+               /**
+                * Filters the list of style nodes with metadata.
+                *
+                * This allows for things like loading block CSS independently.
+                *
+                * @since 6.1.0
+                *
+                * @param array $nodes Style nodes with metadata.
+                */
+               return apply_filters( 'get_style_nodes', $nodes );
+       }
+
+       /**
+        * A public helper to get the block nodes from a theme.json file.
+        *
+        * @since 6.1.0
+        *
+        * @return array The block nodes in theme.json.
+        */
+       public function get_styles_block_nodes() {
+               return static::get_block_nodes( $this->theme_json );
+       }
+
+       /**
+        * An internal method to get the block nodes from a theme.json file.
+        *
+        * @since 6.1.0
+        *
+        * @param array $theme_json The theme.json converted to an array.
+        * @return array The block nodes in theme.json.
+        */
+       private static function get_block_nodes( $theme_json ) {
+               $selectors = static::get_blocks_metadata();
+               $nodes     = array();
+               if ( ! isset( $theme_json['styles'] ) ) {
+                       return $nodes;
+               }
+
+               // Blocks.
+               if ( ! isset( $theme_json['styles']['blocks'] ) ) {
+                       return $nodes;
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 foreach ( $theme_json['styles']['blocks'] as $name => $node ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $selector = null;
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( isset( $selectors[ $name ]['selector'] ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1510,6 +1622,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">                        $nodes[] = array(
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                'name'     => $name,
</ins><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><span class="cx" style="display: block; padding: 0 10px">                                'duotone'  => $duotone_selector,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1521,6 +1634,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                'path'     => array( 'styles', 'blocks', $name, 'elements', $element ),
</span><span class="cx" style="display: block; padding: 0 10px">                                                'selector' => $selectors[ $name ]['elements'][ $element ],
</span><span class="cx" style="display: block; padding: 0 10px">                                        );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                                       // Handle any pseudo selectors for the element.
+                                       if ( array_key_exists( $element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
+                                               foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) {
+                                                       if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] ) ) {
+                                                               $nodes[] = array(
+                                                                       'path'     => array( 'styles', 'blocks', $name, 'elements', $element ),
+                                                                       'selector' => static::append_to_selector( $selectors[ $name ]['elements'][ $element ], $pseudo_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">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1529,6 +1654,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">+         * Gets the CSS rules for a particular block from theme.json.
+        *
+        * @since 6.1.0
+        *
+        * @param array $block_metadata Meta data about the block to get styles for.
+        * @return array Styles for the block.
+        */
+       public function get_styles_for_block( $block_metadata ) {
+
+               $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );
+
+               $selector = $block_metadata['selector'];
+               $settings = _wp_array_get( $this->theme_json, array( 'settings' ) );
+
+               /*
+                * Get a reference to element name from path.
+                * $block_metadata['path'] = array( 'styles','elements','link' );
+                * Make sure that $block_metadata['path'] describes an element node, like [ 'styles', 'element', 'link' ].
+                * Skip non-element paths like just ['styles'].
+                */
+               $is_processing_element = in_array( 'elements', $block_metadata['path'], true );
+
+               $current_element = $is_processing_element ? $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ] : null;
+
+               $element_pseudo_allowed = array();
+
+               if ( array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
+                       $element_pseudo_allowed = static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ];
+               }
+
+               /*
+                * Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover").
+                * This also resets the array keys.
+                */
+               $pseudo_matches = array_values(
+                       array_filter(
+                               $element_pseudo_allowed,
+                               function( $pseudo_selector ) use ( $selector ) {
+                                       return str_contains( $selector, $pseudo_selector );
+                               }
+                       )
+               );
+
+               $pseudo_selector = isset( $pseudo_matches[0] ) ? $pseudo_matches[0] : null;
+
+               /*
+                * If the current selector is a pseudo selector that's defined in the allow list for the current
+                * element then compute the style properties for it.
+                * Otherwise just compute the styles for the default selector as normal.
+                */
+               if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) &&
+                       array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS )
+                       && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true )
+               ) {
+                       $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json );
+               } else {
+                       $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json );
+               }
+
+               $block_rules = '';
+
+               /*
+                * 1. Separate the declarations that use the general selector
+                * from the ones using the duotone selector.
+                */
+               $declarations_duotone = array();
+               foreach ( $declarations as $index => $declaration ) {
+                       if ( 'filter' === $declaration['name'] ) {
+                               unset( $declarations[ $index ] );
+                               $declarations_duotone[] = $declaration;
+                       }
+               }
+
+               /*
+                * Reset default browser margin on the root body element.
+                * This is set on the root selector **before** generating the ruleset
+                * from the `theme.json`. This is to ensure that if the `theme.json` declares
+                * `margin` in its `spacing` declaration for the `body` element then these
+                * user-generated values take precedence in the CSS cascade.
+                * @link https://github.com/WordPress/gutenberg/issues/36147.
+                */
+               if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
+                       $block_rules .= 'body { margin: 0; }';
+               }
+
+               // 2. Generate and append the rules that use the general selector.
+               $block_rules .= static::to_ruleset( $selector, $declarations );
+
+               // 3. Generate and append the rules that use the duotone selector.
+               if ( isset( $block_metadata['duotone'] ) && ! empty( $declarations_duotone ) ) {
+                       $selector_duotone = static::scope_selector( $block_metadata['selector'], $block_metadata['duotone'] );
+                       $block_rules     .= static::to_ruleset( $selector_duotone, $declarations_duotone );
+               }
+
+               if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
+                       $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-block-start: 0; margin-block-end: 0; }';
+                               $block_rules .= '.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }';
+                       }
+               }
+
+               return $block_rules;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * For metadata values that can either be booleans or paths to booleans, gets the value.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * ```php
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1837,10 +2072,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $valid_block_names   = array_keys( static::get_blocks_metadata() );
</span><span class="cx" style="display: block; padding: 0 10px">                $valid_element_names = array_keys( static::ELEMENTS );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $theme_json          = static::sanitize( $theme_json, $valid_block_names, $valid_element_names );
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $theme_json = static::sanitize( $theme_json, $valid_block_names, $valid_element_names );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $blocks_metadata = static::get_blocks_metadata();
</span><span class="cx" style="display: block; padding: 0 10px">                $style_nodes     = static::get_style_nodes( $theme_json, $blocks_metadata );
</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">                        $input = _wp_array_get( $theme_json, $metadata['path'], array() );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( empty( $input ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1848,6 +2085,25 @@
</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">                        $output = static::remove_insecure_styles( $input );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       /*
+                        * Get a reference to element name from path.
+                        * $metadata['path'] = array( 'styles', 'elements', 'link' );
+                        */
+                       $current_element = $metadata['path'][ count( $metadata['path'] ) - 1 ];
+
+                       /*
+                        * $output is stripped of pseudo selectors. Re-add and process them
+                        * or insecure styles here.
+                        */
+                       if ( array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
+                               foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] as $pseudo_selector ) {
+                                       if ( isset( $input[ $pseudo_selector ] ) ) {
+                                               $output[ $pseudo_selector ] = static::remove_insecure_styles( $input[ $pseudo_selector ] );
+                                       }
+                               }
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         if ( ! empty( $output ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                _wp_array_set( $sanitized, $metadata['path'], $output );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span></span></pre></div>
<a id="trunksrcwpincludesglobalstylesandsettingsphp"></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/global-styles-and-settings.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/global-styles-and-settings.php      2022-09-09 12:37:47 UTC (rev 54117)
+++ trunk/src/wp-includes/global-styles-and-settings.php        2022-09-10 12:37:00 UTC (rev 54118)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -192,3 +192,45 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        return $svgs;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * Adds global style rules to the inline style for each block.
+ *
+ * @since 6.1.0
+ */
+function wp_add_global_styles_for_blocks() {
+       $tree        = WP_Theme_JSON_Resolver::get_merged_data();
+       $block_nodes = $tree->get_styles_block_nodes();
+       foreach ( $block_nodes as $metadata ) {
+               $block_css = $tree->get_styles_for_block( $metadata );
+
+               if ( isset( $metadata['name'] ) ) {
+                       $block_name = str_replace( 'core/', '', $metadata['name'] );
+                       /*
+                        * These block styles are added on block_render.
+                        * This hooks inline CSS to them so that they are loaded conditionally
+                        * based on whether or not the block is used on the page.
+                        */
+                       wp_add_inline_style( 'wp-block-' . $block_name, $block_css );
+               }
+
+               // The likes of block element styles from theme.json do not have  $metadata['name'] set.
+               if ( ! isset( $metadata['name'] ) && ! empty( $metadata['path'] ) ) {
+                       $result = array_values(
+                               array_filter(
+                                       $metadata['path'],
+                                       function ( $item ) {
+                                               if ( strpos( $item, 'core/' ) !== false ) {
+                                                       return true;
+                                               }
+                                               return false;
+                                       }
+                               )
+                       );
+                       if ( isset( $result[0] ) ) {
+                               $block_name = str_replace( 'core/', '', $result[0] );
+                               wp_add_inline_style( 'wp-block-' . $block_name, $block_css );
+                       }
+               }
+       }
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesscriptloaderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/script-loader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/script-loader.php   2022-09-09 12:37:47 UTC (rev 54117)
+++ trunk/src/wp-includes/script-loader.php     2022-09-10 12:37:00 UTC (rev 54118)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2354,6 +2354,30 @@
</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">+ * Applies a filter to the list of style nodes that comes from WP_Theme_JSON::get_style_nodes().
+ *
+ * This particular filter removes all of the blocks from the array.
+ *
+ * We want WP_Theme_JSON to be ignorant of the implementation details of how the CSS is being used.
+ * This filter allows us to modify the output of WP_Theme_JSON depending on whether or not we are
+ * loading separate assets, without making the class aware of that detail.
+ *
+ * @since 6.1.0
+ *
+ * @param array $nodes The nodes to filter.
+ * @return array A filtered array of style nodes.
+ */
+function wp_filter_out_block_nodes( $nodes ) {
+       return array_filter(
+               $nodes,
+               function( $node ) {
+                       return ! in_array( 'blocks', $node['path'], true );
+               },
+               ARRAY_FILTER_USE_BOTH
+       );
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * Enqueues the global styles defined via theme.json.
</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">@@ -2377,6 +2401,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">                return;
</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">+        /*
+        * If we are loading CSS for each block separately, then we can load the theme.json CSS conditionally.
+        * This removes the CSS from the global-styles stylesheet and adds it to the inline CSS for each block.
+        */
+       if ( $separate_assets ) {
+               add_filter( 'get_style_nodes', 'wp_filter_out_block_nodes' );
+               // Add each block as an inline css.
+               wp_add_global_styles_for_blocks();
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         $stylesheet = wp_get_global_stylesheet();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        if ( empty( $stylesheet ) ) {
</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   2022-09-09 12:37:47 UTC (rev 54117)
+++ trunk/tests/phpunit/tests/theme/wpThemeJson.php     2022-09-10 12:37:00 UTC (rev 54118)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -367,8 +367,8 @@
</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">                $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;}';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertEquals( $styles, $theme_json->get_stylesheet() );
-               $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertSame( $styles, $theme_json->get_stylesheet() );
+               $this->assertSame( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
</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">@@ -399,8 +399,8 @@
</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">                $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; }';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertEquals( $expected, $theme_json->get_stylesheet() );
-               $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertSame( $expected, $theme_json->get_stylesheet() );
+               $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
</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">@@ -424,8 +424,8 @@
</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">                $expected = 'body { margin: 0; }body{--wp--style--block-gap: 1em;}.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-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertEquals( $expected, $theme_json->get_stylesheet() );
-               $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertSame( $expected, $theme_json->get_stylesheet() );
+               $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
</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">@@ -458,9 +458,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">-                                        'spacing'    => array(
-                                               'blockGap' => false,
-                                       ),
</del><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">@@ -553,13 +550,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">                $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;}';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $styles    = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);}.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-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: 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;}';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $styles    = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);}.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; }a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){back
 ground-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}';
</ins><span class="cx" style="display: block; padding: 0 10px">                 $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;}';
</span><span class="cx" style="display: block; padding: 0 10px">                $all       = $variables . $styles . $presets;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $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' ) ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertSame( $all, $theme_json->get_stylesheet() );
+               $this->assertSame( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
+               $this->assertSame( $presets, $theme_json->get_stylesheet( array( 'presets' ) ) );
+               $this->assertSame( $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="lines" style="display: block; padding: 0 10px; color: #888">@@ -587,7 +584,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->assertEquals(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertSame(
</ins><span class="cx" style="display: block; padding: 0 10px">                         '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;}',
</span><span class="cx" style="display: block; padding: 0 10px">                        $theme_json->get_stylesheet( array( 'presets' ) )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -631,10 +628,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $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;}';
</span><span class="cx" style="display: block; padding: 0 10px">                $variables = '.wp-block-group{--wp--preset--color--grey: grey;}';
</span><span class="cx" style="display: block; padding: 0 10px">                $all       = $variables . $styles . $presets;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $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' ) ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertSame( $all, $theme_json->get_stylesheet() );
+               $this->assertSame( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
+               $this->assertSame( $presets, $theme_json->get_stylesheet( array( 'presets' ) ) );
+               $this->assertSame( $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="lines" style="display: block; padding: 0 10px; color: #888">@@ -672,11 +669,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">-                $this->assertEquals(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertSame(
</ins><span class="cx" style="display: block; padding: 0 10px">                         '.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--col
 or--light-grey) !important;}.has-white-2-black-border-color{border-color: var(--wp--preset--color--white-2-black) !important;}',
</span><span class="cx" style="display: block; padding: 0 10px">                        $theme_json->get_stylesheet( array( '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">-                $this->assertEquals(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertSame(
</ins><span class="cx" style="display: block; padding: 0 10px">                         '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;--wp--custom--white-2-black: value;}',
</span><span class="cx" style="display: block; padding: 0 10px">                        $theme_json->get_stylesheet( array( 'variables' ) )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -719,7 +716,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'default'
</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->assertEquals(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertSame(
</ins><span class="cx" style="display: block; padding: 0 10px">                         '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;}',
</span><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="lines" style="display: block; padding: 0 10px; color: #888">@@ -726,6 +723,279 @@
</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 56467
+        */
+       public function test_get_stylesheet_handles_whitelisted_element_pseudo_selectors() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'elements' => array(
+                                               'link' => array(
+                                                       'color'  => array(
+                                                               'text'       => 'green',
+                                                               'background' => 'red',
+                                                       ),
+                                                       ':hover' => array(
+                                                               'color'      => array(
+                                                                       'text'       => 'red',
+                                                                       'background' => 'green',
+                                                               ),
+                                                               'typography' => array(
+                                                                       'textTransform' => 'uppercase',
+                                                                       'fontSize'      => '10em',
+                                                               ),
+                                                       ),
+                                                       ':focus' => array(
+                                                               'color' => array(
+                                                                       'text'       => 'yellow',
+                                                                       'background' => 'black',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $base_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; }';
+
+               $element_styles = 'a:where(:not(.wp-element-button)){background-color: red;color: green;}a:where(:not(.wp-element-button)):hover{background-color: green;color: red;font-size: 10em;text-transform: uppercase;}a:where(:not(.wp-element-button)):focus{background-color: black;color: yellow;}';
+
+               $expected = $base_styles . $element_styles;
+
+               $this->assertSame( $expected, $theme_json->get_stylesheet() );
+               $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
+       }
+
+       /**
+        * @ticket 56467
+        */
+       public function test_get_stylesheet_handles_only_pseudo_selector_rules_for_given_property() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'elements' => array(
+                                               'link' => array(
+                                                       ':hover' => array(
+                                                               'color'      => array(
+                                                                       'text'       => 'red',
+                                                                       'background' => 'green',
+                                                               ),
+                                                               'typography' => array(
+                                                                       'textTransform' => 'uppercase',
+                                                                       'fontSize'      => '10em',
+                                                               ),
+                                                       ),
+                                                       ':focus' => array(
+                                                               'color' => array(
+                                                                       'text'       => 'yellow',
+                                                                       'background' => 'black',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $base_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; }';
+
+               $element_styles = 'a:where(:not(.wp-element-button)):hover{background-color: green;color: red;font-size: 10em;text-transform: uppercase;}a:where(:not(.wp-element-button)):focus{background-color: black;color: yellow;}';
+
+               $expected = $base_styles . $element_styles;
+
+               $this->assertSame( $expected, $theme_json->get_stylesheet() );
+               $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
+       }
+
+       /**
+        * @ticket 56467
+        */
+       public function test_get_stylesheet_ignores_pseudo_selectors_on_non_whitelisted_elements() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'elements' => array(
+                                               'h4' => array(
+                                                       'color'  => array(
+                                                               'text'       => 'green',
+                                                               'background' => 'red',
+                                                       ),
+                                                       ':hover' => array(
+                                                               'color' => array(
+                                                                       'text'       => 'red',
+                                                                       'background' => 'green',
+                                                               ),
+                                                       ),
+                                                       ':focus' => array(
+                                                               'color' => array(
+                                                                       'text'       => 'yellow',
+                                                                       'background' => 'black',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $base_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; }';
+
+               $element_styles = 'h4{background-color: red;color: green;}';
+
+               $expected = $base_styles . $element_styles;
+
+               $this->assertSame( $expected, $theme_json->get_stylesheet() );
+               $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
+       }
+
+       /**
+        * @ticket 56467
+        */
+       public function test_get_stylesheet_ignores_non_whitelisted_pseudo_selectors() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'elements' => array(
+                                               'link' => array(
+                                                       'color'     => array(
+                                                               'text'       => 'green',
+                                                               'background' => 'red',
+                                                       ),
+                                                       ':hover'    => array(
+                                                               'color' => array(
+                                                                       'text'       => 'red',
+                                                                       'background' => 'green',
+                                                               ),
+                                                       ),
+                                                       ':levitate' => array(
+                                                               'color' => array(
+                                                                       'text'       => 'yellow',
+                                                                       'background' => 'black',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $base_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; }';
+
+               $element_styles = 'a:where(:not(.wp-element-button)){background-color: red;color: green;}a:where(:not(.wp-element-button)):hover{background-color: green;color: red;}';
+
+               $expected = $base_styles . $element_styles;
+
+               $this->assertSame( $expected, $theme_json->get_stylesheet() );
+               $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
+               $this->assertStringNotContainsString( 'a:levitate{', $theme_json->get_stylesheet( array( 'styles' ) ) );
+       }
+
+       /**
+        * @ticket 56467
+        */
+       public function test_get_stylesheet_handles_priority_of_elements_vs_block_elements_pseudo_selectors() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'blocks' => array(
+                                               'core/group' => array(
+                                                       'elements' => array(
+                                                               'link' => array(
+                                                                       'color'  => array(
+                                                                               'text'       => 'green',
+                                                                               'background' => 'red',
+                                                                       ),
+                                                                       ':hover' => array(
+                                                                               'color'      => array(
+                                                                                       'text'       => 'red',
+                                                                                       'background' => 'green',
+                                                                               ),
+                                                                               'typography' => array(
+                                                                                       'textTransform' => 'uppercase',
+                                                                                       'fontSize' => '10em',
+                                                                               ),
+                                                                       ),
+                                                                       ':focus' => array(
+                                                                               'color' => array(
+                                                                                       'text'       => 'yellow',
+                                                                                       'background' => 'black',
+                                                                               ),
+                                                                       ),
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $base_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; }';
+
+               $element_styles = '.wp-block-group a:where(:not(.wp-element-button)){background-color: red;color: green;}.wp-block-group a:where(:not(.wp-element-button)):hover{background-color: green;color: red;font-size: 10em;text-transform: uppercase;}.wp-block-group a:where(:not(.wp-element-button)):focus{background-color: black;color: yellow;}';
+
+               $expected = $base_styles . $element_styles;
+
+               $this->assertSame( $expected, $theme_json->get_stylesheet() );
+               $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
+       }
+
+       /**
+        * @ticket 56467
+        */
+       public function test_get_stylesheet_handles_whitelisted_block_level_element_pseudo_selectors() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'elements' => array(
+                                               'link' => array(
+                                                       'color'  => array(
+                                                               'text'       => 'green',
+                                                               'background' => 'red',
+                                                       ),
+                                                       ':hover' => array(
+                                                               'color' => array(
+                                                                       'text'       => 'red',
+                                                                       'background' => 'green',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                                       'blocks'   => array(
+                                               'core/group' => array(
+                                                       'elements' => array(
+                                                               'link' => array(
+                                                                       ':hover' => array(
+                                                                               'color' => array(
+                                                                                       'text'       => 'yellow',
+                                                                                       'background' => 'black',
+                                                                               ),
+                                                                       ),
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $base_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; }';
+
+               $element_styles = 'a:where(:not(.wp-element-button)){background-color: red;color: green;}a:where(:not(.wp-element-button)):hover{background-color: green;color: red;}.wp-block-group a:where(:not(.wp-element-button)):hover{background-color: black;color: yellow;}';
+
+               $expected = $base_styles . $element_styles;
+
+               $this->assertSame( $expected, $theme_json->get_stylesheet() );
+               $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @ticket 52991
</span><span class="cx" style="display: block; padding: 0 10px">         * @ticket 54336
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2087,6 +2357,56 @@
</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 56467
+        */
+       public function test_remove_invalid_element_pseudo_selectors() {
+               $actual = WP_Theme_JSON::remove_insecure_properties(
+                       array(
+                               'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                               'styles'  => array(
+                                       'elements' => array(
+                                               'link' => array(
+                                                       'color'  => array(
+                                                               'text'       => 'hotpink',
+                                                               'background' => 'yellow',
+                                                       ),
+                                                       ':hover' => array(
+                                                               'color' => array(
+                                                                       'text'       => 'red',
+                                                                       'background' => 'blue',
+                                                               ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       true
+               );
+
+               $expected = array(
+                       'version' => WP_Theme_JSON::LATEST_SCHEMA,
+                       'styles'  => array(
+                               'elements' => array(
+                                       'link' => array(
+                                               'color'  => array(
+                                                       'text'       => 'hotpink',
+                                                       'background' => 'yellow',
+                                               ),
+                                               ':hover' => array(
+                                                       'color' => array(
+                                                               'text'       => 'red',
+                                                               'background' => 'blue',
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+               );
+
+               $this->assertEqualSetsWithIndex( $expected, $actual );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @ticket 54336
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_get_custom_templates() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2621,4 +2941,144 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertEqualSetsWithIndex( $expected, $actual );
</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 56467
+        */
+       public function test_get_element_class_name_button() {
+               $expected = 'wp-element-button';
+               $actual   = WP_Theme_JSON::get_element_class_name( 'button' );
+
+               $this->assertSame( $expected, $actual );
+       }
+
+       /**
+        * @ticket 56467
+        */
+       public function test_get_element_class_name_invalid() {
+               $expected = '';
+               $actual   = WP_Theme_JSON::get_element_class_name( 'unknown-element' );
+
+               $this->assertSame( $expected, $actual );
+       }
+
+       /**
+        * Testing that dynamic properties in theme.json return the value they refrence,
+        * e.g. array( 'ref' => 'styles.color.background' ) => "#ffffff".
+        *
+        * @ticket 56467
+        */
+       public function test_get_property_value_valid() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => 2,
+                               'styles'  => array(
+                                       'color'    => array(
+                                               'background' => '#ffffff',
+                                               'text'       => '#000000',
+                                       ),
+                                       'elements' => array(
+                                               'button' => array(
+                                                       'color' => array(
+                                                               'background' => array( 'ref' => 'styles.color.text' ),
+                                                               'text'       => array( 'ref' => 'styles.color.background' ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $expected = 'body { margin: 0; }body{background-color: #ffffff;color: #000000;}.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-element-button, .wp-block-button__link{background-color: #000000;color: #ffffff;}';
+               $this->assertSame( $expected, $theme_json->get_stylesheet() );
+       }
+
+       /**
+        * Testing that dynamic properties in theme.json that refer to other dynamic properties in a loop
+        * should be left untouched.
+        *
+        * @ticket 56467
+        * @expectedIncorrectUsage get_property_value
+        */
+       public function test_get_property_value_loop() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => 2,
+                               'styles'  => array(
+                                       'color'    => array(
+                                               'background' => '#ffffff',
+                                               'text'       => array( 'ref' => 'styles.elements.button.color.background' ),
+                                       ),
+                                       'elements' => array(
+                                               'button' => array(
+                                                       'color' => array(
+                                                               'background' => array( 'ref' => 'styles.color.text' ),
+                                                               'text'       => array( 'ref' => 'styles.color.background' ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $expected = 'body { margin: 0; }body{background-color: #ffffff;}.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-element-button, .wp-block-button__link{color: #ffffff;}';
+               $this->assertSame( $expected, $theme_json->get_stylesheet() );
+       }
+
+       /**
+        * Testing that dynamic properties in theme.json that refer to other dynamic properties
+        * should be left unprocessed.
+        *
+        * @ticket 56467
+        * @expectedIncorrectUsage get_property_value
+        */
+       public function test_get_property_value_recursion() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => 2,
+                               'styles'  => array(
+                                       'color'    => array(
+                                               'background' => '#ffffff',
+                                               'text'       => array( 'ref' => 'styles.color.background' ),
+                                       ),
+                                       'elements' => array(
+                                               'button' => array(
+                                                       'color' => array(
+                                                               'background' => array( 'ref' => 'styles.color.text' ),
+                                                               'text'       => array( 'ref' => 'styles.color.background' ),
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $expected = 'body { margin: 0; }body{background-color: #ffffff;color: #ffffff;}.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-element-button, .wp-block-button__link{color: #ffffff;}';
+               $this->assertSame( $expected, $theme_json->get_stylesheet() );
+       }
+
+       /**
+        * Testing that dynamic properties in theme.json that refer to themselves
+        * should be left unprocessed.
+        *
+        * @ticket 56467
+        * @expectedIncorrectUsage get_property_value
+        */
+       public function test_get_property_value_self() {
+               $theme_json = new WP_Theme_JSON(
+                       array(
+                               'version' => 2,
+                               'styles'  => array(
+                                       'color' => array(
+                                               'background' => '#ffffff',
+                                               'text'       => array( 'ref' => 'styles.color.text' ),
+                                       ),
+                               ),
+                       )
+               );
+
+               $expected = 'body { margin: 0; }body{background-color: #ffffff;}.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->assertSame( $expected, $theme_json->get_stylesheet() );
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre>
</div>
</div>

</body>
</html>