core/includes/theme.inc | 26 ++-- core/lib/Drupal/Core/Ajax/AjaxResponse.php | 12 +- core/lib/Drupal/Core/Asset/AssetResolver.php | 144 +++++---------------- .../Drupal/Core/Asset/AssetResolverInterface.php | 89 ++----------- core/lib/Drupal/Core/Asset/AttachedAssets.php | 95 ++++++++++++++ .../Drupal/Core/Asset/AttachedAssetsInterface.php | 85 ++++++++++++ core/modules/locale/locale.module | 5 +- core/modules/simpletest/simpletest.module | 3 +- .../system/src/Tests/Ajax/FrameworkTest.php | 20 +-- .../system/src/Tests/Common/AttachedAssetsTest.php | 85 ++++++------ core/modules/system/system.module | 18 +-- .../tests/modules/common_test/common_test.module | 4 +- core/modules/system/theme.api.php | 12 +- core/modules/user/user.module | 3 +- 14 files changed, 326 insertions(+), 275 deletions(-) diff --git a/core/includes/theme.inc b/core/includes/theme.inc index af3b6fa..8bd1818 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -15,6 +15,7 @@ use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\UrlHelper; use Drupal\Component\Utility\Xss; +use Drupal\Core\Asset\AttachedAssets; use Drupal\Core\Config\Config; use Drupal\Core\Config\StorageException; use Drupal\Core\Extension\Extension; @@ -1403,27 +1404,20 @@ function template_preprocess_html(&$variables) { // Render the attachments into HTML markup to be used directly in the template // for #type => html: html.html.twig. $all_attached = ['#attached' => $attached]; - - // Optimize CSS/JS if necessary, but only during normal site operation. - $optimize_css = !defined('MAINTENANCE_MODE') && \Drupal::config('system.performance')->get('css.preprocess'); - $optimize_js = !defined('MAINTENANCE_MODE') && \Drupal::config('system.performance')->get('js.preprocess'); - - // Resolve the attached libraries into asset collections. - $asset_resolver = \Drupal::service('asset.resolver'); - $asset_resolver->setLibraries($all_attached['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($all_attached); // 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']); - } - + $assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []); + // Optimize CSS/JS if necessary, but only during normal site operation. + $optimize_css = !defined('MAINTENANCE_MODE') && \Drupal::config('system.performance')->get('css.preprocess'); + $optimize_js = !defined('MAINTENANCE_MODE') && \Drupal::config('system.performance')->get('js.preprocess'); // Render the asset collections. - $variables['styles'] = \Drupal::service('asset.css.collection_renderer')->render($asset_resolver->getCssAssets($optimize_css)); - $variables['scripts'] = \Drupal::service('asset.js.collection_renderer')->render($asset_resolver->getJsAssetsForHeader($optimize_js)); - $variables['scripts_bottom'] = \Drupal::service('asset.js.collection_renderer')->render($asset_resolver->getJsAssetsForFooter($optimize_js)); + $asset_resolver = \Drupal::service('asset.resolver'); + $variables['styles'] = \Drupal::service('asset.css.collection_renderer')->render($asset_resolver->getCssAssets($assets, $optimize_css)); + $variables['scripts'] = \Drupal::service('asset.js.collection_renderer')->render($asset_resolver->getJsAssetsForHeader($assets, $optimize_js)); + $variables['scripts_bottom'] = \Drupal::service('asset.js.collection_renderer')->render($asset_resolver->getJsAssetsForFooter($assets, $optimize_js)); // Handle all non-asset attachments. drupal_process_attached($all_attached); diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponse.php b/core/lib/Drupal/Core/Ajax/AjaxResponse.php index 51c7e72..524e48e 100644 --- a/core/lib/Drupal/Core/Ajax/AjaxResponse.php +++ b/core/lib/Drupal/Core/Ajax/AjaxResponse.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Ajax; +use Drupal\Core\Asset\AttachedAssets; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -124,13 +125,14 @@ protected function ajaxRender(Request $request) { $optimize_js = !defined('MAINTENANCE_MODE') && $config->get('js.preprocess'); // Resolve the attached libraries into asset collections. - $asset_resolver = \Drupal::service('asset.resolver'); - $asset_resolver->setLibraries(isset($this->attachments['library']) ? $this->attachments['library'] : []) + $assets = new AttachedAssets(); + $assets->setLibraries(isset($this->attachments['library']) ? $this->attachments['library'] : []) ->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []) ->setSettings(isset($this->attachments['drupalSettings']) ? $this->attachments['drupalSettings'] : []); - $css_assets = $asset_resolver->getCssAssets($optimize_css); - $js_assets_header = $asset_resolver->getJsAssetsForHeader($optimize_js); - $js_assets_footer = $asset_resolver->getJsAssetsForFooter($optimize_js); + $asset_resolver = \Drupal::service('asset.resolver'); + $css_assets = $asset_resolver->getCssAssets($assets, $optimize_css); + $js_assets_header = $asset_resolver->getJsAssetsForHeader($assets, $optimize_js); + $js_assets_footer = $asset_resolver->getJsAssetsForFooter($assets, $optimize_js); // Render the HTML to load these files, and add AJAX commands to insert this // HTML in the page. Settings are handled separately, afterwards. diff --git a/core/lib/Drupal/Core/Asset/AssetResolver.php b/core/lib/Drupal/Core/Asset/AssetResolver.php index e2f824a..6d88391 100644 --- a/core/lib/Drupal/Core/Asset/AssetResolver.php +++ b/core/lib/Drupal/Core/Asset/AssetResolver.php @@ -44,13 +44,6 @@ class AssetResolver implements AssetResolverInterface { protected $themeManager; /** - * The (ordered) list of asset libraries attached to the current response. - * - * @var string[] - */ - protected $libraries = []; - - /** * $libraries, along with all the depended asset libraries. * * @var string[] @@ -58,13 +51,6 @@ class AssetResolver implements AssetResolverInterface { protected $librariesWithDependencies = []; /** - * The set of asset libraries that the client has already loaded. - * - * @var string[] - */ - protected $alreadyLoadedLibraries = []; - - /** * $alreadyLoadedLibraries, along with all the depended asset libraries. * * @var string[] @@ -72,13 +58,6 @@ class AssetResolver implements AssetResolverInterface { protected $alreadyLoadedLibrariesWithDependencies = []; /** - * The JavaScript settings attached to the current response. - * - * @var array - */ - protected $settings = []; - - /** * Static cache of the JavaScript assets attached to the current response. * * @see reset() @@ -108,73 +87,6 @@ public function __construct(LibraryDiscoveryInterface $library_discovery, Librar } /** - * {@inheritdoc} - */ - public function setLibraries(array $libraries) { - $this->libraries = array_unique($libraries); - $this->librariesWithDependencies = $this->libraryDependencyResolver->getLibrariesWithDependencies($this->libraries); - return $this; - } - - /** - * {@inheritdoc} - */ - public function getLibraries() { - return $this->libraries; - } - - /** - * {@inheritdoc} - */ - public function getLibrariesWithDependencies() { - return $this->librariesWithDependencies; - } - - /** - * {@inheritdoc} - */ - public function setAlreadyLoadedLibraries(array $libraries) { - $this->alreadyLoadedLibraries = $libraries; - $this->alreadyLoadedLibrariesWithDependencies = $this->libraryDependencyResolver->getLibrariesWithDependencies($this->alreadyLoadedLibraries); - return $this; - } - - /** - * {@inheritdoc} - */ - public function getAlreadyLoadedLibraries() { - return $this->alreadyLoadedLibraries; - } - - /** - * {@inheritdoc} - */ - public function getAlreadyLoadedLibrariesWithDependencies() { - return $this->alreadyLoadedLibrariesWithDependencies; - } - - /** - * {@inheritdoc} - */ - public function setSettings(array $settings) { - $this->settings = $settings; - return $this; - } - - /** - * {@inheritdoc} - */ - public function reset() { - $this->libraries = []; - $this->librariesWithDependencies = []; - $this->settings = []; - $this->alreadyLoadedLibraries = []; - $this->alreadyLoadedLibrariesWithDependencies = []; - $this->jsAssets = NULL; - return $this; - } - - /** * Given lists of needed & loaded libraries, resolve which libraries to load. * * For example, with core/a depending on core/c and core/b on core/d: @@ -184,24 +96,30 @@ public function reset() { * $asset_resolver->getLibrariesToLoad() === ['core/a', 'core/b', 'core/d'] * @endcode * + * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets + * The assets attached to the current response. + * * @return string[] * A list of libraries, in the order they should be loaded, including their * dependencies. (Any libraries that have already been loaded, and their * dependencies, are excluded.) */ - protected function getLibrariesToLoad() { - return array_diff($this->getLibrariesWithDependencies(), $this->getAlreadyLoadedLibrariesWithDependencies()); + protected function getLibrariesToLoad(AttachedAssetsInterface $assets) { + return array_diff( + $this->libraryDependencyResolver->getLibrariesWithDependencies($assets->getLibraries()), + $this->libraryDependencyResolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()) + ); } /** * {@inheritdoc} */ - public function getCssAssets($optimize) { + public function getCssAssets(AttachedAssetsInterface $assets, $optimize) { $theme_info = $this->themeManager->getActiveTheme(); $css = []; - foreach ($this->getLibrariesToLoad() as $library) { + foreach ($this->getLibrariesToLoad($assets) as $library) { list($extension, $name) = explode('/', $library, 2); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name); if (isset($definition['css'])) { @@ -250,8 +168,8 @@ public function getCssAssets($optimize) { } // Allow modules and themes to alter the CSS assets. - $this->moduleHandler->alter('css', $css); - $this->themeManager->alter('css', $css); + $this->moduleHandler->alter('css', $css, $assets); + $this->themeManager->alter('css', $css, $assets); // Sort CSS items, so that they appear in the correct order. uasort($css, 'static::sort'); @@ -283,8 +201,8 @@ public function getCssAssets($optimize) { /** * {@inheritdoc} */ - public function getJsAssetsForHeader($optimize) { - $js_assets = $this->getJsAssets(); + public function getJsAssetsForHeader(AttachedAssetsInterface $assets, $optimize) { + $js_assets = $this->getJsAssets($assets); // Filter out elements of the given scope. $js_assets_header = array(); @@ -299,10 +217,11 @@ public function getJsAssetsForHeader($optimize) { // If the core/drupalSettings library is being loaded or is already loaded, // get the JavaScript settings assets. and converts them into a single // "regular" JavaScript asset. - $settings_needed = in_array('core/drupalSettings', $this->getLibrariesToLoad()) || in_array('core/drupalSettings', $this->getAlreadyLoadedLibrariesWithDependencies()); - $settings_have_changed = count($this->getLibrariesToLoad()) > 0 || !empty($this->settings); + $libraries_to_load = $this->getLibrariesToLoad($assets); + $settings_needed = in_array('core/drupalSettings', $libraries_to_load) || in_array('core/drupalSettings', $this->libraryDependencyResolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries())); + $settings_have_changed = count($libraries_to_load) > 0 || !empty($assets->getSettings()); if ($settings_needed && $settings_have_changed) { - $settings = $this->getJsSettingsAssets(); + $settings = $this->getJsSettingsAssets($assets); if (!empty($settings)) { // Prepend to the list of JavaScript assets, to render it first. $settings_as_inline_javascript = [ @@ -328,15 +247,17 @@ public function getJsAssetsForHeader($optimize) { * setSettings()) and finally invokes hook_js_settings_alter() to allow * alterations of JavaScript settings by modules and themes. * + * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets + * The assets attached to the current response. * @return array * A (possibly optimized) collection of JavaScript assets. * * @see ::setSettings() */ - protected function getJsSettingsAssets() { + protected function getJsSettingsAssets(AttachedAssetsInterface $assets) { $settings = []; - foreach ($this->getLibrariesToLoad() as $library) { + foreach ($this->getLibrariesToLoad($assets) as $library) { list($extension, $name) = explode('/', $library, 2); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name); if (isset($definition['drupalSettings'])) { @@ -345,11 +266,11 @@ protected function getJsSettingsAssets() { } // Attached settings win over settings in libraries. - $settings = NestedArray::mergeDeepArray([$settings, $this->settings], TRUE); + $settings = NestedArray::mergeDeepArray([$settings, $assets->getSettings()], TRUE); // Allow modules and themes to alter the JavaScript settings. - $this->moduleHandler->alter('js_settings', $settings); - $this->themeManager->alter('js_settings', $settings); + $this->moduleHandler->alter('js_settings', $settings, $assets); + $this->themeManager->alter('js_settings', $settings, $assets); return $settings; } @@ -357,8 +278,8 @@ protected function getJsSettingsAssets() { /** * {@inheritdoc} */ - public function getJsAssetsForFooter($optimize) { - $js_assets = $this->getJsAssets(); + public function getJsAssetsForFooter(AttachedAssetsInterface $assets, $optimize) { + $js_assets = $this->getJsAssets($assets); // Filter out elements of the given scope. $js_assets_footer = array(); @@ -380,9 +301,12 @@ public function getJsAssetsForFooter($optimize) { * @see ::getJsAssetsForHeader() * @see ::getJsAssetsForFooter() * + * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets + * The assets attached to the current response. + * * @return array */ - protected function getJsAssets() { + protected function getJsAssets(AttachedAssetsInterface $assets) { // Static caching, because this is called twice (for header & footer). if (isset($this->jsAssets)) { return $this->jsAssets; @@ -390,7 +314,7 @@ protected function getJsAssets() { $javascript = []; - foreach ($this->getLibrariesToLoad() as $library) { + foreach ($this->getLibrariesToLoad($assets) as $library) { list($extension, $name) = explode('/', $library, 2); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name); if (isset($definition['js'])) { @@ -422,8 +346,8 @@ protected function getJsAssets() { } // Allow modules and themes to alter the JavaScript assets. - \Drupal::moduleHandler()->alter('js', $javascript); - \Drupal::theme()->alter('js', $javascript); + $this->moduleHandler->alter('js', $javascript, $assets); + $this->themeManager->alter('js', $javascript, $assets); // Sort JavaScript assets, so that they appear in the correct order. uasort($javascript, 'static::sort'); diff --git a/core/lib/Drupal/Core/Asset/AssetResolverInterface.php b/core/lib/Drupal/Core/Asset/AssetResolverInterface.php index 02fd938..a3f9d29 100644 --- a/core/lib/Drupal/Core/Asset/AssetResolverInterface.php +++ b/core/lib/Drupal/Core/Asset/AssetResolverInterface.php @@ -9,81 +9,19 @@ /** * Resolves asset libraries into concrete CSS and JavaScript assets. * - * Given an ordered list of asset libraries (to be loaded for the current - * response), the asset resolver can resolve those asset libraries into a list of - * concrete CSS and JavaScript assets. + * Given an attached assets collection (to be loaded for the current response), + * the asset resolver can resolve those asset libraries into a list of concrete + * CSS and JavaScript assets. * * In other words: this allows developers to translate Drupal's asset * abstraction (asset libraries) into concrete assets. * - * Optionally, a set of asset libraries that the client already has loaded can - * be specified, in which case the asset resolver will figure out which concrete - * CSS and JavaScript assets the client already has, to prevent loading them - * again. - * + * @see \Drupal\Core\Asset\AttachedAssetsInterface * @see \Drupal\Core\Asset\LibraryDependencyResolverInterface */ interface AssetResolverInterface { /** - * Sets the asset libraries attached to the current response. - * - * @param string[] $libraries - * A list of libraries, in the order they should be loaded. - * - * @return $this - */ - public function setLibraries(array $libraries); - - /** - * Returns the asset libraries attached to the current response. - * - * @return string[] - */ - public function getLibraries(); - - /** - * Returns the libraries attached to the current response, with dependencies. - * - * @return string[] - */ - public function getLibrariesWithDependencies(); - - /** - * Sets the asset libraries that the current request marked as already loaded. - * - * @param string[] $libraries - * The set of already loaded libraries. - * - * @return $this - */ - public function setAlreadyLoadedLibraries(array $libraries); - - /** - * Returns the set of already loaded asset libraries. - * - * @return string[] - */ - public function getAlreadyLoadedLibraries(); - - /** - * Returns the set of already loaded asset libraries, with dependencies. - * - * @return string[] - */ - public function getAlreadyLoadedLibrariesWithDependencies(); - - /** - * Sets the JavaScript settings that are attached to the current response. - * - * @param array $settings - * The needed JavaScript settings. - * - * @return $this - */ - public function setSettings(array $settings); - - /** * Returns the CSS assets for the current response's libraries. * * It loads the CSS in order, with 'module' first, then 'theme' afterwards. @@ -97,16 +35,16 @@ public function setSettings(array $settings); * * Also invokes hook_css_alter(), to allow CSS assets to be altered. * + * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets + * The assets attached to the current response. * @param bool $optimize * Whether to apply the CSS asset collection optimizer, to return an * optimized CSS asset collection rather than an unoptimized one. * * @return array * A (possibly optimized) collection of CSS assets. - * - * @see ::getLibrariesToLoad() */ - public function getCssAssets($optimize); + public function getCssAssets(AttachedAssetsInterface $assets, $optimize); /** * Returns the header JavaScript assets for the current response's libraries. @@ -122,6 +60,8 @@ public function getCssAssets($optimize); * the $javascript array, deriving from drupal_js_defaults(). See * locale_js_alter() for an example of this. * + * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets + * The assets attached to the current response. * @param bool $optimize * Whether to apply the JavaScript asset collection optimizer, to return an * optimized JavaScript asset collection rather than an unoptimized one. @@ -129,11 +69,13 @@ public function getCssAssets($optimize); * @return array * A (possibly optimized) collection of JavaScript assets. */ - public function getJsAssetsForHeader($optimize); + public function getJsAssetsForHeader(AttachedAssetsInterface $assets, $optimize); /** * Returns the footer JavaScript assets for the current response's libraries. * + * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets + * The assets attached to the current response. * @param bool $optimize * Whether to apply the JavaScript asset collection optimizer, to return an * optimized JavaScript asset collection rather than an unoptimized one. @@ -143,11 +85,6 @@ public function getJsAssetsForHeader($optimize); * * @see ::getJsAssetsForHeader() */ - public function getJsAssetsForFooter($optimize); - - /** - * Resets all internal state. - */ - public function reset(); + public function getJsAssetsForFooter(AttachedAssetsInterface $assets, $optimize); } diff --git a/core/lib/Drupal/Core/Asset/AttachedAssets.php b/core/lib/Drupal/Core/Asset/AttachedAssets.php new file mode 100644 index 0000000..7562f7c --- /dev/null +++ b/core/lib/Drupal/Core/Asset/AttachedAssets.php @@ -0,0 +1,95 @@ +setLibraries($render_array['#attached']['library']); + } + if (isset($render_array['#attached']['drupalSettings'])) { + $assets->setSettings($render_array['#attached']['drupalSettings']); + } + return $assets; + } + + /** + * {@inheritdoc} + */ + public function setLibraries(array $libraries) { + $this->libraries = array_unique($libraries); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getLibraries() { + return $this->libraries; + } + + /** + * {@inheritdoc} + */ + public function setSettings(array $settings) { + $this->settings = $settings; + return $this; + } + + public function getSettings() { + return $this->settings; + } + + /** + * {@inheritdoc} + */ + public function getAlreadyLoadedLibraries() { + return $this->alreadyLoadedLibraries; + } + + /** + * {@inheritdoc} + */ + public function setAlreadyLoadedLibraries(array $libraries) { + $this->alreadyLoadedLibraries = $libraries; + return $this; + } + +} diff --git a/core/lib/Drupal/Core/Asset/AttachedAssetsInterface.php b/core/lib/Drupal/Core/Asset/AttachedAssetsInterface.php new file mode 100644 index 0000000..338e8b4 --- /dev/null +++ b/core/lib/Drupal/Core/Asset/AttachedAssetsInterface.php @@ -0,0 +1,85 @@ +getCurrentLanguage(); $settings['jquery']['ui']['datepicker']['isRTL'] = $language_interface->getDirection() == LanguageInterface::DIRECTION_RTL; diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index 7c0c34f..59173cb 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -1,5 +1,6 @@ 'test'), TRUE); $build['#attached']['library'][] = 'ajax_test/order-css-command'; - $asset_resolver->setLibraries($build['#attached']['library']); - $css_render_array = $css_collection_renderer->render($asset_resolver->getCssAssets(FALSE)); + $assets = AttachedAssets::createFromRenderArray($build); + $css_render_array = $css_collection_renderer->render($asset_resolver->getCssAssets($assets, FALSE)); $expected_commands[1] = new AddCssCommand($renderer->render($css_render_array)); $build['#attached']['library'][] = 'ajax_test/order-js-command'; - $asset_resolver->setLibraries($build['#attached']['library']); - $js_header_render_array = $js_collection_renderer->render($asset_resolver->getJsAssetsForHeader(FALSE)); - $js_footer_render_array = $js_collection_renderer->render($asset_resolver->getJsAssetsForFooter(FALSE)); + $assets = AttachedAssets::createFromRenderArray($build); + $js_header_render_array = $js_collection_renderer->render($asset_resolver->getJsAssetsForHeader($assets, FALSE)); + $js_footer_render_array = $js_collection_renderer->render($asset_resolver->getJsAssetsForFooter($assets, FALSE)); $expected_commands[2] = new PrependCommand('head', $renderer->render($js_header_render_array)); $expected_commands[3] = new AppendCommand('body', $renderer->render($js_footer_render_array)); $expected_commands[4] = new HtmlCommand('body', 'Hello, world!'); @@ -116,14 +117,15 @@ public function testLazyLoad() { $this->assertTrue(!in_array($expected['library_2'], $original_libraries), format_string('Page originally lacks the %library library, as expected.', array('%library' => $expected['library_2']))); // Calculate the expected CSS and JS. - $asset_resolver->setLibraries([$expected['library_1']]) + $assets = new AttachedAssets(); + $assets->setLibraries([$expected['library_1']]) ->setAlreadyLoadedLibraries($original_libraries); - $css_render_array = $css_collection_renderer->render($asset_resolver->getCssAssets(FALSE)); + $css_render_array = $css_collection_renderer->render($asset_resolver->getCssAssets($assets, FALSE)); $expected_css_html = $renderer->render($css_render_array); - $asset_resolver->setLibraries([$expected['library_2']]) + $assets->setLibraries([$expected['library_2']]) ->setAlreadyLoadedLibraries($original_libraries); - $js_assets = $asset_resolver->getJsAssetsForHeader(FALSE); + $js_assets = $asset_resolver->getJsAssetsForHeader($assets, FALSE); unset($js_assets['drupalSettings']); $js_render_array = $js_collection_renderer->render($js_assets); $expected_js_html = $renderer->render($js_render_array); diff --git a/core/modules/system/src/Tests/Common/AttachedAssetsTest.php b/core/modules/system/src/Tests/Common/AttachedAssetsTest.php index 7f4f694..ad492ab 100644 --- a/core/modules/system/src/Tests/Common/AttachedAssetsTest.php +++ b/core/modules/system/src/Tests/Common/AttachedAssetsTest.php @@ -10,6 +10,7 @@ use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\Crypt; +use Drupal\Core\Asset\AttachedAssets; use Drupal\simpletest\KernelTestBase; /** @@ -60,9 +61,10 @@ protected function setUp() { * Tests that default CSS and JavaScript is empty. */ function testDefault() { - $this->assertEqual(array(), $this->assetResolver->getCssAssets(FALSE), 'Default CSS is empty.'); - $this->assertEqual(array(), $this->assetResolver->getJsAssetsForHeader(FALSE), 'Default header JavaScript is empty.'); - $this->assertEqual(array(), $this->assetResolver->getJsAssetsForFooter(FALSE), 'Default footer JavaScript is empty.'); + $assets = new AttachedAssets(); + $this->assertEqual(array(), $this->assetResolver->getCssAssets($assets, FALSE), 'Default CSS is empty.'); + $this->assertEqual(array(), $this->assetResolver->getJsAssetsForHeader($assets, FALSE), 'Default header JavaScript is empty.'); + $this->assertEqual(array(), $this->assetResolver->getJsAssetsForFooter($assets, FALSE), 'Default footer JavaScript is empty.'); } /** @@ -70,9 +72,9 @@ function testDefault() { */ function testLibraryUnknown() { $build['#attached']['library'][] = 'unknown/unknown'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $this->assertIdentical([], $this->assetResolver->getJsAssetsForHeader(FALSE), 'Unknown library was not added to the page.'); + $this->assertIdentical([], $this->assetResolver->getJsAssetsForHeader($assets, FALSE), 'Unknown library was not added to the page.'); } /** @@ -80,10 +82,10 @@ function testLibraryUnknown() { */ function testAddFiles() { $build['#attached']['library'][] = 'common_test/files'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $css = $this->assetResolver->getCssAssets(FALSE); - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $css = $this->assetResolver->getCssAssets($assets, FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $this->assertTrue(array_key_exists('bar.css', $css), 'CSS files are correctly added.'); $this->assertTrue(array_key_exists('core/modules/system/tests/modules/common_test/foo.js', $js), 'JavaScript files are correctly added.'); @@ -102,13 +104,13 @@ function testAddFiles() { function testAddJsSettings() { // Add a file in order to test default settings. $build['#attached']['library'][] = 'core/drupalSettings'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $javascript = $this->assetResolver->getJsAssetsForHeader(FALSE); + $javascript = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $this->assertTrue(array_key_exists('currentPath', $javascript['drupalSettings']['data']['path']), 'The current path JavaScript setting is set correctly.'); - $this->assetResolver->setSettings(['drupal' => 'rocks', 'dries' => 280342800]); - $javascript = $this->assetResolver->getJsAssetsForHeader(FALSE); + $assets->setSettings(['drupal' => 'rocks', 'dries' => 280342800]); + $javascript = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $this->assertEqual(280342800, $javascript['drupalSettings']['data']['dries'], 'JavaScript setting is set correctly.'); $this->assertEqual('rocks', $javascript['drupalSettings']['data']['drupal'], 'The other JavaScript setting is set correctly.'); } @@ -118,10 +120,10 @@ function testAddJsSettings() { */ function testAddExternalFiles() { $build['#attached']['library'][] = 'common_test/external'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $css = $this->assetResolver->getCssAssets(FALSE); - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $css = $this->assetResolver->getCssAssets($assets, FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $this->assertTrue(array_key_exists('http://example.com/stylesheet.css', $css), 'External CSS files are correctly added.'); $this->assertTrue(array_key_exists('http://example.com/script.js', $js), 'External JavaScript files are correctly added.'); @@ -138,9 +140,9 @@ function testAddExternalFiles() { */ function testAttributes() { $build['#attached']['library'][] = 'common_test/js-attributes'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $rendered_js = $this->renderer->render($js_render_array); $expected_1 = ''; @@ -154,9 +156,9 @@ function testAttributes() { */ function testAggregatedAttributes() { $build['#attached']['library'][] = 'common_test/js-attributes'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $js = $this->assetResolver->getJsAssetsForHeader(TRUE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, TRUE); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $rendered_js = $this->renderer->render($js_render_array); $expected_1 = ''; @@ -173,10 +175,9 @@ function testHeaderSetting() { $build['#attached']['library'][] = 'core/drupalSettings'; // Nonsensical value to verify if it's possible to override path settings. $build['#attached']['drupalSettings']['path']['pathPrefix'] = 'yarhar'; - $this->assetResolver->setLibraries($build['#attached']['library']) - ->setSettings($build['#attached']['drupalSettings']); + $assets = AttachedAssets::createFromRenderArray($build); - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $rendered_js = $this->renderer->render($js_render_array); @@ -209,9 +210,9 @@ function testHeaderSetting() { */ function testFooterHTML() { $build['#attached']['library'][] = 'common_test/js-footer'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $js = $this->assetResolver->getJsAssetsForFooter(FALSE); + $js = $this->assetResolver->getJsAssetsForFooter($assets, FALSE); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $rendered_js = $this->renderer->render($js_render_array); $query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0'; @@ -223,9 +224,9 @@ function testFooterHTML() { */ function testNoCache() { $build['#attached']['library'][] = 'common_test/no-cache'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $this->assertFalse($js['core/modules/system/tests/modules/common_test/nocache.js']['preprocess'], 'Setting cache to FALSE sets preprocess to FALSE when adding JavaScript.'); } @@ -238,9 +239,9 @@ function testBrowserConditionalComments() { $default_query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0'; $build['#attached']['library'][] = 'common_test/browsers'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $rendered_js = $this->renderer->render($js_render_array); $expected_1 = ""; @@ -256,9 +257,9 @@ function testBrowserConditionalComments() { function testVersionQueryString() { $build['#attached']['library'][] = 'core/backbone'; $build['#attached']['library'][] = 'core/domready'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $rendered_js = $this->renderer->render($js_render_array); $this->assertTrue(strpos($rendered_js, 'core/assets/vendor/backbone/backbone-min.js?v=1.1.2') > 0 && strpos($rendered_js, 'core/assets/vendor/domready/ready.min.js?v=1.0.7') > 0 , 'JavaScript version identifiers correctly appended to URLs'); @@ -269,7 +270,7 @@ function testVersionQueryString() { */ function testRenderOrder() { $build['#attached']['library'][] = 'common_test/order'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); // Construct the expected result from the regex. $expected_order_js = [ @@ -286,7 +287,7 @@ function testRenderOrder() { ]; // Retrieve the rendered JavaScript and test against the regex. - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $rendered_js = $this->renderer->render($js_render_array); $matches = array(); @@ -328,7 +329,7 @@ function testRenderOrder() { ]; // Retrieve the rendered CSS and test against the regex. - $css = $this->assetResolver->getCssAssets(FALSE); + $css = $this->assetResolver->getCssAssets($assets, FALSE); $css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css); $rendered_css = $this->renderer->render($css_render_array); $matches = array(); @@ -349,9 +350,9 @@ function testRenderDifferentWeight() { // still make itself appear first by defining a lower weight. $build['#attached']['library'][] = 'core/jquery'; $build['#attached']['library'][] = 'common_test/weight'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $rendered_js = $this->renderer->render($js_render_array); $this->assertTrue(strpos($rendered_js, 'lighter.css') < strpos($rendered_js, 'first.js'), 'Lighter CSS assets are rendered first.'); @@ -368,12 +369,12 @@ function testAlter() { // Add both tableselect.js and simpletest.js. $build['#attached']['library'][] = 'core/drupal.tableselect'; $build['#attached']['library'][] = 'simpletest/drupal.simpletest'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); // Render the JavaScript, testing if simpletest.js was altered to be before // tableselect.js. See simpletest_js_alter() to see where this alteration // takes place. - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $rendered_js = $this->renderer->render($js_render_array); $this->assertTrue(strpos($rendered_js, 'simpletest.js') < strpos($rendered_js, 'core/misc/tableselect.js'), 'Altering JavaScript weight through the alter hook.'); @@ -393,8 +394,8 @@ function testLibraryAlter() { // common_test_library_info_alter() also added a dependency on jQuery Form. $build['#attached']['library'][] = 'core/jquery.farbtastic'; - $this->assetResolver->setLibraries($build['#attached']['library']); - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $assets = AttachedAssets::createFromRenderArray($build); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $rendered_js = $this->renderer->render($js_render_array); $this->assertTrue(strpos($rendered_js, 'core/assets/vendor/jquery-form/jquery.form.js'), 'Altered library dependencies are added to the page.'); @@ -417,10 +418,10 @@ function testLibraryNameConflicts() { */ function testAddJsFileWithQueryString() { $build['#attached']['library'][] = 'common_test/querystring'; - $this->assetResolver->setLibraries($build['#attached']['library']); + $assets = AttachedAssets::createFromRenderArray($build); - $css = $this->assetResolver->getCssAssets(FALSE); - $js = $this->assetResolver->getJsAssetsForHeader(FALSE); + $css = $this->assetResolver->getCssAssets($assets, FALSE); + $js = $this->assetResolver->getJsAssetsForHeader($assets, FALSE); $this->assertTrue(array_key_exists('querystring.css?arg1=value1&arg2=value2', $css), 'CSS file with query string is correctly added.'); $this->assertTrue(array_key_exists('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2', $js), 'JavaScript file with query string is correctly added.'); diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 27e2f3e..d33c466 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -6,6 +6,7 @@ */ use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Cache\Cache; use Drupal\Core\Extension\Extension; use Drupal\Core\Extension\ExtensionDiscovery; @@ -621,7 +622,7 @@ function system_page_attachments(array &$page) { * * Sets values for the core/drupalSettings and core/drupal.ajax libraries. */ -function system_js_settings_alter(&$settings) { +function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) { // url() generates the script and prefix using hook_url_outbound_alter(). // Instead of running the hook_url_outbound_alter() again here, extract // them from url(). @@ -666,9 +667,9 @@ function system_js_settings_alter(&$settings) { // - core/drupal.ajax already has been loaded and hence this is an AJAX // Response in which we must send the list of extra asset libraries that are // being added in this AJAX Response. - /** @var \Drupal\Core\Asset\AssetResolverInterface $asset_resolver */ - $asset_resolver = \Drupal::service('asset.resolver'); - if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $asset_resolver->getAlreadyLoadedLibrariesWithDependencies())) { + /** @var \Drupal\Core\Asset\LibraryDependencyResolver $library_dependency_resolver */ + $library_dependency_resolver = \Drupal::service('library.dependency_resolver'); + if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()))) { // Provide the page with information about the theme that's used, so that // a later AJAX request can be rendered using the same theme. // @see \Drupal\Core\Theme\AjaxBasePageNegotiator @@ -681,11 +682,10 @@ function system_js_settings_alter(&$settings) { // Provide the page with information about the individual asset libraries // used, information not otherwise available when aggregation is enabled. - $library_dependency_resolver = \Drupal::service('library.dependency_resolver'); - $minimal_libraries = $library_dependency_resolver->getMinimalRepresentativeSubset(array_unique(array_merge( - $asset_resolver->getLibrariesWithDependencies(), - $asset_resolver->getAlreadyLoadedLibrariesWithDependencies() - ))); + $minimal_libraries = $library_dependency_resolver->getMinimalRepresentativeSubset(array_merge( + $assets->getLibraries(), + $assets->getAlreadyLoadedLibraries() + )); sort($minimal_libraries); $settings['ajaxPageState']['libraries'] = implode(',', $minimal_libraries); } diff --git a/core/modules/system/tests/modules/common_test/common_test.module b/core/modules/system/tests/modules/common_test/common_test.module index 8dbf98a..b63deb6 100644 --- a/core/modules/system/tests/modules/common_test/common_test.module +++ b/core/modules/system/tests/modules/common_test/common_test.module @@ -5,6 +5,8 @@ * Helper module for the Common tests. */ +use \Drupal\Core\Asset\AttachedAssetsInterface; + /** * Applies #printed to an element to help test #pre_render. */ @@ -324,7 +326,7 @@ function common_test_page_attachments_alter(array &$page) { * * @see \Drupal\system\Tests\Common\JavaScriptTest::testHeaderSetting() */ -function common_test_js_settings_alter(&$settings) { +function common_test_js_settings_alter(&$settings, AttachedAssetsInterface $assets) { // Modify an existing setting. if (array_key_exists('pluralDelimiter', $settings['locale'])) { $settings['locale']['pluralDelimiter'] = '☃'; diff --git a/core/modules/system/theme.api.php b/core/modules/system/theme.api.php index 9ee2ab1..337b5df 100644 --- a/core/modules/system/theme.api.php +++ b/core/modules/system/theme.api.php @@ -711,11 +711,13 @@ function hook_element_info_alter(array &$types) { * * @param $javascript * An array of all JavaScript being presented on the page. + * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets + * The assets attached to the current response. * * @see drupal_js_defaults() * @see \Drupal\Core\Asset\AssetResolver */ -function hook_js_alter(&$javascript) { +function hook_js_alter(&$javascript, \Drupal\Core\Asset\AttachedAssetsInterface $assets) { // Swap out jQuery to use an updated version of the library. $javascript['core/assets/vendor/jquery/jquery.min.js']['data'] = drupal_get_path('module', 'jquery_update') . '/jquery.js'; } @@ -726,10 +728,12 @@ function hook_js_alter(&$javascript) { * @param array &$settings * An array of all JavaScript settings (drupalSettings) being presented on the * page. + * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets + * The assets attached to the current response. * * @see \Drupal\Core\Asset\AssetResolver */ -function hook_js_settings_alter(array &$settings) { +function hook_js_settings_alter(array &$settings, \Drupal\Core\Asset\AttachedAssetsInterface $assets) { // Add settings. $settings['user']['uid'] = \Drupal::currentUser(); @@ -789,10 +793,12 @@ function hook_library_info_alter(&$libraries, $module) { * * @param $css * An array of all CSS items (files and inline CSS) being requested on the page. + * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets + * The assets attached to the current response. * * @see Drupal\Core\Asset\LibraryResolverInterface::getCssAssets() */ -function hook_css_alter(&$css) { +function hook_css_alter(&$css, \Drupal\Core\Asset\AttachedAssetsInterface $assets) { // Remove defaults.css file. unset($css[drupal_get_path('module', 'system') . '/defaults.css']); } diff --git a/core/modules/user/user.module b/core/modules/user/user.module index a251fd7..e6db06e 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -3,6 +3,7 @@ use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Unicode; +use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; @@ -110,7 +111,7 @@ function user_theme() { /** * Implements hook_js_settings_alter(). */ -function user_js_settings_alter(&$settings) { +function user_js_settings_alter(&$settings, AttachedAssetsInterface $assets) { // Provide the user ID in drupalSettings to allow JavaScript code to customize // the experience for the end user, rather than the server side, which would // break the render cache.