--- a/wp/wp-includes/class-wp-theme-json.php Fri Sep 05 18:40:08 2025 +0200
+++ b/wp/wp-includes/class-wp-theme-json.php Fri Sep 05 18:52:52 2025 +0200
@@ -226,7 +226,7 @@
* @since 6.4.0 Added `writing-mode` property.
* @since 6.5.0 Added `aspect-ratio` property.
* @since 6.6.0 Added `background-[image|position|repeat|size]` properties.
- *
+ * @since 6.7.0 Added `background-attachment` property.
* @var array
*/
const PROPERTIES_METADATA = array(
@@ -237,6 +237,7 @@
'background-position' => array( 'background', 'backgroundPosition' ),
'background-repeat' => array( 'background', 'backgroundRepeat' ),
'background-size' => array( 'background', 'backgroundSize' ),
+ 'background-attachment' => array( 'background', 'backgroundAttachment' ),
'border-radius' => array( 'border', 'radius' ),
'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ),
'border-top-right-radius' => array( 'border', 'radius', 'topRight' ),
@@ -305,7 +306,6 @@
*
* @since 6.2.0
* @since 6.6.0 Added background-image properties.
- *
* @var array
*/
const INDIRECT_PROPERTIES_METADATA = array(
@@ -337,6 +337,7 @@
* setting key.
*
* @since 5.9.0
+ * @var array
*/
const PROTECTED_PROPERTIES = array(
'spacing.blockGap' => array( 'spacing', 'blockGap' ),
@@ -469,11 +470,10 @@
),
);
- /*
+ /**
* The valid properties for fontFamilies under settings key.
*
* @since 6.5.0
- *
* @var array
*/
const FONT_FAMILY_SCHEMA = array(
@@ -515,15 +515,15 @@
* @since 6.3.0 Added support for `typography.textColumns`.
* @since 6.5.0 Added support for `dimensions.aspectRatio`.
* @since 6.6.0 Added `background` sub properties to top-level only.
- *
* @var array
*/
const VALID_STYLES = array(
'background' => array(
- 'backgroundImage' => 'top',
- 'backgroundPosition' => 'top',
- 'backgroundRepeat' => 'top',
- 'backgroundSize' => 'top',
+ 'backgroundImage' => null,
+ 'backgroundPosition' => null,
+ 'backgroundRepeat' => null,
+ 'backgroundSize' => null,
+ 'backgroundAttachment' => null,
),
'border' => array(
'color' => null,
@@ -578,7 +578,7 @@
/**
* Defines which pseudo selectors are enabled for which elements.
*
- * The order of the selectors should be: link, any-link, visited, hover, focus, active.
+ * The order of the selectors should be: link, any-link, visited, hover, focus, focus-visible, active.
* This is to ensure the user action (hover, focus and active) styles have a higher
* specificity than the visited styles, which in turn have a higher specificity than
* the unvisited styles.
@@ -588,10 +588,12 @@
*
* @since 6.1.0
* @since 6.2.0 Added support for ':link' and ':any-link'.
+ * @since 6.8.0 Added support for ':focus-visible'.
+ * @var array
*/
const VALID_ELEMENT_PSEUDO_SELECTORS = array(
- 'link' => array( ':link', ':any-link', ':visited', ':hover', ':focus', ':active' ),
- 'button' => array( ':link', ':any-link', ':visited', ':hover', ':focus', ':active' ),
+ 'link' => array( ':link', ':any-link', ':visited', ':hover', ':focus', ':focus-visible', ':active' ),
+ 'button' => array( ':link', ':any-link', ':visited', ':hover', ':focus', ':focus-visible', ':active' ),
);
/**
@@ -756,9 +758,10 @@
}
$this->theme_json = WP_Theme_JSON_Schema::migrate( $theme_json, $origin );
- $valid_block_names = array_keys( static::get_blocks_metadata() );
+ $blocks_metadata = static::get_blocks_metadata();
+ $valid_block_names = array_keys( $blocks_metadata );
$valid_element_names = array_keys( static::ELEMENTS );
- $valid_variations = static::get_valid_block_style_variations();
+ $valid_variations = static::get_valid_block_style_variations( $blocks_metadata );
$this->theme_json = static::unwrap_shared_block_style_variations( $this->theme_json, $valid_variations );
$this->theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations );
$this->theme_json = static::maybe_opt_in_into_settings( $this->theme_json );
@@ -935,7 +938,6 @@
* @return array The sanitized output.
*/
protected static function sanitize( $input, $valid_block_names, $valid_element_names, $valid_variations ) {
-
$output = array();
if ( ! is_array( $input ) ) {
@@ -1316,6 +1318,8 @@
* - `variables`: only the CSS Custom Properties for presets & custom ones.
* - `styles`: only the styles section in theme.json.
* - `presets`: only the classes for the presets.
+ * - `base-layout-styles`: only the base layout styles.
+ * - `custom-css`: only the custom CSS.
* @param string[] $origins A list of origins to include. By default it includes VALID_ORIGINS.
* @param array $options {
* Optional. An array of options for now used for internal purposes only (may change without notice).
@@ -1423,6 +1427,12 @@
$stylesheet .= $this->get_preset_classes( $setting_nodes, $origins );
}
+ // Load the custom CSS last so it has the highest specificity.
+ if ( in_array( 'custom-css', $types, true ) ) {
+ // Add the global styles root CSS.
+ $stylesheet .= _wp_array_get( $this->theme_json, array( 'styles', 'css' ) );
+ }
+
return $stylesheet;
}
@@ -1488,10 +1498,12 @@
* Returns the global styles custom CSS.
*
* @since 6.2.0
+ * @deprecated 6.7.0 Use {@see 'get_stylesheet'} instead.
*
* @return string The global styles custom CSS.
*/
public function get_custom_css() {
+ _deprecated_function( __METHOD__, '6.7.0', 'get_stylesheet' );
// Add the global styles root CSS.
$stylesheet = isset( $this->theme_json['styles']['css'] ) ? $this->theme_json['styles']['css'] : '';
@@ -2284,7 +2296,7 @@
*
* array(
* 'name' => 'property_name',
- * 'value' => 'property_value,
+ * 'value' => 'property_value',
* )
*
* @since 5.8.0
@@ -2292,6 +2304,7 @@
* @since 6.1.0 Added `$theme_json`, `$selector`, and `$use_root_padding` parameters.
* @since 6.5.0 Output a `min-height: unset` rule when `aspect-ratio` is set.
* @since 6.6.0 Pass current theme JSON settings to wp_get_typography_font_size_value(), and process background properties.
+ * @since 6.7.0 `ref` resolution of background properties, and assigning custom default values.
*
* @param array $styles Styles to process.
* @param array $settings Theme settings.
@@ -2299,26 +2312,32 @@
* @param array $theme_json Theme JSON array.
* @param string $selector The style block selector.
* @param boolean $use_root_padding Whether to add custom properties at root level.
- * @return array Returns the modified $declarations.
+ * @return array Returns the modified $declarations.
*/
protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) {
+ if ( empty( $styles ) ) {
+ return array();
+ }
+
if ( null === $properties ) {
$properties = static::PROPERTIES_METADATA;
}
-
- $declarations = array();
- if ( empty( $styles ) ) {
- return $declarations;
- }
-
+ $declarations = array();
$root_variable_duplicates = array();
+ $root_style_length = strlen( '--wp--style--root--' );
foreach ( $properties as $css_property => $value_path ) {
- $value = static::get_property_value( $styles, $value_path, $theme_json );
-
- if ( str_starts_with( $css_property, '--wp--style--root--' ) && ( static::ROOT_BLOCK_SELECTOR !== $selector || ! $use_root_padding ) ) {
+ if ( ! is_array( $value_path ) ) {
continue;
}
+
+ $is_root_style = str_starts_with( $css_property, '--wp--style--root--' );
+ if ( $is_root_style && ( static::ROOT_BLOCK_SELECTOR !== $selector || ! $use_root_padding ) ) {
+ continue;
+ }
+
+ $value = static::get_property_value( $styles, $value_path, $theme_json );
+
/*
* Root-level padding styles don't currently support strings with CSS shorthand values.
* This may change: https://github.com/WordPress/gutenberg/issues/40132.
@@ -2327,33 +2346,48 @@
continue;
}
- if ( str_starts_with( $css_property, '--wp--style--root--' ) && $use_root_padding ) {
- $root_variable_duplicates[] = substr( $css_property, strlen( '--wp--style--root--' ) );
+ if ( $is_root_style && $use_root_padding ) {
+ $root_variable_duplicates[] = substr( $css_property, $root_style_length );
+ }
+
+ /*
+ * Processes background image styles.
+ * If the value is a URL, it will be converted to a CSS `url()` value.
+ * For uploaded image (images with a database ID), apply size and position defaults,
+ * equal to those applied in block supports in lib/background.php.
+ */
+ if ( 'background-image' === $css_property && ! empty( $value ) ) {
+ $background_styles = wp_style_engine_get_styles(
+ array( 'background' => array( 'backgroundImage' => $value ) )
+ );
+ $value = $background_styles['declarations'][ $css_property ];
+ }
+ if ( empty( $value ) && static::ROOT_BLOCK_SELECTOR !== $selector && ! empty( $styles['background']['backgroundImage']['id'] ) ) {
+ if ( 'background-size' === $css_property ) {
+ $value = 'cover';
+ }
+ // If the background size is set to `contain` and no position is set, set the position to `center`.
+ if ( 'background-position' === $css_property ) {
+ $background_size = $styles['background']['backgroundSize'] ?? null;
+ $value = 'contain' === $background_size ? '50% 50%' : null;
+ }
+ }
+
+ // Skip if empty and not "0" or value represents array of longhand values.
+ $has_missing_value = empty( $value ) && ! is_numeric( $value );
+ if ( $has_missing_value || is_array( $value ) ) {
+ continue;
}
/*
* Look up protected properties, keyed by value path.
* Skip protected properties that are explicitly set to `null`.
*/
- if ( is_array( $value_path ) ) {
- $path_string = implode( '.', $value_path );
- if (
- isset( static::PROTECTED_PROPERTIES[ $path_string ] ) &&
- _wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null
- ) {
- continue;
- }
- }
-
- // Processes background styles.
- if ( 'background' === $value_path[0] && isset( $styles['background'] ) ) {
- $background_styles = wp_style_engine_get_styles( array( 'background' => $styles['background'] ) );
- $value = isset( $background_styles['declarations'][ $css_property ] ) ? $background_styles['declarations'][ $css_property ] : $value;
- }
-
- // Skip if empty and not "0" or value represents array of longhand values.
- $has_missing_value = empty( $value ) && ! is_numeric( $value );
- if ( $has_missing_value || is_array( $value ) ) {
+ $path_string = implode( '.', $value_path );
+ if (
+ isset( static::PROTECTED_PROPERTIES[ $path_string ] ) &&
+ _wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null
+ ) {
continue;
}
@@ -2410,6 +2444,7 @@
* to the standard form "--wp--preset--color--secondary".
* This is already done by the sanitize method,
* so every property will be in the standard form.
+ * @since 6.7.0 Added support for background image refs.
*
* @param array $styles Styles subtree.
* @param array $path Which property to process.
@@ -2426,14 +2461,18 @@
/*
* 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.
+ * where the value is an array with a "ref" key, pointing to a path.
* For example: { "ref": "style.color.background" } => "#fff".
+ * In the case of backgroundImage, if both a ref and a URL are present in the value,
+ * the URL takes precedence and the ref is ignored.
*/
if ( is_array( $value ) && isset( $value['ref'] ) ) {
$value_path = explode( '.', $value['ref'] );
$ref_value = _wp_array_get( $theme_json, $value_path );
+ // Background Image refs can refer to a string or an array containing a URL string.
+ $ref_value_url = $ref_value['url'] ?? null;
// Only use the ref value if we find anything.
- if ( ! empty( $ref_value ) && is_string( $ref_value ) ) {
+ if ( ! empty( $ref_value ) && ( is_string( $ref_value ) || is_string( $ref_value_url ) ) ) {
$value = $ref_value;
}
@@ -2656,69 +2695,101 @@
* @since 6.1.0
* @since 6.3.0 Refactored and stabilized selectors API.
* @since 6.6.0 Added optional selectors and options for generating block nodes.
+ * @since 6.7.0 Added $include_node_paths_only option.
*
* @param array $theme_json The theme.json converted to an array.
* @param array $selectors Optional list of selectors per block.
* @param array $options {
* Optional. An array of options for now used for internal purposes only (may change without notice).
*
- * @type bool $include_block_style_variations Includes nodes for block style variations. Default false.
+ * @type bool $include_block_style_variations Include nodes for block style variations. Default false.
+ * @type bool $include_node_paths_only Return only block nodes node paths. Default false.
* }
* @return array The block nodes in theme.json.
*/
private static function get_block_nodes( $theme_json, $selectors = array(), $options = array() ) {
- $selectors = empty( $selectors ) ? static::get_blocks_metadata() : $selectors;
- $nodes = array();
- if ( ! isset( $theme_json['styles'] ) ) {
+ $nodes = array();
+
+ if ( ! isset( $theme_json['styles']['blocks'] ) ) {
return $nodes;
}
- // Blocks.
- if ( ! isset( $theme_json['styles']['blocks'] ) ) {
- return $nodes;
+ $include_variations = $options['include_block_style_variations'] ?? false;
+ $include_node_paths_only = $options['include_node_paths_only'] ?? false;
+
+ // If only node paths are to be returned, skip selector assignment.
+ if ( ! $include_node_paths_only ) {
+ $selectors = empty( $selectors ) ? static::get_blocks_metadata() : $selectors;
}
foreach ( $theme_json['styles']['blocks'] as $name => $node ) {
- $selector = null;
- if ( isset( $selectors[ $name ]['selector'] ) ) {
- $selector = $selectors[ $name ]['selector'];
- }
-
- $duotone_selector = null;
- if ( isset( $selectors[ $name ]['duotone'] ) ) {
- $duotone_selector = $selectors[ $name ]['duotone'];
- }
-
- $feature_selectors = null;
- if ( isset( $selectors[ $name ]['selectors'] ) ) {
- $feature_selectors = $selectors[ $name ]['selectors'];
+ $node_path = array( 'styles', 'blocks', $name );
+ if ( $include_node_paths_only ) {
+ $variation_paths = array();
+ if ( $include_variations && isset( $node['variations'] ) ) {
+ foreach ( $node['variations'] as $variation => $variation_node ) {
+ $variation_paths[] = array(
+ 'path' => array( 'styles', 'blocks', $name, 'variations', $variation ),
+ );
+ }
+ }
+ $node = array(
+ 'path' => $node_path,
+ );
+ if ( ! empty( $variation_paths ) ) {
+ $node['variations'] = $variation_paths;
+ }
+ $nodes[] = $node;
+ } else {
+ $selector = null;
+ if ( isset( $selectors[ $name ]['selector'] ) ) {
+ $selector = $selectors[ $name ]['selector'];
+ }
+
+ $duotone_selector = null;
+ if ( isset( $selectors[ $name ]['duotone'] ) ) {
+ $duotone_selector = $selectors[ $name ]['duotone'];
+ }
+
+ $feature_selectors = null;
+ if ( isset( $selectors[ $name ]['selectors'] ) ) {
+ $feature_selectors = $selectors[ $name ]['selectors'];
+ }
+
+ $variation_selectors = array();
+ if ( $include_variations && isset( $node['variations'] ) ) {
+ foreach ( $node['variations'] as $variation => $node ) {
+ $variation_selectors[] = array(
+ 'path' => array( 'styles', 'blocks', $name, 'variations', $variation ),
+ 'selector' => $selectors[ $name ]['styleVariations'][ $variation ],
+ );
+ }
+ }
+
+ $nodes[] = array(
+ 'name' => $name,
+ 'path' => $node_path,
+ 'selector' => $selector,
+ 'selectors' => $feature_selectors,
+ 'duotone' => $duotone_selector,
+ 'features' => $feature_selectors,
+ 'variations' => $variation_selectors,
+ 'css' => $selector,
+ );
}
- $variation_selectors = array();
- $include_variations = $options['include_block_style_variations'] ?? false;
- if ( $include_variations && isset( $node['variations'] ) ) {
- foreach ( $node['variations'] as $variation => $node ) {
- $variation_selectors[] = array(
- 'path' => array( 'styles', 'blocks', $name, 'variations', $variation ),
- 'selector' => $selectors[ $name ]['styleVariations'][ $variation ],
- );
- }
- }
-
- $nodes[] = array(
- 'name' => $name,
- 'path' => array( 'styles', 'blocks', $name ),
- 'selector' => $selector,
- 'selectors' => $feature_selectors,
- 'duotone' => $duotone_selector,
- 'features' => $feature_selectors,
- 'variations' => $variation_selectors,
- );
-
if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) {
foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) {
+ $node_path = array( 'styles', 'blocks', $name, 'elements', $element );
+ if ( $include_node_paths_only ) {
+ $nodes[] = array(
+ 'path' => $node_path,
+ );
+ continue;
+ }
+
$nodes[] = array(
- 'path' => array( 'styles', 'blocks', $name, 'elements', $element ),
+ 'path' => $node_path,
'selector' => $selectors[ $name ]['elements'][ $element ],
);
@@ -2726,8 +2797,16 @@
if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] ) ) {
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) {
if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] ) ) {
+ $node_path = array( 'styles', 'blocks', $name, 'elements', $element );
+ if ( $include_node_paths_only ) {
+ $nodes[] = array(
+ 'path' => $node_path,
+ );
+ continue;
+ }
+
$nodes[] = array(
- 'path' => array( 'styles', 'blocks', $name, 'elements', $element ),
+ 'path' => $node_path,
'selector' => static::append_to_selector( $selectors[ $name ]['elements'][ $element ], $pseudo_selector ),
);
}
@@ -2749,7 +2828,6 @@
* Fixed custom CSS output in block style variations.
*
* @param array $block_metadata Metadata about the block to get styles for.
- *
* @return string Styles for the block.
*/
public function get_styles_for_block( $block_metadata ) {
@@ -2773,8 +2851,14 @@
// Combine selectors with style variation's selector and add to overall style variation declarations.
foreach ( $variation_declarations as $current_selector => $new_declarations ) {
- // If current selector includes block classname, remove it but leave the whitespace in.
- $shortened_selector = str_replace( $block_metadata['selector'] . ' ', ' ', $current_selector );
+ /*
+ * Clean up any whitespace between comma separated selectors.
+ * This prevents these spaces breaking compound selectors such as:
+ * - `.wp-block-list:not(.wp-block-list .wp-block-list)`
+ * - `.wp-block-image img, .wp-block-image.my-class img`
+ */
+ $clean_current_selector = preg_replace( '/,\s+/', ',', $current_selector );
+ $shortened_selector = str_replace( $block_metadata['selector'], '', $clean_current_selector );
// Prepend the variation selector to the current selector.
$split_selectors = explode( ',', $shortened_selector );
@@ -2822,7 +2906,11 @@
array_filter(
$element_pseudo_allowed,
static function ( $pseudo_selector ) use ( $selector ) {
- return str_contains( $selector, $pseudo_selector );
+ /*
+ * Check if the pseudo selector is in the current selector,
+ * ensuring it is not followed by a dash (e.g., :focus should not match :focus-visible).
+ */
+ return preg_match( '/' . preg_quote( $pseudo_selector, '/' ) . '(?!-)/', $selector ) === 1;
}
)
);
@@ -2949,7 +3037,7 @@
}
}
- // 7. Generate and append any custom CSS rules pertaining to nested block style variations.
+ // 7. Generate and append any custom CSS rules.
if ( isset( $node['css'] ) && ! $is_root_selector ) {
$block_rules .= $this->process_blocks_custom_css( $node['css'], $selector );
}
@@ -2974,9 +3062,9 @@
$use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments'];
/*
- * If there are content and wide widths in theme.json, output them
- * as custom properties on the body element so all blocks can use them.
- */
+ * If there are content and wide widths in theme.json, output them
+ * as custom properties on the body element so all blocks can use them.
+ */
if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) {
$content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize'];
$content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial';
@@ -2987,13 +3075,13 @@
}
/*
- * Reset default browser margin on the body element.
- * This is set on the body 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.
- */
+ * Reset default browser margin on the body element.
+ * This is set on the body 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.
+ */
$css .= ':where(body) { margin: 0; }';
if ( $use_root_padding ) {
@@ -3071,6 +3159,7 @@
*
* @since 5.8.0
* @since 5.9.0 Duotone preset also has origins.
+ * @since 6.7.0 Replace background image objects during merge.
*
* @param WP_Theme_JSON $incoming Data to merge.
*/
@@ -3194,6 +3283,33 @@
}
}
}
+
+ /*
+ * Style values are merged at the leaf level, however
+ * some values provide exceptions, namely style values that are
+ * objects and represent unique definitions for the style.
+ */
+ $style_nodes = static::get_block_nodes(
+ $this->theme_json,
+ array(),
+ array( 'include_node_paths_only' => true )
+ );
+
+ // Add top-level styles.
+ $style_nodes[] = array( 'path' => array( 'styles' ) );
+
+ foreach ( $style_nodes as $style_node ) {
+ $path = $style_node['path'];
+ /*
+ * Background image styles should be replaced, not merged,
+ * as they themselves are specific object definitions for the style.
+ */
+ $background_image_path = array_merge( $path, static::PROPERTIES_METADATA['background-image'] );
+ $content = _wp_array_get( $incoming_data, $background_image_path, null );
+ if ( isset( $content ) ) {
+ _wp_array_set( $this->theme_json, $background_image_path, $content );
+ }
+ }
}
/**
@@ -3222,7 +3338,7 @@
continue;
}
foreach ( $duotone_presets[ $origin ] as $duotone_preset ) {
- $filters .= wp_get_duotone_filter_svg( $duotone_preset );
+ $filters .= WP_Duotone::get_filter_svg_from_preset( $duotone_preset );
}
}
}
@@ -3379,8 +3495,8 @@
* @since 6.6.0 Updated to allow variation element styles and $origin parameter.
*
* @param array $theme_json Structure to sanitize.
- * @param string $origin Optional. What source of data this object represents.
- * One of 'blocks', 'default', 'theme', or 'custom'. Default 'theme'.
+ * @param string $origin Optional. What source of data this object represents.
+ * One of 'blocks', 'default', 'theme', or 'custom'. Default 'theme'.
* @return array Sanitized structure.
*/
public static function remove_insecure_properties( $theme_json, $origin = 'theme' ) {
@@ -3392,9 +3508,10 @@
$theme_json = WP_Theme_JSON_Schema::migrate( $theme_json, $origin );
- $valid_block_names = array_keys( static::get_blocks_metadata() );
+ $blocks_metadata = static::get_blocks_metadata();
+ $valid_block_names = array_keys( $blocks_metadata );
$valid_element_names = array_keys( static::ELEMENTS );
- $valid_variations = static::get_valid_block_style_variations();
+ $valid_variations = static::get_valid_block_style_variations( $blocks_metadata );
$theme_json = static::sanitize( $theme_json, $valid_block_names, $valid_element_names, $valid_variations );
@@ -3446,26 +3563,12 @@
$variation_output = static::remove_insecure_styles( $variation_input );
- // Process a variation's elements and element pseudo selector styles.
+ if ( isset( $variation_input['blocks'] ) ) {
+ $variation_output['blocks'] = static::remove_insecure_inner_block_styles( $variation_input['blocks'] );
+ }
+
if ( isset( $variation_input['elements'] ) ) {
- foreach ( $valid_element_names as $element_name ) {
- $element_input = $variation_input['elements'][ $element_name ] ?? null;
- if ( $element_input ) {
- $element_output = static::remove_insecure_styles( $element_input );
-
- if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] ) ) {
- foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] as $pseudo_selector ) {
- if ( isset( $element_input[ $pseudo_selector ] ) ) {
- $element_output[ $pseudo_selector ] = static::remove_insecure_styles( $element_input[ $pseudo_selector ] );
- }
- }
- }
-
- if ( ! empty( $element_output ) ) {
- _wp_array_set( $variation_output, array( 'elements', $element_name ), $element_output );
- }
- }
- }
+ $variation_output['elements'] = static::remove_insecure_element_styles( $variation_input['elements'] );
}
if ( ! empty( $variation_output ) ) {
@@ -3504,6 +3607,59 @@
}
/**
+ * Remove insecure element styles within a variation or block.
+ *
+ * @since 6.8.0
+ *
+ * @param array $elements The elements to process.
+ * @return array The sanitized elements styles.
+ */
+ protected static function remove_insecure_element_styles( $elements ) {
+ $sanitized = array();
+ $valid_element_names = array_keys( static::ELEMENTS );
+
+ foreach ( $valid_element_names as $element_name ) {
+ $element_input = $elements[ $element_name ] ?? null;
+ if ( $element_input ) {
+ $element_output = static::remove_insecure_styles( $element_input );
+
+ if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] ) ) {
+ foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] as $pseudo_selector ) {
+ if ( isset( $element_input[ $pseudo_selector ] ) ) {
+ $element_output[ $pseudo_selector ] = static::remove_insecure_styles( $element_input[ $pseudo_selector ] );
+ }
+ }
+ }
+
+ $sanitized[ $element_name ] = $element_output;
+ }
+ }
+ return $sanitized;
+ }
+
+ /**
+ * Remove insecure styles from inner blocks and their elements.
+ *
+ * @since 6.8.0
+ *
+ * @param array $blocks The block styles to process.
+ * @return array Sanitized block type styles.
+ */
+ protected static function remove_insecure_inner_block_styles( $blocks ) {
+ $sanitized = array();
+ foreach ( $blocks as $block_type => $block_input ) {
+ $block_output = static::remove_insecure_styles( $block_input );
+
+ if ( isset( $block_input['elements'] ) ) {
+ $block_output['elements'] = static::remove_insecure_element_styles( $block_input['elements'] );
+ }
+
+ $sanitized[ $block_type ] = $block_output;
+ }
+ return $sanitized;
+ }
+
+ /**
* Processes a setting node and returns the same node
* without the insecure settings.
*
@@ -4112,6 +4268,7 @@
* For example, `var:preset|color|vivid-green-cyan` becomes `var(--wp--preset--color--vivid-green-cyan)`.
*
* @since 6.3.0
+ *
* @param string $value The variable such as var:preset|color|vivid-green-cyan to convert.
* @return string The converted variable.
*/
@@ -4137,7 +4294,8 @@
* It is recursive and modifies the input in-place.
*
* @since 6.3.0
- * @param array $tree Input to process.
+ *
+ * @param array $tree Input to process.
* @return array The modified $tree.
*/
private static function resolve_custom_css_format( $tree ) {
@@ -4161,7 +4319,6 @@
*
* @param object $block_type The block type.
* @param string $root_selector The block's root selector.
- *
* @return array The custom selectors set by the block.
*/
protected static function get_block_selectors( $block_type, $root_selector ) {
@@ -4220,9 +4377,8 @@
*
* @param object $metadata The related block metadata containing selectors.
* @param object $node A merged theme.json node for block or variation.
- *
* @return array The style declarations for the node's features with custom
- * selectors.
+ * selectors.
*/
protected function get_feature_declarations_for_node( $metadata, &$node ) {
$declarations = array();
@@ -4380,8 +4536,8 @@
* Resolves the values of CSS variables in the given styles.
*
* @since 6.3.0
+ *
* @param WP_Theme_JSON $theme_json The theme json resolver.
- *
* @return WP_Theme_JSON The $theme_json with resolved variables.
*/
public static function resolve_variables( $theme_json ) {
@@ -4441,12 +4597,15 @@
* Collects valid block style variations keyed by block type.
*
* @since 6.6.0
- *
+ * @since 6.8.0 Added the `$blocks_metadata` parameter.
+ *
+ * @param array $blocks_metadata Optional. List of metadata per block. Default is the metadata for all blocks.
* @return array Valid block style variations by block type.
*/
- protected static function get_valid_block_style_variations() {
+ protected static function get_valid_block_style_variations( $blocks_metadata = array() ) {
$valid_variations = array();
- foreach ( self::get_blocks_metadata() as $block_name => $block_meta ) {
+ $blocks_metadata = empty( $blocks_metadata ) ? static::get_blocks_metadata() : $blocks_metadata;
+ foreach ( $blocks_metadata as $block_name => $block_meta ) {
if ( ! isset( $block_meta['styleVariations'] ) ) {
continue;
}