]> source.dussan.org Git - nextcloud-server.git/commitdiff
feat(OC/Template): Add `type="module"` for ES6 scripts
authorFerdinand Thiessen <rpm@fthiessen.de>
Tue, 10 Jan 2023 00:40:42 +0000 (01:40 +0100)
committerFerdinand Thiessen <rpm@fthiessen.de>
Wed, 22 Feb 2023 20:19:37 +0000 (21:19 +0100)
Signed-off-by: Ferdinand Thiessen <rpm@fthiessen.de>
lib/private/legacy/template/functions.php
tests/lib/TemplateFunctionsTest.php

index 56c488d5abe79c63f26ef59803d0b37b370cfd8b..7081bd4f7437151241af23c6cd364d718476843d 100644 (file)
@@ -72,14 +72,19 @@ function emit_css_loading_tags($obj) {
  * Prints a <script> tag with nonce and defer depending on config
  * @param string $src the source URL, ignored when empty
  * @param string $script_content the inline script content, ignored when empty
+ * @param string $content_type the type of the source (e.g. 'module')
  */
-function emit_script_tag($src, $script_content = '') {
+function emit_script_tag(string $src, string $script_content = '', string $content_type = '') {
+       $nonceManager = \OC::$server->get(\OC\Security\CSP\ContentSecurityPolicyNonceManager::class);
+
        $defer_str = ' defer';
-       $s = '<script nonce="' . \OC::$server->getContentSecurityPolicyNonceManager()->getNonce() . '"';
+       $type = $content_type !== '' ? ' type="' . $content_type . '"' : '';
+
+       $s = '<script nonce="' . $nonceManager->getNonce() . '"';
        if (!empty($src)) {
                // emit script tag for deferred loading from $src
-               $s .= $defer_str.' src="' . $src .'">';
-       } elseif (!empty($script_content)) {
+               $s .= $defer_str.' src="' . $src .'"' . $type . '>';
+       } elseif ($script_content !== '') {
                // emit script tag for inline script from $script_content without defer (see MDN)
                $s .= ">\n".$script_content."\n";
        } else {
@@ -96,7 +101,8 @@ function emit_script_tag($src, $script_content = '') {
  */
 function emit_script_loading_tags($obj) {
        foreach ($obj['jsfiles'] as $jsfile) {
-               emit_script_tag($jsfile, '');
+               $type = str_ends_with($jsfile, '.mjs') ? 'module' : '';
+               emit_script_tag($jsfile, '', $type);
        }
        if (!empty($obj['inline_ocjs'])) {
                emit_script_tag('', $obj['inline_ocjs']);
index caecdfc76acb9972c4b63448c0666745b7e22681..b2b25ab654ce7be6fc2fceeec9127e0823a08882 100644 (file)
@@ -57,6 +57,35 @@ class TemplateFunctionsTest extends \Test\TestCase {
                print_unescaped($string);
        }
 
+       public function testEmitScriptTagWithContent() {
+               $this->expectOutputRegex('/<script nonce="[^"]+">\nalert\(\)\n<\/script>\n?/');
+               emit_script_tag('', 'alert()');
+       }
+
+       public function testEmitScriptTagWithSource() {
+               $this->expectOutputRegex('/<script nonce=".*" defer src="some.js"><\/script>/');
+               emit_script_tag('some.js');
+       }
+
+       public function testEmitScriptTagWithModuleSource() {
+               $this->expectOutputRegex('/<script nonce=".*" defer src="some.mjs" type="module"><\/script>/');
+               emit_script_tag('some.mjs', '', 'module');
+       }
+
+       public function testEmitScriptLoadingTags() {
+               // Test mjs js and inline content
+               $pattern = '/src="some\.mjs"[^>]+type="module"[^>]*>.+\n'; // some.mjs with type = module
+               $pattern .= '<script[^>]+src="other\.js"[^>]*>.+\n'; // other.js as plain javascript
+               $pattern .= '<script[^>]*>\n?.*inline.*\n?<\/script>'; // inline content
+               $pattern .= '/'; // no flags
+
+               $this->expectOutputRegex($pattern);
+               emit_script_loading_tags([
+                       'jsfiles' => ['some.mjs', 'other.js'],
+                       'inline_ocjs' => '// inline'
+               ]);
+       }
+
        // ---------------------------------------------------------------------------
        // Test relative_modified_date with dates only
        // ---------------------------------------------------------------------------