Skip to content

Commit d17deb6

Browse files
authored
Merge branch 'WordPress:trunk' into trunk
2 parents 12c5196 + cdb2bff commit d17deb6

2 files changed

Lines changed: 92 additions & 7 deletions

File tree

src/wp-includes/block-supports/custom-css.php

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@
2525
* },
2626
* ...
2727
* } $parsed_block
28+
* @phpstan-return array{
29+
* blockName: string|null,
30+
* attrs: array{
31+
* className?: string,
32+
* style?: array{
33+
* css?: string,
34+
* ...
35+
* },
36+
* ...
37+
* },
38+
* ...
39+
* }
2840
*/
2941
function wp_render_custom_css_support_styles( $parsed_block ) {
3042
$custom_css = $parsed_block['attrs']['style']['css'] ?? null;
@@ -49,20 +61,30 @@ function wp_render_custom_css_support_styles( $parsed_block ) {
4961
? "$existing_class_name $class_name"
5062
: $class_name;
5163

52-
_wp_array_set( $parsed_block, array( 'attrs', 'className' ), $updated_class_name );
64+
$parsed_block['attrs']['className'] = $updated_class_name;
5365

5466
// Process the custom CSS using the same method as global styles.
5567
$selector = '.' . $class_name;
5668
$processed_css = WP_Theme_JSON::process_blocks_custom_css( $custom_css, $selector );
5769

5870
if ( ! empty( $processed_css ) ) {
59-
/*
60-
* Register and add inline style for block custom CSS.
61-
* The style depends on global-styles to ensure custom CSS loads after
62-
* and can override global styles.
71+
/**
72+
* Skip CSS that has already been added. Blocks with identical attributes
73+
* share the same class name and processed CSS via {@see wp_unique_id_from_values()},
74+
* so the same style would otherwise be enqueued more than once (e.g. inside
75+
* a Query Loop or when blocks share identical custom CSS).
6376
*/
64-
wp_register_style( 'wp-block-custom-css', false, array( 'global-styles' ) );
65-
wp_add_inline_style( 'wp-block-custom-css', $processed_css );
77+
$handle = 'wp-block-custom-css';
78+
if ( ! wp_style_is( $handle, 'registered' ) ) {
79+
wp_register_style( $handle, false, array( 'global-styles' ) );
80+
}
81+
$after_styles = wp_styles()->get_data( $handle, 'after' );
82+
if ( ! is_array( $after_styles ) ) {
83+
$after_styles = array();
84+
}
85+
if ( ! in_array( $processed_css, $after_styles, true ) ) {
86+
wp_add_inline_style( $handle, $processed_css );
87+
}
6688
}
6789

6890
return $parsed_block;

tests/phpunit/tests/block-supports/wpRenderCustomCssSupportStyles.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@ class Tests_Block_Supports_WpRenderCustomCssSupportStyles extends WP_UnitTestCas
1414
public function set_up() {
1515
parent::set_up();
1616
$this->test_block_name = null;
17+
18+
global $wp_styles;
19+
$wp_styles = null;
1720
}
1821

1922
public function tear_down() {
2023
if ( $this->test_block_name ) {
2124
unregister_block_type( $this->test_block_name );
2225
}
2326
$this->test_block_name = null;
27+
28+
global $wp_styles;
29+
$wp_styles = null;
30+
2431
parent::tear_down();
2532
}
2633

@@ -264,4 +271,60 @@ public function data_does_not_add_class_name() {
264271
),
265272
);
266273
}
274+
275+
/**
276+
* Tests that CSS is enqueued only once when the same block is rendered
277+
* multiple times, as happens inside a Query Loop.
278+
*
279+
* @ticket 65268
280+
*
281+
* @covers ::wp_render_custom_css_support_styles
282+
*/
283+
public function test_css_not_duplicated_on_repeated_renders(): void {
284+
$this->test_block_name = 'test/custom-css-query-loop-dedup';
285+
register_block_type(
286+
$this->test_block_name,
287+
array(
288+
'api_version' => 3,
289+
'attributes' => array(
290+
'style' => array(
291+
'type' => 'object',
292+
),
293+
),
294+
'supports' => array( 'customCSS' => true ),
295+
)
296+
);
297+
298+
$parsed_block = array(
299+
'blockName' => 'test/custom-css-query-loop-dedup',
300+
'attrs' => array(
301+
'style' => array(
302+
'css' => 'font-size: 2em; /* query-loop-dedup-test */',
303+
),
304+
),
305+
);
306+
307+
// Simulate the same block being rendered multiple times inside a Query Loop.
308+
$result = wp_render_custom_css_support_styles( $parsed_block );
309+
wp_render_custom_css_support_styles( $parsed_block );
310+
wp_render_custom_css_support_styles( $parsed_block );
311+
312+
// Extract the generated class name from the first render's result.
313+
$this->assertSame( 1, preg_match( '/(?:^|\s)(wp-custom-css-\S+)/', $result['attrs']['className'] ?? '', $matches ) );
314+
$class_name = $matches[1];
315+
316+
// Count how many times the CSS selector for this block appears in the enqueued inline styles.
317+
$inline_styles = (array) wp_styles()->get_data( 'wp-block-custom-css', 'after' );
318+
$occurrences = 0;
319+
foreach ( $inline_styles as $style ) {
320+
$this->assertIsString( $style );
321+
$occurrences += substr_count( $style, '.' . $class_name );
322+
}
323+
324+
$this->assertSame(
325+
1,
326+
$occurrences,
327+
'CSS should be enqueued exactly once even when the same block renders multiple times.'
328+
);
329+
}
267330
}

0 commit comments

Comments
 (0)