From 53a59af925490ae8ab8026b232ff68a18a438b7a Mon Sep 17 00:00:00 2001 From: catch 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 f4ca58bb3fb..35d6608256c 100644 --- a/core/lib/Drupal/Core/Asset/AssetResolver.php +++ b/core/lib/Drupal/Core/Asset/AssetResolver.php @@ -138,6 +138,8 @@ class AssetResolver implements AssetResolverInterface { 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 @@ class AssetResolver implements AssetResolverInterface { 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 8841866ff09..b3855c98b17 100644 --- a/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php +++ b/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php @@ -124,7 +124,14 @@ class CssCollectionOptimizer implements AssetCollectionOptimizerInterface { 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 @@ class CssCollectionOptimizer implements AssetCollectionOptimizerInterface { 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 1c273438ca8..a54c33cb927 100644 --- a/core/lib/Drupal/Core/Asset/CssCollectionOptimizerLazy.php +++ b/core/lib/Drupal/Core/Asset/CssCollectionOptimizerLazy.php @@ -160,7 +160,14 @@ class CssCollectionOptimizerLazy implements AssetCollectionGroupOptimizerInterfa 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 @@ class CssCollectionOptimizerLazy implements AssetCollectionGroupOptimizerInterfa 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 ceb8505c438..d0420d0f80e 100644 --- a/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php +++ b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php @@ -124,7 +124,14 @@ class JsCollectionOptimizer implements AssetCollectionOptimizerInterface { 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 3a2a7137dc9..688459a69b0 100644 --- a/core/lib/Drupal/Core/Asset/JsCollectionOptimizerLazy.php +++ b/core/lib/Drupal/Core/Asset/JsCollectionOptimizerLazy.php @@ -172,7 +172,14 @@ class JsCollectionOptimizerLazy implements AssetCollectionGroupOptimizerInterfac */ 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 8e58467ea88..5e7eaa3b450 100644 --- a/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerLazyUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerLazyUnitTest.php @@ -53,6 +53,11 @@ class CssCollectionOptimizerLazyUnitTest extends UnitTestCase { $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 @@ class CssCollectionOptimizerLazyUnitTest extends UnitTestCase { '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 @@ class CssCollectionOptimizerLazyUnitTest extends UnitTestCase { 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 f08dd9e1e31..d6b17c34a15 100644 --- a/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerUnitTest.php @@ -64,20 +64,96 @@ class CssCollectionOptimizerUnitTest extends UnitTestCase { $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 5ca58da4667..cad7c7f0cfb 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 00000000000..40e90107348 --- /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;}