diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php index 54b96aa1dc064..41fd62eaa5ff7 100644 --- a/src/wp-includes/block-supports/elements.php +++ b/src/wp-includes/block-supports/elements.php @@ -16,7 +16,8 @@ * @return string The unique class name. */ function wp_get_elements_class_name( $block ) { - return 'wp-elements-' . md5( serialize( $block ) ); + $hash = md5( serialize( $block ) ); + return wp_unique_prefixed_id( 'wp-elements-' . $hash ); } /** diff --git a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php index 007ba8312e495..831189b6af6fc 100644 --- a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php +++ b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php @@ -159,6 +159,74 @@ 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() { + $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 = '

Hello WordPress!

'; + $block_one = wp_render_elements_support_styles( $block ); + $block_two = wp_render_elements_support_styles( $block ); + $markup_one = wp_render_elements_class_name( $block_markup, $block_one ); + $markup_two = wp_render_elements_class_name( $block_markup, $block_two ); + + $this->assertMatchesRegularExpression( + '/^

Hello WordPress<\/a>!<\/p>$/', + $markup_one, + 'First block should have wp-elements class applied' + ); + $this->assertMatchesRegularExpression( + '/^

Hello WordPress<\/a>!<\/p>$/', + $markup_two, + 'Second block should also have wp-elements class applied' + ); + + // Extract class names and verify they are different. + preg_match( '/wp-elements-([a-f0-9]{32}[0-9]+)/', $markup_one, $match_one ); + preg_match( '/wp-elements-([a-f0-9]{32}[0-9]+)/', $markup_two, $match_two ); + $this->assertNotEmpty( $match_one, 'First block class name should be extractable' ); + $this->assertNotEmpty( $match_two, 'Second block class name should be extractable' ); + $this->assertNotSame( $match_one[1], $match_two[1], 'Class names for identical blocks should be unique' ); + } + /** * Data provider. * @@ -238,7 +306,7 @@ public function data_elements_block_support_class() { 'button' => array( 'color' => $color_styles ), ), 'block_markup' => '

Hello WordPress!

', - 'expected_markup' => '/^

Hello WordPress<\/a>!<\/p>$/', + 'expected_markup' => '/^

Hello WordPress<\/a>!<\/p>$/', ), 'link element styles apply class to wrapper' => array( 'color_settings' => array( 'link' => true ), @@ -246,7 +314,7 @@ public function data_elements_block_support_class() { 'link' => array( 'color' => $color_styles ), ), 'block_markup' => '

Hello WordPress!

', - 'expected_markup' => '/^

Hello WordPress<\/a>!<\/p>$/', + 'expected_markup' => '/^

Hello WordPress<\/a>!<\/p>$/', ), 'heading element styles apply class to wrapper' => array( 'color_settings' => array( 'heading' => true ), @@ -254,7 +322,7 @@ public function data_elements_block_support_class() { 'heading' => array( 'color' => $color_styles ), ), 'block_markup' => '

Hello WordPress!

', - 'expected_markup' => '/^

Hello WordPress<\/a>!<\/p>$/', + 'expected_markup' => '/^

Hello WordPress<\/a>!<\/p>$/', ), 'element styles apply class to wrapper when it has other classes' => array( 'color_settings' => array( 'link' => true ), @@ -262,7 +330,7 @@ public function data_elements_block_support_class() { 'link' => array( 'color' => $color_styles ), ), 'block_markup' => '

Hello WordPress!

', - 'expected_markup' => '/^

Hello WordPress<\/a>!<\/p>$/', + 'expected_markup' => '/^

Hello WordPress<\/a>!<\/p>$/', ), 'element styles apply class to wrapper when it has other attributes' => array( 'color_settings' => array( 'link' => true ), @@ -270,7 +338,7 @@ public function data_elements_block_support_class() { 'link' => array( 'color' => $color_styles ), ), 'block_markup' => '

Hello WordPress!

', - 'expected_markup' => '/^

Hello WordPress<\/a>!<\/p>$/', + 'expected_markup' => '/^

Hello WordPress<\/a>!<\/p>$/', ), ); } diff --git a/tests/phpunit/tests/block-supports/wpRenderElementsSupportStyles.php b/tests/phpunit/tests/block-supports/wpRenderElementsSupportStyles.php index 16ed26fc9c7bc..7707afee962fa 100644 --- a/tests/phpunit/tests/block-supports/wpRenderElementsSupportStyles.php +++ b/tests/phpunit/tests/block-supports/wpRenderElementsSupportStyles.php @@ -68,6 +68,71 @@ 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() { + $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. + wp_render_elements_support_styles( $block ); + wp_render_elements_support_styles( $block ); + $actual_stylesheet = wp_style_engine_get_stylesheet_from_context( 'block-supports', array( 'prettify' => false ) ); + + // Both rules should be present with distinct class names. + $this->assertMatchesRegularExpression( + '/\.wp-elements-[a-f0-9]{32}[0-9]+ a:where\(:not\(\.wp-element-button\)\)\{color:blue;\}/', + $actual_stylesheet, + 'First block element style should be present' + ); + $this->assertMatchesRegularExpression( + '/\.wp-elements-[a-f0-9]{32}[0-9]+ a:where\(:not\(\.wp-element-button\)\)\{color:blue;\}/', + $actual_stylesheet, + 'Second block element style should also be present' + ); + // Count the number of distinct class names to confirm uniqueness. + preg_match_all( '/\.wp-elements-([a-f0-9]{32}[0-9]+)/', $actual_stylesheet, $matches ); + $unique_classes = array_unique( $matches[1] ); + $this->assertCount( 2, $unique_classes, 'Both blocks should produce distinct class names' ); + } + /** * Data provider. * @@ -127,7 +192,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 ), @@ -139,15 +204,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 ), @@ -159,12 +224,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 . '$/', ), ); }