diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php index 7d36b51c718e879487ee56a5dde58ceffb43fb37..69a8f13788875b6c428e6fcb4c3adb2b8e29d0b1 100644 --- a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php +++ b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php @@ -148,6 +148,16 @@ protected function buildAttachmentsCommands(AjaxResponse $response, Request $req $css_assets = $this->assetResolver->getCssAssets($assets, $optimize_css); list($js_assets_header, $js_assets_footer) = $this->assetResolver->getJsAssets($assets, $optimize_js); + // First, AttachedAssets::setLibraries() ensures duplicate libraries are + // removed: it converts it to a set of libraries if necessary. Second, + // AssetResolver::getJsSettings() ensures $assets contains the final set of + // JavaScript settings. AttachmentsResponseProcessorInterface also mandates + // that the response it processes contains the final attachment values, so + // update both the 'library' and 'drupalSettings' attachments accordingly. + $attachments['library'] = $assets->getLibraries(); + $attachments['drupalSettings'] = $assets->getSettings(); + $response->setAttachments($attachments); + // Render the HTML to load these files, and add AJAX commands to insert this // HTML in the page. Settings are handled separately, afterwards. $settings = []; diff --git a/core/lib/Drupal/Core/Asset/AssetResolver.php b/core/lib/Drupal/Core/Asset/AssetResolver.php index 9927049bea6ef5a46c3a6746d06e8caeab633f0d..a533c8ad61df0c94129bccf971e1b9e1dddaeeec 100644 --- a/core/lib/Drupal/Core/Asset/AssetResolver.php +++ b/core/lib/Drupal/Core/Asset/AssetResolver.php @@ -334,6 +334,9 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize) { // Allow modules and themes to alter the JavaScript settings. $this->moduleHandler->alter('js_settings', $settings, $assets); $this->themeManager->alter('js_settings', $settings, $assets); + // Update the $assets object accordingly, so that it reflects the final + // settings. + $assets->setSettings($settings); $settings_as_inline_javascript = [ 'type' => 'setting', 'group' => JS_SETTING, diff --git a/core/lib/Drupal/Core/Asset/AssetResolverInterface.php b/core/lib/Drupal/Core/Asset/AssetResolverInterface.php index c912d7663c4968638546362f9744906405bef72f..e0845c62ee877146535cd29884196c94dedb66b2 100644 --- a/core/lib/Drupal/Core/Asset/AssetResolverInterface.php +++ b/core/lib/Drupal/Core/Asset/AssetResolverInterface.php @@ -69,6 +69,8 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize); * * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets * The assets attached to the current response. + * Note that this object is modified to reflect the final JavaScript + * settings assets. * @param bool $optimize * Whether to apply the JavaScript asset collection optimizer, to return * optimized JavaScript asset collections rather than an unoptimized ones. diff --git a/core/lib/Drupal/Core/Render/AttachmentsResponseProcessorInterface.php b/core/lib/Drupal/Core/Render/AttachmentsResponseProcessorInterface.php index 6067a862571183f356d1821577a55e7bba6d3f46..74614eec077a9ba78c59361a2109160b093bb549 100644 --- a/core/lib/Drupal/Core/Render/AttachmentsResponseProcessorInterface.php +++ b/core/lib/Drupal/Core/Render/AttachmentsResponseProcessorInterface.php @@ -46,7 +46,8 @@ interface AttachmentsResponseProcessorInterface { * The response to process. * * @return \Drupal\Core\Render\AttachmentsInterface - * The processed response. + * The processed response, with the attachments updated to reflect their + * final values. * * @throws \InvalidArgumentException * Thrown when the $response parameter is not the type of response object diff --git a/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php index e0160d1b46a9063cb18e16832ea230ccf2edd7e6..d4070629c62686666cd83ad917f8c427ab57b6f7 100644 --- a/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php +++ b/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php @@ -9,6 +9,7 @@ use Drupal\Core\Asset\AssetCollectionRendererInterface; use Drupal\Core\Asset\AssetResolverInterface; use Drupal\Core\Asset\AttachedAssets; +use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Form\EnforcedResponseException; use Drupal\Core\Extension\ModuleHandlerInterface; @@ -155,7 +156,19 @@ public function processAttachments(AttachmentsInterface $response) { $attachment_placeholders = $attached['html_response_attachment_placeholders']; unset($attached['html_response_attachment_placeholders']); - $variables = $this->processAssetLibraries($attached, $attachment_placeholders); + $assets = AttachedAssets::createFromRenderArray(['#attached' => $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 = $this->requestStack->getCurrentRequest()->get('ajax_page_state'); + $assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []); + $variables = $this->processAssetLibraries($assets, $attachment_placeholders); + // $variables now contains the markup to load the asset libraries. Update + // $attached with the final list of libraries and JavaScript settings, so + // that $response can be updated with those. Then the response object will + // list the final, processed attachments. + $attached['library'] = $assets->getLibraries(); + $attached['drupalSettings'] = $assets->getSettings(); // Since we can only replace content in the HTML head section if there's a // placeholder for it, we can safely avoid processing the render array if @@ -168,6 +181,7 @@ public function processAttachments(AttachmentsInterface $response) { $attached, $this->processFeed($attached['feed']) ); + unset($attached['feed']); } // 'html_head_link' is a special case of 'html_head' which can be present // as a head element, but also as a Link: HTTP header depending on @@ -182,6 +196,7 @@ public function processAttachments(AttachmentsInterface $response) { $attached, $this->processHtmlHeadLink($attached['html_head_link']) ); + unset($attached['html_head_link']); } // Now we can process 'html_head', which contains both 'feed' and @@ -200,6 +215,10 @@ public function processAttachments(AttachmentsInterface $response) { $this->setHeaders($response, $attached['http_header']); } + // AttachmentsResponseProcessorInterface mandates that the response it + // processes contains the final attachment values. + $response->setAttachments($attached); + return $response; } @@ -255,8 +274,8 @@ protected function renderPlaceholders(HtmlResponse $response) { /** * Processes asset libraries into render arrays. * - * @param array $attached - * The attachments to process. + * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets + * The attached assets collection for the current response. * @param array $placeholders * The placeholders that exist in the response. * @@ -266,16 +285,7 @@ protected function renderPlaceholders(HtmlResponse $response) { * - scripts * - scripts_bottom */ - protected function processAssetLibraries(array $attached, array $placeholders) { - $all_attached = ['#attached' => $attached]; - $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 = $this->requestStack->getCurrentRequest()->get('ajax_page_state'); - $assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []); - + protected function processAssetLibraries(AttachedAssetsInterface $assets, array $placeholders) { $variables = []; // Print styles - if present. diff --git a/core/modules/system/src/Tests/Common/AttachedAssetsTest.php b/core/modules/system/src/Tests/Common/AttachedAssetsTest.php index 7440d995df6aa274dff590f1a29209ee9d56db54..cc04606bcc9f65ea46e34576c88b637cecc52a1d 100644 --- a/core/modules/system/src/Tests/Common/AttachedAssetsTest.php +++ b/core/modules/system/src/Tests/Common/AttachedAssetsTest.php @@ -108,8 +108,10 @@ function testAddJsSettings() { $build['#attached']['library'][] = 'core/drupalSettings'; $assets = AttachedAssets::createFromRenderArray($build); + $this->assertEqual([], $assets->getSettings(), 'JavaScript settings on $assets are empty.'); $javascript = $this->assetResolver->getJsAssets($assets, FALSE)[1]; $this->assertTrue(array_key_exists('currentPath', $javascript['drupalSettings']['data']['path']), 'The current path JavaScript setting is set correctly.'); + $this->assertTrue(array_key_exists('currentPath', $assets->getSettings()['path']), 'JavaScript settings on $assets are resolved after retrieving JavaScript assets, and are equal to the returned JavaScript settings.'); $assets->setSettings(['drupal' => 'rocks', 'dries' => 280342800]); $javascript = $this->assetResolver->getJsAssets($assets, FALSE)[1];