diff --git a/core/lib/Drupal/Core/Render/Element/HtmlTag.php b/core/lib/Drupal/Core/Render/Element/HtmlTag.php index 0b9ac91e4bc92d5c70770847ab5756512bc5dd7b..2413784586cd95bfefbc413d44af515fc0e2e7e2 100644 --- a/core/lib/Drupal/Core/Render/Element/HtmlTag.php +++ b/core/lib/Drupal/Core/Render/Element/HtmlTag.php @@ -41,6 +41,7 @@ class HtmlTag extends RenderElement { static protected $voidElements = [ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr', + 'rect', 'circle', 'polygon', 'ellipse', 'stop', 'use', 'path', ]; /** @@ -59,7 +60,7 @@ public function getInfo() { } /** - * Pre-render callback: Renders a generic HTML tag with attributes into #markup. + * Pre-render callback: Renders a generic HTML tag with attributes. * * @param array $element * An associative array containing: @@ -84,21 +85,27 @@ public static function preRenderHtmlTag($element) { // An HTML tag should not contain any special characters. Escape them to // ensure this cannot be abused. $escaped_tag = HtmlUtility::escape($element['#tag']); - $markup = '<' . $escaped_tag . $attributes; + $open_tag = '<' . $escaped_tag . $attributes; + $close_tag = '\n"; + $prefix = isset($element['#prefix']) ? $element['#prefix'] . $open_tag : $open_tag; + $suffix = isset($element['#suffix']) ? $close_tag . $element['#suffix'] : $close_tag; // Construct a void element. if (in_array($element['#tag'], self::$voidElements)) { - $markup .= " />\n"; + $prefix .= " />\n"; + $suffix = ''; } // Construct all other elements. else { - $markup .= '>'; - $markup .= $element['#value'] instanceof MarkupInterface ? $element['#value'] : Xss::filterAdmin($element['#value']); - $markup .= '\n"; + $prefix .= '>'; + $markup = $element['#value'] instanceof MarkupInterface ? $element['#value'] : Xss::filterAdmin($element['#value']); + $element['#markup'] = Markup::create($markup); } if (!empty($element['#noscript'])) { - $markup = ""; + $prefix = ''; } - $element['#markup'] = Markup::create($markup); + $element['#prefix'] = Markup::create($prefix); + $element['#suffix'] = Markup::create($suffix); return $element; } diff --git a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php index 8ec7c5497a107164c047d6e1a9b98a7e2904f07b..60c2ce31aeb67a0c23e8df39653f8629a89cb5f6 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php +++ b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php @@ -182,7 +182,7 @@ public function testRender() { $output = \Drupal::service('renderer')->renderRoot($build); $this->assertEquals('core', \Drupal::theme()->getActiveTheme()->getName()); - $this->assertEquals($expected, $build['#children']); + $this->assertEquals($expected, $build['#markup']); $this->assertEquals($expected, $output); } diff --git a/core/tests/Drupal/Tests/Core/Render/Element/HtmlTagTest.php b/core/tests/Drupal/Tests/Core/Render/Element/HtmlTagTest.php index 46dd91a87d618a9b50ae189dea978c9df3b14ad5..713da2f9c0b8089cd42357edf32d355aba090d17 100644 --- a/core/tests/Drupal/Tests/Core/Render/Element/HtmlTagTest.php +++ b/core/tests/Drupal/Tests/Core/Render/Element/HtmlTagTest.php @@ -3,14 +3,14 @@ namespace Drupal\Tests\Core\Render\Element; use Drupal\Core\Render\Markup; -use Drupal\Tests\UnitTestCase; +use Drupal\Tests\Core\Render\RendererTestBase; use Drupal\Core\Render\Element\HtmlTag; /** * @coversDefaultClass \Drupal\Core\Render\Element\HtmlTag * @group Render */ -class HtmlTagTest extends UnitTestCase { +class HtmlTagTest extends RendererTestBase { /** * @covers ::getInfo @@ -29,8 +29,12 @@ public function testGetInfo() { */ public function testPreRenderHtmlTag($element, $expected) { $result = HtmlTag::preRenderHtmlTag($element); - $this->assertArrayHasKey('#markup', $result); - $this->assertEquals($expected, $result['#markup']); + foreach ($result as &$child) { + if (is_array($child) && isset($child['#tag'])) { + $child = HtmlTag::preRenderHtmlTag($child); + } + } + $this->assertEquals($expected, (string) $this->renderer->renderRoot($result)); } /** @@ -92,6 +96,112 @@ public function providerPreRenderHtmlTag() { ]; $tags[] = [$element, "

value

\n"]; + // Ensure that nested render arrays render properly. + $element = [ + '#tag' => 'p', + '#value' => NULL, + [ + ['#markup' => 'value1'], + ['#markup' => 'value2'], + ], + ]; + $tags[] = [$element, "

value1value2

\n"]; + + // Ensure svg elements. + $element = [ + '#tag' => 'rect', + '#attributes' => [ + 'width' => 25, + 'height' => 25, + 'x' => 5, + 'y' => 10, + ], + ]; + $tags[] = [$element, '' . "\n"]; + + $element = [ + '#tag' => 'circle', + '#attributes' => [ + 'cx' => 100, + 'cy' => 100, + 'r' => 100, + ], + ]; + $tags[] = [$element, '' . "\n"]; + + $element = [ + '#tag' => 'polygon', + '#attributes' => [ + 'points' => '60,20 100,40 100,80 60,100 20,80 20,40', + ], + ]; + $tags[] = [$element, '' . "\n"]; + + $element = [ + '#tag' => 'ellipse', + '#attributes' => [ + 'cx' => 60, + 'cy' => 60, + 'rx' => 50, + 'ry' => 25, + ], + ]; + $tags[] = [$element, '' . "\n"]; + + $element = [ + '#tag' => 'use', + '#attributes' => [ + 'x' => 50, + 'y' => 10, + 'width' => 50, + 'height' => 50, + ], + ]; + $tags[] = [$element, '' . "\n"]; + + $element = [ + '#tag' => 'path', + '#attributes' => [ + 'd' => 'M 100 100 L 300 100 L 200 300 z', + 'fill' => 'orange', + 'stroke' => 'black', + 'stroke-width' => 3, + ], + ]; + $tags[] = [$element, '' . "\n"]; + + $element = [ + '#tag' => 'stop', + '#attributes' => [ + 'offset' => '5%', + 'stop-color' => '#F60', + ], + ]; + $tags[] = [$element, '' . "\n"]; + + // Nested svg elements. + $element = [ + '#tag' => 'linearGradient', + '#value' => NULL, + [ + '#tag' => 'stop', + '#value' => NULL, + '#attributes' => [ + 'offset' => '5%', + 'stop-color' => '#F60', + ], + ], + [ + '#tag' => 'stop', + '#value' => NULL, + '#attributes' => [ + 'offset' => '95%', + 'stop-color' => '#FF6', + ], + ], + ]; + $tags[] = [$element, '' . "\n" . '' . "\n" . '' . "\n"]; + return $tags; }