Skip to content

Commit 856d015

Browse files
authored
Merge pull request #33199 from basvanpoppel/set-self-closing-tags-for-void-elements
Set self closing tags for void elements
2 parents 567941c + 19978d1 commit 856d015

File tree

5 files changed

+97
-12
lines changed

5 files changed

+97
-12
lines changed

dev/tests/integration/testsuite/Magento/Csp/CspUtilTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function testPhtmlHelper(): void
3030
$content = $this->getResponse()->getContent();
3131

3232
$this->assertStringContainsString(
33-
'<script src="http&#x3A;&#x2F;&#x2F;my.magento.com&#x2F;static&#x2F;script.js"/>',
33+
'<script src="http&#x3A;&#x2F;&#x2F;my.magento.com&#x2F;static&#x2F;script.js"></script>',
3434
$content
3535
);
3636
$this->assertStringContainsString("<script>\n let myVar = 1;\n</script>", $content);

dev/tests/integration/testsuite/Magento/Csp/Helper/InlineUtilTest.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public function getTags(): array
9696
'script',
9797
['src' => 'http://magento.com/static/some-script.js'],
9898
null,
99-
'<script src="http&#x3A;&#x2F;&#x2F;magento.com&#x2F;static&#x2F;some-script.js"/>',
99+
'<script src="http&#x3A;&#x2F;&#x2F;magento.com&#x2F;static&#x2F;some-script.js"></script>',
100100
[new FetchPolicy('script-src', false, ['http://magento.com'])]
101101
],
102102
'inline-script' => [
@@ -209,7 +209,7 @@ public function getTags(): array
209209
'iframe',
210210
['src' => 'http://magento.com/some-page'],
211211
null,
212-
'<iframe src="http&#x3A;&#x2F;&#x2F;magento.com&#x2F;some-page"/>',
212+
'<iframe src="http&#x3A;&#x2F;&#x2F;magento.com&#x2F;some-page"></iframe>',
213213
[new FetchPolicy('frame-src', false, ['http://magento.com'])]
214214
],
215215
'remote-track' => [
@@ -230,21 +230,21 @@ public function getTags(): array
230230
'video',
231231
['src' => 'https://magento.com/static/video.mp4'],
232232
null,
233-
'<video src="https&#x3A;&#x2F;&#x2F;magento.com&#x2F;static&#x2F;video.mp4"/>',
233+
'<video src="https&#x3A;&#x2F;&#x2F;magento.com&#x2F;static&#x2F;video.mp4"></video>',
234234
[new FetchPolicy('media-src', false, ['https://magento.com'])]
235235
],
236236
'remote-audio' => [
237237
'audio',
238238
['src' => 'https://magento.com/static/audio.mp3'],
239239
null,
240-
'<audio src="https&#x3A;&#x2F;&#x2F;magento.com&#x2F;static&#x2F;audio.mp3"/>',
240+
'<audio src="https&#x3A;&#x2F;&#x2F;magento.com&#x2F;static&#x2F;audio.mp3"></audio>',
241241
[new FetchPolicy('media-src', false, ['https://magento.com'])]
242242
],
243243
'remote-object' => [
244244
'object',
245245
['data' => 'http://magento.com/static/flash.swf'],
246246
null,
247-
'<object data="http&#x3A;&#x2F;&#x2F;magento.com&#x2F;static&#x2F;flash.swf"/>',
247+
'<object data="http&#x3A;&#x2F;&#x2F;magento.com&#x2F;static&#x2F;flash.swf"></object>',
248248
[new FetchPolicy('object-src', false, ['http://magento.com'])]
249249
],
250250
'remote-embed' => [
@@ -259,7 +259,7 @@ public function getTags(): array
259259
['code' => 'SomeApplet.class', 'archive' => 'https://magento.com/applet/my-applet.jar'],
260260
null,
261261
'<applet code="SomeApplet.class" '
262-
. 'archive="https&#x3A;&#x2F;&#x2F;magento.com&#x2F;applet&#x2F;my-applet.jar"/>',
262+
. 'archive="https&#x3A;&#x2F;&#x2F;magento.com&#x2F;applet&#x2F;my-applet.jar"></applet>',
263263
[new FetchPolicy('object-src', false, ['https://magento.com'])]
264264
]
265265
];
@@ -294,7 +294,7 @@ public function testSecureHtmlRenderer(): void
294294
$eventListener = $this->secureHtmlRenderer->renderEventListener('onclick', 'alert()');
295295

