Unverified Commit 5aa154d2 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #2936067 by pfrenssen, bradjones1, nod_, eiriksm, DuaelFr, Clemens Sahs,...

Issue #2936067 by pfrenssen, bradjones1, nod_, eiriksm, DuaelFr, Clemens Sahs, AndyF, alexpott, lauriii, idimopoulos: CSS aggregation fails on many variations of @import

(cherry picked from commit 7374845d)
parent 1a6a815c
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -123,8 +123,12 @@ public function optimize(array $css_assets) {
              // Per the W3C specification at
              // http://www.w3.org/TR/REC-CSS2/cascade.html#at-import, @import
              // rules must precede any other style, so we move those to the
              // top.
              $regexp = '/@import[^;]+;/i';
              // top. The regular expression is expressed in NOWDOC since it is
              // detecting backslashes as well as single and double quotes. It
              // is difficult to read when represented as a quoted string.
              $regexp = <<<'REGEXP'
/@import\s*(?:'(?:\\'|.)*'|"(?:\\"|.)*"|url\(\s*(?:\\[\)\'\"]|[^'")])*\s*\)|url\(\s*'(?:\'|.)*'\s*\)|url\(\s*"(?:\"|.)*"\s*\)).*;/iU
REGEXP;
              preg_match_all($regexp, $data, $matches);
              $data = preg_replace($regexp, '', $data);
              $data = implode('', $matches[0]) . $data;
+4 −2
Original line number Diff line number Diff line
@@ -166,7 +166,7 @@ protected function loadNestedFile($matches) {
    // the url() path.
    $directory = $directory == '.' ? '' : $directory . '/';

    // Alter all internal url() paths. Leave external paths alone. We don't need
    // Alter all internal asset paths. Leave external paths alone. We don't need
    // to normalize absolute paths here because that will be done later.
    return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)([^\'")]+)([\'"]?)\s*\)/i', 'url(\1' . $directory . '\2\3)', $file);
  }
@@ -230,7 +230,9 @@ protected function processCss($contents, $optimize = FALSE) {
    }

    // Replaces @import commands with the actual stylesheet content.
    // This happens recursively but omits external files.
    // This happens recursively but omits external files and local files
    // with supports- or media-query qualifiers, as those are conditionally
    // loaded depending on the user agent.
    $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)(?!\/\/)([^\'"\()]+)[\'"]?\s*\)?\s*;/', [$this, 'loadNestedFile'], $contents);

    return $contents;
+1 −0
Original line number Diff line number Diff line
@@ -1045,6 +1045,7 @@ nothere
notnull
notsimpletest
nourriture
nowdoc
nplurals
nresponse
ntfs
+82 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\Core\Asset;

use Drupal\Core\Asset\AssetCollectionGrouperInterface;
use Drupal\Core\Asset\AssetDumperInterface;
use Drupal\Core\Asset\AssetOptimizerInterface;
use Drupal\Core\Asset\CssCollectionOptimizer;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Tests\UnitTestCase;

/**
 * Tests the CSS asset optimizer.
 *
 * @group Asset
 */
class CssCollectionOptimizerUnitTest extends UnitTestCase {

  /**
   * The data from the dumper.
   *
   * @var string
   */
  protected $dumperData;

  /**
   * A CSS Collection optimizer.
   *
   * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface
   */
  protected $optimizer;

  protected function setUp(): void {
    parent::setUp();
    $mock_grouper = $this->createMock(AssetCollectionGrouperInterface::class);
    $mock_grouper->method('group')
      ->willReturnCallback(function ($assets) {
        return [
          [
            'items' => $assets,
            'type' => 'file',
            'preprocess' => TRUE,
          ],
        ];
      });
    $mock_optimizer = $this->createMock(AssetOptimizerInterface::class);
    $mock_optimizer->method('optimize')
      ->willReturn(
        file_get_contents(__DIR__ . '/css_test_files/css_input_with_import.css.optimized.css'),
        file_get_contents(__DIR__ . '/css_test_files/css_subfolder/css_input_with_import.css.optimized.css')
      );
    $mock_dumper = $this->createMock(AssetDumperInterface::class);
    $mock_dumper->method('dump')
      ->willReturnCallback(function ($css) {
        $this->dumperData = $css;
      });
    $mock_state = $this->createMock(StateInterface::class);
    $mock_file_system = $this->createMock(FileSystemInterface::class);
    $this->optimizer = new CssCollectionOptimizer($mock_grouper, $mock_optimizer, $mock_dumper, $mock_state, $mock_file_system);
  }

  /**
   * Test that css imports with strange letters do not destroy the css output.
   */
  public function testCssImport() {
    $this->optimizer->optimize([
      'core/modules/system/tests/modules/common_test/common_test_css_import.css' => [
        'type' => 'file',
        'data' => 'core/modules/system/tests/modules/common_test/common_test_css_import.css',
        'preprocess' => TRUE,
      ],
      'core/modules/system/tests/modules/common_test/common_test_css_import_not_preprocessed.css' => [
        'type' => 'file',
        'data' => 'core/modules/system/tests/modules/common_test/common_test_css_import.css',
        'preprocess' => TRUE,
      ],
    ]);
    self::assertEquals(file_get_contents(__DIR__ . '/css_test_files/css_input_with_import.css.optimized.aggregated.css'), $this->dumperData);
  }

}
+3 −1
Original line number Diff line number Diff line
@@ -61,6 +61,8 @@ public function providerTestOptimize() {
      //   file_create_url(). (https://www.drupal.org/node/1961340)
      // - Imported files that are external (protocol-relative URL or not)
      //   should not be expanded. (https://www.drupal.org/node/2014851)
      //   Potential forms of @import might also include media queries.
      //   (https://developer.mozilla.org/en-US/docs/Web/CSS/@import)
      [
        [
          'group' => -100,
@@ -72,7 +74,7 @@ public function providerTestOptimize() {
          'browsers' => ['IE' => TRUE, '!IE' => TRUE],
          'basename' => 'css_input_with_import.css',
        ],
        str_replace('url(images/icon.png)', 'url(' . file_url_transform_relative(file_create_url($path . 'images/icon.png')) . ')', file_get_contents($absolute_path . 'css_input_with_import.css.optimized.css')),
        str_replace("url('import1.css')", 'url(' . file_url_transform_relative(file_create_url($path . 'import1.css')) . ')', str_replace('url(images/icon.png)', 'url(' . file_url_transform_relative(file_create_url($path . 'images/icon.png')) . ')', file_get_contents($absolute_path . 'css_input_with_import.css.optimized.css'))),
      ],
      // File. Tests:
      // - Retain comment hacks.
Loading