[FEATURE] Add crossorigin property to JavaScript files 55/48455/8
authorJacob Dreesen <j.dreesen@neusta.de>
Sat, 9 Sep 2017 16:30:55 +0000 (18:30 +0200)
committerStefan Neufeind <typo3.neufeind@speedpartner.de>
Sun, 10 Sep 2017 13:11:08 +0000 (15:11 +0200)
Add a property 'crossorigin="some-value"' to JavaScript files via TypoScript
page.includeJSlibs.<array>.crossorigin = some-value

The crossorigin property is automatically set to the value "anonymous" for
external JavaScript files with an integrity property if not explicitly set.

This patch affects the TypoScript PAGE properties
* includeJSlibs
* includeJSFooterlibs
* includeJS
* includeJSFooter

Resolves: #76459
Releases: master
Change-Id: Ie8d1d86ca5a8863c2a637395f1f87a463729691c
Reviewed-on: https://review.typo3.org/48455
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Stefan Neufeind <typo3.neufeind@speedpartner.de>
Tested-by: Stefan Neufeind <typo3.neufeind@speedpartner.de>
typo3/sysext/core/Classes/Page/PageRenderer.php
typo3/sysext/core/Documentation/Changelog/master/Feature-76459-AddCrossoriginPropertyToJavaScriptFiles.rst [new file with mode: 0644]
typo3/sysext/frontend/Classes/Page/PageGenerator.php

index ba2bfda..789a1b3 100644 (file)
@@ -1038,8 +1038,9 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
      * @param bool $async Flag if property 'async="async"' should be added to JavaScript tags
      * @param string $integrity Subresource Integrity (SRI)
      * @param bool $defer Flag if property 'defer="defer"' should be added to JavaScript tags
+     * @param string $crossorigin CORS settings attribute
      */
-    public function addJsLibrary($name, $file, $type = 'text/javascript', $compress = false, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '', $defer = false)
+    public function addJsLibrary($name, $file, $type = 'text/javascript', $compress = false, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '', $defer = false, $crossorigin = '')
     {
         if (!$type) {
             $type = 'text/javascript';
@@ -1057,6 +1058,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
                 'async' => $async,
                 'integrity' => $integrity,
                 'defer' => $defer,
+                'crossorigin' => $crossorigin,
             ];
         }
     }
@@ -1075,8 +1077,9 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
      * @param bool $async Flag if property 'async="async"' should be added to JavaScript tags
      * @param string $integrity Subresource Integrity (SRI)
      * @param bool $defer Flag if property 'defer="defer"' should be added to JavaScript tags
+     * @param string $crossorigin CORS settings attribute
      */
-    public function addJsFooterLibrary($name, $file, $type = 'text/javascript', $compress = false, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '', $defer = false)
+    public function addJsFooterLibrary($name, $file, $type = 'text/javascript', $compress = false, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '', $defer = false, $crossorigin = '')
     {
         if (!$type) {
             $type = 'text/javascript';
@@ -1094,6 +1097,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
                 'async' => $async,
                 'integrity' => $integrity,
                 'defer' => $defer,
+                'crossorigin' => $crossorigin,
             ];
         }
     }
@@ -1111,8 +1115,9 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
      * @param bool $async Flag if property 'async="async"' should be added to JavaScript tags
      * @param string $integrity Subresource Integrity (SRI)
      * @param bool $defer Flag if property 'defer="defer"' should be added to JavaScript tags
+     * @param string $crossorigin CORS settings attribute
      */
-    public function addJsFile($file, $type = 'text/javascript', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '', $defer = false)
+    public function addJsFile($file, $type = 'text/javascript', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '', $defer = false, $crossorigin = '')
     {
         if (!$type) {
             $type = 'text/javascript';
@@ -1130,6 +1135,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
                 'async' => $async,
                 'integrity' => $integrity,
                 'defer' => $defer,
+                'crossorigin' => $crossorigin,
             ];
         }
     }
@@ -1147,8 +1153,9 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
      * @param bool $async Flag if property 'async="async"' should be added to JavaScript tags
      * @param string $integrity Subresource Integrity (SRI)
      * @param string $defer Flag if property 'defer="defer"' should be added to JavaScript tags
+     * @param string $crossorigin CORS settings attribute
      */
-    public function addJsFooterFile($file, $type = 'text/javascript', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '', $defer = false)
+    public function addJsFooterFile($file, $type = 'text/javascript', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '', $defer = false, $crossorigin = '')
     {
         if (!$type) {
             $type = 'text/javascript';
@@ -1166,6 +1173,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
                 'async' => $async,
                 'integrity' => $integrity,
                 'defer' => $defer,
+                'crossorigin' => $crossorigin,
             ];
         }
     }
@@ -2288,8 +2296,9 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
                 $properties['file'] = $this->getStreamlinedFileName($properties['file']);
                 $async = ($properties['async']) ? ' async="async"' : '';
                 $defer = ($properties['defer']) ? ' defer="defer"' : '';
