[BUGFIX] Ensure correct order of @-rules in CSS 36/47336/2
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Mon, 14 Mar 2016 13:39:51 +0000 (14:39 +0100)
committerAndreas Fernandez <typo3@scripting-base.de>
Mon, 21 Mar 2016 09:34:56 +0000 (10:34 +0100)
The ResourceCompressor must ensure the correct order of
@-rules, which is:
1. charset
2. namespace
3. import

If the concatenated CSS contains multiple @charset rules, only
the first one is taken into account.

Change-Id: I8c912874d486eac16505884e68a04b0bba400611
Resolves: #55690
Releases: master, 7.6
Reviewed-on: https://review.typo3.org/47336
Reviewed-by: Andreas Fernandez <typo3@scripting-base.de>
Tested-by: Andreas Fernandez <typo3@scripting-base.de>
typo3/sysext/core/Classes/Resource/ResourceCompressor.php
typo3/sysext/core/Tests/Unit/Resource/ResourceCompressorTest.php
typo3/sysext/core/Tests/Unit/Resource/ResourceCompressorTest/Fixtures/charset.css.optimized.css
typo3/sysext/core/Tests/Unit/Resource/ResourceCompressorTest/Fixtures/charset_newline.css

index 4a6e4ad..c5f9370 100644 (file)
@@ -688,10 +688,37 @@ class ResourceCompressor
         $regex = '/@(charset|import|namespace)\\s*(url)?\\s*\\(?\\s*["\']?[^"\'\\)]+["\']?\\s*\\)?\\s*;/i';
         preg_match_all($regex, $contents, $matches);
         if (!empty($matches[0])) {
+            // Ensure correct order of @charset, @namespace and @import
+            $charset = '';
+            $namespaces = [];
+            $imports = [];
+            foreach ($matches[1] as $index => $keyword) {
+                switch ($keyword) {
+                    case 'charset':
+                        if (empty($charset)) {
+                            $charset = $matches[0][$index];
+                        }
+                        break;
+                    case 'namespace':
+                        $namespaces[] = $matches[0][$index];
+                        break;
+                    case 'import':
+                        $imports[] = $matches[0][$index];
+                        break;
+                }
+            }
+
+            $namespaces = !empty($namespaces) ? implode('', $namespaces) . $comment : '';
+            $imports = !empty($imports) ? implode('', $imports) . $comment : '';
             // remove existing statements
             $contents = str_replace($matches[0], '', $contents);
             // add statements to the top of contents in the order they occur in original file
-            $contents = $comment . implode($comment, $matches[0]) . LF . trim($contents);
+            $contents =
+                $charset
+                . $comment
+                . $namespaces
+                . $imports
+                . trim($contents);
         }
         return $contents;
     }
@@ -757,8 +784,6 @@ class ResourceCompressor
      */
     protected function compressCssString($contents)
     {
-        // Remove multiple charset declarations for standards compliance (and fixing Safari problems).
-        $contents = preg_replace('/^@charset\s+[\'"](\S*?)\b[\'"];/i', '', $contents);
         // Perform some safe CSS optimizations.
         // Regexp to match comment blocks.
         $comment = '/\*[^*]*\*+(?:[^/*][^*]*\*+)*/';
index 792e1db..4e538b0 100644 (file)
@@ -47,23 +47,31 @@ class ResourceCompressorTest extends BaseTestCase
             ),
             'import in front' => array(
                 '@import url(http://www.example.com/css); body { background: #ffffff; }',
-                'LF/* moved by compressor */LF@import url(http://www.example.com/css);LFbody { background: #ffffff; }'
+                'LF/* moved by compressor */LF@import url(http://www.example.com/css);LF/* moved by compressor */LFbody { background: #ffffff; }'
             ),
             'import in back, without quotes' => array(
                 'body { background: #ffffff; } @import url(http://www.example.com/css);',
-                'LF/* moved by compressor */LF@import url(http://www.example.com/css);LFbody { background: #ffffff; }'
+                'LF/* moved by compressor */LF@import url(http://www.example.com/css);LF/* moved by compressor */LFbody { background: #ffffff; }'
             ),
             'import in back, with double-quotes' => array(
                 'body { background: #ffffff; } @import url("http://www.example.com/css");',
-                'LF/* moved by compressor */LF@import url("http://www.example.com/css");LFbody { background: #ffffff; }'
+                'LF/* moved by compressor */LF@import url("http://www.example.com/css");LF/* moved by compressor */LFbody { background: #ffffff; }'
             ),
             'import in back, with single-quotes' => array(
                 'body { background: #ffffff; } @import url(\'http://www.example.com/css\');',
-                'LF/* moved by compressor */LF@import url(\'http://www.example.com/css\');LFbody { background: #ffffff; }'
+                'LF/* moved by compressor */LF@import url(\'http://www.example.com/css\');LF/* moved by compressor */LFbody { background: #ffffff; }'
             ),
             'import in middle and back, without quotes' => array(
                 'body { background: #ffffff; } @import url(http://www.example.com/A); div { background: #000; } @import url(http://www.example.com/B);',
-                'LF/* moved by compressor */LF@import url(http://www.example.com/A);LF/* moved by compressor */LF@import url(http://www.example.com/B);LFbody { background: #ffffff; }  div { background: #000; }'
+                'LF/* moved by compressor */LF@import url(http://www.example.com/A);@import url(http://www.example.com/B);LF/* moved by compressor */LFbody { background: #ffffff; }  div { background: #000; }'
+            ),
+            'charset declaration is unique' => array(
+                'body { background: #ffffff; } @charset "UTF-8"; div { background: #000; }; @charset "UTF-8";',
+                '@charset "UTF-8";LF/* moved by compressor */LFbody { background: #ffffff; }  div { background: #000; };'
+            ),
+            'order of charset, namespace and import is correct' => array(
+                'body { background: #ffffff; } @charset "UTF-8"; div { background: #000; }; @import "file2.css"; @namespace url(http://www.w3.org/1999/xhtml);',
+                '@charset "UTF-8";LF/* moved by compressor */LF@namespace url(http://www.w3.org/1999/xhtml);LF/* moved by compressor */LF@import "file2.css";LF/* moved by compressor */LFbody { background: #ffffff; }  div { background: #000; };'
             ),
         );
     }