From 53a59af925490ae8ab8026b232ff68a18a438b7a Mon Sep 17 00:00:00 2001
From: catch <catch@35733.no-reply.drupal.org>
Date: Tue, 6 Sep 2022 11:11:11 +0100
Subject: [PATCH] Issue #2258313 by Wim Leers, nod_, ravi.shankar, lauriii,
 catch, mfb, longwave, corbacho, alexpott, sun, Owen Barton, tstoeckler: Add
 license information to aggregated assets

---
 core/lib/Drupal/Core/Asset/AssetResolver.php  |  4 +
 .../Core/Asset/CssCollectionOptimizer.php     |  9 ++-
 .../Core/Asset/CssCollectionOptimizerLazy.php |  9 ++-
 .../Core/Asset/JsCollectionOptimizer.php      |  7 ++
 .../Core/Asset/JsCollectionOptimizerLazy.php  |  7 ++
 .../CssCollectionOptimizerLazyUnitTest.php    | 77 +++++++++++++++++++
 .../Asset/CssCollectionOptimizerUnitTest.php  | 76 ++++++++++++++++++
 ...t_with_import.css.optimized.aggregated.css |  4 +-
 .../css_license.css.optimized.aggregated.css  | 21 +++++
 9 files changed, 211 insertions(+), 3 deletions(-)
 create mode 100644 core/tests/Drupal/Tests/Core/Asset/css_test_files/css_license.css.optimized.aggregated.css

