core/core.services.yml | 2 +- core/includes/common.inc | 17 ++++----- core/includes/theme.inc | 8 +++-- core/lib/Drupal/Core/Asset/AssetResolver.php | 40 +++++++++++++++++----- .../Tests/Core/Controller/AjaxRendererTest.php | 4 +-- 5 files changed, 48 insertions(+), 23 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index df486c8..26c098f 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1079,7 +1079,7 @@ services: arguments: ['@library.discovery'] asset.resolver: class: Drupal\Core\Asset\AssetResolver - arguments: ['@library.discovery', '@library.dependency_resolver'] + arguments: ['@library.discovery', '@library.dependency_resolver', '@module_handler', '@theme.manager'] info_parser: class: Drupal\Core\Extension\InfoParser twig: diff --git a/core/includes/common.inc b/core/includes/common.inc index 4f9e839..e871343 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -1004,7 +1004,7 @@ function drupal_merge_attached(array $a, array $b) { } /** - * Adds attachments to a render() structure. + * Processes non-asset attachments in a render() structure. * * Libraries, JavaScript settings, feeds, HTML tags and HTML links * are attached to elements using the #attached property. The #attached property @@ -1015,6 +1015,9 @@ function drupal_merge_attached(array $a, array $b) { * $build['#attached'] = [ * 'library' => ['core/jquery'] * ]; + * $build['#attached']['http_header'] = [ + * ['Content-Type', 'application/rss+xml; charset=utf-8'], + * ]; * @endcode * * The available keys are: @@ -1025,20 +1028,18 @@ function drupal_merge_attached(array $a, array $b) { * - 'html_head_link' ( tags in HTML ) * - 'http_header' (HTTP headers) * - * For example: - * @code - * $build['#attached']['http_header'] = array( - * array('Content-Type', 'application/rss+xml; charset=utf-8'), - * ); - * @endcode + * This function processes all non-asset attachments, to apply them to the + * current response (that means all keys except 'library' and 'drupalSettings'). * * @param array $elements * The structured array describing the data being rendered. * * @see drupal_render() + * + * @see \Drupal\Core\Asset\AssetResolver */ function drupal_process_attached(array $elements) { - // Handled by \Drupal\Core\Asset\AssetResolverInterface. + // Asset attachments are handled by \Drupal\Core\Asset\AssetResolver. foreach (array('library', 'drupalSettings') as $type) { unset($elements['#attached'][$type]); } diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 18cc2d5..af3b6fa 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1410,8 +1410,12 @@ function template_preprocess_html(&$variables) { // Resolve the attached libraries into asset collections. $asset_resolver = \Drupal::service('asset.resolver'); - $asset_resolver->setLibraries($all_attached['#attached']['library']) - ->setAlreadyLoadedLibraries([]); + $asset_resolver->setLibraries($all_attached['#attached']['library']); + // Take Ajax page state into account, to allow for something like Turbolinks + // to be implemented without altering core. + // @see https://github.com/rails/turbolinks/ + $ajax_page_state = \Drupal::request()->request->get('ajax_page_state'); + $asset_resolver->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []); if (isset($all_attached['#attached']['drupalSettings'])) { $asset_resolver->setSettings($all_attached['#attached']['drupalSettings']); } diff --git a/core/lib/Drupal/Core/Asset/AssetResolver.php b/core/lib/Drupal/Core/Asset/AssetResolver.php index 9f17a5f..e2f824a 100644 --- a/core/lib/Drupal/Core/Asset/AssetResolver.php +++ b/core/lib/Drupal/Core/Asset/AssetResolver.php @@ -7,11 +7,13 @@ namespace Drupal\Core\Asset; use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Theme\ThemeManagerInterface; /** * The default asset resolver. */ -class AssetResolver { +class AssetResolver implements AssetResolverInterface { /** * The library discovery service. @@ -28,6 +30,20 @@ class AssetResolver { protected $libraryDependencyResolver; /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * The theme manager. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface + */ + protected $themeManager; + + /** * The (ordered) list of asset libraries attached to the current response. * * @var string[] @@ -79,10 +95,16 @@ class AssetResolver { * The library discovery service. * @param \Drupal\Core\Asset\LibraryDependencyResolverInterface $library_dependency_resolver * The library dependency resolver. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. + * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager + * The theme manager. */ - public function __construct(LibraryDiscoveryInterface $library_discovery, LibraryDependencyResolverInterface $library_dependency_resolver) { + public function __construct(LibraryDiscoveryInterface $library_discovery, LibraryDependencyResolverInterface $library_dependency_resolver, ModuleHandlerInterface $module_handler, ThemeManagerInterface $theme_manager) { $this->libraryDiscovery = $library_discovery; $this->libraryDependencyResolver = $library_dependency_resolver; + $this->moduleHandler = $module_handler; + $this->themeManager = $theme_manager; } /** @@ -175,7 +197,7 @@ protected function getLibrariesToLoad() { * {@inheritdoc} */ public function getCssAssets($optimize) { - $theme_info = \Drupal::theme()->getActiveTheme(); + $theme_info = $this->themeManager->getActiveTheme(); $css = []; @@ -228,8 +250,8 @@ public function getCssAssets($optimize) { } // Allow modules and themes to alter the CSS assets. - \Drupal::moduleHandler()->alter('css', $css); - \Drupal::theme()->alter('css', $css); + $this->moduleHandler->alter('css', $css); + $this->themeManager->alter('css', $css); // Sort CSS items, so that they appear in the correct order. uasort($css, 'static::sort'); @@ -261,7 +283,7 @@ public function getCssAssets($optimize) { /** * {@inheritdoc} */ - public function getJsAssetsForHeader() { + public function getJsAssetsForHeader($optimize) { $js_assets = $this->getJsAssets(); // Filter out elements of the given scope. @@ -326,8 +348,8 @@ protected function getJsSettingsAssets() { $settings = NestedArray::mergeDeepArray([$settings, $this->settings], TRUE); // Allow modules and themes to alter the JavaScript settings. - \Drupal::moduleHandler()->alter('js_settings', $settings); - \Drupal::theme()->alter('js_settings', $settings); + $this->moduleHandler->alter('js_settings', $settings); + $this->themeManager->alter('js_settings', $settings); return $settings; } @@ -335,7 +357,7 @@ protected function getJsSettingsAssets() { /** * {@inheritdoc} */ - public function getJsAssetsForFooter() { + public function getJsAssetsForFooter($optimize) { $js_assets = $this->getJsAssets(); // Filter out elements of the given scope. diff --git a/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php b/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php index 6552a4d..bac2964 100644 --- a/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php +++ b/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php @@ -70,9 +70,7 @@ class TestAjaxRenderer extends AjaxRenderer { * {@inheritdoc} */ protected function drupalRenderRoot(&$elements, $is_root_call = FALSE) { - if (!isset($elements['#attached'])) { - $elements['#attached'] = []; - } + $elements += ['#attached' => []]; if (isset($elements['#markup'])) { return $elements['#markup']; }