Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 41 additions & 6 deletions src/wp-includes/block-supports/elements.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,20 @@
* @since 6.0.0
* @access private
*
* @param array $block Block object.
* @param array $parsed_block Block object.
* @return string The unique class name.
*
* @phpstan-param array{
* attrs: array{
* className?: string,
* ...
* },
* ...
* } $parsed_block
*/
function wp_get_elements_class_name( $block ) {
return 'wp-elements-' . md5( serialize( $block ) );
function wp_get_elements_class_name( $parsed_block ): string {
$hash = md5( serialize( $parsed_block ) );
return wp_unique_prefixed_id( 'wp-elements-' . $hash );
}

/**
Expand Down Expand Up @@ -109,6 +118,29 @@ function wp_should_add_elements_class_name( $block, $options ) {
*
* @param array $parsed_block The parsed block.
* @return array The same parsed block with elements classname added if appropriate.
*
* @phpstan-param array{
* blockName: string,
* attrs: array{
* className?: string,
* style?: array{
* elements?: array<string, array{
* ":hover"?: array<string, string>,
* ...
* }>,
* },
* ...
* },
* ...
* } $parsed_block
* @phpstan-return array{
* blockName: string,
* attrs: array{
* className?: string,
* ...
* },
* ...
* }
*/
function wp_render_elements_support_styles( $parsed_block ) {
/*
Expand All @@ -129,9 +161,12 @@ function wp_render_elements_support_styles( $parsed_block ) {
);
}

$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );
$element_block_styles = $parsed_block['attrs']['style']['elements'] ?? null;
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );
if ( ! $block_type ) {
return $parsed_block;
}
Comment on lines +165 to +167

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition handles a possible case where the parsed block doesn't exist in the registry.


$element_block_styles = $parsed_block['attrs']['style']['elements'] ?? null;
if ( ! $element_block_styles ) {
return $parsed_block;
}
Expand Down Expand Up @@ -197,7 +232,7 @@ function wp_render_elements_support_styles( $parsed_block ) {
)
);

if ( isset( $element_style_object[':hover'] ) ) {
if ( isset( $element_style_object[':hover'], $element_config['hover_selector'] ) ) {
wp_style_engine_get_styles(
$element_style_object[':hover'],
array(
Expand Down
74 changes: 69 additions & 5 deletions tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,70 @@ public function test_elements_block_support_class_with_invalid_elements_prefix()
);
}

/**
* Tests that duplicate blocks get distinct elements class names
* on their rendered markup to avoid CSS cascade conflicts.
*
* @ticket 65435
*
* @covers ::wp_get_elements_class_name
*/
public function test_elements_block_support_class_with_duplicate_blocks(): void {
$this->test_block_name = 'test/element-block-supports';

register_block_type(
$this->test_block_name,
array(
'api_version' => 3,
'attributes' => array(
'style' => array(
'type' => 'object',
),
),
'supports' => array(
'color' => array(
'link' => true,
),
),
)
);

$block = array(
'blockName' => $this->test_block_name,
'attrs' => array(
'style' => array(
'elements' => array(
'link' => array(
'color' => array(
'text' => 'var:preset|color|vivid-red',
),
),
),
),
),
);

$block_markup = '<p>Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>';
$elements_class_names = array();
$count = 2;
for ( $i = 0; $i < $count; $i++ ) {
$rendered_block = wp_render_elements_class_name( $block_markup, wp_render_elements_support_styles( $block ) );

$processor = new WP_HTML_Tag_Processor( $rendered_block );
$this->assertTrue( $processor->next_tag( 'P' ), "Expected paragraph in block #$i." );
$elements_class_name = array_first(
array_filter(
iterator_to_array( $processor->class_list() ),
fn( string $class_name ) => (bool) preg_match( '/^wp-elements-[a-f0-9]{32}[0-9]+$/', $class_name )
)
);
$this->assertIsString( $elements_class_name, "Expected wp-elements class in block #$i." );
$elements_class_names[] = $elements_class_name;
}

$this->assertSame( $count, count( array_unique( $elements_class_names ) ), 'Expected each rendered block to have a unique wp-elements class name.' );
}

/**
* Data provider.
*
Expand Down Expand Up @@ -238,39 +302,39 @@ public function data_elements_block_support_class() {
'button' => array( 'color' => $color_styles ),
),
'block_markup' => '<p>Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>',
'expected_markup' => '/^<p class="wp-elements-[a-f0-9]{32}">Hello <a href="http:\/\/www.wordpress.org\/">WordPress<\/a>!<\/p>$/',
'expected_markup' => '/^<p class="wp-elements-[a-f0-9]{32}[0-9]+">Hello <a href="http:\/\/www.wordpress.org\/">WordPress<\/a>!<\/p>$/',
),
'link element styles apply class to wrapper' => array(
'color_settings' => array( 'link' => true ),
'elements_styles' => array(
'link' => array( 'color' => $color_styles ),
),
'block_markup' => '<p>Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>',
'expected_markup' => '/^<p class="wp-elements-[a-f0-9]{32}">Hello <a href="http:\/\/www.wordpress.org\/">WordPress<\/a>!<\/p>$/',
'expected_markup' => '/^<p class="wp-elements-[a-f0-9]{32}[0-9]+">Hello <a href="http:\/\/www.wordpress.org\/">WordPress<\/a>!<\/p>$/',
),
'heading element styles apply class to wrapper' => array(
'color_settings' => array( 'heading' => true ),
'elements_styles' => array(
'heading' => array( 'color' => $color_styles ),
),
'block_markup' => '<p>Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>',
'expected_markup' => '/^<p class="wp-elements-[a-f0-9]{32}">Hello <a href="http:\/\/www.wordpress.org\/">WordPress<\/a>!<\/p>$/',
'expected_markup' => '/^<p class="wp-elements-[a-f0-9]{32}[0-9]+">Hello <a href="http:\/\/www.wordpress.org\/">WordPress<\/a>!<\/p>$/',
),
'element styles apply class to wrapper when it has other classes' => array(
'color_settings' => array( 'link' => true ),
'elements_styles' => array(
'link' => array( 'color' => $color_styles ),
),
'block_markup' => '<p class="has-dark-gray-background-color has-background">Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>',
'expected_markup' => '/^<p class="has-dark-gray-background-color has-background wp-elements-[a-f0-9]{32}">Hello <a href="http:\/\/www.wordpress.org\/">WordPress<\/a>!<\/p>$/',
'expected_markup' => '/^<p class="has-dark-gray-background-color has-background wp-elements-[a-f0-9]{32}[0-9]+">Hello <a href="http:\/\/www.wordpress.org\/">WordPress<\/a>!<\/p>$/',
),
'element styles apply class to wrapper when it has other attributes' => array(
'color_settings' => array( 'link' => true ),
'elements_styles' => array(
'link' => array( 'color' => $color_styles ),
),
'block_markup' => '<p id="anchor">Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>',
'expected_markup' => '/^<p class="wp-elements-[a-f0-9]{32}" id="anchor">Hello <a href="http:\/\/www.wordpress.org\/">WordPress<\/a>!<\/p>$/',
'expected_markup' => '/^<p class="wp-elements-[a-f0-9]{32}[0-9]+" id="anchor">Hello <a href="http:\/\/www.wordpress.org\/">WordPress<\/a>!<\/p>$/',
),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,62 @@ public function test_elements_block_support_styles( $color_settings, $elements_s
);
}

/**
* Tests that identical blocks with different elements styles
* generate distinct class names to avoid CSS cascade conflicts.
*
* @ticket 65435
*
* @covers ::wp_get_elements_class_name
*/
public function test_elements_block_support_styles_with_duplicate_blocks(): void {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this test? Is it not the same as in Tests_Block_Supports_WpRenderElementsSupport?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is almost the same, but they test different outputs.

the Tests_Block_Supports_WpRenderElementsSupport one checks for the html output
while this one checks for the styles part.

so something like <p class="wp-elements-abc123"> vs .wp-elements-abc123 a { color: blue; }

$this->test_block_name = 'test/element-block-supports';

register_block_type(
$this->test_block_name,
array(
'api_version' => 3,
'attributes' => array(
'style' => array(
'type' => 'object',
),
),
'supports' => array(
'color' => array(
'link' => true,
),
),
)
);

$block = array(
'blockName' => $this->test_block_name,
'attrs' => array(
'style' => array(
'elements' => array(
'link' => array(
'color' => array(
'text' => 'blue',
),
),
),
),
),
);

// Process two identical blocks with the same elements styles.
$count = 2;
for ( $i = 0; $i < $count; $i++ ) {
wp_render_elements_support_styles( $block );
}
$actual_stylesheet = wp_style_engine_get_stylesheet_from_context( 'block-supports', array( 'prettify' => false ) );

// Count the number of distinct class names to confirm uniqueness.
$this->assertSame( $count, preg_match_all( '/\.wp-elements-([a-f0-9]{32}[0-9]+)/', $actual_stylesheet, $matches ) );
$unique_classes = array_unique( $matches[1] );
$this->assertCount( $count, $unique_classes, 'Both blocks should produce distinct class names' );
}

/**
* Data provider.
*
Expand Down Expand Up @@ -127,7 +183,7 @@ public function data_elements_block_support_styles() {
'elements_styles' => array(
'button' => array( 'color' => $color_styles ),
),
'expected_styles' => '/^.wp-elements-[a-f0-9]{32} .wp-element-button, .wp-elements-[a-f0-9]{32} .wp-block-button__link' . $color_css_rules . '$/',
'expected_styles' => '/^.wp-elements-[a-f0-9]{32}[0-9]+ .wp-element-button, .wp-elements-[a-f0-9]{32}[0-9]+ .wp-block-button__link' . $color_css_rules . '$/',
),
'link element styles are applied' => array(
'color_settings' => array( 'link' => true ),
Expand All @@ -139,15 +195,15 @@ public function data_elements_block_support_styles() {
),
),
),
'expected_styles' => '/^.wp-elements-[a-f0-9]{32} a:where\(:not\(.wp-element-button\)\)' . $color_css_rules .
'.wp-elements-[a-f0-9]{32} a:where\(:not\(.wp-element-button\)\):hover' . $color_css_rules . '$/',
'expected_styles' => '/^.wp-elements-[a-f0-9]{32}[0-9]+ a:where\(:not\(.wp-element-button\)\)' . $color_css_rules .
'.wp-elements-[a-f0-9]{32}[0-9]+ a:where\(:not\(.wp-element-button\)\):hover' . $color_css_rules . '$/',
),
'generic heading element styles are applied' => array(
'color_settings' => array( 'heading' => true ),
'elements_styles' => array(
'heading' => array( 'color' => $color_styles ),
),
'expected_styles' => '/^.wp-elements-[a-f0-9]{32} h1, .wp-elements-[a-f0-9]{32} h2, .wp-elements-[a-f0-9]{32} h3, .wp-elements-[a-f0-9]{32} h4, .wp-elements-[a-f0-9]{32} h5, .wp-elements-[a-f0-9]{32} h6' . $color_css_rules . '$/',
'expected_styles' => '/^.wp-elements-[a-f0-9]{32}[0-9]+ h1, .wp-elements-[a-f0-9]{32}[0-9]+ h2, .wp-elements-[a-f0-9]{32}[0-9]+ h3, .wp-elements-[a-f0-9]{32}[0-9]+ h4, .wp-elements-[a-f0-9]{32}[0-9]+ h5, .wp-elements-[a-f0-9]{32}[0-9]+ h6' . $color_css_rules . '$/',
),
'individual heading element styles are applied' => array(
'color_settings' => array( 'heading' => true ),
Expand All @@ -159,12 +215,12 @@ public function data_elements_block_support_styles() {
'h5' => array( 'color' => $color_styles ),
'h6' => array( 'color' => $color_styles ),
),
'expected_styles' => '/^.wp-elements-[a-f0-9]{32} h1' . $color_css_rules .
'.wp-elements-[a-f0-9]{32} h2' . $color_css_rules .
'.wp-elements-[a-f0-9]{32} h3' . $color_css_rules .
'.wp-elements-[a-f0-9]{32} h4' . $color_css_rules .
'.wp-elements-[a-f0-9]{32} h5' . $color_css_rules .
'.wp-elements-[a-f0-9]{32} h6' . $color_css_rules . '$/',
'expected_styles' => '/^.wp-elements-[a-f0-9]{32}[0-9]+ h1' . $color_css_rules .
'.wp-elements-[a-f0-9]{32}[0-9]+ h2' . $color_css_rules .
'.wp-elements-[a-f0-9]{32}[0-9]+ h3' . $color_css_rules .
'.wp-elements-[a-f0-9]{32}[0-9]+ h4' . $color_css_rules .
'.wp-elements-[a-f0-9]{32}[0-9]+ h5' . $color_css_rules .
'.wp-elements-[a-f0-9]{32}[0-9]+ h6' . $color_css_rules . '$/',
),
);
}
Expand Down
Loading