296296
$this->assertEquals(
297-
'<script src="https&#x3A;&#x2F;&#x2F;test.magento.com&#x2F;static&#x2F;script.js"/>',
297+
'<script src="https&#x3A;&#x2F;&#x2F;test.magento.com&#x2F;static&#x2F;script.js"></script>',
298298
$scriptTag
299299
);
300300
$this->assertEquals(

dev/tests/integration/testsuite/Magento/Framework/View/Helper/SecureHtmlRendererTemplateTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function testTemplateUsage(): void
3333
$content
3434
);
3535
$this->assertStringContainsString(
36-
'<script src="http&#x3A;&#x2F;&#x2F;my.magento.com&#x2F;static&#x2F;script.js"/>',
36+
'<script src="http&#x3A;&#x2F;&#x2F;my.magento.com&#x2F;static&#x2F;script.js"></script>',
3737
$content
3838
);
3939
$this->assertStringContainsString(

lib/internal/Magento/Framework/View/Helper/SecureHtmlRender/HtmlRenderer.php

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,31 @@
1313
*/
1414
class HtmlRenderer
1515
{
16+
17+
/**
18+
* List of void elements which require a self-closing tag and don't allow content
19+
*
20+
* @var array
21+
*/
22+
private const VOID_ELEMENTS_MAP = [
23+
'area' => true,
24+
'base' => true,
25+
'br' => true,
26+
'col' => true,
27+
'command' => true,
28+
'embed' => true,
29+
'hr' => true,
30+
'img' => true,
31+
'input' => true,
32+
'keygen' => true,
33+
'link' => true,
34+
'meta' => true,
35+
'param' => true,
36+
'source' => true,
37+
'track' => true,
38+
'wbr' => true,
39+
];
40+
1641
/**
1742
* @var Escaper
1843
*/
@@ -49,10 +74,10 @@ public function renderTag(TagData $tagData): string
4974
}
5075

5176
$html = '<' .$tagData->getTag() .$attributesHtml;
52-
if ($content) {
53-
$html .= '>' .$content .'</' .$tagData->getTag() .'>';
54-
} else {
77+
if (isset(self::VOID_ELEMENTS_MAP[$tagData->getTag()])) {
5578
$html .= '/>';
79+
} else {
80+
$html .= '>' .$content .'</' .$tagData->getTag() .'>';
5681
}
5782

5883
return $html;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Framework\View\Test\Unit\Helper\SecureHtmlRenderer;
9+
10+
use Magento\Framework\Escaper;
11+
use Magento\Framework\View\Helper\SecureHtmlRender\HtmlRenderer;
12+
use Magento\Framework\View\Helper\SecureHtmlRender\TagData;
13+
use PHPUnit\Framework\TestCase;
14+
15+
class HtmlRendererTest extends TestCase
16+
{
17+
/**
18+
* @return void
19+
*/
20+
protected function setUp(): void
21+
{
22+
$this->escaperMock = $this->createMock(Escaper::class);
23+
}
24+
25+
/**
26+
* @covers \Magento\Framework\View\Helper\SecureHtmlRender\HtmlRenderer::renderTag
27+
*/
28+
public function testRenderTag()
29+
{
30+
$helper = new HtmlRenderer($this->escaperMock);
31+
32+
/** Test void element to have closing tag */
33+
$tag = new TagData('hr', [], null, true);
34+
$this->assertEquals(
35+
"<hr/>",
36+
$helper->renderTag($tag)
37+
);
38+
39+
/** Test void element to never have content */
40+
$tag = new TagData('hr', [], 'content', false);
41+
$this->assertEquals(
42+
"<hr/>",
43+
$helper->renderTag($tag)
44+
);
45+
46+
/** Test any non-void element to not have a closing tag while not having content */
47+
$tags = new TagData('script', [], null, false);
48+
$this->assertEquals(
49+
"<script></script>",
50+
$helper->renderTag($tags)
51+
);
52+
53+
/** Test any non-void element to not have a closing tag and allow content */
54+
$tags = new TagData('script', [], 'content', false);
55+
$this->assertEquals(
56+
"<script>content</script>",
57+
$helper->renderTag($tags)
58+
);
59+
}
60+
}

0 commit comments

Comments
 (0)