diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index fc70f82..cdb012c 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1754,26 +1754,6 @@ function template_preprocess_html(&$variables) {
   }
   $variables['head_title'] = SafeMarkup::set($output);
 
-  // @todo Remove drupal_*_html_head() and refactor accordingly.
-  $html_heads = drupal_get_html_head(FALSE);
-  uasort($html_heads, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
-  foreach ($html_heads as $name => $tag) {
-    if ($tag['#tag'] == 'link') {
-      $link = new LinkElement($name, isset($tag['#attributes']['content']) ? $tag['#attributes']['content'] : NULL, $tag['#attributes']);
-      if (!empty($tag['#noscript'])) {
-        $link->setNoScript();
-      }
-      $page->addLinkElement($link);
-    }
-    elseif ($tag['#tag'] == 'meta') {
-      $metatag = new MetaElement(NULL, $tag['#attributes']);
-      if (!empty($tag['#noscript'])) {
-        $metatag->setNoScript();
-      }
-      $page->addMetaElement($metatag);
-    }
-  }
-
   // Add favicon.
   if (theme_get_setting('features.favicon')) {
     $url = UrlHelper::stripDangerousProtocols(theme_get_setting('favicon.url'));
diff --git a/core/lib/Drupal/Core/Controller/HtmlControllerBase.php b/core/lib/Drupal/Core/Controller/HtmlControllerBase.php
index eb4f140..5f99cc0 100644
--- a/core/lib/Drupal/Core/Controller/HtmlControllerBase.php
+++ b/core/lib/Drupal/Core/Controller/HtmlControllerBase.php
@@ -9,6 +9,8 @@
 
 use Drupal\Core\Page\FeedLinkElement;
 use Drupal\Core\Page\HtmlFragment;
+use Drupal\Core\Page\LinkElement;
+use Drupal\Core\Page\MetaElement;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\Utility\Title;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
@@ -73,11 +75,40 @@ protected function createHtmlFragment($page_content, Request $request) {
     }
 
     $content = $this->drupalRender($page_content);
-    if (!empty($page_content)) {
-      drupal_process_attached($page_content);
-    }
+
+    $js = isset($content['#attached']['js']) ? $content['#attached']['js'] : [];
+    $css = isset($content['#attached']['css']) ? $content['#attached']['css'] : [];
+    $library = isset($content['#attached']['library']) ? $content['#attached']['library'] : [];
+
     $cache = !empty($page_content['#cache']['tags']) ? array('tags' => $page_content['#cache']['tags']) : array();
-    $fragment = new HtmlFragment($content, $cache);
+    $fragment = (new HtmlFragment($content, $cache))
+      ->setCss($css)
+      ->setJs($js)
+      ->setLibraries($library);
+
+    $html_head_link = isset($content['#attached']['drupal_add_html_head_link']) ? $content['#attached']['drupal_add_html_head_link'] : [];
+
+    foreach ($html_head_link as $name => $tag) {
+      if ($tag['#tag'] == 'link') {
+        $link = new LinkElement($name, isset($tag['#attributes']['content']) ? $tag['#attributes']['content'] : NULL, $tag['#attributes']);
+        if (!empty($tag['#noscript'])) {
+          $link->setNoScript();
+        }
+        $fragment->addLinkElement($link);
+      }
+    }
+
+    $html_head = isset($content['#attached']['drupal_add_html_head']) ? $content['#attached']['drupal_add_html_head'] : [];
+    uasort($html_heads, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
+    foreach ($html_head as $name => $tag) {
+      if ($tag['#tag'] == 'meta') {
+        $metatag = new MetaElement(NULL, $tag['#attributes']);
+        if (!empty($tag['#noscript'])) {
+          $metatag->setNoScript();
+        }
+        $fragment->addMetaElement($metatag);
+      }
+    }
 
     // A title defined in the return always wins.
     if (isset($page_content['#title'])) {
diff --git a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php
index bcb8146..adf098f 100644
--- a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php
+++ b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php
@@ -67,6 +67,19 @@ public function render(HtmlFragmentInterface $fragment, $status_code = 200) {
       drupal_process_attached($page_array['page_bottom']);
     }
 
+    // Extract meta information.
+    $js = $page->getJs();
+    $js += $fragment->getJs();
+    $css = $page->getCss();
+    $css += $fragment->getCss();
+    $libraries = $page->getLibraries();
+    $libraries += $fragment->getLibraries();
+
+    $elements = &$page->getMetaElements();
+    $elements += $fragment->getMetaElements();
+    $links = &$page->getLinkElements();
+    $links += $fragment->getLinkElements();
+
     if ($fragment instanceof CacheableInterface) {
       // Collect cache tags for all the content in all the regions on the page.
       $tags = $page_array['#cache']['tags'];
diff --git a/core/lib/Drupal/Core/Page/HtmlFragment.php b/core/lib/Drupal/Core/Page/HtmlFragment.php
index b3c55d8..c477ffb 100644
--- a/core/lib/Drupal/Core/Page/HtmlFragment.php
+++ b/core/lib/Drupal/Core/Page/HtmlFragment.php
@@ -52,6 +52,12 @@ class HtmlFragment implements CacheableInterface, HtmlFragmentInterface {
    */
   protected $cache = array();
 
+  protected $js = [];
+
+  protected $css = [];
+
+  protected $libraries = [];
+
   /**
    * Constructs a new HtmlFragment.
    *
@@ -234,4 +240,55 @@ public function isCacheable() {
     return $this->cache['is_cacheable'];
   }
 
+  /**
+   *
+   */
+  public function &getJs() {
+    return $this->js;
+  }
+
+  public function addJs($js) {
+    $this->js[] = $js;
+    return $this;
+  }
+
+  public function setJs($js) {
+    $this->js = $js;
+    return $this;
+  }
+
+  /**
+   *
+   */
+  public function &getCss() {
+    return $this->css;
+  }
+
+  public function addCss($css) {
+    $this->css[] = $css;
+    return $this;
+  }
+
+  public function setCss($css) {
+    $this->css = $css;
+    return $this;
+  }
+
+  /**
+   *
+   */
+  public function &getLibraries() {
+    return $this->libraries;
+  }
+
+  public function addLibrary($library) {
+    $this->libraries[] = $library;
+    return $this;
+  }
+
+  public function setLibraries($libraries) {
+    $this->libraries = $libraries;
+    return $this;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Page/HtmlFragmentInterface.php b/core/lib/Drupal/Core/Page/HtmlFragmentInterface.php
index f9c481b..4fb60d2 100644
--- a/core/lib/Drupal/Core/Page/HtmlFragmentInterface.php
+++ b/core/lib/Drupal/Core/Page/HtmlFragmentInterface.php
@@ -63,4 +63,19 @@ public function getFeedLinkElements();
    */
   public function getMetaElements();
 
+  /**
+   *
+   */
+  public function getJs();
+
+  /**
+   *
+   */
+  public function getCss();
+
+  /**
+   *
+   */
+  public function getLibraries();
+
 }
diff --git a/core/modules/block/src/Plugin/DisplayVariant/FullPageVariant.php b/core/modules/block/src/Plugin/DisplayVariant/FullPageVariant.php
index 7243362..b5ebad7 100644
--- a/core/modules/block/src/Plugin/DisplayVariant/FullPageVariant.php
+++ b/core/modules/block/src/Plugin/DisplayVariant/FullPageVariant.php
@@ -9,10 +9,12 @@
 
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityViewBuilderInterface;
+use Drupal\Core\Page\HtmlFragment;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Display\VariantBase;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
+use Drupal\Core\Utility\Title;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -121,7 +123,20 @@ public function build() {
       /** @var $blocks \Drupal\block\BlockInterface[] */
       foreach ($blocks as $key => $block) {
         if ($block->access('view')) {
-          $build[$region][$key] = $this->blockViewBuilder->view($block);
+          $build = $this->blockViewBuilder->view($block);
+
+          $rendered = $this->drupalRender($build);
+          $cache = !empty($build['#cache']['tags']) ? array('tags' => $build['#cache']['tags']) : array();
+          $attached = drupal_render_collect_attached($build);
+
+          $fragment = (new HtmlFragment($rendered, $cache))
+            ->setCss($attached['css'])
+            ->setJs($attached['js'])
+            ->setLibraries($attached['library']);
+          if (isset($rendered['#title'])) {
+            $fragment->setTitle($build['#title'], Title::FILTER_XSS_ADMIN);
+          }
+          $build[$region][$key] = $fragment;
         }
       }
       if (!empty($build[$region])) {
@@ -133,6 +148,15 @@ public function build() {
   }
 
   /**
+   * Wraps drupal_render().
+   *
+   * @todo: Remove as part of https://drupal.org/node/2182149
+   */
+  protected function drupalRender(&$elements, $is_recursive_call = FALSE) {
+    return drupal_render($elements, $is_recursive_call);
+  }
+
+  /**
    * Returns the human-readable list of regions keyed by machine name.
    *
    * @return array
