diff --git a/core/core.services.yml b/core/core.services.yml index 53a2725..bcae2d9 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1281,15 +1281,15 @@ services: class: Drupal\Core\Asset\AssetDumper library.discovery: class: Drupal\Core\Asset\LibraryDiscovery - arguments: ['@library.discovery.collector', '@module_handler'] + arguments: ['@library.discovery.collector', '@cache_tags.invalidator', '@module_handler', '@theme.manager'] library.discovery.collector: class: Drupal\Core\Asset\LibraryDiscoveryCollector - arguments: ['@cache.discovery', '@lock', '@library.discovery.parser'] + arguments: ['@cache.discovery', '@lock', '@library.discovery.parser', '@theme.manager'] tags: - { name: needs_destruction } library.discovery.parser: class: Drupal\Core\Asset\LibraryDiscoveryParser - arguments: ['@app.root', '@module_handler'] + arguments: ['@app.root', '@module_handler', '@theme.manager'] library.dependency_resolver: class: Drupal\Core\Asset\LibraryDependencyResolver arguments: ['@library.discovery'] diff --git a/core/lib/Drupal/Core/Asset/LibraryDiscovery.php b/core/lib/Drupal/Core/Asset/LibraryDiscovery.php index c79674a..71926ee 100644 --- a/core/lib/Drupal/Core/Asset/LibraryDiscovery.php +++ b/core/lib/Drupal/Core/Asset/LibraryDiscovery.php @@ -8,7 +8,9 @@ namespace Drupal\Core\Asset; use Drupal\Core\Cache\CacheCollectorInterface; +use Drupal\Core\Cache\CacheTagsInvalidatorInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Theme\ThemeManagerInterface; /** * Discovers available asset libraries in Drupal. @@ -23,6 +25,13 @@ class LibraryDiscovery implements LibraryDiscoveryInterface { protected $collector; /** + * The cache tag invalidator. + * + * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface + */ + protected $cacheTagInvalidator; + + /** * The module handler. * * @var \Drupal\Core\Extension\ModuleHandlerInterface @@ -30,6 +39,13 @@ class LibraryDiscovery implements LibraryDiscoveryInterface { protected $moduleHandler; /** + * The theme manager. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface + */ + protected $themeManager; + + /** * The final library definitions, statically cached. * * hook_library_info_alter() and hook_js_settings_alter() allows modules @@ -44,12 +60,18 @@ class LibraryDiscovery implements LibraryDiscoveryInterface { * * @param \Drupal\Core\Cache\CacheCollectorInterface $library_discovery_collector * The library discovery cache collector. + * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tag_invalidator + * The cache tag invalidator. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. + * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager + * The theme manager. */ - public function __construct(CacheCollectorInterface $library_discovery_collector, ModuleHandlerInterface $module_handler) { + public function __construct(CacheCollectorInterface $library_discovery_collector, CacheTagsInvalidatorInterface $cache_tag_invalidator, ModuleHandlerInterface $module_handler, ThemeManagerInterface $theme_manager) { $this->collector = $library_discovery_collector; + $this->cacheTagInvalidator = $cache_tag_invalidator; $this->moduleHandler = $module_handler; + $this->themeManager = $theme_manager; } /** @@ -64,6 +86,7 @@ public function getLibrariesByExtension($extension) { // specific data for this library; e.g., localization. $library_name = "$extension/$name"; $this->moduleHandler->alter('library', $definition, $library_name); + $this->themeManager->alter('library', $definition, $library_name); $this->libraryDefinitions[$extension][$name] = $definition; } } @@ -83,7 +106,7 @@ public function getLibraryByName($extension, $name) { * {@inheritdoc} */ public function clearCachedDefinitions() { - $this->collector->clear(); + $this->cacheTagInvalidator->invalidateTags(['library_info']); } } diff --git a/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php index dfac4fa..b366329 100644 --- a/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php +++ b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php @@ -10,6 +10,7 @@ use Drupal\Core\Cache\CacheCollector; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Lock\LockBackendInterface; +use Drupal\Core\Theme\ThemeManagerInterface; /** * A CacheCollector implementation for building library extension info. @@ -21,7 +22,7 @@ class LibraryDiscoveryCollector extends CacheCollector { * * @var string */ - protected $cacheKey = 'library_info'; + protected $cid; /** * The cache backend. @@ -45,6 +46,13 @@ class LibraryDiscoveryCollector extends CacheCollector { protected $discoveryParser; /** + * The theme manager. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface + */ + protected $themeManager; + + /** * Constructs a CacheCollector object. * * @param string $cid @@ -56,8 +64,10 @@ class LibraryDiscoveryCollector extends CacheCollector { * @param \Drupal\Core\Asset\LibraryDiscoveryParser $discovery_parser * The library discovery parser. */ - public function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, LibraryDiscoveryParser $discovery_parser) { - parent::__construct($this->cacheKey, $cache, $lock, array($this->cacheKey)); + public function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, LibraryDiscoveryParser $discovery_parser, ThemeManagerInterface $theme_manager) { + $this->themeManager = $theme_manager; + $this->cid = 'library_info:' . $this->themeManager->getActiveTheme()->getName(); + parent::__construct($this->cid, $cache, $lock, ['library_info']); $this->discoveryParser = $discovery_parser; } @@ -71,5 +81,4 @@ protected function resolveCacheMiss($key) { return $this->storage[$key]; } - } diff --git a/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php index 26ba97a..7e9ba06 100644 --- a/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php +++ b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php @@ -11,6 +11,7 @@ use Drupal\Core\Asset\Exception\InvalidLibraryFileException; use Drupal\Core\Asset\Exception\LibraryDefinitionMissingLicenseException; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Theme\ThemeManagerInterface; use Drupal\Component\Serialization\Exception\InvalidDataTypeException; use Drupal\Component\Serialization\Yaml; use Drupal\Component\Utility\NestedArray; @@ -28,6 +29,13 @@ class LibraryDiscoveryParser { protected $moduleHandler; /** + * The theme manager. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface + */ + protected $themeManager; + + /** * The app root. * * @var string @@ -42,9 +50,10 @@ class LibraryDiscoveryParser { * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. */ - public function __construct($root, ModuleHandlerInterface $module_handler) { + public function __construct($root, ModuleHandlerInterface $module_handler, ThemeManagerInterface $theme_manager) { $this->root = $root; $this->moduleHandler = $module_handler; + $this->themeManager = $theme_manager; } /** @@ -241,6 +250,7 @@ protected function parseLibraryInfo($extension, $path) { // Allow modules to alter the module's registered libraries. $this->moduleHandler->alter('library_info', $libraries, $extension); + $this->themeManager->alter('library_info', $libraries, $extension); return $libraries; } diff --git a/core/modules/system/src/Tests/Asset/LibraryDiscoveryIntegrationTest.php b/core/modules/system/src/Tests/Asset/LibraryDiscoveryIntegrationTest.php new file mode 100644 index 0000000..94e26a4 --- /dev/null +++ b/core/modules/system/src/Tests/Asset/LibraryDiscoveryIntegrationTest.php @@ -0,0 +1,48 @@ +container->get('theme_handler')->install(['test_theme', 'classy']); + } + + /** + * Ensures that the element info can be altered by themes. + */ + public function testElementInfoByTheme() { + /** @var \Drupal\Core\Theme\ThemeInitializationInterface $theme_initializer */ + $theme_initializer = $this->container->get('theme.initialization'); + + /** @var \Drupal\Core\Theme\ThemeManagerInterface $theme_manager */ + $theme_manager = $this->container->get('theme.manager'); + + /** @var \Drupal\Core\Render\ElementInfoManagerInterface $element_info */ + $library_discovery = $this->container->get('library.discovery'); + + $theme_manager->setActiveTheme($theme_initializer->getActiveThemeByName('classy')); + $this->assertFalse($library_discovery->getLibraryByName('test_theme', 'kitten')); + + $theme_manager->setActiveTheme($theme_initializer->getActiveThemeByName('test_theme')); + $this->assertTrue($library_discovery->getLibraryByName('test_theme', 'kitten')); + } + +} diff --git a/core/modules/system/tests/themes/test_theme/test_theme.theme b/core/modules/system/tests/themes/test_theme/test_theme.theme index 951fd58..9350755 100644 --- a/core/modules/system/tests/themes/test_theme/test_theme.theme +++ b/core/modules/system/tests/themes/test_theme/test_theme.theme @@ -29,6 +29,10 @@ function test_theme_element_info_alter(&$info) { } } +function test_theme_library_info_alter(&$libraries) { + $libraries['kitten']['js'][] = 'kittens.js'; +} + /** * Tests a theme implementing an alter hook. * diff --git a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryCollectorTest.php b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryCollectorTest.php index 5993669..e0ac986 100644 --- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryCollectorTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryCollectorTest.php @@ -41,6 +41,13 @@ class LibraryDiscoveryCollectorTest extends UnitTestCase { protected $libraryDiscoveryCollector; /** + * The mocked theme manager. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $themeManager; + + /** * Test library data. * * @var array @@ -56,17 +63,21 @@ class LibraryDiscoveryCollectorTest extends UnitTestCase { ), ); + protected $activeTheme; + /** * {@inheritdoc} */ protected function setUp() { $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); $this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface'); + $this->themeManager = $this->getMockBuilder('Drupal\Core\Theme\ThemeManagerInterface') + ->disableOriginalConstructor() + ->getMock(); $this->libraryDiscoveryParser = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscoveryParser') ->disableOriginalConstructor() ->getMock(); - $this->libraryDiscoveryCollector = new LibraryDiscoveryCollector($this->cache, $this->lock, $this->libraryDiscoveryParser); } /** @@ -75,6 +86,17 @@ protected function setUp() { * @covers ::resolveCacheMiss */ public function testResolveCacheMiss() { + $this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme') + ->disableOriginalConstructor() + ->getMock(); + $this->themeManager->expects($this->once()) + ->method('getActiveTheme') + ->will($this->returnValue($this->activeTheme)); + $this->activeTheme->expects($this->once()) + ->method('getName') + ->will($this->returnValue('kitten_theme')); + $this->libraryDiscoveryCollector = new LibraryDiscoveryCollector($this->cache, $this->lock, $this->libraryDiscoveryParser, $this->themeManager); + $this->libraryDiscoveryParser->expects($this->once()) ->method('buildByExtension') ->with('test') @@ -90,12 +112,23 @@ public function testResolveCacheMiss() { * @covers ::destruct */ public function testDestruct() { + $this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme') + ->disableOriginalConstructor() + ->getMock(); + $this->themeManager->expects($this->once()) + ->method('getActiveTheme') + ->will($this->returnValue($this->activeTheme)); + $this->activeTheme->expects($this->once()) + ->method('getName') + ->will($this->returnValue('kitten_theme')); + $this->libraryDiscoveryCollector = new LibraryDiscoveryCollector($this->cache, $this->lock, $this->libraryDiscoveryParser, $this->themeManager); + $this->libraryDiscoveryParser->expects($this->once()) ->method('buildByExtension') ->with('test') ->will($this->returnValue($this->libraryData)); - $lock_key = 'library_info:Drupal\Core\Cache\CacheCollector'; + $lock_key = 'library_info:kitten_theme:Drupal\Core\Cache\CacheCollector'; $this->lock->expects($this->once()) ->method('acquire') @@ -103,11 +136,11 @@ public function testDestruct() { ->will($this->returnValue(TRUE)); $this->cache->expects($this->exactly(2)) ->method('get') - ->with('library_info') + ->with('library_info:kitten_theme') ->will($this->returnValue(FALSE)); $this->cache->expects($this->once()) ->method('set') - ->with('library_info', array('test' => $this->libraryData), Cache::PERMANENT, array('library_info')); + ->with('library_info:kitten_theme', array('test' => $this->libraryData), Cache::PERMANENT, ['library_info']); $this->lock->expects($this->once()) ->method('release') ->with($lock_key); diff --git a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php index 528e579..2980040 100644 --- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php @@ -52,6 +52,13 @@ class LibraryDiscoveryParserTest extends UnitTestCase { protected $moduleHandler; /** + * The mocked theme manager. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $themeManager; + + /** * The mocked lock backend. * * @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit_Framework_MockObject_MockObject @@ -65,7 +72,8 @@ protected function setUp() { parent::setUp(); $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); - $this->libraryDiscoveryParser = new TestLibraryDiscoveryParser($this->root, $this->moduleHandler); + $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface'); + $this->libraryDiscoveryParser = new TestLibraryDiscoveryParser($this->root, $this->moduleHandler, $this->themeManager); } /** diff --git a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php index 343f51a..3492439 100644 --- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php @@ -38,6 +38,20 @@ class LibraryDiscoveryTest extends UnitTestCase { protected $moduleHandler; /** + * The mocked theme manager. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $themeManager; + + /** + * The cache tags invalidator. + * + * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheTagsInvalidator; + + /** * Test library data. * * @var array @@ -63,11 +77,13 @@ class LibraryDiscoveryTest extends UnitTestCase { protected function setUp() { parent::setUp(); + $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface'); $this->libraryDiscoveryCollector = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscoveryCollector') ->disableOriginalConstructor() ->getMock(); $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); - $this->libraryDiscovery = new LibraryDiscovery($this->libraryDiscoveryCollector, $this->moduleHandler); + $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface'); + $this->libraryDiscovery = new LibraryDiscovery($this->libraryDiscoveryCollector, $this->cacheTagsInvalidator, $this->moduleHandler, $this->themeManager); } /** @@ -85,6 +101,13 @@ public function testGetLibrariesByExtension() { $this->logicalOr($this->libraryData['test_1'], $this->libraryData['test_2']), $this->logicalOr('test/test_1', 'test/test_2') ); + $this->themeManager->expects($this->exactly(2)) + ->method('alter') + ->with( + 'library', + $this->logicalOr($this->libraryData['test_1'], $this->libraryData['test_2']), + $this->logicalOr('test/test_1', 'test/test_2') + ); $this->libraryDiscovery->getLibrariesbyExtension('test'); // Verify that subsequent calls don't trigger hook_library_info_alter()