diff --git a/core/lib/Drupal/Core/Asset/AssetResolver.php b/core/lib/Drupal/Core/Asset/AssetResolver.php
index f4ca58bb3fb6..35d6608256c0 100644
--- a/core/lib/Drupal/Core/Asset/AssetResolver.php
+++ b/core/lib/Drupal/Core/Asset/AssetResolver.php
@@ -138,6 +138,8 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize, Languag
       if (isset($definition['css'])) {
         foreach ($definition['css'] as $options) {
           $options += $default_options;
+          // Copy the asset library license information to each file.
+          $options['license'] = $definition['license'];
 
           // Files with a query string cannot be preprocessed.
           if ($options['type'] === 'file' && $options['preprocess'] && strpos($options['data'], '?') !== FALSE) {
@@ -244,6 +246,8 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize, Language
         if (isset($definition['js'])) {
           foreach ($definition['js'] as $options) {
             $options += $default_options;
+            // Copy the asset library license information to each file.
+            $options['license'] = $definition['license'];
 
             // 'scope' is a calculated option, based on which libraries are
             // marked to be loaded from the header (see above).
diff --git a/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php b/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php
index 8841866ff097..b3855c98b17a 100644
--- a/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php
+++ b/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php
@@ -124,7 +124,14 @@ public function optimize(array $css_assets, array $libraries) {
             if (empty($uri) || !file_exists($uri)) {
               // Optimize each asset within the group.
               $data = '';
+              $current_license = FALSE;
               foreach ($css_group['items'] as $css_asset) {
+                // Ensure license information is available as a comment after
+                // optimization.
+                if ($css_asset['license'] !== $current_license) {
+                  $data .= "/* @license " . $css_asset['license']['name'] . " " . $css_asset['license']['url'] . " */\n";
+                }
+                $current_license = $css_asset['license'];
                 $data .= $this->optimizer->optimize($css_asset);
               }
               // Per the W3C specification at
@@ -138,7 +145,7 @@ public function optimize(array $css_assets, array $libraries) {
 REGEXP;
               preg_match_all($regexp, $data, $matches);
               $data = preg_replace($regexp, '', $data);
-              $data = implode('', $matches[0]) . $data;
+              $data = implode('', $matches[0]) . (!empty($matches[0]) ? "\n" : '') . $data;
               // Dump the optimized CSS for this group into an aggregate file.
               $uri = $this->dumper->dump($data, 'css');
               // Set the URI for this group's aggregate file.
diff --git a/core/lib/Drupal/Core/Asset/CssCollectionOptimizerLazy.php b/core/lib/Drupal/Core/Asset/CssCollectionOptimizerLazy.php
index 1c273438ca86..a54c33cb9272 100644
--- a/core/lib/Drupal/Core/Asset/CssCollectionOptimizerLazy.php
+++ b/core/lib/Drupal/Core/Asset/CssCollectionOptimizerLazy.php
@@ -160,7 +160,14 @@ public function deleteAll() {
   public function optimizeGroup(array $group): string {
     // Optimize each asset within the group.
     $data = '';
+    $current_license = FALSE;
     foreach ($group['items'] as $css_asset) {
+      // Ensure license information is available as a comment after
+      // optimization.
+      if ($css_asset['license'] !== $current_license) {
+        $data .= "/* @license " . $css_asset['license']['name'] . " " . $css_asset['license']['url'] . " */\n";
+      }
+      $current_license = $css_asset['license'];
       $data .= $this->optimizer->optimize($css_asset);
     }
     // Per the W3C specification at
@@ -174,7 +181,7 @@ public function optimizeGroup(array $group): string {
 REGEXP;
     preg_match_all($regexp, $data, $matches);
     $data = preg_replace($regexp, '', $data);
-    return implode('', $matches[0]) . $data;
+    return implode('', $matches[0]) . (!empty($matches[0]) ? "\n" : '') . $data;
   }
 
 }
diff --git a/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
index ceb8505c438f..d0420d0f80e8 100644
--- a/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
+++ b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
@@ -124,7 +124,14 @@ public function optimize(array $js_assets, array $libraries) {
             if (empty($uri) || !file_exists($uri)) {
               // Concatenate each asset within the group.
               $data = '';
+              $current_license = FALSE;
               foreach ($js_group['items'] as $js_asset) {
+                // Ensure license information is available as a comment after
+                // optimization.
+                if ($js_asset['license'] !== $current_license) {
+                  $data .= "/* @license " . $js_asset['license']['name'] . " " . $js_asset['license']['url'] . " */\n";
+                }
+                $current_license = $js_asset['license'];
                 // Optimize this JS file, but only if it's not yet minified.
                 if (isset($js_asset['minified']) && $js_asset['minified']) {
                   $data .= file_get_contents($js_asset['data']);
diff --git a/core/lib/Drupal/Core/Asset/JsCollectionOptimizerLazy.php b/core/lib/Drupal/Core/Asset/JsCollectionOptimizerLazy.php
index 3a2a7137dc91..688459a69b03 100644
--- a/core/lib/Drupal/Core/Asset/JsCollectionOptimizerLazy.php
+++ b/core/lib/Drupal/Core/Asset/JsCollectionOptimizerLazy.php
@@ -172,7 +172,14 @@ public function deleteAll() {
    */
   public function optimizeGroup(array $group): string {
     $data = '';
+    $current_license = FALSE;
     foreach ($group['items'] as $js_asset) {
+      // Ensure license information is available as a comment after
+      // optimization.
+      if ($js_asset['license'] !== $current_license) {
+        $data .= "/* @license " . $js_asset['license']['name'] . " " . $js_asset['license']['url'] . " */\n";
+      }
+      $current_license = $js_asset['license'];
       // Optimize this JS file, but only if it's not yet minified.
       if (isset($js_asset['minified']) && $js_asset['minified']) {
         $data .= file_get_contents($js_asset['data']);
diff --git a/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerLazyUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerLazyUnitTest.php
index 8e58467ea885..5e7eaa3b4506 100644
--- a/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerLazyUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerLazyUnitTest.php
@@ -53,6 +53,11 @@ public function testCssImport(): void {
     $mock_time = $this->createMock(TimeInterface::class);
     $mock_language = $this->createMock(LanguageManagerInterface::class);
     $optimizer = new CssCollectionOptimizerLazy($mock_grouper, $mock_optimizer, $mock_theme_manager, $mock_dependency_resolver, new RequestStack(), $mock_file_system, $mock_config_factory, $mock_file_url_generator, $mock_time, $mock_language, $mock_state);
+    $gpl_license = [
+      'name' => 'GNU-GPL-2.0-or-later',
+      'url' => 'https://www.drupal.org/licensing/faq',
+      'gpl-compatible' => TRUE,
+    ];
     $aggregate = $optimizer->optimizeGroup(
       [
         'items' => [
@@ -60,11 +65,13 @@ public function testCssImport(): void {
             'type' => 'file',
             'data' => 'core/modules/system/tests/modules/common_test/common_test_css_import.css',
             'preprocess' => TRUE,
+            'license' => $gpl_license,
           ],
           '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,
+            'license' => $gpl_license,
           ],
         ],
       ],
@@ -72,4 +79,74 @@ public function testCssImport(): void {
     self::assertStringEqualsFile(__DIR__ . '/css_test_files/css_input_with_import.css.optimized.aggregated.css', $aggregate);
   }
 
+  /**
+   * Test that license information is added correctly to aggregated CSS.
+   *
+   * Checks that license information is added only once when several files
+   * have the same license. Checks that multiple licenses are added properly.
+   */
+  public function testCssLicenseAggregation(): void {
+    $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'),
+        file_get_contents(__DIR__ . '/css_test_files/css_input_without_import.css.optimized.css')
+      );
+    $mock_theme_manager = $this->createMock(ThemeManagerInterface::class);
+    $mock_dependency_resolver = $this->createMock(LibraryDependencyResolverInterface::class);
+    $mock_state = $this->createMock(StateInterface::class);
+    $mock_file_system = $this->createMock(FileSystemInterface::class);
+    $mock_config_factory = $this->createMock(ConfigFactoryInterface::class);
+    $mock_file_url_generator = $this->createMock(FileUrlGeneratorInterface::class);
+    $mock_time = $this->createMock(TimeInterface::class);
+    $mock_language = $this->createMock(LanguageManagerInterface::class);
+    $optimizer = new CssCollectionOptimizerLazy($mock_grouper, $mock_optimizer, $mock_theme_manager, $mock_dependency_resolver, new RequestStack(), $mock_file_system, $mock_config_factory, $mock_file_url_generator, $mock_time, $mock_language, $mock_state);
+    $gpl_license = [
+      'name' => 'GNU-GPL-2.0-or-later',
+      'url' => 'https://www.drupal.org/licensing/faq',
+      'gpl-compatible' => TRUE,
+    ];
+    $aggregate = $optimizer->optimizeGroup(
+      [
+        'items' => [
+          '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,
+            'license' => $gpl_license,
+          ],
+          '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,
+            'license' => $gpl_license,
+          ],
+          'core/modules/system/tests/modules/common_test/css_input_without_import.css' => [
+            'type' => 'file',
+            'data' => 'core/modules/system/tests/modules/common_test/css_input_without_import.css',
+            'preprocess' => TRUE,
+            'license' => [
+              'name' => 'MIT',
+              'url' => 'https://opensource.org/licenses/MIT',
+              'gpl-compatible' => TRUE,
+            ],
+          ],
+        ],
+      ],
+    );
+    self::assertStringEqualsFile(__DIR__ . '/css_test_files/css_license.css.optimized.aggregated.css', $aggregate);
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerUnitTest.php
index f08dd9e1e310..d6b17c34a15a 100644
--- a/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerUnitTest.php
@@ -64,20 +64,96 @@ public function testCssImport() {
     $mock_file_system = $this->createMock(FileSystemInterface::class);
     $mock_time = $this->createMock(TimeInterface::class);
     $this->optimizer = new CssCollectionOptimizer($mock_grouper, $mock_optimizer, $mock_dumper, $mock_state, $mock_file_system, $mock_time);
+    $gpl_license = [
+      'name' => 'GNU-GPL-2.0-or-later',
+      'url' => 'https://www.drupal.org/licensing/faq',
+      'gpl-compatible' => TRUE,
+    ];
     $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,
+        'license' => $gpl_license,
       ],
       '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,
+        'license' => $gpl_license,
       ],
     ],
     []);
     self::assertEquals(file_get_contents(__DIR__ . '/css_test_files/css_input_with_import.css.optimized.aggregated.css'), $this->dumperData);
   }
 
+  /**
+   * Tests that CSS imports with strange letters do not destroy the CSS output.
+   *
+   * Checks that license information is added only once when several files
+   * have the same license. Checks that multiple licenses are added properly.
+   *
+   * @group legacy
+   */
+  public function testCssLicenseAggregation() {
+    $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'),
+        file_get_contents(__DIR__ . '/css_test_files/css_input_without_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);
+    $mock_time = $this->createMock(TimeInterface::class);
+    $this->optimizer = new CssCollectionOptimizer($mock_grouper, $mock_optimizer, $mock_dumper, $mock_state, $mock_file_system, $mock_time);
+    $gpl_license = [
+      'name' => 'GNU-GPL-2.0-or-later',
+      'url' => 'https://www.drupal.org/licensing/faq',
+      'gpl-compatible' => TRUE,
+    ];
+    $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,
+        'license' => $gpl_license,
+      ],
+      '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,
+        'license' => $gpl_license,
+      ],
+      'core/modules/system/tests/modules/common_test/css_input_without_import.css' => [
+        'type' => 'file',
+        'data' => 'core/modules/system/tests/modules/common_test/css_input_without_import.css',
+        'preprocess' => TRUE,
+        'license' => [
+          'name' => 'MIT',
+          'url' => 'https://opensource.org/licenses/MIT',
+          'gpl-compatible' => TRUE,
+        ],
+      ],
+    ],
+    []);
+    self::assertEquals(file_get_contents(__DIR__ . '/css_test_files/css_license.css.optimized.aggregated.css'), $this->dumperData);
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.aggregated.css b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.aggregated.css
index 5ca58da46673..cad7c7f0cfb7 100644
--- a/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.aggregated.css
+++ b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.aggregated.css
@@ -1,4 +1,6 @@
-@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap") print;@import url('import1.css') screen;@import url("http://example.com/style.css");@import url("//example.com/style.css");@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap");@import url("http://example.com/style.css") screen and (orientation:landscape);@import "http://example.com/style.css" screen;@import "http://example.com/style.css" supports(display:table-cell);@import "http://example.com/style.css" supports(display:table-cell) screen;@import url("http://example.com/style.css") screen and (orientation:landscape);@import url("http://example.com/style.css") screen;@import url("http://user:pass@example.com/style.css") screen and (orientation:landscape);@import url(http://example.com/cus\(t;om.css);@import url('http://example.com/cu(st;o)m.css');@import url("http://user:pass@example.com/cu(s)t;om.css");@import url(http://user:pass@example.com/cu\(s\)t;om.css);ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
+@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap") print;@import url('import1.css') screen;@import url("http://example.com/style.css");@import url("//example.com/style.css");@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap");@import url("http://example.com/style.css") screen and (orientation:landscape);@import "http://example.com/style.css" screen;@import "http://example.com/style.css" supports(display:table-cell);@import "http://example.com/style.css" supports(display:table-cell) screen;@import url("http://example.com/style.css") screen and (orientation:landscape);@import url("http://example.com/style.css") screen;@import url("http://user:pass@example.com/style.css") screen and (orientation:landscape);@import url(http://example.com/cus\(t;om.css);@import url('http://example.com/cu(st;o)m.css');@import url("http://user:pass@example.com/cu(s)t;om.css");@import url(http://user:pass@example.com/cu\(s\)t;om.css);
+/* @license GNU-GPL-2.0-or-later https://www.drupal.org/licensing/faq */
+ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
 p,select{font:1em/160% Verdana,sans-serif;color:#494949;}
 ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
 ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
diff --git a/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_license.css.optimized.aggregated.css b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_license.css.optimized.aggregated.css
new file mode 100644
index 000000000000..40e901073483
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_license.css.optimized.aggregated.css
@@ -0,0 +1,21 @@
+@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap") print;@import url('import1.css') screen;@import url("http://example.com/style.css");@import url("//example.com/style.css");@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap");@import url("http://example.com/style.css") screen and (orientation:landscape);@import "http://example.com/style.css" screen;@import "http://example.com/style.css" supports(display:table-cell);@import "http://example.com/style.css" supports(display:table-cell) screen;@import url("http://example.com/style.css") screen and (orientation:landscape);@import url("http://example.com/style.css") screen;@import url("http://user:pass@example.com/style.css") screen and (orientation:landscape);@import url(http://example.com/cus\(t;om.css);@import url('http://example.com/cu(st;o)m.css');@import url("http://user:pass@example.com/cu(s)t;om.css");@import url(http://user:pass@example.com/cu\(s\)t;om.css);
+/* @license GNU-GPL-2.0-or-later https://www.drupal.org/licensing/faq */
+ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
+p,select{font:1em/160% Verdana,sans-serif;color:#494949;}
+ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
+ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
+body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
+.is
+.a
+.test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;}
+ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(../images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
+p,select{font:1em/160% Verdana,sans-serif;color:#494949;}
+body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
+.is
+.a
+.test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;}
+/* @license MIT https://opensource.org/licenses/MIT */
+body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
+.is
+.a
+.test{font:1em/100% Verdana,sans-serif;color:#494949;}some :pseudo .thing{border-radius:3px;}::-moz-selection{background:#000;color:#fff;}::selection{background:#000;color:#fff;}@media print{*{background:#000 !important;color:#fff !important;}@page{margin:0.5cm;}}@media screen and (max-device-width:480px){background:#000;color:#fff;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;}
-- 
GitLab