-                $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '" crossorigin="anonymous"' : '';
-                $tag = '<script src="' . htmlspecialchars($properties['file']) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $defer . $integrity . '></script>';
+                $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '"' : '';
+                $crossorigin = ($properties['crossorigin']) ? ' crossorigin="' . htmlspecialchars($properties['crossorigin']) . '"' : '';
+                $tag = '<script src="' . htmlspecialchars($properties['file']) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $defer . $integrity . $crossorigin . '></script>';
                 if ($properties['allWrap']) {
                     $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
                     $tag = $wrapArr[0] . $tag . $wrapArr[1];
@@ -2331,8 +2340,9 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
                 $file = $this->getStreamlinedFileName($file);
                 $async = ($properties['async']) ? ' async="async"' : '';
                 $defer = ($properties['defer']) ? ' defer="defer"' : '';
-                $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '" crossorigin="anonymous"' : '';
-                $tag = '<script src="' . htmlspecialchars($file) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $defer . $integrity . '></script>';
+                $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '"' : '';
+                $crossorigin = ($properties['crossorigin']) ? ' crossorigin="' . htmlspecialchars($properties['crossorigin']) . '"' : '';
+                $tag = '<script src="' . htmlspecialchars($file) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $defer . $integrity . $crossorigin . '></script>';
                 if ($properties['allWrap']) {
                     $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
                     $tag = $wrapArr[0] . $tag . $wrapArr[1];
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-76459-AddCrossoriginPropertyToJavaScriptFiles.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-76459-AddCrossoriginPropertyToJavaScriptFiles.rst
new file mode 100644 (file)
index 0000000..181bb28
--- /dev/null
@@ -0,0 +1,43 @@
+.. include:: ../../Includes.txt
+
+==============================================================
+Feature: #76459 - Add crossorigin property to JavaScript files
+==============================================================
+
+See :issue:`76459`
+
+Description
+===========
+
+It is now possible to add the HTML attribute ``crossorigin="some-value"`` to <script> tags for
+Frontend rendering via TypoScript with the following new property
+
+``page.includeJSlibs.<array>.crossorigin = some-value``
+
+The ``crossorigin`` property is automatically set to the value ``anonymous`` for
+external JavaScript files with an ``integrity`` property if not explicitly set.
+
+The feature is available within the following TypoScript PAGE properties
+
+* includeJSlibs
+* includeJSFooterlibs
+* includeJS
+* includeJSFooter
+
+Usage:
+------
+
+.. code-block:: typoscript
+
+   page {
+      includeJS {
+         jQuery = https://code.jquery.com/jquery-2.2.4.min.js
+         jQuery.external = 1
+         jQuery.disableCompression = 1
+         jQuery.excludeFromConcatenation = 1
+         jQuery.integrity = sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=
+         jQuery.crossorigin = anonymous
+      }
+   }
+
+.. index:: Frontend, TypoScript
index 6eef442..348003a 100644 (file)
@@ -393,7 +393,10 @@ class PageGenerator
                         if (!$type) {
                             $type = 'text/javascript';
                         }
-
+                        $crossorigin = $jsFileConfig['crossorigin'];
+                        if (!$crossorigin && $jsFileConfig['integrity'] && $jsFileConfig['external']) {
+                            $crossorigin = 'anonymous';
+                        }
                         $pageRenderer->addJsLibrary(
                             $key,
                             $ss,
@@ -405,7 +408,8 @@ class PageGenerator
                             $jsFileConfig['allWrap.']['splitChar'],
                             (bool)$jsFileConfig['async'],
                             $jsFileConfig['integrity'],
-                            (bool)$jsFileConfig['defer']
+                            (bool)$jsFileConfig['defer'],
+                            $crossorigin
                         );
                         unset($jsFileConfig);
                     }
@@ -425,6 +429,10 @@ class PageGenerator
                         if (!$type) {
                             $type = 'text/javascript';
                         }
+                        $crossorigin = $jsFileConfig['crossorigin'];
+                        if (!$crossorigin && $jsFileConfig['integrity'] && $jsFileConfig['external']) {
+                            $crossorigin = 'anonymous';
+                        }
                         $pageRenderer->addJsFooterLibrary(
                             $key,
                             $ss,
@@ -436,7 +444,8 @@ class PageGenerator
                             $jsFileConfig['allWrap.']['splitChar'],
                             (bool)$jsFileConfig['async'],
                             $jsFileConfig['integrity'],
-                            (bool)$jsFileConfig['defer']
+                            (bool)$jsFileConfig['defer'],
+                            $crossorigin
                         );
                         unset($jsFileConfig);
                     }
@@ -457,6 +466,10 @@ class PageGenerator
                         if (!$type) {
                             $type = 'text/javascript';
                         }
+                        $crossorigin = $jsConfig['crossorigin'];
+                        if (!$crossorigin && $jsConfig['integrity'] && $jsConfig['external']) {
+                            $crossorigin = 'anonymous';
+                        }
                         $pageRenderer->addJsFile(
                             $ss,
                             $type,
@@ -467,7 +480,8 @@ class PageGenerator
                             $jsConfig['allWrap.']['splitChar'],
                             (bool)$jsConfig['async'],
                             $jsConfig['integrity'],
-                            (bool)$jsConfig['defer']
+                            (bool)$jsConfig['defer'],
+                            $crossorigin
                         );
                         unset($jsConfig);
                     }
@@ -487,6 +501,10 @@ class PageGenerator
                         if (!$type) {
                             $type = 'text/javascript';
                         }
+                        $crossorigin = $jsConfig['crossorigin'];
+                        if (!$crossorigin && $jsConfig['integrity'] && $jsConfig['external']) {
+                            $crossorigin = 'anonymous';
+                        }
                         $pageRenderer->addJsFooterFile(
                             $ss,
                             $type,
@@ -497,7 +515,8 @@ class PageGenerator
                             $jsConfig['allWrap.']['splitChar'],
                             (bool)$jsConfig['async'],
                             $jsConfig['integrity'],
-                            (bool)$jsConfig['defer']
+                            (bool)$jsConfig['defer'],
+                            $crossorigin
                         );
                         unset($jsConfig);
                     }