From 08c6714350775208776cbeb003f95f6b0cbdd1b0 Mon Sep 17 00:00:00 2001
From: Jonas Eberle <flightvision@googlemail.com>
Date: Tue, 10 Jan 2023 16:14:00 +0100
Subject: [PATCH] [BUGFIX] Concatenate inline JavaScript with line break

This adds a line break between concatenated "inline" JavaScripts
in order to keep JavaScript without trailing ';' valid.

Resolves: #99503
Releases: main, 12.4, 11.5
Change-Id: Icf902679c715687c5be2e5f572526608ce31f882
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/81686
Reviewed-by: Benjamin Franzke <ben@bnf.dev>
Tested-by: core-ci <typo3@b13.com>
Tested-by: Benjamin Franzke <ben@bnf.dev>
---
 .../ContentObject/ContentObjectRenderer.php   | 21 ++++++++++++++-----
 .../frontend/Classes/Http/RequestHandler.php  | 16 ++++++++++++--
 .../Tests/Unit/Http/RequestHandlerTest.php    |  4 ++++
 3 files changed, 34 insertions(+), 7 deletions(-)

diff --git a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
index 9b34f151cd1f..e3d7a8246699 100644
--- a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
+++ b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
@@ -687,19 +687,30 @@ class ContentObjectRenderer implements LoggerAwareInterface
      */
     public function cObjGet($setup, $addKey = '')
     {
-        if (!is_array($setup)) {
-            return '';
+        return implode('', $this->cObjGetSeparated($setup, $addKey));
+    }
+
+    /**
+     * Rendering of a "numerical array" of cObjects from TypoScript
+     * Will call ->cObjGetSingle() for each cObject found.
+     *
+     * @return list<string>
+     */
+    public function cObjGetSeparated(?array $setup, string $addKey = ''): array
+    {
+        if (!is_array($setup) || $setup === []) {
+            return [];
         }
         $sKeyArray = ArrayUtility::filterAndSortByNumericKeys($setup);
-        $content = '';
+        $contentObjects = [];
         foreach ($sKeyArray as $theKey) {
             $theValue = $setup[$theKey];
             if ((int)$theKey && !str_contains($theKey, '.')) {
                 $conf = $setup[$theKey . '.'] ?? [];
-                $content .= $this->cObjGetSingle($theValue, $conf, $addKey . $theKey);
+                $contentObjects[] = $this->cObjGetSingle($theValue, $conf, $addKey . $theKey);
             }
         }
-        return $content;
+        return $contentObjects;
     }
 
     /**
diff --git a/typo3/sysext/frontend/Classes/Http/RequestHandler.php b/typo3/sysext/frontend/Classes/Http/RequestHandler.php
index f16cefd9ae8c..459d844149f1 100644
--- a/typo3/sysext/frontend/Classes/Http/RequestHandler.php
+++ b/typo3/sysext/frontend/Classes/Http/RequestHandler.php
@@ -716,9 +716,21 @@ class RequestHandler implements RequestHandlerInterface
         );
 
         // Javascript inline code
-        $inlineJS = $controller->cObj->cObjGet($controller->pSetup['jsInline.'] ?? null, 'jsInline.');
+        $inlineJS = implode(
+            LF,
+            $controller->cObj->cObjGetSeparated(
+                $controller->pSetup['jsInline.'] ?? null,
+                'jsInline.'
+            )
+        );
         // Javascript inline code for Footer
-        $inlineFooterJs = $controller->cObj->cObjGet($controller->pSetup['jsFooterInline.'] ?? null, 'jsFooterInline.');
+        $inlineFooterJs = implode(
+            LF,
+            $controller->cObj->cObjGetSeparated(
+                $controller->pSetup['jsFooterInline.'] ?? null,
+                'jsFooterInline.'
+            )
+        );
         $compressJs = (bool)($controller->config['config']['compressJs'] ?? false);
 
         // Needs to be called after call cObjGet() calls in order to get all headerData and footerData and replacements
diff --git a/typo3/sysext/frontend/Tests/Unit/Http/RequestHandlerTest.php b/typo3/sysext/frontend/Tests/Unit/Http/RequestHandlerTest.php
index 80f9889e562b..d1fb89db6044 100644
--- a/typo3/sysext/frontend/Tests/Unit/Http/RequestHandlerTest.php
+++ b/typo3/sysext/frontend/Tests/Unit/Http/RequestHandlerTest.php
@@ -241,6 +241,7 @@ class RequestHandlerTest extends UnitTestCase
         $siteLanguage = $this->createSiteWithLanguage()->getLanguageById(3);
         $cObj = $this->prophesize(ContentObjectRenderer::class);
         $cObj->cObjGet(Argument::cetera())->shouldBeCalled();
+        $cObj->cObjGetSeparated(Argument::cetera())->willReturn([]);
         $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult);
         $tmpl = $this->prophesize(TemplateService::class);
         $frontendControllerProphecy = $this->prophesize(TypoScriptFrontendController::class);
@@ -284,6 +285,7 @@ class RequestHandlerTest extends UnitTestCase
         $siteLanguage = $this->createSiteWithLanguage()->getLanguageById(3);
         $cObj = $this->prophesize(ContentObjectRenderer::class);
         $cObj->cObjGet(Argument::cetera())->shouldBeCalled();
+        $cObj->cObjGetSeparated(Argument::cetera())->willReturn([]);
         $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult);
         $tmpl = $this->prophesize(TemplateService::class);
         $frontendControllerProphecy = $this->prophesize(TypoScriptFrontendController::class);
@@ -332,6 +334,7 @@ class RequestHandlerTest extends UnitTestCase
         $siteLanguage = $this->createSiteWithLanguage()->getLanguageById(3);
         $cObj = $this->prophesize(ContentObjectRenderer::class);
         $cObj->cObjGet(Argument::cetera())->shouldBeCalled();
+        $cObj->cObjGetSeparated(Argument::cetera())->willReturn([]);
         $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult);
         $tmpl = $this->prophesize(TemplateService::class);
         $frontendControllerProphecy = $this->prophesize(TypoScriptFrontendController::class);
@@ -435,6 +438,7 @@ class RequestHandlerTest extends UnitTestCase
         $siteLanguage = $this->createSiteWithLanguage()->getLanguageById(3);
         $cObj = $this->prophesize(ContentObjectRenderer::class);
         $cObj->cObjGet(Argument::cetera())->shouldBeCalled();
+        $cObj->cObjGetSeparated(Argument::cetera())->willReturn([]);
         $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult);
         $tmpl = $this->prophesize(TemplateService::class);
         $frontendControllerProphecy = $this->prophesize(TypoScriptFrontendController::class);
-- 
GitLab