[BUGFIX] HTML5: use self-closing tags only if allowed 64/55664/2
authorEsteban Marín <esteban.marin@bithost.ch>
Wed, 30 Nov 2016 14:57:05 +0000 (15:57 +0100)
committerJigal van Hemert <jigal.van.hemert@typo3.org>
Sun, 11 Feb 2018 12:25:05 +0000 (13:25 +0100)
For HTML5 only certain HTML tags are allowed to be self-closing
(e.g. <img src="..." />), so this needs to be checked when
parsing HTML.

Resolves: #78844
Releases: master, 8.7
Change-Id: If2a319fa83a4baf7d78559aa23ce34903d42d342
Reviewed-on: https://review.typo3.org/55664
Reviewed-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
Tested-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php

index 6bdf321..2264ed9 100644 (file)
@@ -4747,8 +4747,9 @@ class ContentObjectRenderer
                     $attrib['align'] = $defaultAlign;
                 }
                 $params = GeneralUtility::implodeAttributes($attrib, true);
-                if (!($conf['removeWrapping'] && !($emptyTag && $conf['removeWrapping.']['keepSingleTag']))) {
-                    if ($emptyTag) {
+                if (!$conf['removeWrapping'] || ($emptyTag && $conf['removeWrapping.']['keepSingleTag'])) {
+                    $selfClosingTagList = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
+                    if ($emptyTag && in_array(strtolower($uTagName), $selfClosingTagList, true)) {
                         $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . ' />';
                     } else {
                         $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . '>' . $str_content . '</' . strtolower($uTagName) . '>';
index 457411f..b0c4607 100644 (file)
@@ -5071,6 +5071,120 @@ class ContentObjectRendererTest extends \TYPO3\TestingFramework\Core\Unit\UnitTe
     }
 
     /**
+     * Check if stdWrap_encapsLines uses self closing tags
+     * only for allowed tags according to
+     * @see https://www.w3.org/TR/html5/syntax.html#void-elements
+     *
+     * @test
+     * @dataProvider html5SelfClosingTagsDataprovider
+     */
+    public function stdWrap_encapsLines_HTML5SelfClosingTags(string $input, string $expected)
+    {
+        $rteParseFunc = $this->getLibParseFunc_RTE();
+
+        $conf = [
+            'encapsLines' => $rteParseFunc['parseFunc.']['nonTypoTagStdWrap.']['encapsLines'],
+            'encapsLines.' => $rteParseFunc['parseFunc.']['nonTypoTagStdWrap.']['encapsLines.'],
+        ];
+        // don't add an &nbsp; to tag without content
+        $conf['encapsLines.']['innerStdWrap_all.']['ifBlank'] = '';
+        $additionalEncapsTags = ['a', 'b', 'span'];
+
+        // We want to allow any tag to be an encapsulating tag
+        // since this is possible and we don't want an additional tag to be wrapped around.
+        $conf['encapsLines.']['encapsTagList'] .= ',' . implode(',', $additionalEncapsTags);
+        $conf['encapsLines.']['encapsTagList'] .= ',' . implode(',', [$input]);
+
+        // Check if we get a self-closing tag for
+        // empty tags where this is allowed according to HTML5
+        $content = '<' . $input . ' id="myId" class="bodytext" />';
+        $result = $this->subject->stdWrap_encapsLines($content, $conf);
+        $this->assertSame($expected, $result);
+    }
+
+    public function html5SelfClosingTagsDataprovider()
+    {
+        return [
+            'areaTag_selfclosing' => [
+                'input' => 'area',
+                'expected' => '<area id="myId" class="bodytext" />'
+            ],
+            'base_selfclosing' => [
+                'input' => 'base',
+                'expected' => '<base id="myId" class="bodytext" />'
+            ],
+            'br_selfclosing' => [
+                'input' => 'br',
+                'expected' => '<br id="myId" class="bodytext" />'
+            ],
+            'col_selfclosing' => [
+                'input' => 'col',
+                'expected' => '<col id="myId" class="bodytext" />'
+            ],
+            'embed_selfclosing' => [
+                'input' => 'embed',
+                'expected' => '<embed id="myId" class="bodytext" />'
+            ],
+            'hr_selfclosing' => [
+                'input' => 'hr',
+                'expected' => '<hr id="myId" class="bodytext" />'
+            ],
+            'img_selfclosing'  => [
+                'input' => 'img',
+                'expected' => '<img id="myId" class="bodytext" />'
+            ],
+            'input_selfclosing' => [
+                'input' => 'input',
+                'expected' => '<input id="myId" class="bodytext" />'
+            ],
+            'keygen_selfclosing' => [
+                'input' => 'keygen',
+                'expected' => '<keygen id="myId" class="bodytext" />'
+            ],
+            'link_selfclosing' => [
+                'input' => 'link',
+                'expected' => '<link id="myId" class="bodytext" />'
+            ],
+            'meta_selfclosing' => [
+                'input' => 'meta',
+                'expected' => '<meta id="myId" class="bodytext" />'
+            ],
+            'param_selfclosing' => [
+                'input' => 'param',
+                'expected' => '<param id="myId" class="bodytext" />'
+            ],
+            'source_selfclosing' => [
+                'input' => 'source',
+                'expected' => '<source id="myId" class="bodytext" />'
+            ],
+            'track_selfclosing' => [
+                'input' => 'track',
+                'expected' => '<track id="myId" class="bodytext" />'
+            ],
+            'wbr_selfclosing' => [
+                'input' => 'wbr',
+                'expected' => '<wbr id="myId" class="bodytext" />'
+            ],
+            'p_notselfclosing' => [
+                'input' => 'p',
+                'expected' => '<p id="myId" class="bodytext"></p>'
+            ],
+            'a_notselfclosing' => [
+                'input' => 'a',
+                'expected' => '<a id="myId" class="bodytext"></a>'
+            ],
+            'strong_notselfclosing' => [
+                'input' => 'strong',
+                'expected' => '<strong id="myId" class="bodytext"></strong>'
+            ],
+            'span_notselfclosing' => [
+                'input' => 'span',
+                'expected' => '<span id="myId" class="bodytext"></span>'
+            ],
+        ];
+    }
+
+    /**
      * Data provider for stdWrap_editPanel.
      *
      * @return [$expect, $content, $login, $times, $will]