diff --git a/core/authorize.php b/core/authorize.php
index 0eb4c6a..4d9df40 100644
--- a/core/authorize.php
+++ b/core/authorize.php
@@ -166,8 +166,8 @@ function authorize_access_allowed(Request $request) {
'#show_messages' => $show_messages,
)));
- // Prepare attachments, because this does not go the usual way
- // via the Symfony chain, but is send directly.
+ // Prepare attachments, because this does not go the usual way via the
+ // Symfony chain, but is sent directly.
$bare_html_page_renderer->prepareAttachments($response);
$response->send();
diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index 9c71f09..58a9c15 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -144,8 +144,8 @@ function _batch_progress_page() {
// Use a HtmlResponse to process the attachments.
$response = new HtmlResponse($content);
- // Prepare attachments, because this does not go the usual way
- // via the Symfony chain, but is send directly.
+ // Prepare attachments, because this does not go the usual way via the
+ // Symfony chain, but is sent directly.
$bare_html_page_renderer->prepareAttachments($response);
// Just use the content of the response.
diff --git a/core/includes/errors.inc b/core/includes/errors.inc
index 01c747c..de7a77d 100644
--- a/core/includes/errors.inc
+++ b/core/includes/errors.inc
@@ -248,8 +248,8 @@ function _drupal_log_error($error, $fatal = FALSE) {
$output = $bare_html_page_renderer->renderBarePage(['#markup' => $message], 'Error', 'maintenance_page');
$response = new HtmlResponse($output, 500);
- // Prepare attachments, because this does not go the usual way
- // via the Symfony chain, but is send directly.
+ // Prepare attachments, because this does not go the usual way via the
+ // Symfony chain, but is sent directly.
$bare_html_page_renderer->prepareAttachments($response);
$response->setStatusCode(500, '500 Service unavailable (with message)');
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 2166062..d0181b9 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -996,8 +996,8 @@ function install_display_output($output, $install_state) {
$response->headers->add($default_headers);
$response->setContent($bare_html_page_renderer->renderBarePage($output, $output['#title'], 'install_page', $regions));
- // Prepare attachments, because this does not go the usual way
- // via the Symfony chain, but is send directly.
+ // Prepare attachments, because this does not go the usual way via the
+ // Symfony chain, but is sent directly.
$bare_html_page_renderer->prepareAttachments($response);
$response->send();
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index ce95106..1e50907 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1314,17 +1314,20 @@ function template_preprocess_html(&$variables) {
// Create placeholder strings for these keys.
// @see \Drupal\Core\Render\HtmlResponseSubscriber
- $keys = [
+ $types = [
'styles',
'scripts',
'scripts_bottom',
'head',
];
- foreach ($keys as $key) {
+ foreach ($types as $type) {
$token = Crypt::randomBytesBase64(55);
- $placeholder = '';
- $variables[$key]['#markup'] = $placeholder;
- $variables[$key]['#attached']['html_response_placeholders'][$key] = $placeholder;
+ $placeholder = SafeMarkup::format('', [
+ '@type' => $type,
+ '@token' => $token,
+ ]);
+ $variables[$type]['#markup'] = $placeholder;
+ $variables[$type]['#attached']['html_response_placeholders'][$type] = $placeholder;
}
}
diff --git a/core/lib/Drupal/Core/EventSubscriber/HtmlResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/HtmlResponseSubscriber.php
index 8bb245b..a3f8db4 100644
--- a/core/lib/Drupal/Core/EventSubscriber/HtmlResponseSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/HtmlResponseSubscriber.php
@@ -14,6 +14,7 @@
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Render\AttachmentsResponseInterface;
+use Drupal\Core\Render\HtmlResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
@@ -22,7 +23,7 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
- * Response subscriber to handle html responses.
+ * Response subscriber to handle HTML responses.
*/
class HtmlResponseSubscriber implements EventSubscriberInterface {
@@ -88,71 +89,132 @@ public function onRespond(FilterResponseEvent $event) {
}
$response = $event->getResponse();
- $this->prepareAttachments($response);
+ if ($response instanceof HtmlResponse) {
+ $this->prepareAttachments($response);
+ }
}
- public function prepareAttachments($response) {
- if ($response instanceof AttachmentsResponseInterface && !($response instanceof AjaxResponse)) {
- // Render the attachments into HTML markup
- $attached = $response->getAttachments();
+ /**
+ * Prepare attachments contained in a HtmlResponse.
+ *
+ * This renders them into HTML markup and replaces the HTML response
+ * placeholders in the content of the response.
+ *
+ * It also sets any headers found in the attachments' "http_header" key.
+ *
+ * @param \Drupal\Core\Render\AttachmentsResponseInterface $response
+ * The response containing attachments.
+ */
+ public function prepareAttachments(AttachmentsResponseInterface $response) {
+ $attached = $response->getAttachments();
- // Get the placeholders from attached and then remove them.
- $placeholders = $attached['html_response_placeholders'];
- unset($attached['html_response_placeholders']);
+ // Get the placeholders from attached and then remove them.
+ $placeholders = $attached['html_response_placeholders'];
+ unset($attached['html_response_placeholders']);
- $all_attached = ['#attached' => $attached];
- $assets = AttachedAssets::createFromRenderArray($all_attached);
+ $variables = [];
+ $variables += $this->processAssetLibraries($attached, $placeholders);
- // Take Ajax page state into account, to allow for something like Turbolinks
- // to be implemented without altering core.
- // @see https://github.com/rails/turbolinks/
- // @todo https://www.drupal.org/node/2497115 - Below line is broken due to ->request.
- $ajax_page_state = $this->requestStack->getCurrentRequest()->request->get('ajax_page_state');
- $assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []);
+ // Handle all non-asset attachments.
+ $all_attached = ['#attached' => $attached];
+ drupal_process_attached($all_attached);
- $variables = [];
+ // Get HTML head elements - if present.
+ if (isset($placeholders['head'])) {
+ $variables['head'] = drupal_get_html_head(FALSE);
+ }
- // Print styles - if present.
- if (isset($placeholders['styles'])) {
- // Optimize CSS if necessary, but only during normal site operation.
- $optimize_css = !defined('MAINTENANCE_MODE') && $this->config->get('css.preprocess');
- $variables['styles'] = $this->cssCollectionRenderer->render($this->assetResolver->getCssAssets($assets, $optimize_css));
- }
+ // Now replace the placeholders in the response content with the real
+ // data.
+ $this->renderPlaceholders($response, $placeholders, $variables);
- // Print scripts - if any are present.
- if (isset($placeholders['scripts']) || isset($placeholders['scripts_bottom'])) {
- // Optimize JS if necessary, but only during normal site operation.
- $optimize_js = !defined('MAINTENANCE_MODE') && $this->config->get('js.preprocess');
- list($js_assets_header, $js_assets_footer) = $this->assetResolver->getJsAssets($assets, $optimize_js);
- $variables['scripts'] = $this->jsCollectionRenderer->render($js_assets_header);
- $variables['scripts_bottom'] = $this->jsCollectionRenderer->render($js_assets_footer);
- }
+ // Finally set the headers on the response.
+ $headers = drupal_get_http_header();
+ $this->setHeaders($response, $headers);
+ }
- // Handle all non-asset attachments.
- // @todo Remove and move into this class.
- drupal_process_attached($all_attached);
-
- $headers = drupal_get_http_header();
- foreach ($headers as $name => $value) {
- // Symfony special-cases the 'Status' header.
- if ($name === 'status') {
- $response->setStatusCode($value);
- }
- $response->headers->set($name, $value, FALSE);
- }
+ /**
+ * Processes asset libraries into render arrays.
+ *
+ * @param array $attached
+ * The attachments to process.
+ * @param array $placeholders
+ * The placeholders that exist in the response.
+ *
+ * @return array
+ * An array keyed by asset type, with keys:
+ * - styles
+ * - 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/
+ // @todo https://www.drupal.org/node/2497115 - Below line is broken due to ->request.
+ $ajax_page_state = $this->requestStack->getCurrentRequest()->request->get('ajax_page_state');
+ $assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []);
+
+ $variables = [];
+
+ // Print styles - if present.
+ if (isset($placeholders['styles'])) {
+ // Optimize CSS if necessary, but only during normal site operation.
+ $optimize_css = !defined('MAINTENANCE_MODE') && $this->config->get('css.preprocess');
+ $variables['styles'] = $this->cssCollectionRenderer->render($this->assetResolver->getCssAssets($assets, $optimize_css));
+ }
- // Print HTML head elements - if present.
- if (isset($placeholders['head'])) {
- $variables['head'] = drupal_get_html_head(FALSE);
- }
+ // Print scripts - if any are present.
+ if (isset($placeholders['scripts']) || isset($placeholders['scripts_bottom'])) {
+ // Optimize JS if necessary, but only during normal site operation.
+ $optimize_js = !defined('MAINTENANCE_MODE') && $this->config->get('js.preprocess');
+ list($js_assets_header, $js_assets_footer) = $this->assetResolver->getJsAssets($assets, $optimize_js);
+ $variables['scripts'] = $this->jsCollectionRenderer->render($js_assets_header);
+ $variables['scripts_bottom'] = $this->jsCollectionRenderer->render($js_assets_footer);
+ }
+
+ return $variables;
+ }
- // Now replace the placeholders in the response content with the real
- // data.
- $content = $response->getContent();
- foreach ($placeholders as $key => $placeholder) {
- $content = str_replace($placeholder, render($variables[$key]), $content);
+ /**
+ * Renders variables into HTML markup and replaces placeholders in the
+ * response content.
+ *
+ * @param \Drupal\Core\Render\AttachmentsResponseInterface $response
+ * The response object.
+ * @param array placeholders
+ * An array of placeholders, keyed by type with the placeholders
+ * present in the content of the response as values.
+ * @param array variables
+ * The variables to render and replace, keyed by type with renderable
+ * arrays as values.
+ */
+ protected function renderPlaceholders(AttachmentsResponseInterface $response, array $placeholders, array $variables) {
+ $content = $response->getContent();
+ foreach ($placeholders as $key => $placeholder) {
+ $content = str_replace($placeholder, render($variables[$key]), $content);
+ }
+ $response->setContent($content);
+ }
+
+ /**
+ * Sets headers on a response object.
+ *
+ * @param \Drupal\Core\Render\AttachmentsResponseInterface $response
+ * The response object.
+ * @param array $headers
+ * The headers to set.
+ */
+ protected function setHeaders(AttachmentsResponseInterface $response, array $headers) {
+ foreach ($headers as $name => $value) {
+ // Symfony special-cases the 'Status' header.
+ if ($name === 'status') {
+ $response->setStatusCode($value);
}
- $response->setContent($content);
+ $response->headers->set($name, $value, FALSE);
}
}
@@ -166,4 +228,5 @@ public static function getSubscribedEvents() {
$events[KernelEvents::RESPONSE][] = array('onRespond');
return $events;
}
+
}
diff --git a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php
index b07a29d..98de48f 100644
--- a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php
+++ b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php
@@ -79,8 +79,6 @@ public function renderBarePage(array $content, $title, $page_theme_property, arr
$this->renderer->renderRoot($html);
$content = $this->renderCache->getCacheableRenderArray($html);
- // Never cache exception pages.
- $content['#cache']['max-age'] = 0;
return $content;
}
diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
index 10f0d08..c7ed405 100644
--- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
@@ -120,7 +120,8 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
// page.html.twig, hence add them here, just before rendering html.html.twig.
$this->buildPageTopAndBottom($html);
- // @todo Make renderRoot return a cacheable render array directly.
+ // @todo https://www.drupal.org/node/2495001 Make renderRoot return a
+ // cacheable render array directly.
$this->renderer->renderRoot($html);
$content = $this->renderCache->getCacheableRenderArray($html);
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index cfd2ab9..5fdd368 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -291,11 +291,6 @@ function views_theme_suggestions_container_alter(array &$suggestions, array $var
* Implements MODULE_preprocess_HOOK().
*/
function views_preprocess_html(&$variables) {
- // Early-return to prevent adding unnecessary JavaScript.
- if (!\Drupal::moduleHandler()->moduleExists('contextual') || !\Drupal::currentUser()->hasPermission('access contextual links')) {
- return;
- }
-
// If the main content of this page contains a view, attach its contextual
// links to the overall page array. This allows them to be rendered directly
// next to the page title.
@@ -303,6 +298,11 @@ function views_preprocess_html(&$variables) {
views_add_contextual_links($variables['page'], 'page', $view, $view->current_display);
}
+ // Early-return to prevent adding unnecessary JavaScript.
+ if (!\Drupal::moduleHandler()->moduleExists('contextual') || !\Drupal::currentUser()->hasPermission('access contextual links')) {
+ return;
+ }
+
// If the page contains a view as its main content, contextual links may have
// been attached to the page as a whole; for example, by
// views_page_display_pre_render().