diff --git a/core/lib/Drupal/Core/Render/Element/HtmlTag.php b/core/lib/Drupal/Core/Render/Element/HtmlTag.php index e183c3e..553767f 100644 --- a/core/lib/Drupal/Core/Render/Element/HtmlTag.php +++ b/core/lib/Drupal/Core/Render/Element/HtmlTag.php @@ -18,6 +18,16 @@ class HtmlTag extends RenderElement { /** + * Void elements do not contain values or closing tags. + * @see http://www.w3.org/TR/html5/syntax.html#syntax-start-tag + * @see http://www.w3.org/TR/html5/syntax.html#void-elements + */ + static protected $voidElements = array( + 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', + 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr', + ); + + /** * {@inheritdoc} */ public function getInfo() { @@ -59,13 +69,16 @@ public function getInfo() { */ public static function preRenderHtmlTag($element) { $attributes = isset($element['#attributes']) ? new Attribute($element['#attributes']) : ''; - if (!isset($element['#value'])) { + + // Construct a void element. + if (in_array($element['#tag'], self::$voidElements)) { // This function is intended for internal use, so we assume that no unsafe // values are passed in #tag. The attributes are already safe because // Attribute output is already automatically sanitized. // @todo Escape this properly instead? https://www.drupal.org/node/2296101 $markup = SafeMarkup::set('<' . $element['#tag'] . $attributes . " />\n"); } + // Construct all other elements. else { $markup = '<' . $element['#tag'] . $attributes . '>'; if (isset($element['#value_prefix'])) { diff --git a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php index f3f27ab..db6d115 100644 --- a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php +++ b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php @@ -86,22 +86,42 @@ function testContainer() { * Tests system #type 'html_tag'. */ function testHtmlTag() { - // Test auto-closure meta tag generation. + // Test void element. $this->assertElements(array( '#type' => 'html_tag', '#tag' => 'meta', + '#value' => 'ignored', + '#value_prefix' => 'ignored', + '#value_suffix' => 'ignored', '#attributes' => array( 'name' => 'description', 'content' => 'Drupal test', ), - ), '' . "\n", "#type 'html_tag' auto-closure meta tag generation"); + ), '' . "\n", "#type 'html_tag', void element renders properly"); - // Test title tag generation. + // Test non-void element. $this->assertElements(array( '#type' => 'html_tag', - '#tag' => 'title', - '#value' => 'title test', - ), "title test\n", "#type 'html_tag' title tag generation"); + '#tag' => 'section', + '#value' => 'value', + '#value_prefix' => 'value_prefix|', + '#value_suffix' => '|value_suffix', + '#attributes' => array( + 'class' => array('unicorns'), + ), + ), '
value_prefix|value|value_suffix
' . "\n", "#type 'html_tag', non-void element renders properly"); + + // Test empty void element tag. + $this->assertElements(array( + '#type' => 'html_tag', + '#tag' => 'link', + ), "\n", "#type 'html_tag' empty void element renders properly"); + + // Test empty non-void element tag. + $this->assertElements(array( + '#type' => 'html_tag', + '#tag' => 'section', + ), "
\n", "#type 'html_tag' empty non-void element renders properly"); } /** diff --git a/core/tests/Drupal/Tests/Core/Render/Element/HtmlTagTest.php b/core/tests/Drupal/Tests/Core/Render/Element/HtmlTagTest.php new file mode 100644 index 0000000..53abbbe --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Render/Element/HtmlTagTest.php @@ -0,0 +1,148 @@ +getInfo(); + $this->assertArrayHasKey('#pre_render', $info); + $this->assertArrayHasKey('#attributes', $info); + $this->assertArrayHasKey('#value', $info); + } + + /** + * @covers ::preRenderHtmlTag + * @dataProvider providerPreRenderHtmlTag + */ + public function testPreRenderHtmlTag($element, $expected) { + $result = HtmlTag::preRenderHtmlTag($element); + $this->assertArrayHasKey('#markup', $result); + $this->assertSame($expected, $result['#markup']); + } + + /** + * Data provider for preRenderHtmlTag test. + */ + public function providerPreRenderHtmlTag() { + $tags = array(); + + // Value prefix/suffix. + $element = array( + '#value_prefix' => 'value_prefix|', + '#value_suffix' => '|value_suffix', + '#value' => 'value', + '#tag' => 'p', + ); + $tags[] = array($element, '

value_prefix|value|value_suffix

' . "\n"); + + // Normal element without a value should not result in a void element. + $element = array( + '#tag' => 'p', + '#value' => NULL, + ); + $tags[] = array($element, "

\n"); + + // A void element. + $element = array( + '#tag' => 'br', + ); + $tags[] = array($element, "
\n"); + + // Attributes. + $element = array( + '#tag' => 'div', + '#attributes' => array('class' => 'test', 'id' => 'id'), + '#value' => 'value', + ); + $tags[] = array($element, '
value
' . "\n"); + + // No script tags. + $element['#noscript'] = TRUE; + $tags[] = array($element, ''); + + return $tags; + } + + /** + * @covers ::preRenderConditionalComments + * @dataProvider providerPreRenderConditionalComments + */ + public function testPreRenderConditionalComments($element, $expected) { + $this->assertSame($expected, HtmlTag::preRenderConditionalComments($element)); + } + + /** + * Data provider for conditional comments test. + */ + public function providerPreRenderConditionalComments() { + // No browser specification. + $element = array( + '#tag' => 'link', + ); + $tags[] = array($element, $element); + + // Specify all browsers. + $element['#browsers'] = array( + 'IE' => TRUE, + '!IE' => TRUE, + ); + $tags[] = array($element, $element); + + // All IE. + $element = array( + '#tag' => 'link', + '#browsers' => array( + 'IE' => TRUE, + '!IE' => FALSE, + ), + ); + $expected = $element; + $expected['#prefix'] = "\n\n"; + $tags[] = array($element, $expected); + + // Exclude IE. + $element = array( + '#tag' => 'link', + '#browsers' => array( + 'IE' => FALSE, + ), + ); + $expected = $element; + $expected['#prefix'] = "\n\n"; + $expected['#suffix'] = "\n"; + $tags[] = array($element, $expected); + + // IE gt 8 + $element = array( + '#tag' => 'link', + '#browsers' => array( + 'IE' => 'gt IE 8', + ), + ); + $expected = $element; + $expected['#prefix'] = "\n\n"; + $expected['#suffix'] = "\n"; + $tags[] = array($element, $expected); + + return $tags; + } + +}