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;
+ }
+
+}