Skip to content
Snippets Groups Projects
Verified Commit 181827af authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3443108 by catch: Optimize AssetResolver caching

parent 296e471f
Branches
Tags
26 merge requests!11131[10.4.x-only-DO-NOT-MERGE]: Issue ##2842525 Ajax attached to Views exposed filter form does not trigger callbacks,!9470[10.3.x-only-DO-NOT-MERGE]: #3331771 Fix file_get_contents(): Passing null to parameter,!8540Issue #3457061: Bootstrap Modal dialog Not closing after 10.3.0 Update,!8528Issue #3456871 by Tim Bozeman: Support NULL services,!8373Issue #3427374 by danflanagan8, Vighneshh: taxonomy_tid ViewsArgumentDefault...,!3878Removed unused condition head title for views,!3818Issue #2140179: $entity->original gets stale between updates,!3742Issue #3328429: Create item list field formatter for displaying ordered and unordered lists,!3731Claro: role=button on status report items,!3651Issue #3347736: Create new SDC component for Olivero (header-search),!3531Issue #3336994: StringFormatter always displays links to entity even if the user in context does not have access,!3355Issue #3209129: Scrolling problems when adding a block via layout builder,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3133core/modules/system/css/components/hidden.module.css,!2964Issue #2865710 : Dependencies from only one instance of a widget are used in display modes,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2062Issue #3246454: Add weekly granularity to views date sort,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!877Issue #2708101: Default value for link text is not saved,!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493
Pipeline #159453 canceled
......@@ -125,13 +125,24 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize, Languag
if (!$assets->getLibraries()) {
return [];
}
$libraries_to_load = $this->getLibrariesToLoad($assets);
foreach ($libraries_to_load as $key => $library) {
[$extension, $name] = explode('/', $library, 2);
$definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
if (empty($definition['css'])) {
unset($libraries_to_load[$key]);
}
}
$libraries_to_load = array_values($libraries_to_load);
if (!$libraries_to_load) {
return [];
}
if (!isset($language)) {
$language = $this->languageManager->getCurrentLanguage();
}
$theme_info = $this->themeManager->getActiveTheme();
// Add the theme name to the cache key since themes may implement
// hook_library_info_alter().
$libraries_to_load = $this->getLibrariesToLoad($assets);
$cid = 'css:' . $theme_info->getName() . ':' . $language->getId() . Crypt::hashBase64(serialize($libraries_to_load)) . (int) $optimize;
if ($cached = $this->cache->get($cid)) {
return $cached->data;
......@@ -149,11 +160,6 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize, Languag
foreach ($libraries_to_load as $key => $library) {
[$extension, $name] = explode('/', $library, 2);
$definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
if (empty($definition['css'])) {
unset($libraries_to_load[$key]);
continue;
}
foreach ($definition['css'] as $options) {
$options += $default_options;
// Copy the asset library license information to each file.
......@@ -227,10 +233,34 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize, Language
$language = $this->languageManager->getCurrentLanguage();
}
$theme_info = $this->themeManager->getActiveTheme();
$libraries_to_load = $this->getLibrariesToLoad($assets);
// Collect all libraries that contain JS assets and are in the header.
// Also remove any libraries with no JavaScript from the libraries to
// load.
$header_js_libraries = [];
foreach ($libraries_to_load as $key => $library) {
[$extension, $name] = explode('/', $library, 2);
$definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
if (empty($definition['js'])) {
unset($libraries_to_load[$key]);
continue;
}
if (!empty($definition['header'])) {
$header_js_libraries[] = $library;
}
}
$libraries_to_load = array_values($libraries_to_load);
// If all the libraries to load contained only CSS, there is nothing further
// to do here, so return early.
if (!$libraries_to_load && !$assets->getSettings()) {
return [[], []];
}
// Add the theme name to the cache key since themes may implement
// hook_library_info_alter(). Additionally add the current language to
// support translation of JavaScript files via hook_js_alter().
$libraries_to_load = $this->getLibrariesToLoad($assets);
$cid = 'js:' . $theme_info->getName() . ':' . $language->getId() . ':' . Crypt::hashBase64(serialize($libraries_to_load)) . (int) (count($assets->getSettings()) > 0) . (int) $optimize;
if ($cached = $this->cache->get($cid)) {
......@@ -248,22 +278,6 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize, Language
'version' => NULL,
];
// Collect all libraries that contain JS assets and are in the header.
// Also remove any libraries with no JavaScript from the libraries to
// load.
$header_js_libraries = [];
foreach ($libraries_to_load as $key => $library) {
[$extension, $name] = explode('/', $library, 2);
$definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
if (empty($definition['js'])) {
unset($libraries_to_load[$key]);
continue;
}
if (!empty($definition['header'])) {
$header_js_libraries[] = $library;
}
}
$libraries_to_load = array_values($libraries_to_load);
// The current list of header JS libraries are only those libraries that
// are in the header, but their dependencies must also be loaded for them
// to function correctly, so update the list with those.
......
......@@ -111,10 +111,10 @@ public function testAnonymous() {
$this->assertSame($expected_queries, $recorded_queries);
$this->assertSame(34, $performance_data->getQueryCount());
$this->assertSame(124, $performance_data->getCacheGetCount());
$this->assertSame(46, $performance_data->getCacheSetCount());
$this->assertSame(45, $performance_data->getCacheSetCount());
$this->assertSame(0, $performance_data->getCacheDeleteCount());
$this->assertSame(37, $performance_data->getCacheTagChecksumCount());
$this->assertSame(42, $performance_data->getCacheTagIsValidCount());
$this->assertSame(36, $performance_data->getCacheTagChecksumCount());
$this->assertSame(43, $performance_data->getCacheTagIsValidCount());
$this->assertSame(0, $performance_data->getCacheTagInvalidationCount());
// Test node page.
......
......@@ -76,6 +76,10 @@ class AssetResolverTest extends UnitTestCase {
* A mocked Japanese language object.
*/
protected LanguageInterface $japanese;
/**
* An array of library definitions.
*/
protected $libraries = [];
/**
* {@inheritdoc}
......@@ -86,6 +90,44 @@ protected function setUp(): void {
$this->libraryDiscovery = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscovery')
->disableOriginalConstructor()
->getMock();
$this->libraries = [
'drupal' => [
'version' => '1.0.0',
'css' => [],
'js' =>
[
'core/misc/drupal.js' => ['data' => 'core/misc/drupal.js', 'preprocess' => TRUE],
],
'license' => '',
],
'jquery' => [
'version' => '1.0.0',
'css' => [],
'js' =>
[
'core/misc/jquery.js' => ['data' => 'core/misc/jquery.js', 'minified' => TRUE],
],
'license' => '',
],
'llama' => [
'version' => '1.0.0',
'css' =>
[
'core/misc/llama.css' => ['data' => 'core/misc/llama.css'],
],
'js' => [],
'license' => '',
],
'piggy' => [
'version' => '1.0.0',
'css' =>
[
'core/misc/piggy.css' => ['data' => 'core/misc/piggy.css'],
],
'js' => [],
'license' => '',
],
];
$this->libraryDependencyResolver = $this->createMock('\Drupal\Core\Asset\LibraryDependencyResolverInterface');
$this->libraryDependencyResolver->expects($this->any())
->method('getLibrariesWithDependencies')
......@@ -124,40 +166,77 @@ protected function setUp(): void {
/**
* @covers ::getCssAssets
* @dataProvider providerAttachedAssets
* @dataProvider providerAttachedCssAssets
*/
public function testGetCssAssets(AttachedAssetsInterface $assets_a, AttachedAssetsInterface $assets_b, $expected_cache_item_count) {
public function testGetCssAssets(AttachedAssetsInterface $assets_a, AttachedAssetsInterface $assets_b, $expected_css_cache_item_count) {
$this->libraryDiscovery->expects($this->any())
->method('getLibraryByName')
->willReturnOnConsecutiveCalls(
$this->libraries['drupal'],
$this->libraries['llama'],
$this->libraries['llama'],
$this->libraries['piggy'],
$this->libraries['piggy'],
);
$this->assetResolver->getCssAssets($assets_a, FALSE, $this->english);
$this->assetResolver->getCssAssets($assets_b, FALSE, $this->english);
$this->assertCount($expected_cache_item_count, $this->cache->getAllCids());
$this->assertCount($expected_css_cache_item_count, $this->cache->getAllCids());
}
public static function providerAttachedCssAssets() {
$time = time();
return [
'one js only library and one css only library' => [
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['core/drupal']),
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['llama/css']),
1,
],
'two different css libraries' => [
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['core/drupal', 'llama/css']),
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['piggy/css']),
2,
],
];
}
/**
* @covers ::getJsAssets
* @dataProvider providerAttachedAssets
* @dataProvider providerAttachedJsAssets
*/
public function testGetJsAssets(AttachedAssetsInterface $assets_a, AttachedAssetsInterface $assets_b, $expected_cache_item_count) {
public function testGetJsAssets(AttachedAssetsInterface $assets_a, AttachedAssetsInterface $assets_b, $expected_js_cache_item_count, $expected_multilingual_js_cache_item_count) {
$this->libraryDiscovery->expects($this->any())
->method('getLibraryByName')
->willReturnOnConsecutiveCalls(
$this->libraries['drupal'],
$this->libraries['drupal'],
$this->libraries['jquery'],
$this->libraries['drupal'],
$this->libraries['drupal'],
$this->libraries['jquery'],
);
$this->assetResolver->getJsAssets($assets_a, FALSE, $this->english);
$this->assetResolver->getJsAssets($assets_b, FALSE, $this->english);
$this->assertCount($expected_cache_item_count, $this->cache->getAllCids());
$this->assertCount($expected_js_cache_item_count, $this->cache->getAllCids());
$this->assetResolver->getJsAssets($assets_a, FALSE, $this->japanese);
$this->assetResolver->getJsAssets($assets_b, FALSE, $this->japanese);
$this->assertCount($expected_cache_item_count * 2, $this->cache->getAllCids());
$this->assertCount($expected_multilingual_js_cache_item_count, $this->cache->getAllCids());
}
public static function providerAttachedAssets() {
public static function providerAttachedJsAssets() {
$time = time();
return [
'same libraries, different timestamps' => [
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['core/drupal'])->setSettings(['currentTime' => $time]),
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['core/drupal'])->setSettings(['currentTime' => $time + 100]),
1,
2,
],
'different libraries, same timestamps' => [
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['core/drupal'])->setSettings(['currentTime' => $time]),
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['core/drupal', 'core/jquery'])->setSettings(['currentTime' => $time]),
2,
3,
],
];
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment