 core/core.services.yml                             |   5 +
 core/includes/common.inc                           |   2 +-
 .../Core/Ajax/AjaxResponseAttachmentsProcessor.php |   6 +-
 .../EarlyRenderingControllerWrapper.php            | 143 +++++++++++++++
 core/lib/Drupal/Core/Render/Element/Actions.php    |   2 +-
 .../Core/Render/MainContent/HtmlRenderer.php       |   2 +-
 core/lib/Drupal/Core/Render/RenderContext.php      |  60 +++++++
 core/lib/Drupal/Core/Render/Renderer.php           | 136 +++++++-------
 core/lib/Drupal/Core/Render/RendererInterface.php  |  43 ++++-
 .../block/src/Tests/BlockViewBuilderTest.php       |  33 ++--
 .../src/Tests/BlockContentCacheTagsTest.php        |   2 +-
 core/modules/book/book.module                      |   7 +-
 core/modules/book/book.services.yml                |   2 +-
 core/modules/book/src/BookExport.php               |   6 +-
 core/modules/book/src/BookOutline.php              |   3 +-
 .../modules/book/src/Controller/BookController.php |  18 +-
 .../ckeditor/src/Plugin/Editor/CKEditor.php        |  18 +-
 core/modules/comment/comment.module                |   2 +-
 .../Plugin/Action/UnpublishByKeywordComment.php    |   2 +-
 .../Tests/CommentDefaultFormatterCacheTagsTest.php |  17 +-
 core/modules/config/src/Form/ConfigSync.php        |  18 +-
 core/modules/contact/src/MessageViewBuilder.php    |   6 +-
 .../contextual/src/ContextualController.php        |   2 +-
 .../datetime/src/Tests/DateTimeFieldTest.php       |   2 +-
 core/modules/field/field.module                    |   2 +-
 .../field/src/Tests/Boolean/BooleanFieldTest.php   |   2 +-
 .../field/src/Tests/Email/EmailFieldTest.php       |   2 +-
 .../EntityReferenceFormatterTest.php               |  10 +-
 .../field/src/Tests/String/StringFieldTest.php     |   2 +-
 .../field_ui/src/Tests/ManageDisplayTest.php       |   2 +-
 core/modules/file/file.field.inc                   |   2 +-
 core/modules/file/file.module                      |   2 +-
 .../src/Controller/FileWidgetAjaxController.php    |   8 +-
 .../src/Plugin/Field/FieldWidget/FileWidget.php    |   2 +-
 .../file/src/Tests/FileFieldDisplayTest.php        |   2 +-
 core/modules/file/src/Tests/FileItemTest.php       |   2 +-
 core/modules/forum/forum.module                    |   4 +-
 core/modules/forum/src/Tests/ForumTest.php         |   4 +-
 .../src/Tests/Views/HistoryTimestampTest.php       |   2 +-
 .../src/Plugin/Field/FieldWidget/ImageWidget.php   |   2 +-
 .../image/src/Tests/ImageDimensionsTest.php        |   2 +-
 .../image/src/Tests/ImageFieldDisplayTest.php      |  21 ++-
 .../image/src/Tests/ImageThemeFunctionTest.php     |  25 ++-
 core/modules/link/src/Tests/LinkFieldTest.php      |   2 +-
 core/modules/node/src/Plugin/Search/NodeSearch.php |   6 +-
 core/modules/node/src/Tests/SummaryLengthTest.php  |   7 +-
 .../modules/node/src/Tests/Views/RowPluginTest.php |   6 +-
 .../quickedit/src/Tests/QuickEditLoadingTest.php   |   2 +-
 .../Tests/EntityReferenceFieldAttributesTest.php   |   2 +-
 .../rdf/src/Tests/Field/FieldRdfaTestBase.php      |   2 +-
 .../rdf/src/Tests/FileFieldAttributesTest.php      |   2 +-
 .../rdf/src/Tests/ImageFieldAttributesTest.php     |   2 +-
 .../src/Tests/ResponsiveImageFieldDisplayTest.php  |   6 +-
 core/modules/simpletest/src/AssertContentTrait.php |   8 +-
 .../simpletest/src/Form/SimpletestResultsForm.php  |  10 +-
 .../simpletest/src/Form/SimpletestTestForm.php     |  32 +++-
 .../simpletest/src/Tests/KernelTestBaseTest.php    |   6 +-
 core/modules/system/src/Tests/Ajax/DialogTest.php  |   2 +-
 .../system/src/Tests/Common/AddFeedTest.php        |   2 +-
 .../system/src/Tests/Common/AttachedAssetsTest.php |  34 ++--
 .../src/Tests/Common/RenderElementTypesTest.php    |   8 +-
 .../modules/system/src/Tests/Common/RenderTest.php |   2 +-
 core/modules/system/src/Tests/Common/UrlTest.php   |  29 ++-
 .../src/Tests/Entity/EntityTranslationTest.php     |  11 +-
 .../src/Tests/Entity/EntityViewBuilderTest.php     |  17 +-
 .../modules/system/src/Tests/Form/CheckboxTest.php |   2 +-
 core/modules/system/src/Tests/Form/FormTest.php    |   2 +-
 .../system/src/Tests/Render/RenderCacheTest.php    |  10 +-
 .../system/src/Tests/Theme/FunctionsTest.php       |   6 +-
 .../system/src/Tests/Theme/TwigDebugMarkupTest.php |  10 +-
 .../system/src/Tests/Theme/TwigEnvironmentTest.php |   8 +-
 .../system/src/Tests/Theme/TwigFilterTest.php      |   2 +-
 .../system/src/Tests/Theme/TwigNamespaceTest.php   |   2 +-
 .../modules/system/src/Tests/Theme/TwigRawTest.php |   4 +-
 .../src/Controller/CommonTestController.php        |   3 +-
 .../src/EventSubscriber/ThemeTestSubscriber.php    |  15 +-
 .../modules/theme_test/theme_test.services.yml     |   2 +-
 .../text/src/Tests/Formatter/TextFormatterTest.php |   2 +-
 core/modules/text/src/Tests/TextFieldTest.php      |   7 +-
 core/modules/toolbar/toolbar.module                |   2 +-
 .../src/Plugin/tour/tip/TipPluginImage.php         |  11 +-
 core/modules/user/src/Tests/UserBlocksTest.php     |   2 +-
 .../views/src/Controller/ViewAjaxController.php    |   3 +-
 .../src/Plugin/views/style/StylePluginBase.php     |   2 +-
 .../src/Tests/Entity/RowEntityRenderersTest.php    |   2 +-
 .../views/src/Tests/Handler/AreaEntityTest.php     |  10 +-
 core/modules/views/src/Tests/Handler/AreaTest.php  |   8 +-
 .../views/src/Tests/Handler/AreaTextTest.php       |   8 +-
 .../views/src/Tests/Handler/AreaViewTest.php       |   6 +-
 .../src/Tests/Handler/FieldFieldAccessTestBase.php |   4 +-
 .../views/src/Tests/Handler/FieldGroupRowsTest.php |  24 ++-
 .../views/src/Tests/Handler/FieldUnitTest.php      | 195 ++++++++++++++++-----
 .../views/src/Tests/Handler/FieldWebTest.php       | 157 ++++++++++++-----
 core/modules/views/src/Tests/Plugin/CacheTest.php  |   9 +-
 .../modules/views/src/Tests/Plugin/DisplayTest.php |  17 +-
 .../views/src/Tests/Plugin/ExposedFormTest.php     |   2 +-
 core/modules/views/src/Tests/Plugin/PagerTest.php  |   2 +-
 .../views/src/Tests/Plugin/StyleGridTest.php       |   2 +-
 .../views/src/Tests/Plugin/StyleHtmlListTest.php   |   4 +-
 .../views/src/Tests/Plugin/StyleMappingTest.php    |   2 +-
 .../views/src/Tests/Plugin/StyleTableUnitTest.php  |   6 +-
 core/modules/views/src/Tests/Plugin/StyleTest.php  |   9 +-
 .../src/Tests/Plugin/StyleUnformattedTest.php      |   2 +-
 core/modules/views/src/Tests/ViewElementTest.php   |  12 +-
 core/modules/views/src/Tests/ViewRenderTest.php    |   2 +-
 core/modules/views/src/Tests/ViewsTemplateTest.php |   2 +-
 .../views_ui/src/Controller/ViewsUIController.php  |   2 +-
 .../views_ui/src/Tests/CustomBooleanTest.php       |   2 +-
 core/modules/views_ui/src/ViewEditForm.php         |   4 +-
 .../Tests/Core/Render/RendererBubblingTest.php     |  22 +--
 .../Drupal/Tests/Core/Render/RendererTest.php      |  22 +--
 111 files changed, 1038 insertions(+), 427 deletions(-)

diff --git a/core/core.services.yml b/core/core.services.yml
index d8a8d24..bb0626a 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1392,6 +1392,11 @@ services:
   renderer:
     class: Drupal\Core\Render\Renderer
     arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@render_cache', '%renderer.config%']
+  early_rendering_controller_wrapper:
+    class: Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapper
+    arguments: ['@controller_resolver', '@renderer']
+    tags:
+      - { name: event_subscriber }
   email.validator:
     class: Egulias\EmailValidator\EmailValidator
 
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 9337556..a68fa5a 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -195,7 +195,7 @@ function drupal_get_html_head($render = TRUE) {
   $elements = _drupal_add_html_head();
   \Drupal::moduleHandler()->alter('html_head', $elements);
   if ($render) {
-    return drupal_render($elements);
+    return \Drupal::service('renderer')->renderPlain($elements);
   }
   else {
     return $elements;
diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
index a689b76..7d36b51 100644
--- a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
+++ b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
@@ -164,15 +164,15 @@ protected function buildAttachmentsCommands(AjaxResponse $response, Request $req
     $resource_commands = array();
     if ($css_assets) {
       $css_render_array = $this->cssCollectionRenderer->render($css_assets);
-      $resource_commands[] = new AddCssCommand($this->renderer->render($css_render_array));
+      $resource_commands[] = new AddCssCommand($this->renderer->renderPlain($css_render_array));
     }
     if ($js_assets_header) {
       $js_header_render_array = $this->jsCollectionRenderer->render($js_assets_header);
-      $resource_commands[] = new PrependCommand('head', $this->renderer->render($js_header_render_array));
+      $resource_commands[] = new PrependCommand('head', $this->renderer->renderPlain($js_header_render_array));
     }
     if ($js_assets_footer) {
       $js_footer_render_array = $this->jsCollectionRenderer->render($js_assets_footer);
-      $resource_commands[] = new AppendCommand('body', $this->renderer->render($js_footer_render_array));
+      $resource_commands[] = new AppendCommand('body', $this->renderer->renderPlain($js_footer_render_array));
     }
     foreach (array_reverse($resource_commands) as $resource_command) {
       $response->addCommand($resource_command, TRUE);
diff --git a/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapper.php b/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapper.php
new file mode 100644
index 0000000..125ba49
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapper.php
@@ -0,0 +1,143 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapper.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Drupal\Core\Cache\CacheableDependencyInterface;
+use Drupal\Core\Controller\ControllerResolverInterface;
+use Drupal\Core\Render\AttachmentsInterface;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\Render\RenderContext;
+use Drupal\Core\Render\RendererInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+/**
+ * Subscribes to wrap controllers, to handle early rendering.
+ *
+ * When controllers call drupal_render() (RendererInterface::render()), we call
+ * that "early rendering". Controllers *should* return only render arrays, but
+ * we cannot prevent controllers from doing early rendering. The problem with
+ * early rendering is that the bubbleable metadata (cacheability & attachments)
+ * are lost.
+ *
+ * This can lead to broken pages (missing assets), stale pages (missing cache
+ * tags causing a page not to be invalidated) or even security problems (missing
+ * cache contexts causing a cached page not to be varied sufficiently).
+ *
+ * This event subscriber wraps all controller executions in a closure that sets
+ * up a render context. Consequently, any drupal_render() invocations during the
+ * actual controller's execution will have their bubbleable metadata (assets &
+ * cacheability) stored on that render context.
+ *
+ * If the render context is empty, then the controller either did not do any
+ * rendering at all, or used the RendererInterface::renderRoot() or
+ * ::renderPlain() methods. In that case, no bubbleable metadata is lost.
+ *
+ * If the render context is not empty, then the controller did use
+ * drupal_render(), and bubbleable metadata is lost. But, rather then letting it
+ * go lost, the closure will update the render array returned by the controller
+ * by merging the "lost" bubbleable metadata onto the render array. Disaster
+ * averted.
+ *
+ * In other words: this just exists to ease the transition to Drupal 8: it
+ * allows controllers that return render arrays (the bulk) to still do early
+ * rendering. But controllers that return responses are already expected to do
+ * the right thing: if early rendering is detected in such a case, an exception
+ * is thrown.
+ *
+ * @see \Drupal\Core\Render\RendererInterface
+ * @see \Drupal\Core\Render\Renderer
+ *
+ * @todo Remove in Drupal 9.0.0, by disallowing early rendering.
+ */
+class EarlyRenderingControllerWrapper implements EventSubscriberInterface {
+
+  /**
+   * The controller resolver.
+   *
+   * @var \Drupal\Core\Controller\ControllerResolverInterface
+   */
+  protected $controllerResolver;
+
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * Constructs a new EarlyRenderingControllerWrapper instance.
+   *
+   * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
+   *   The controller resolver.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
+   */
+  public function __construct(ControllerResolverInterface $controller_resolver, RendererInterface $renderer) {
+    $this->controllerResolver = $controller_resolver;
+    $this->renderer = $renderer;
+  }
+
+  /**
+   * Ensures bubbleable metadata from early rendering is not lost.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\FilterControllerEvent $event
+   *   The controller event.
+   */
+  public function onController(FilterControllerEvent $event) {
+    $controller = $event->getController();
+
+    // See \Symfony\Component\HttpKernel\HttpKernel::handleRaw().
+    $arguments = $this->controllerResolver->getArguments($event->getRequest(), $controller);
+
+    $event->setController(function() use ($controller, $arguments) {
+      $context = new RenderContext();
+
+      $response = $this->renderer->executeInRenderContext($context, function() use ($controller, $arguments) {
+        // Now call the actual controller, just like HttpKernel does.
+        return call_user_func_array($controller, $arguments);
+      });
+
+      // If early rendering happened, i.e. if code in the controller called
+      // drupal_render(), then the bubbleable metadata for that is stored in
+      // the current render context.
+      if (!$context->isEmpty()) {
+        if (is_array($response)) {
+          // Merging the "lost" bubbleable metadata onto the render array
+          // returned by the controller.
+          $early_rendering_bubbleable_metadata = $context->pop();
+          BubbleableMetadata::createFromRenderArray($response)
+            ->merge($early_rendering_bubbleable_metadata)
+            ->applyTo($response);
+        }
+        // If early rendering happened, yet a response object is returned, not a
+        // render array, and the response object cares about attachments or
+        // cacheability, then throw an exception: early rendering is not
+        // permitted in that case. It is the developer's responsibility to not
+        // use early rendering.
+        elseif ($response instanceof AttachmentsInterface || $response instanceof CacheableDependencyInterface) {
+          throw new \LogicException(sprintf('Early rendering is not permitted for controllers returning anything else than render arrays. Response class: %s.', get_class($response)));
+        }
+      }
+
+      return $response;
+    });
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[KernelEvents::CONTROLLER][] = ['onController'];
+
+    return $events;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Render/Element/Actions.php b/core/lib/Drupal/Core/Render/Element/Actions.php
index 754f7f8..674e229 100644
--- a/core/lib/Drupal/Core/Render/Element/Actions.php
+++ b/core/lib/Drupal/Core/Render/Element/Actions.php
@@ -94,7 +94,7 @@ public static function preRenderActionsDropbutton(&$element, FormStateInterface
         // Add this button to the corresponding dropbutton.
         // @todo Change #type 'dropbutton' to be based on item-list.html.twig
         //   instead of links.html.twig to avoid this preemptive rendering.
-        $button = drupal_render($element[$key]);
+        $button = \Drupal::service('renderer')->renderPlain($element[$key]);
         $dropbuttons[$dropbutton]['#links'][$key] = array(
           'title' => $button,
         );
diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
index cb987f4..88753b7 100644
--- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
@@ -181,7 +181,7 @@ protected function prepare(array $main_content, Request $request, RouteMatchInte
       // ::renderResponse().
       // @todo Remove this once https://www.drupal.org/node/2359901 lands.
       if (!empty($main_content)) {
-        $this->renderer->render($main_content, FALSE);
+        $this->renderer->renderPlain($main_content, FALSE);
         $main_content = $this->renderCache->getCacheableRenderArray($main_content) + [
           '#title' => isset($main_content['#title']) ? $main_content['#title'] : NULL
         ];
diff --git a/core/lib/Drupal/Core/Render/RenderContext.php b/core/lib/Drupal/Core/Render/RenderContext.php
new file mode 100644
index 0000000..e255005
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/RenderContext.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Render\RenderContext.
+ */
+
+namespace Drupal\Core\Render;
+
+/**
+ * The render context: a stack containing bubbleable rendering metadata.
+ *
+ * @see \Drupal\Core\Render\RendererInterface
+ * @see \Drupal\Core\Render\Renderer
+ * @see \Drupal\Core\Render\BubbleableMetadata
+ *
+ * @internal
+ */
+class RenderContext extends \SplStack {
+
+  /**
+   * Updates the stack.
+   *
+   * @param array &$element
+   *   The element of the render array that has just been rendered. The stack
+   *   frame for this element will be updated with the bubbleable rendering
+   *   metadata of this element.
+   */
+  public function update(&$element) {
+    // The latest frame represents the bubbleable metadata for the subtree.
+    $frame = $this->pop();
+    // Update the frame, but also update the current element, to ensure it
+    // contains up-to-date information in case it gets render cached.
+    $updated_frame = BubbleableMetadata::createFromRenderArray($element)->merge($frame);
+    $updated_frame->applyTo($element);
+    $this->push($updated_frame);
+  }
+
+  /**
+   * Bubbles the stack.
+   *
+   * Whenever another level in the render array has been rendered, the stack
+   * must be bubbled, to merge its rendering metadata with that of the parent
+   * element.
+   */
+  public function bubble() {
+    // If there's only one frame on the stack, then this is the root call, and
+    // we can't bubble up further. ::renderRoot() will reset the stack, but we
+    // must not reset it here to allow users of ::executeInRenderContext() to
+    // access the stack directly.
+    if ($this->count() === 1) {
+      return;
+    }
+
+    // Merge the current and the parent stack frame.
+    $current = $this->pop();
+    $parent = $this->pop();
+    $this->push($current->merge($parent));
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index d24b7d7..3fd99f2 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -57,11 +57,21 @@ class Renderer implements RendererInterface {
   protected $rendererConfig;
 
   /**
-   * The stack containing bubbleable rendering metadata.
+   * The render context.
    *
-   * @var \SplStack|null
+   * This must be static as long as some controllers rebuild the container
+   * during a request.
+   *
+   * @var \Drupal\Core\Render\RenderContext|null
+   */
+  protected static $context;
+
+  /**
+   * Whether we're currently in a ::renderRoot() call.
+   *
+   * @var bool
    */
-  protected static $stack;
+  protected $isRenderingRoot = FALSE;
 
   /**
    * Constructs a new Renderer.
@@ -89,18 +99,31 @@ public function __construct(ControllerResolverInterface $controller_resolver, Th
    * {@inheritdoc}
    */
   public function renderRoot(&$elements) {
-    return $this->render($elements, TRUE);
+    // Disallow calling ::renderRoot() from within another ::renderRoot() call.
+    if ($this->isRenderingRoot) {
+      $this->isRenderingRoot = FALSE;
+      throw new \LogicException('A stray renderRoot() invocation is causing bubbling of attached assets to break.');
+    }
+
+    // Render in its own render context.
+    $this->isRenderingRoot = TRUE;
+    $output = $this->executeInRenderContext(new RenderContext(), function () use (&$elements) {
+      return $this->render($elements, TRUE);
+    });
+    $this->isRenderingRoot = FALSE;
+
+    return $output;
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @todo Rename to ::renderInIsolation()
    */
-  public function renderPlain(&$elements) {
-    $current_stack = static::$stack;
-    $this->resetStack();
-    $output = $this->renderRoot($elements);
-    static::$stack = $current_stack;
-    return $output;
+  public function renderPlain(&$elements, $is_root_call = TRUE) {
+    return $this->executeInRenderContext(new RenderContext(), function () use (&$elements, $is_root_call) {
+      return $this->render($elements, $is_root_call);
+    });
   }
 
   /**
@@ -150,16 +173,17 @@ public function render(&$elements, $is_root_call = FALSE) {
     // possible that any of them throw an exception that will cause a different
     // page to be rendered (e.g. throwing
     // \Symfony\Component\HttpKernel\Exception\NotFoundHttpException will cause
-    // the 404 page to be rendered). That page might also use Renderer::render()
-    // but if exceptions aren't caught here, the stack will be left in an
-    // inconsistent state.
-    // Hence, catch all exceptions and reset the stack and re-throw them.
+    // the 404 page to be rendered). That page might also use
+    // Renderer::renderRoot() but if exceptions aren't caught here, it will be
+    // impossible to call Renderer::renderRoot() again.
+    // Hence, catch all exceptions, reset the isRenderingRoot property and
+    // re-throw exceptions.
     try {
       return $this->doRender($elements, $is_root_call);
     }
     catch (\Exception $e) {
-      // Reset stack and re-throw exception.
-      $this->resetStack();
+      // Mark the ::rootRender() call finished due to this exception & re-throw.
+      $this->isRenderingRoot = FALSE;
       throw $e;
     }
   }
@@ -185,10 +209,10 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
       return '';
     }
 
-    if (!isset(static::$stack)) {
-      static::$stack = new \SplStack();
+    if (!isset(static::$context)) {
+      throw new \LogicException("Render context is empty, because render() was called outside of a renderRoot() or renderPlain() call. Use renderPlain()/renderRoot() or #lazy_builder/#pre_render instead.");
     }
-    static::$stack->push(new BubbleableMetadata());
+    static::$context->push(new BubbleableMetadata());
 
     // Set the bubbleable rendering metadata that has configurable defaults, if:
     // - this is the root call, to ensure that the final render array definitely
@@ -229,10 +253,10 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
         }
         // The render cache item contains all the bubbleable rendering metadata
         // for the subtree.
-        $this->updateStack($elements);
+        static::$context->update($elements);
         // Render cache hit, so rendering is finished, all necessary info
         // collected!
-        $this->bubbleStack();
+        static::$context->bubble();
         return $elements['#markup'];
       }
     }
@@ -330,9 +354,9 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     if (!empty($elements['#printed'])) {
       // The #printed element contains all the bubbleable rendering metadata for
       // the subtree.
-      $this->updateStack($elements);
+      static::$context->update($elements);
       // #printed, so rendering is finished, all necessary info collected!
-      $this->bubbleStack();
+      static::$context->bubble();
       return '';
     }
 
@@ -458,8 +482,8 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
 
     $elements['#markup'] = $prefix . $elements['#children'] . $suffix;
 
-    // We've rendered this element (and its subtree!), now update the stack.
-    $this->updateStack($elements);
+    // We've rendered this element (and its subtree!), now update the context.
+    static::$context->update($elements);
 
     // Cache the processed element if both $pre_bubbling_elements and $elements
     // have the metadata necessary to generate a cache ID.
@@ -481,13 +505,13 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     // that is handled earlier in Renderer::render().
     if ($is_root_call) {
       $this->replacePlaceholders($elements);
-      if (static::$stack->count() !== 1) {
+      if (static::$context->count() !== 1) {
         throw new \LogicException('A stray drupal_render() invocation with $is_root_call = TRUE is causing bubbling of attached assets to break.');
       }
     }
 
     // Rendering is finished, all necessary info collected!
-    $this->bubbleStack();
+    static::$context->bubble();
 
     $elements['#printed'] = TRUE;
     $elements['#markup'] = SafeMarkup::set($elements['#markup']);
@@ -495,52 +519,24 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
   }
 
   /**
-   * Resets the renderer service's internal stack (used for bubbling metadata).
-   *
-   * Only necessary in very rare/advanced situations, such as when rendering an
-   * error page if an exception occurred *during* rendering.
-   */
-  protected function resetStack() {
-    static::$stack = NULL;
-  }
-
-  /**
-   * Updates the stack.
-   *
-   * @param array &$element
-   *   The element of the render array that has just been rendered. The stack
-   *   frame for this element will be updated with the bubbleable rendering
-   *   metadata of this element.
-   */
-  protected function updateStack(&$element) {
-    // The latest frame represents the bubbleable metadata for the subtree.
-    $frame = static::$stack->pop();
-    // Update the frame, but also update the current element, to ensure it
-    // contains up-to-date information in case it gets render cached.
-    $updated_frame = BubbleableMetadata::createFromRenderArray($element)->merge($frame);
-    $updated_frame->applyTo($element);
-    static::$stack->push($updated_frame);
-  }
-
-  /**
-   * Bubbles the stack.
-   *
-   * Whenever another level in the render array has been rendered, the stack
-   * must be bubbled, to merge its rendering metadata with that of the parent
-   * element.
+   * {@inheritdoc}
    */
-  protected function bubbleStack() {
-    // If there's only one frame on the stack, then this is the root call, and
-    // we can't bubble up further. Reset the stack for the next root call.
-    if (static::$stack->count() === 1) {
-      $this->resetStack();
-      return;
+  public function executeInRenderContext(RenderContext $context, callable $callable) {
+    // Store the current render context.
+    $current_context = static::$context;
+
+    // Set the provided context and call the callable, it will use that context.
+    static::$context = $context;
+    $result = $callable();
+    // @todo Convert to an assertion in https://www.drupal.org/node/2408013
+    if (static::$context->count() > 1) {
+      throw new \LogicException('Bubbling failed.');
     }
 
-    // Merge the current and the parent stack frame.
-    $current = static::$stack->pop();
-    $parent = static::$stack->pop();
-    static::$stack->push($current->merge($parent));
+    // Restore the original render context.
+    static::$context = $current_context;
+
+    return $result;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Render/RendererInterface.php b/core/lib/Drupal/Core/Render/RendererInterface.php
index 5d59d4d..1e420b1 100644
--- a/core/lib/Drupal/Core/Render/RendererInterface.php
+++ b/core/lib/Drupal/Core/Render/RendererInterface.php
@@ -22,6 +22,8 @@
    * - system internals that are responsible for rendering the final HTML
    * - render arrays for non-HTML responses, such as feeds
    *
+   * (Cannot be executed within another render context.)
+   *
    * @param array $elements
    *   The structured array describing the data to be rendered.
    *
@@ -45,9 +47,11 @@ public function renderRoot(&$elements);
    * ::renderRoot() call, but that is generally highly problematic (and hence an
    * exception is thrown when a ::renderRoot() call happens within another
    * ::renderRoot() call). However, in this case, we only care about the output,
-   * not about the bubbling. Hence this uses a separate render stack, to not
+   * not about the bubbling. Hence this uses a separate render context, to not
    * affect the parent ::renderRoot() call.
    *
+   * (Can be executed within another render context: it runs in isolation.)
+   *
    * @param array $elements
    *   The structured array describing the data to be rendered.
    *
@@ -88,8 +92,8 @@ public function renderPlain(&$elements);
    *   or configuration that can affect that rendering changes.
    * - Placeholders, with associated self-contained placeholder render arrays,
    *   for executing code to handle dynamic requirements that cannot be cached.
-   * A stack of \Drupal\Core\Render\BubbleableMetadata objects can be used to
-   * perform this bubbling.
+   * A render context (\Drupal\Core\Render\RenderContext) can be used to perform
+   * bubbling; it is a stack of \Drupal\Core\Render\BubbleableMetadata objects.
    *
    * Additionally, whether retrieving from cache or not, it is important to
    * know all of the assets (CSS and JavaScript) required by the rendered HTML,
@@ -103,9 +107,9 @@ public function renderPlain(&$elements);
    *   - If this element has already been printed (#printed = TRUE) or the user
    *     does not have access to it (#access = FALSE), then an empty string is
    *     returned.
-   *   - If no stack data structure has been created yet, it is done now. Next,
+   *   - If no render context is set yet, an exception is thrown. Otherwise,
    *     an empty \Drupal\Core\Render\BubbleableMetadata is pushed onto the
-   *     stack.
+   *     render context.
    *   - If this element has #cache defined then the cached markup for this
    *     element will be returned if it exists in Renderer::render()'s cache. To
    *     use Renderer::render() caching, set the element's #cache property to an
@@ -316,6 +320,35 @@ public function renderPlain(&$elements);
   public function render(&$elements, $is_root_call = FALSE);
 
   /**
+   * Executes a callable within a render context.
+   *
+   * Only for very advanced use cases. Prefer to use ::renderRoot() and
+   * ::renderPlain() instead.
+   *
+   * All rendering must happen within a render context: within a render context,
+   * all bubbleable metadata is bubbled and hence tracked, without a render
+   * context, it would be lost. This could lead to missing assets, incorrect
+   * cache variations (and thus security issues), insufficient cache
+   * invalidations, and so on.
+   *
+   * Any and all rendering must therefore happen within a render context, and it
+   * is this method that provides that.
+   *
+   * @see \Drupal\Core\Render\BubbleableMetadata
+   *
+   * @param \Drupal\Core\Render\RenderContext $context
+   *   The render context to execute the callable within.
+   * @param callable $callable
+   *   The callable to execute.
+   * @return mixed
+   *   The callable's return value.
+   *
+   * @see \Drupal\Core\Render\RenderContext
+   * @throws \LogicException
+   */
+  public function executeInRenderContext(RenderContext $context, callable $callable);
+
+  /**
    * Merges the bubbleable rendering metadata o/t 2nd render array with the 1st.
    *
    * @param array $a
diff --git a/core/modules/block/src/Tests/BlockViewBuilderTest.php b/core/modules/block/src/Tests/BlockViewBuilderTest.php
index 7a7aa64..ccb53e4 100644
--- a/core/modules/block/src/Tests/BlockViewBuilderTest.php
+++ b/core/modules/block/src/Tests/BlockViewBuilderTest.php
@@ -44,6 +44,13 @@ class BlockViewBuilderTest extends KernelTestBase {
   protected $controller;
 
   /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
@@ -64,6 +71,8 @@ protected function setUp() {
     $this->block->save();
 
     $this->container->get('cache.render')->deleteAll();
+
+    $this->renderer = $this->container->get('renderer');
   }
 
   /**
@@ -90,7 +99,7 @@ public function testBasicRendering() {
     $expected[] = '  </div>';
     $expected[] = '';
     $expected_output = implode("\n", $expected);
-    $this->assertEqual(drupal_render($output), $expected_output);
+    $this->assertEqual($this->renderer->renderRoot($output), $expected_output);
 
     // Reset the HTML IDs so that the next render is not affected.
     Html::resetSeenIds();
@@ -115,7 +124,7 @@ public function testBasicRendering() {
     $expected[] = '  </div>';
     $expected[] = '';
     $expected_output = implode("\n", $expected);
-    $this->assertEqual(drupal_render($output), $expected_output);
+    $this->assertEqual($this->renderer->renderRoot($output), $expected_output);
   }
 
   /**
@@ -144,7 +153,7 @@ public function testBlockViewBuilderCache() {
    * @see ::testBlockViewBuilderCache()
    */
   protected function verifyRenderCacheHandling() {
-    // Force a request via GET so we can get drupal_render() cache working.
+    // Force a request via GET so we can test the render cache.
     $request = \Drupal::request();
     $request_method = $request->server->get('REQUEST_METHOD');
     $request->setMethod('GET');
@@ -152,7 +161,7 @@ protected function verifyRenderCacheHandling() {
     // Test that a cache entry is created.
     $build = $this->getBlockRenderArray();
     $cid = 'entity_view:block:test_block:en:core';
-    drupal_render($build);
+    $this->renderer->renderRoot($build);
     $this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
 
     // Re-save the block and check that the cache entry has been deleted.
@@ -166,7 +175,7 @@ protected function verifyRenderCacheHandling() {
     // removes it.
     $build['#block'] = $this->block;
 
-    drupal_render($build);
+    $this->renderer->renderRoot($build);
     $this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
     $this->block->delete();
     $this->assertFalse($this->container->get('cache.render')->get($cid), 'The block render cache entry has been cleared when the block was deleted.');
@@ -181,17 +190,17 @@ protected function verifyRenderCacheHandling() {
   public function testBlockViewBuilderAlter() {
     // Establish baseline.
     $build = $this->getBlockRenderArray();
-    $this->assertIdentical(drupal_render($build), 'Llamas &gt; unicorns!');
+    $this->assertIdentical($this->renderer->renderRoot($build), 'Llamas &gt; unicorns!');
 
     // Enable the block view alter hook that adds a suffix, for basic testing.
     \Drupal::state()->set('block_test_view_alter_suffix', TRUE);
     Cache::invalidateTags($this->block->getCacheTags());
     $build = $this->getBlockRenderArray();
     $this->assertTrue(isset($build['#suffix']) && $build['#suffix'] === '<br>Goodbye!', 'A block with content is altered.');
-    $this->assertIdentical(drupal_render($build), 'Llamas &gt; unicorns!<br>Goodbye!');
+    $this->assertIdentical($this->renderer->renderRoot($build), 'Llamas &gt; unicorns!<br>Goodbye!');
     \Drupal::state()->set('block_test_view_alter_suffix', FALSE);
 
-    // Force a request via GET so we can get drupal_render() cache working.
+    // Force a request via GET so we can test the render cache.
     $request = \Drupal::request();
     $request_method = $request->server->get('REQUEST_METHOD');
     $request->setMethod('GET');
@@ -209,7 +218,7 @@ public function testBlockViewBuilderAlter() {
     $expected_keys = array_merge($default_keys, array($alter_add_key));
     $build = $this->getBlockRenderArray();
     $this->assertIdentical($expected_keys, $build['#cache']['keys'], 'An altered cacheable block has the expected cache keys.');
-    $this->assertIdentical(drupal_render($build), '');
+    $this->assertIdentical($this->renderer->renderRoot($build), '');
     $cache_entry = $this->container->get('cache.render')->get($cid);
     $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
     $expected_tags = array_merge($default_tags, ['rendered']);
@@ -224,7 +233,7 @@ public function testBlockViewBuilderAlter() {
     $build = $this->getBlockRenderArray();
     sort($build['#cache']['tags']);
     $this->assertIdentical($expected_tags, $build['#cache']['tags'], 'An altered cacheable block has the expected cache tags.');
-    $this->assertIdentical(drupal_render($build), '');
+    $this->assertIdentical($this->renderer->renderRoot($build), '');
     $cache_entry = $this->container->get('cache.render')->get($cid);
     $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
     $expected_tags = array_merge($default_tags, [$alter_add_tag, 'rendered']);
@@ -236,8 +245,8 @@ public function testBlockViewBuilderAlter() {
     // alter the eventual content.
     \Drupal::state()->set('block_test_view_alter_append_pre_render_prefix', TRUE);
     $build = $this->getBlockRenderArray();
-    $this->assertFalse(isset($build['#prefix']), 'The appended #pre_render callback has not yet run before calling drupal_render().');
-    $this->assertIdentical(drupal_render($build), 'Hiya!<br>');
+    $this->assertFalse(isset($build['#prefix']), 'The appended #pre_render callback has not yet run before rendering.');
+    $this->assertIdentical($this->renderer->renderRoot($build), 'Hiya!<br>');
     $this->assertTrue(isset($build['#prefix']) && $build['#prefix'] === 'Hiya!<br>', 'A cached block without content is altered.');
 
     // Restore the previous request method.
diff --git a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
index 582d832..fe2c789 100644
--- a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
+++ b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
@@ -84,7 +84,7 @@ public function testBlock() {
     //   Drupal\Core\Render\Renderer.
     $request_stack = $this->container->get('request_stack');
     $request_stack->push(new Request());
-    $this->container->get('renderer')->render($build);
+    $this->container->get('renderer')->renderRoot($build);
     $request_stack->pop();
 
     // Expected keys, contexts, and tags for the block.
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index f7ee374..e80b5e3 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -71,7 +71,7 @@ function book_theme() {
       'render element' => 'book_menus',
     ),
     'book_node_export_html' => array(
-      'variables' => array('node' => NULL, 'children' => NULL),
+      'variables' => array('node' => NULL, 'content' => NULL, 'children' => NULL),
     ),
   );
 }
@@ -234,9 +234,9 @@ function book_node_view(array &$build, EntityInterface $node, EntityViewDisplayI
       if (!$book_node->access()) {
         return;
       }
-      $book_navigation = array( '#theme' => 'book_navigation', '#book_link' => $node->book);
       $build['book_navigation'] = array(
-        '#markup' => drupal_render($book_navigation),
+        '#theme' => 'book_navigation',
+        '#book_link' => $node->book,
         '#weight' => 100,
         // The book navigation is a listing of Node entities, so associate its
         // list cache tag for correct invalidation.
@@ -491,7 +491,6 @@ function template_preprocess_book_export_html(&$variables) {
 function template_preprocess_book_node_export_html(&$variables) {
   $variables['depth'] = $variables['node']->book['depth'];
   $variables['title'] = SafeMarkup::checkPlain($variables['node']->label());
-  $variables['content'] = $variables['node']->rendered;
 }
 
 /**
diff --git a/core/modules/book/book.services.yml b/core/modules/book/book.services.yml
index 3b45dae..0a022a7 100644
--- a/core/modules/book/book.services.yml
+++ b/core/modules/book/book.services.yml
@@ -12,7 +12,7 @@ services:
     arguments: ['@book.manager']
   book.export:
     class: Drupal\book\BookExport
-    arguments: ['@entity.manager', '@book.manager']
+    arguments: ['@entity.manager', '@book.manager', '@renderer']
   book.outline_storage:
     class: Drupal\book\BookOutlineStorage
     arguments: ['@database']
diff --git a/core/modules/book/src/BookExport.php b/core/modules/book/src/BookExport.php
index b71c7a0..dbb62e9 100644
--- a/core/modules/book/src/BookExport.php
+++ b/core/modules/book/src/BookExport.php
@@ -8,6 +8,7 @@
 namespace Drupal\book;
 
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\node\Entity\Node;
 use Drupal\node\NodeInterface;
 
@@ -118,7 +119,7 @@ protected function exportTraverse(array $tree, $callable) {
       }
     }
 
-    return drupal_render($build);
+    return $build;
   }
 
   /**
@@ -139,10 +140,9 @@ protected function bookNodeExport(NodeInterface $node, $children = '') {
     $build = $this->viewBuilder->view($node, 'print', NULL);
     unset($build['#theme']);
 
-    // @todo Rendering should happen in the template using render().
-    $node->rendered = drupal_render($build);
     return array(
       '#theme' => 'book_node_export_html',
+      '#content' => $build,
       '#node' => $node,
       '#children' => $children,
     );
diff --git a/core/modules/book/src/BookOutline.php b/core/modules/book/src/BookOutline.php
index 484fd2c..c4b4533 100644
--- a/core/modules/book/src/BookOutline.php
+++ b/core/modules/book/src/BookOutline.php
@@ -126,8 +126,7 @@ public function childrenLinks(array $book_link) {
     }
 
     if ($children) {
-      $elements = $this->bookManager->bookTreeOutput($children);
-      return drupal_render($elements);
+      return $this->bookManager->bookTreeOutput($children);
     }
     return '';
   }
diff --git a/core/modules/book/src/Controller/BookController.php b/core/modules/book/src/Controller/BookController.php
index 2f89937..53c64ad 100644
--- a/core/modules/book/src/Controller/BookController.php
+++ b/core/modules/book/src/Controller/BookController.php
@@ -10,6 +10,7 @@
 use Drupal\book\BookExport;
 use Drupal\book\BookManagerInterface;
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Url;
 use Drupal\node\Entity\Node;
 use Drupal\node\NodeInterface;
@@ -38,16 +39,26 @@ class BookController extends ControllerBase {
   protected $bookExport;
 
   /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
    * Constructs a BookController object.
    *
    * @param \Drupal\book\BookManagerInterface $bookManager
    *   The book manager.
    * @param \Drupal\book\BookExport $bookExport
    *   The book export service.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
    */
-  public function __construct(BookManagerInterface $bookManager, BookExport $bookExport) {
+  public function __construct(BookManagerInterface $bookManager, BookExport $bookExport, RendererInterface $renderer) {
     $this->bookManager = $bookManager;
     $this->bookExport = $bookExport;
+    $this->renderer = $renderer;
   }
 
   /**
@@ -56,7 +67,8 @@ public function __construct(BookManagerInterface $bookManager, BookExport $bookE
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('book.manager'),
-      $container->get('book.export')
+      $container->get('book.export'),
+      $container->get('renderer')
     );
   }
 
@@ -154,7 +166,7 @@ public function bookExport($type, NodeInterface $node) {
     }
 
     $exported_book = $this->bookExport->{$method}($node);
-    return new Response(drupal_render($exported_book));
+    return new Response($this->renderer->renderRoot($exported_book));
   }
 
 }
diff --git a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
index 949c8e5..0e237b1 100644
--- a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
+++ b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\editor\Plugin\EditorBase;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\editor\Entity\Editor as EditorEntity;
@@ -56,6 +57,13 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
   protected $ckeditorPluginManager;
 
   /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
    * Constructs a Drupal\Component\Plugin\PluginBase object.
    *
    * @param array $configuration
@@ -70,12 +78,15 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
    *   The module handler to invoke hooks on.
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, CKEditorPluginManager $ckeditor_plugin_manager, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, CKEditorPluginManager $ckeditor_plugin_manager, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, RendererInterface $renderer) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
     $this->ckeditorPluginManager = $ckeditor_plugin_manager;
     $this->moduleHandler = $module_handler;
     $this->languageManager = $language_manager;
+    $this->renderer = $renderer;
   }
 
   /**
@@ -88,7 +99,8 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_definition,
       $container->get('plugin.manager.ckeditor.plugin'),
       $container->get('module_handler'),
-      $container->get('language_manager')
+      $container->get('language_manager'),
+      $container->get('renderer')
     );
   }
 
@@ -145,7 +157,7 @@ public function settingsForm(array $form, FormStateInterface $form_state, Editor
         'library' => array('ckeditor/drupal.ckeditor.admin'),
         'drupalSettings' => [
           'ckeditor' => [
-            'toolbarAdmin' => drupal_render($ckeditor_settings_toolbar),
+            'toolbarAdmin' => $this->renderer->renderPlain($ckeditor_settings_toolbar),
           ],
         ],
       ),
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 319067d..8bcac2c 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -462,7 +462,7 @@ function comment_node_update_index(EntityInterface $node, $langcode) {
       }
     }
   }
-  return drupal_render($build);
+  return \Drupal::service('renderer')->renderPlain($build);
 }
 
 /**
diff --git a/core/modules/comment/src/Plugin/Action/UnpublishByKeywordComment.php b/core/modules/comment/src/Plugin/Action/UnpublishByKeywordComment.php
index 56706ad..6b70450 100644
--- a/core/modules/comment/src/Plugin/Action/UnpublishByKeywordComment.php
+++ b/core/modules/comment/src/Plugin/Action/UnpublishByKeywordComment.php
@@ -28,7 +28,7 @@ class UnpublishByKeywordComment extends ConfigurableActionBase {
    */
   public function execute($comment = NULL) {
     $build = comment_view($comment);
-    $text = drupal_render($build);
+    $text = \Drupal::service('renderer')->renderPlain($build);
     foreach ($this->configuration['keywords'] as $keyword) {
       if (strpos($text, $keyword) !== FALSE) {
         $comment->setPublished(FALSE);
diff --git a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
index 342765f..8e7bf27 100644
--- a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
@@ -57,6 +57,9 @@ protected function setUp() {
    * Tests the bubbling of cache tags.
    */
   public function testCacheTags() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     // Create the entity that will be commented upon.
     $commented_entity = entity_create('entity_test', array('name' => $this->randomMachineName()));
     $commented_entity->save();
@@ -65,11 +68,16 @@ public function testCacheTags() {
     $build = \Drupal::entityManager()
       ->getViewBuilder('entity_test')
       ->view($commented_entity);
-    drupal_render($build);
+    $renderer->renderRoot($build);
     $expected_cache_tags = array(
       'entity_test_view',
       'entity_test:'  . $commented_entity->id(),
       'comment_list',
+      'config:core.entity_form_display.comment.comment.default',
+      'config:field.field.comment.comment.comment_body',
+      'config:field.field.entity_test.entity_test.comment',
+      'config:field.storage.comment.comment_body',
+      'config:user.settings',
     );
     sort($expected_cache_tags);
     $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags before it has comments.');
@@ -102,7 +110,7 @@ public function testCacheTags() {
     $build = \Drupal::entityManager()
       ->getViewBuilder('entity_test')
       ->view($commented_entity);
-    drupal_render($build);
+    $renderer->renderRoot($build);
     $expected_cache_tags = array(
       'entity_test_view',
       'entity_test:' . $commented_entity->id(),
@@ -112,6 +120,11 @@ public function testCacheTags() {
       'config:filter.format.plain_text',
       'user_view',
       'user:2',
+      'config:core.entity_form_display.comment.comment.default',
+      'config:field.field.comment.comment.comment_body',
+      'config:field.field.entity_test.entity_test.comment',
+      'config:field.storage.comment.comment_body',
+      'config:user.settings',
     );
     sort($expected_cache_tags);
     $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags when it has comments.');
diff --git a/core/modules/config/src/Form/ConfigSync.php b/core/modules/config/src/Form/ConfigSync.php
index 2c71254..7f240e3 100644
--- a/core/modules/config/src/Form/ConfigSync.php
+++ b/core/modules/config/src/Form/ConfigSync.php
@@ -21,6 +21,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Lock\LockBackendInterface;
 use Drupal\Core\Config\StorageComparer;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Url;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -101,6 +102,13 @@ class ConfigSync extends FormBase {
   protected $moduleInstaller;
 
   /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
    * Constructs the object.
    *
    * @param \Drupal\Core\Config\StorageInterface $staging_storage
@@ -123,8 +131,10 @@ class ConfigSync extends FormBase {
    *   The module installer.
    * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
    *   The theme handler.
+   * @param \Drupal\Core\Render\RendererInterface
+   *   The renderer.
    */
-  public function __construct(StorageInterface $staging_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, ThemeHandlerInterface $theme_handler) {
+  public function __construct(StorageInterface $staging_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, ThemeHandlerInterface $theme_handler, RendererInterface $renderer) {
     $this->stagingStorage = $staging_storage;
     $this->activeStorage = $active_storage;
     $this->snapshotStorage = $snapshot_storage;
@@ -135,6 +145,7 @@ public function __construct(StorageInterface $staging_storage, StorageInterface
     $this->moduleHandler = $module_handler;
     $this->moduleInstaller = $module_installer;
     $this->themeHandler = $theme_handler;
+    $this->renderer = $renderer;
   }
 
   /**
@@ -151,7 +162,8 @@ public static function create(ContainerInterface $container) {
       $container->get('config.typed'),
       $container->get('module_handler'),
       $container->get('module_installer'),
-      $container->get('theme_handler')
+      $container->get('theme_handler'),
+      $container->get('renderer')
     );
   }
 
@@ -209,7 +221,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
           '#theme' => 'item_list',
           '#items' => $change_list,
         );
-        $change_list_html = drupal_render($change_list_render);
+        $change_list_html = $this->renderer->renderPlain($change_list_render);
         drupal_set_message($this->t('The following items in your active configuration have changes since the last import that may be lost on the next import. !changes', array('!changes' => $change_list_html)), 'warning');
       }
     }
diff --git a/core/modules/contact/src/MessageViewBuilder.php b/core/modules/contact/src/MessageViewBuilder.php
index 5b94582..a086f7a 100644
--- a/core/modules/contact/src/MessageViewBuilder.php
+++ b/core/modules/contact/src/MessageViewBuilder.php
@@ -65,9 +65,9 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N
           $build[$key]['#label_display'] = 'hidden';
         }
       }
-      $build = array(
-        '#markup' => MailFormatHelper::htmlToText(drupal_render($build)),
-      );
+      $build['#post_render'][] = function ($html, array $elements) {
+        return MailFormatHelper::htmlToText($html);
+      };
     }
     return $build;
   }
diff --git a/core/modules/contextual/src/ContextualController.php b/core/modules/contextual/src/ContextualController.php
index aeee4a6..975113d 100644
--- a/core/modules/contextual/src/ContextualController.php
+++ b/core/modules/contextual/src/ContextualController.php
@@ -44,7 +44,7 @@ public function render(Request $request) {
         '#type' => 'contextual_links',
         '#contextual_links' => _contextual_id_to_links($id),
       );
-      $rendered[$id] = drupal_render($element);
+      $rendered[$id] = $this->container->get('renderer')->renderRoot($element);
     }
 
     return new JsonResponse($rendered);
diff --git a/core/modules/datetime/src/Tests/DateTimeFieldTest.php b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
index f3fcae1..c85f71f 100644
--- a/core/modules/datetime/src/Tests/DateTimeFieldTest.php
+++ b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
@@ -490,7 +490,7 @@ protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) {
     $entity = entity_load('entity_test', $id);
     $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode);
     $build = $display->build($entity);
-    $output = drupal_render($build);
+    $output = \Drupal::service('renderer')->renderRoot($build);
     $this->setRawContent($output);
     $this->verbose($output);
   }
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 88ccff9..d09049e 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -121,7 +121,7 @@ function field_help($route_name, RouteMatchInterface $route_match) {
           '#theme' => 'item_list',
           '#items' => $items,
         );
-        $output .= drupal_render($item_list);
+        $output .= \Drupal::service('renderer')->renderPlain($item_list);
         $output .= '</dd>';
       }
 
diff --git a/core/modules/field/src/Tests/Boolean/BooleanFieldTest.php b/core/modules/field/src/Tests/Boolean/BooleanFieldTest.php
index 647180c..2364a8a 100644
--- a/core/modules/field/src/Tests/Boolean/BooleanFieldTest.php
+++ b/core/modules/field/src/Tests/Boolean/BooleanFieldTest.php
@@ -114,7 +114,7 @@ function testBooleanField() {
     $entity = entity_load('entity_test', $id);
     $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
     $content = $display->build($entity);
-    $this->setRawContent(drupal_render($content));
+    $this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
     $this->assertRaw('<div class="field-item">' . $on . '</div>');
 
     // Test if we can change the on label.
diff --git a/core/modules/field/src/Tests/Email/EmailFieldTest.php b/core/modules/field/src/Tests/Email/EmailFieldTest.php
index 169d067..b916ed0 100644
--- a/core/modules/field/src/Tests/Email/EmailFieldTest.php
+++ b/core/modules/field/src/Tests/Email/EmailFieldTest.php
@@ -102,7 +102,7 @@ function testEmailField() {
     $entity = entity_load('entity_test', $id);
     $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
     $content = $display->build($entity);
-    $this->setRawContent(drupal_render($content));
+    $this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
     $this->assertLinkByHref('mailto:test@example.com');
   }
 
diff --git a/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php b/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php
index 52fdc08..6f04ba2 100644
--- a/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php
+++ b/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php
@@ -179,6 +179,8 @@ public function testIdFormatter() {
    * Tests the entity formatter.
    */
   public function testEntityFormatter() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $formatter = 'entity_reference_entity_view';
     $build = $this->buildRenderArray([$this->referencedEntity, $this->unsavedReferencedEntity], $formatter);
 
@@ -196,7 +198,7 @@ public function testEntityFormatter() {
       </div>
 </div>
 ';
-    drupal_render($build[0]);
+    $renderer->renderRoot($build[0]);
     $this->assertEqual($build[0]['#markup'], 'default | ' . $this->referencedEntity->label() .  $expected_rendered_name_field_1 . $expected_rendered_body_field_1, sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
     $expected_cache_tags = Cache::mergeTags(
       \Drupal::entityManager()->getViewBuilder($this->entityType)->getCacheTags(),
@@ -206,7 +208,7 @@ public function testEntityFormatter() {
     $this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags.', array('@formatter' => $formatter)));
 
     // Test the second field item.
-    drupal_render($build[1]);
+    $renderer->renderRoot($build[1]);
     $this->assertEqual($build[1]['#markup'], $this->unsavedReferencedEntity->label(), sprintf('The markup returned by the %s formatter is correct for an item with a unsaved entity.', $formatter));
   }
 
@@ -214,6 +216,8 @@ public function testEntityFormatter() {
    * Tests the label formatter.
    */
   public function testLabelFormatter() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $formatter = 'entity_reference_label';
 
     // The 'link' settings is TRUE by default.
@@ -237,7 +241,7 @@ public function testLabelFormatter() {
         'tags' => $this->referencedEntity->getCacheTags(),
       ),
     );
-    $this->assertEqual(drupal_render($build[0]), drupal_render($expected_item_1), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
+    $this->assertEqual($renderer->renderRoot($build[0]), $renderer->renderRoot($expected_item_1), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
     $this->assertEqual(CacheableMetadata::createFromRenderArray($build[0]), CacheableMetadata::createFromRenderArray($expected_item_1));
 
     // The second referenced entity is "autocreated", therefore not saved and
diff --git a/core/modules/field/src/Tests/String/StringFieldTest.php b/core/modules/field/src/Tests/String/StringFieldTest.php
index a097ec5..4eedca8 100644
--- a/core/modules/field/src/Tests/String/StringFieldTest.php
+++ b/core/modules/field/src/Tests/String/StringFieldTest.php
@@ -97,7 +97,7 @@ function _testTextfieldWidgets($field_type, $widget_type) {
     $entity = entity_load('entity_test', $id);
     $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
     $content = $display->build($entity);
-    $this->setRawContent(drupal_render($content));
+    $this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
     $this->assertText($value, 'Filtered tags are not displayed');
   }
 }
diff --git a/core/modules/field_ui/src/Tests/ManageDisplayTest.php b/core/modules/field_ui/src/Tests/ManageDisplayTest.php
index 25294c4..04a353f 100644
--- a/core/modules/field_ui/src/Tests/ManageDisplayTest.php
+++ b/core/modules/field_ui/src/Tests/ManageDisplayTest.php
@@ -454,7 +454,7 @@ function assertNodeViewTextHelper(EntityInterface $node, $view_mode, $text, $mes
     // Render a cloned node, so that we do not alter the original.
     $clone = clone $node;
     $element = node_view($clone, $view_mode);
-    $output = drupal_render($element);
+    $output = \Drupal::service('renderer')->renderRoot($element);
     $this->verbose(t('Rendered node - view mode: @view_mode', array('@view_mode' => $view_mode)) . '<hr />'. $output);
 
     // Assign content so that WebTestBase functions can be used.
diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index 782ae70..f09760f 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -72,7 +72,7 @@ function template_preprocess_file_widget_multiple(&$variables) {
     // Save the uploading row for last.
     if (empty($widget['#files'])) {
       $widget['#title'] = $element['#file_upload_title'];
-      $widget['#description'] = drupal_render($element['#file_upload_description']);
+      $widget['#description'] = \Drupal::service('renderer')->renderPlain($element['#file_upload_description']);
       continue;
     }
 
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 9f0074f..01b4ddb 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -861,7 +861,7 @@ function file_save_upload($form_field_name, $validators = array(), $destination
       );
       // @todo Add support for render arrays in drupal_set_message()? See
       //  https://www.drupal.org/node/2505497.
-      drupal_set_message(\Drupal::service('renderer')->render($message), 'error');
+      drupal_set_message(\Drupal::service('renderer')->renderPlain($message), 'error');
       $files[$i] = FALSE;
       continue;
     }
diff --git a/core/modules/file/src/Controller/FileWidgetAjaxController.php b/core/modules/file/src/Controller/FileWidgetAjaxController.php
index 90176f8..7439c02 100644
--- a/core/modules/file/src/Controller/FileWidgetAjaxController.php
+++ b/core/modules/file/src/Controller/FileWidgetAjaxController.php
@@ -39,7 +39,7 @@ public function upload(Request $request) {
       drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', array('@size' => format_size(file_upload_max_size()))), 'error');
       $response = new AjaxResponse();
       $status_messages = array('#type' => 'status_messages');
-      return $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($status_messages)));
+      return $response->addCommand(new ReplaceCommand(NULL, $status_messages));
     }
 
     try {
@@ -54,7 +54,7 @@ public function upload(Request $request) {
       drupal_set_message(t('An unrecoverable error occurred. Use of this form has expired. Try reloading the page and submitting again.'), 'error');
       $response = new AjaxResponse();
       $status_messages = array('#type' => 'status_messages');
-      return $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($status_messages)));
+      return $response->addCommand(new ReplaceCommand(NULL, $status_messages));
     }
 
     // Get the current element and count the number of files.
@@ -78,14 +78,12 @@ public function upload(Request $request) {
 
     $status_messages = array('#type' => 'status_messages');
     $form['#prefix'] .= $this->renderer->renderRoot($status_messages);
-    $output = $this->renderer->renderRoot($form);
 
     $response = new AjaxResponse();
-    $response->setAttachments($form['#attached']);
     foreach ($commands as $command) {
       $response->addCommand($command, TRUE);
     }
-    return $response->addCommand(new ReplaceCommand(NULL, $output));
+    return $response->addCommand(new ReplaceCommand(NULL, $form));
   }
 
   /**
diff --git a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
index da48f74..6cc3fd6 100644
--- a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
+++ b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
@@ -267,7 +267,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
         '#upload_validators' => $element['#upload_validators'],
         '#cardinality' => $cardinality,
       );
-      $element['#description'] = drupal_render($file_upload_help);
+      $element['#description'] = \Drupal::service('renderer')->renderPlain($file_upload_help);
       $element['#multiple'] = $cardinality != 1 ? TRUE : FALSE;
       if ($cardinality != 1 && $cardinality != -1) {
         $element['#element_validate'] = array(array(get_class($this), 'validateMultipleCount'));
diff --git a/core/modules/file/src/Tests/FileFieldDisplayTest.php b/core/modules/file/src/Tests/FileFieldDisplayTest.php
index 900374c..12193e3 100644
--- a/core/modules/file/src/Tests/FileFieldDisplayTest.php
+++ b/core/modules/file/src/Tests/FileFieldDisplayTest.php
@@ -62,7 +62,7 @@ function testNodeDisplay() {
       '#theme' => 'file_link',
       '#file' => $node_file,
     );
-    $default_output = drupal_render($file_link);
+    $default_output = \Drupal::service('renderer')->renderRoot($file_link);
     $this->assertRaw($default_output, 'Default formatter displaying correctly on full node view.');
 
     // Turn the "display" option off and check that the file is no longer displayed.
diff --git a/core/modules/file/src/Tests/FileItemTest.php b/core/modules/file/src/Tests/FileItemTest.php
index 2c5c11c..140b00c 100644
--- a/core/modules/file/src/Tests/FileItemTest.php
+++ b/core/modules/file/src/Tests/FileItemTest.php
@@ -117,7 +117,7 @@ public function testFileItem() {
     $entity->file_test = array('entity' => $file3);
     $uri = $file3->getFileUri();
     $output = entity_view($entity, 'default');
-    drupal_render($output);
+    \Drupal::service('renderer')->renderRoot($output);
     $this->assertTrue(!empty($entity->file_test->entity));
     $this->assertEqual($entity->file_test->entity->getFileUri(), $uri);
   }
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index eb25bf8..8ce12ba 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -61,12 +61,12 @@ function forum_help($route_name, RouteMatchInterface $route_match) {
       );
       $container = array(
         '#theme' => 'container',
-        '#children' => drupal_render($more_help_link),
+        '#children' => $more_help_link,
         '#attributes' => array(
           'class' => array('more-link'),
         ),
       );
-      $output .= drupal_render($container);
+      $output .= \Drupal::service('renderer')->renderPlain($container);
       return $output;
 
     case 'forum.add_container':
diff --git a/core/modules/forum/src/Tests/ForumTest.php b/core/modules/forum/src/Tests/ForumTest.php
index 9440179..1e9eac9 100644
--- a/core/modules/forum/src/Tests/ForumTest.php
+++ b/core/modules/forum/src/Tests/ForumTest.php
@@ -602,7 +602,7 @@ private function verifyForums(EntityInterface $node, $admin, $response = 200) {
       '#theme' => 'breadcrumb',
       '#links' => $breadcrumb_build,
     );
-    $this->assertRaw(drupal_render($breadcrumb), 'Breadcrumbs were displayed');
+    $this->assertRaw(\Drupal::service('renderer')->renderRoot($breadcrumb), 'Breadcrumbs were displayed');
 
     // View forum edit node.
     $this->drupalGet('node/' . $node->id() . '/edit');
@@ -662,7 +662,7 @@ private function verifyForumView($forum, $parent = NULL) {
       '#theme' => 'breadcrumb',
       '#links' => $breadcrumb_build,
     );
-    $this->assertRaw(drupal_render($breadcrumb), 'Breadcrumbs were displayed');
+    $this->assertRaw(\Drupal::service('renderer')->renderRoot($breadcrumb), 'Breadcrumbs were displayed');
   }
 
   /**
diff --git a/core/modules/history/src/Tests/Views/HistoryTimestampTest.php b/core/modules/history/src/Tests/Views/HistoryTimestampTest.php
index ed9a3bd..44f67f9 100644
--- a/core/modules/history/src/Tests/Views/HistoryTimestampTest.php
+++ b/core/modules/history/src/Tests/Views/HistoryTimestampTest.php
@@ -70,7 +70,7 @@ public function testHandlers() {
     $this->executeView($view);
     $this->assertEqual(count($view->result), 2);
     $output = $view->preview();
-    $this->setRawContent(drupal_render($output));
+    $this->setRawContent(\Drupal::service('renderer')->renderRoot($output));
     $result = $this->xpath('//span[@class=:class]', array(':class' => 'marker'));
     $this->assertEqual(count($result), 1, 'Just one node is marked as new');
 
diff --git a/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php b/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php
index ad18e4c..f202293 100644
--- a/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php
+++ b/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php
@@ -98,7 +98,7 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
       // If there's only one field, return it as delta 0.
       if (empty($elements[0]['#default_value']['fids'])) {
         $file_upload_help['#description'] = $this->fieldFilterXss($this->fieldDefinition->getDescription());
-        $elements[0]['#description'] = drupal_render($file_upload_help);
+        $elements[0]['#description'] = \Drupal::service('renderer')->renderPlain($file_upload_help);
       }
     }
     else {
diff --git a/core/modules/image/src/Tests/ImageDimensionsTest.php b/core/modules/image/src/Tests/ImageDimensionsTest.php
index 2db7e24..6d97142 100644
--- a/core/modules/image/src/Tests/ImageDimensionsTest.php
+++ b/core/modules/image/src/Tests/ImageDimensionsTest.php
@@ -243,7 +243,7 @@ function testImageDimensions() {
    * re-rendered each time.
    */
   protected function getImageTag($variables) {
-    return str_replace("\n", NULL, drupal_render($variables));
+    return str_replace("\n", NULL, \Drupal::service('renderer')->renderRoot($variables));
   }
 
 }
diff --git a/core/modules/image/src/Tests/ImageFieldDisplayTest.php b/core/modules/image/src/Tests/ImageFieldDisplayTest.php
index 8800020..360dca0 100644
--- a/core/modules/image/src/Tests/ImageFieldDisplayTest.php
+++ b/core/modules/image/src/Tests/ImageFieldDisplayTest.php
@@ -48,6 +48,8 @@ function testImageFieldFormattersPrivate() {
    * Test image formatters on node display.
    */
   function _testImageFieldFormatters($scheme) {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $node_storage = $this->container->get('entity.manager')->getStorage('node');
     $field_name = strtolower($this->randomMachineName());
     $field_settings = array('alt_field_required' => 0);
@@ -103,7 +105,7 @@ function _testImageFieldFormatters($scheme) {
       '#height' => 20,
       '#alt' => $alt,
     );
-    $default_output = str_replace("\n", NULL, drupal_render($image));
+    $default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
     $this->assertRaw($default_output, 'Default formatter displaying correctly on full node view.');
 
     // Test the image linked to file formatter.
@@ -122,7 +124,7 @@ function _testImageFieldFormatters($scheme) {
       '#height' => 20,
       '#alt' => $alt,
     );
-    $default_output = '<a href="' . file_create_url($image_uri) . '">' . drupal_render($image) . '</a>';
+    $default_output = '<a href="' . file_create_url($image_uri) . '">' . $renderer->renderRoot($image) . '</a>';
     $this->drupalGet('node/' . $nid);
     $this->assertCacheTag($file->getCacheTags()[0]);
     $cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
@@ -188,7 +190,7 @@ function _testImageFieldFormatters($scheme) {
       '#style_name' => 'thumbnail',
       '#alt' => $alt,
     );
-    $default_output = drupal_render($image_style);
+    $default_output = $renderer->renderRoot($image_style);
     $this->drupalGet('node/' . $nid);
     $image_style = ImageStyle::load('thumbnail');
     $this->assertCacheTag($image_style->getCacheTags()[0]);
@@ -206,6 +208,8 @@ function _testImageFieldFormatters($scheme) {
    * Tests for image field settings.
    */
   function testImageFieldSettings() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $node_storage = $this->container->get('entity.manager')->getStorage('node');
     $test_image = current($this->drupalGetTestFiles('image'));
     list(, $test_image_extension) = explode('.', $test_image->filename);
@@ -269,7 +273,7 @@ function testImageFieldSettings() {
       $field_name . '[0][title]' => $image['#title'],
     );
     $this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save and keep published'));
-    $default_output = str_replace("\n", NULL, drupal_render($image));
+    $default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
     $this->assertRaw($default_output, 'Image displayed using user supplied alt and title attributes.');
 
     // Verify that alt/title longer than allowed results in a validation error.
@@ -319,6 +323,9 @@ function testImageFieldSettings() {
    * Test use of a default image with an image field.
    */
   function testImageFieldDefaultImage() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     $node_storage = $this->container->get('entity.manager')->getStorage('node');
     // Create a new image field.
     $field_name = strtolower($this->randomMachineName());
@@ -358,7 +365,7 @@ function testImageFieldDefaultImage() {
       '#width' => 40,
       '#height' => 20,
     );
-    $default_output = str_replace("\n", NULL, drupal_render($image));
+    $default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
     $this->drupalGet('node/' . $node->id());
     $this->assertCacheTag($file->getCacheTags()[0]);
     $cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
@@ -382,7 +389,7 @@ function testImageFieldDefaultImage() {
       '#height' => 20,
       '#alt' => $alt,
     );
-    $image_output = str_replace("\n", NULL, drupal_render($image));
+    $image_output = str_replace("\n", NULL, $renderer->renderRoot($image));
     $this->drupalGet('node/' . $nid);
     $this->assertCacheTag($file->getCacheTags()[0]);
     $cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
@@ -430,7 +437,7 @@ function testImageFieldDefaultImage() {
       '#width' => 40,
       '#height' => 20,
     );
-    $default_output = str_replace("\n", NULL, drupal_render($image));
+    $default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
     $this->drupalGet('node/' . $node->id());
     $this->assertCacheTag($file->getCacheTags()[0]);
     $cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
diff --git a/core/modules/image/src/Tests/ImageThemeFunctionTest.php b/core/modules/image/src/Tests/ImageThemeFunctionTest.php
index a67c2eb..9a2046b 100644
--- a/core/modules/image/src/Tests/ImageThemeFunctionTest.php
+++ b/core/modules/image/src/Tests/ImageThemeFunctionTest.php
@@ -63,6 +63,9 @@ protected function setUp() {
    * Tests usage of the image field formatters.
    */
   function testImageFormatterTheme() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     // Create an image.
     $files = $this->drupalGetTestFiles('image');
     $file = reset($files);
@@ -92,7 +95,7 @@ function testImageFormatterTheme() {
 
     // Test using theme_image_formatter() with a NULL value for the alt option.
     $element = $base_element;
-    $this->setRawContent(drupal_render($element));
+    $this->setRawContent($renderer->renderRoot($element));
     $elements = $this->xpath('//a[@href=:path]/img[@class="image-style-test" and @src=:url and @width=:width and @height=:height]', array(':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()));
     $this->assertEqual(count($elements), 1, 'theme_image_formatter() correctly renders with a NULL value for the alt option.');
 
@@ -100,7 +103,7 @@ function testImageFormatterTheme() {
     // link options.
     $element = $base_element;
     $element['#item']->alt = '';
-    $this->setRawContent(drupal_render($element));
+    $this->setRawContent($renderer->renderRoot($element));
     $elements = $this->xpath('//a[@href=:path]/img[@class="image-style-test" and @src=:url and @width=:width and @height=:height and @alt=""]', array(':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()));
     $this->assertEqual(count($elements), 1, 'theme_image_formatter() correctly renders without title, alt, or path options.');
 
@@ -108,7 +111,7 @@ function testImageFormatterTheme() {
     $fragment = $this->randomMachineName();
     $element = $base_element;
     $element['#url'] = Url::fromRoute('<none>', [], ['fragment' => $fragment]);
-    $this->setRawContent(drupal_render($element));
+    $this->setRawContent($renderer->renderRoot($element));
     $elements = $this->xpath('//a[@href=:fragment]/img[@class="image-style-test" and @src=:url and @width=:width and @height=:height and @alt=""]', array(
       ':fragment' => '#' . $fragment,
       ':url' => $url,
@@ -122,6 +125,9 @@ function testImageFormatterTheme() {
    * Tests usage of the image style theme function.
    */
   function testImageStyleTheme() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     // Create an image.
     $files = $this->drupalGetTestFiles('image');
     $file = reset($files);
@@ -140,14 +146,14 @@ function testImageStyleTheme() {
     );
 
     $element = $base_element;
-    $this->setRawContent(drupal_render($element));
+    $this->setRawContent($renderer->renderRoot($element));
     $elements = $this->xpath('//img[@class="image-style-image-test" and @src=:url and @alt=""]', array(':url' => $url));
     $this->assertEqual(count($elements), 1, 'theme_image_style() renders an image correctly.');
 
     // Test using theme_image_style() with a NULL value for the alt option.
     $element = $base_element;
     $element['#alt'] = NULL;
-    $this->setRawContent(drupal_render($element));
+    $this->setRawContent($renderer->renderRoot($element));
     $elements = $this->xpath('//img[@class="image-style-image-test" and @src=:url]', array(':url' => $url));
     $this->assertEqual(count($elements), 1, 'theme_image_style() renders an image correctly with a NULL value for the alt option.');
   }
@@ -156,6 +162,9 @@ function testImageStyleTheme() {
    * Tests image alt attribute functionality.
    */
   function testImageAltFunctionality() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     // Test using alt directly with alt attribute.
     $image_with_alt_property = array(
       '#theme' => 'image',
@@ -167,7 +176,7 @@ function testImageAltFunctionality() {
       '#attributes' => array('class' => 'image-with-regular-alt', 'id' => 'my-img'),
     );
 
-    $this->setRawContent(drupal_render($image_with_alt_property));
+    $this->setRawContent($renderer->renderRoot($image_with_alt_property));
     $elements = $this->xpath('//img[contains(@class, class) and contains(@alt, :alt)]', array(":class" => "image-with-regular-alt", ":alt" => "Regular alt"));
     $this->assertEqual(count($elements), 1, 'Regular alt displays correctly');
 
@@ -185,7 +194,7 @@ function testImageAltFunctionality() {
       ),
     );
 
-    $this->setRawContent(drupal_render($image_with_alt_attribute_alt_attribute));
+    $this->setRawContent($renderer->renderRoot($image_with_alt_attribute_alt_attribute));
     $elements = $this->xpath('//img[contains(@class, class) and contains(@alt, :alt)]', array(":class" => "image-with-attribute-alt", ":alt" => "Attribute alt"));
     $this->assertEqual(count($elements), 1, 'Attribute alt displays correctly');
 
@@ -204,7 +213,7 @@ function testImageAltFunctionality() {
       ),
     );
 
-    $this->setRawContent(drupal_render($image_with_alt_attribute_both));
+    $this->setRawContent($renderer->renderRoot($image_with_alt_attribute_both));
     $elements = $this->xpath('//img[contains(@class, class) and contains(@alt, :alt)]', array(":class" => "image-with-attribute-alt", ":alt" => "Attribute alt"));
     $this->assertEqual(count($elements), 1, 'Attribute alt overrides alt property if both set.');
   }
diff --git a/core/modules/link/src/Tests/LinkFieldTest.php b/core/modules/link/src/Tests/LinkFieldTest.php
index a1cdc65..0279ed1 100644
--- a/core/modules/link/src/Tests/LinkFieldTest.php
+++ b/core/modules/link/src/Tests/LinkFieldTest.php
@@ -588,7 +588,7 @@ protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) {
     $entity = entity_load('entity_test', $id);
     $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), $view_mode);
     $content = $display->build($entity);
-    $output = drupal_render($content);
+    $output = \Drupal::service('renderer')->renderRoot($content);
     $this->setRawContent($output);
     $this->verbose($output);
   }
diff --git a/core/modules/node/src/Plugin/Search/NodeSearch.php b/core/modules/node/src/Plugin/Search/NodeSearch.php
index 00b6b06..9dc4442 100644
--- a/core/modules/node/src/Plugin/Search/NodeSearch.php
+++ b/core/modules/node/src/Plugin/Search/NodeSearch.php
@@ -329,7 +329,7 @@ protected function prepareResults(StatementInterface $found) {
 
       // Fetch comment count for snippet.
       $rendered = SafeMarkup::set(
-        $this->renderer->render($build) . ' ' .
+        $this->renderer->renderPlain($build) . ' ' .
         SafeMarkup::escape($this->moduleHandler->invoke('comment', 'node_update_index', array($node, $item->langcode)))
       );
 
@@ -354,7 +354,7 @@ protected function prepareResults(StatementInterface $found) {
 
       if ($type->displaySubmitted()) {
         $result += array(
-          'user' => $this->renderer->render($username),
+          'user' => $this->renderer->renderPlain($username),
           'date' => $node->getChangedTime(),
         );
       }
@@ -443,7 +443,7 @@ protected function indexNode(NodeInterface $node) {
       $build = $node_render->view($node, 'search_index', $language->getId());
 
       unset($build['#theme']);
-      $rendered = $this->renderer->render($build);
+      $rendered = $this->renderer->renderPlain($build);
 
       $text = '<h1>' . SafeMarkup::checkPlain($node->label($language->getId())) . '</h1>' . $rendered;
 
diff --git a/core/modules/node/src/Tests/SummaryLengthTest.php b/core/modules/node/src/Tests/SummaryLengthTest.php
index 0c34eb0..3eb44df 100644
--- a/core/modules/node/src/Tests/SummaryLengthTest.php
+++ b/core/modules/node/src/Tests/SummaryLengthTest.php
@@ -19,6 +19,9 @@ class SummaryLengthTest extends NodeTestBase {
    * Tests the node summary length functionality.
    */
   public function testSummaryLength() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     // Create a node to view.
     $settings = array(
       'body' => array(array('value' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae arcu at leo cursus laoreet. Curabitur dui tortor, adipiscing malesuada tempor in, bibendum ac diam. Cras non tellus a libero pellentesque condimentum. What is a Drupalism? Suspendisse ac lacus libero. Ut non est vel nisl faucibus interdum nec sed leo. Pellentesque sem risus, vulputate eu semper eget, auctor in libero. Ut fermentum est vitae metus convallis scelerisque. Phasellus pellentesque rhoncus tellus, eu dignissim purus posuere id. Quisque eu fringilla ligula. Morbi ullamcorper, lorem et mattis egestas, tortor neque pretium velit, eget eleifend odio turpis eu purus. Donec vitae metus quis leo pretium tincidunt a pulvinar sem. Morbi adipiscing laoreet mauris vel placerat. Nullam elementum, nisl sit amet scelerisque malesuada, dolor nunc hendrerit quam, eu ultrices erat est in orci. Curabitur feugiat egestas nisl sed accumsan.')),
@@ -30,7 +33,7 @@ public function testSummaryLength() {
     // Render the node as a teaser.
     $content = $this->drupalBuildEntityView($node, 'teaser');
     $this->assertTrue(strlen($content['body'][0]['#markup']) < 600, 'Teaser is less than 600 characters long.');
-    $this->setRawContent(drupal_render($content));
+    $this->setRawContent($renderer->renderRoot($content));
     // The string 'What is a Drupalism?' is between the 200th and 600th
     // characters of the node body, so it should be included if the summary is
     // 600 characters long.
@@ -48,7 +51,7 @@ public function testSummaryLength() {
     // 200 characters in length and so does not include 'What is a Drupalism?'.
     $content = $this->drupalBuildEntityView($node, 'teaser');
     $this->assertTrue(strlen($content['body'][0]['#markup']) < 200, 'Teaser is less than 200 characters long.');
-    $this->setRawContent(drupal_render($content));
+    $this->setRawContent($renderer->renderRoot($content));
     $this->assertText($node->label());
     $this->assertNoRaw($expected);
   }
diff --git a/core/modules/node/src/Tests/Views/RowPluginTest.php b/core/modules/node/src/Tests/Views/RowPluginTest.php
index a2f6585..4da5ad9 100644
--- a/core/modules/node/src/Tests/Views/RowPluginTest.php
+++ b/core/modules/node/src/Tests/Views/RowPluginTest.php
@@ -64,6 +64,8 @@ protected function setUp() {
    * Tests the node row plugin.
    */
   public function testRowPlugin() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_node_row_plugin');
     $view->initDisplay();
     $view->setDisplay('page_1');
@@ -72,7 +74,7 @@ public function testRowPlugin() {
 
     // Test with view_mode full.
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     foreach ($this->nodes as $node) {
       $this->assertFalse(strpos($output, $node->body->summary) !== FALSE, 'Make sure the teaser appears in the output of the view.');
       $this->assertTrue(strpos($output, $node->body->value) !== FALSE, 'Make sure the full text appears in the output of the view.');
@@ -81,7 +83,7 @@ public function testRowPlugin() {
     // Test with teasers.
     $view->rowPlugin->options['view_mode'] = 'teaser';
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     foreach ($this->nodes as $node) {
       $this->assertTrue(strpos($output, $node->body->summary) !== FALSE, 'Make sure the teaser appears in the output of the view.');
       $this->assertFalse(strpos($output, $node->body->value) !== FALSE, 'Make sure the full text does not appears in the output of the view if teaser is set as viewmode.');
diff --git a/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php b/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php
index 1747b19..22cdb45 100644
--- a/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php
+++ b/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php
@@ -422,7 +422,7 @@ public function testDisplayOptions() {
       'label' => 'inline',
     );
     $build = $node->body->view($display_settings);
-    $output = drupal_render($build);
+    $output = \Drupal::service('renderer')->renderRoot($build);
     $this->assertFalse(strpos($output, 'data-quickedit-field-id'), 'data-quickedit-field-id attribute not added when rendering field using dynamic display options.');
   }
 
diff --git a/core/modules/rdf/src/Tests/EntityReferenceFieldAttributesTest.php b/core/modules/rdf/src/Tests/EntityReferenceFieldAttributesTest.php
index 44f8d2f..52c4e86 100644
--- a/core/modules/rdf/src/Tests/EntityReferenceFieldAttributesTest.php
+++ b/core/modules/rdf/src/Tests/EntityReferenceFieldAttributesTest.php
@@ -103,7 +103,7 @@ function testNodeTeaser() {
 
     // Render the node.
     $node_render_array = entity_view_multiple(array($node), 'teaser');
-    $html = drupal_render($node_render_array);
+    $html = \Drupal::service('renderer')->renderRoot($node_render_array);
 
     // Parse the teaser.
     $parser = new \EasyRdf_Parser_Rdfa();
diff --git a/core/modules/rdf/src/Tests/Field/FieldRdfaTestBase.php b/core/modules/rdf/src/Tests/Field/FieldRdfaTestBase.php
index 6161326..cb8f584 100644
--- a/core/modules/rdf/src/Tests/Field/FieldRdfaTestBase.php
+++ b/core/modules/rdf/src/Tests/Field/FieldRdfaTestBase.php
@@ -93,7 +93,7 @@ protected function assertFormatterRdfa($formatter, $property, $expected_rdf_valu
       ->setComponent($this->fieldName, $formatter)
       ->save();
     $build = entity_view($this->entity, 'default');
-    $output = drupal_render($build);
+    $output = \Drupal::service('renderer')->renderRoot($build);
     $graph = new \EasyRdf_Graph($this->uri, $output, 'rdfa');
     $this->setRawContent($output);
 
diff --git a/core/modules/rdf/src/Tests/FileFieldAttributesTest.php b/core/modules/rdf/src/Tests/FileFieldAttributesTest.php
index e6aea38..ead4c2b 100644
--- a/core/modules/rdf/src/Tests/FileFieldAttributesTest.php
+++ b/core/modules/rdf/src/Tests/FileFieldAttributesTest.php
@@ -81,7 +81,7 @@ protected function setUp() {
   function testNodeTeaser() {
     // Render the teaser.
     $node_render_array = entity_view_multiple(array($this->node), 'teaser');
-    $html = drupal_render($node_render_array);
+    $html = \Drupal::service('renderer')->renderRoot($node_render_array);
 
     // Parses front page where the node is displayed in its teaser form.
     $parser = new \EasyRdf_Parser_Rdfa();
diff --git a/core/modules/rdf/src/Tests/ImageFieldAttributesTest.php b/core/modules/rdf/src/Tests/ImageFieldAttributesTest.php
index 4ae8f0b..c5b482a 100644
--- a/core/modules/rdf/src/Tests/ImageFieldAttributesTest.php
+++ b/core/modules/rdf/src/Tests/ImageFieldAttributesTest.php
@@ -87,7 +87,7 @@ function testNodeTeaser() {
 
     // Render the teaser.
     $node_render_array = node_view($this->node, 'teaser');
-    $html = drupal_render($node_render_array);
+    $html = \Drupal::service('renderer')->renderRoot($node_render_array);
 
     // Parse the teaser.
     $parser = new \EasyRdf_Parser_Rdfa();
diff --git a/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php b/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php
index ccc09be..facd063 100644
--- a/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php
+++ b/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php
@@ -163,6 +163,8 @@ protected function addTestImageStyleMappings($empty_styles = FALSE) {
    * Defaults to false.
    */
   protected function doTestResponsiveImageFieldFormatters($scheme, $empty_styles = FALSE) {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $node_storage = $this->container->get('entity.manager')->getStorage('node');
     $field_name = Unicode::strtolower($this->randomMachineName());
     $this->createImageField($field_name, 'article', array('uri_scheme' => $scheme));
@@ -186,7 +188,7 @@ protected function doTestResponsiveImageFieldFormatters($scheme, $empty_styles =
       '#height' => 240,
       '#alt' => $alt,
     );
-    $default_output = str_replace("\n", NULL, drupal_render($image));
+    $default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
     $this->assertRaw($default_output, 'Default formatter displaying correctly on full node view.');
 
     // Use the responsive image formatter linked to file formatter.
@@ -282,7 +284,7 @@ protected function doTestResponsiveImageFieldFormatters($scheme, $empty_styles =
     // The image.html.twig template has a newline after the <img> tag but
     // responsive-image.html.twig doesn't have one after the fallback image, so
     // we remove it here.
-    $default_output = trim(drupal_render($fallback_image));
+    $default_output = trim($renderer->renderRoot($fallback_image));
     $this->assertRaw($default_output, 'Image style large formatter displaying correctly on full node view.');
 
     if ($scheme == 'private') {
diff --git a/core/modules/simpletest/src/AssertContentTrait.php b/core/modules/simpletest/src/AssertContentTrait.php
index b043ea9..3cce705 100644
--- a/core/modules/simpletest/src/AssertContentTrait.php
+++ b/core/modules/simpletest/src/AssertContentTrait.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Xss;
+use Drupal\Core\Render\RenderContext;
 use Symfony\Component\CssSelector\CssSelector;
 
 /**
@@ -808,7 +809,12 @@ protected function assertNoTitle($title, $message = '', $group = 'Other') {
    *   TRUE on pass, FALSE on fail.
    */
   protected function assertThemeOutput($callback, array $variables = array(), $expected = '', $message = '', $group = 'Other') {
-    $output = \Drupal::theme()->render($callback, $variables);
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
+    $output = $renderer->executeInRenderContext(new RenderContext(), function() use ($callback, $variables) {
+      return \Drupal::theme()->render($callback, $variables);
+    });
     $this->verbose(
       '<hr />' . 'Result:' . '<pre>' . SafeMarkup::checkPlain(var_export($output, TRUE)) . '</pre>'
       . '<hr />' . 'Expected:' . '<pre>' . SafeMarkup::checkPlain(var_export($expected, TRUE)) . '</pre>'
diff --git a/core/modules/simpletest/src/Form/SimpletestResultsForm.php b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
index d41654f..a1b97ba 100644
--- a/core/modules/simpletest/src/Form/SimpletestResultsForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
@@ -94,10 +94,10 @@ protected static function buildStatusImageMap() {
       '#alt' => 'Debug',
     );
     return array(
-      'pass' => drupal_render($image_pass),
-      'fail' => drupal_render($image_fail),
-      'exception' => drupal_render($image_exception),
-      'debug' => drupal_render($image_debug),
+      'pass' => $image_pass,
+      'fail' => $image_fail,
+      'exception' => $image_exception,
+      'debug' => $image_debug,
     );
   }
 
@@ -205,7 +205,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     // Under normal circumstances, a form object's submitForm() should never be
     // called directly, FormBuilder::submitForm() should be called instead.
     // However, it calls $form_state->setProgrammed(), which disables the Batch API.
-    $simpletest_test_form = new SimpletestTestForm();
+    $simpletest_test_form = SimpletestTestForm::create(\Drupal::getContainer());
     $simpletest_test_form->buildForm($form_execute, $form_state_execute);
     $simpletest_test_form->submitForm($form_execute, $form_state_execute);
     if ($redirect = $form_state_execute->getRedirect()) {
diff --git a/core/modules/simpletest/src/Form/SimpletestTestForm.php b/core/modules/simpletest/src/Form/SimpletestTestForm.php
index 92c8a54..f6d0367 100644
--- a/core/modules/simpletest/src/Form/SimpletestTestForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestTestForm.php
@@ -11,6 +11,8 @@
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\RendererInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * List tests arranged in groups that can be selected and run.
@@ -18,6 +20,32 @@
 class SimpletestTestForm extends FormBase {
 
   /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('renderer')
+    );
+  }
+
+  /**
+   * Constructs a new SimpletestTestForm.
+   *
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
+   */
+  public function __construct(RendererInterface $renderer) {
+    $this->renderer = $renderer;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getFormId() {
@@ -99,8 +127,8 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#suffix' => '<a href="#" class="simpletest-collapse">(' . $this->t('Collapse') . ')</a>',
     );
     $form['tests']['#attached']['drupalSettings']['simpleTest']['images'] = [
-      drupal_render($image_collapsed),
-      drupal_render($image_extended),
+      $this->renderer->renderPlain($image_collapsed),
+      $this->renderer->renderPlain($image_extended),
     ];
 
     // Generate the list of tests arranged by group.
diff --git a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
index 2e8a8c2..b22ece7 100644
--- a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
+++ b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
@@ -284,6 +284,8 @@ function testEnableModulesFixedList() {
    * Tests that _theme() works right after loading a module.
    */
   function testEnableModulesTheme() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $original_element = $element = array(
       '#type' => 'container',
       '#markup' => 'Foo',
@@ -291,11 +293,11 @@ function testEnableModulesTheme() {
     );
     $this->enableModules(array('system'));
     // _theme() throws an exception if modules are not loaded yet.
-    $this->assertTrue(drupal_render($element));
+    $this->assertTrue($renderer->renderRoot($element));
 
     $element = $original_element;
     $this->disableModules(array('entity_test'));
-    $this->assertTrue(drupal_render($element));
+    $this->assertTrue($renderer->renderRoot($element));
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Ajax/DialogTest.php b/core/modules/system/src/Tests/Ajax/DialogTest.php
index 70cbc5d..58426c9 100644
--- a/core/modules/system/src/Tests/Ajax/DialogTest.php
+++ b/core/modules/system/src/Tests/Ajax/DialogTest.php
@@ -35,7 +35,7 @@ public function testDialog() {
 
     // Set up variables for this test.
     $dialog_renderable = \Drupal\ajax_test\Controller\AjaxTestController::dialogContents();
-    $dialog_contents = drupal_render($dialog_renderable);
+    $dialog_contents = \Drupal::service('renderer')->renderRoot($dialog_renderable);
     $modal_expected_response = array(
       'command' => 'openDialog',
       'selector' => '#drupal-modal',
diff --git a/core/modules/system/src/Tests/Common/AddFeedTest.php b/core/modules/system/src/Tests/Common/AddFeedTest.php
index 4378279..fbb9cc2 100644
--- a/core/modules/system/src/Tests/Common/AddFeedTest.php
+++ b/core/modules/system/src/Tests/Common/AddFeedTest.php
@@ -90,7 +90,7 @@ function testFeedIconEscaping() {
       '#url' => 'node',
       '#title' => '<>&"\'',
     );
-    $text = drupal_render($variables);
+    $text = \Drupal::service('renderer')->renderRoot($variables);
     preg_match('/title="(.*?)"/', $text, $matches);
     $this->assertEqual($matches[1], 'Subscribe to &amp;&quot;&#039;', 'feed_icon template escapes reserved HTML characters.');
   }
diff --git a/core/modules/system/src/Tests/Common/AttachedAssetsTest.php b/core/modules/system/src/Tests/Common/AttachedAssetsTest.php
index 21dc6b2..a9701ff 100644
--- a/core/modules/system/src/Tests/Common/AttachedAssetsTest.php
+++ b/core/modules/system/src/Tests/Common/AttachedAssetsTest.php
@@ -94,8 +94,8 @@ function testAddFiles() {
 
     $css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_css = $this->renderer->render($css_render_array);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_css = $this->renderer->renderPlain($css_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
     $this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="' . file_create_url('core/modules/system/tests/modules/common_test/bar.css') . '?' . $query_string . '" media="all" />'), FALSE, 'Rendering an external CSS file.');
     $this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/foo.js') . '?' . $query_string . '"></script>'), FALSE, 'Rendering an external JavaScript file.');
@@ -132,8 +132,8 @@ function testAddExternalFiles() {
 
     $css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_css = $this->renderer->render($css_render_array);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_css = $this->renderer->renderPlain($css_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="http://example.com/stylesheet.css" media="all" />'), FALSE, 'Rendering an external CSS file.');
     $this->assertNotIdentical(strpos($rendered_js, '<script src="http://example.com/script.js"></script>'), FALSE, 'Rendering an external JavaScript file.');
   }
@@ -147,7 +147,7 @@ function testAttributes() {
 
     $js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>';
     $expected_2 = '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js') . '?v=1" defer bar="foo"></script>';
     $this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered external JavaScript with correct defer and random attributes.');
@@ -163,7 +163,7 @@ function testAggregatedAttributes() {
 
     $js = $this->assetResolver->getJsAssets($assets, TRUE)[1];
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>';
     $expected_2 = '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js') . '?v=1" defer bar="foo"></script>';
     $this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered external JavaScript with correct defer and random attributes.');
@@ -203,7 +203,7 @@ function testSettings() {
 
     $js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
 
     // Parse the generated drupalSettings <script> back to a PHP representation.
     $startToken = 'drupalSettings = ';
@@ -238,7 +238,7 @@ function testHeaderHTML() {
 
     $js = $this->assetResolver->getJsAssets($assets, FALSE)[0];
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
     $this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/header.js') . '?' . $query_string . '"></script>'), FALSE, 'The JS asset in common_test/js-header appears in the header.');
     $this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_create_url('core/misc/drupal.js')), FALSE, 'The JS asset of the direct dependency (core/drupal) of common_test/js-header appears in the header.');
@@ -269,7 +269,7 @@ function testBrowserConditionalComments() {
 
     $js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $expected_1 = "<!--[if lte IE 8]>\n" . '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/old-ie.js') . '?' . $default_query_string . '"></script>' . "\n<![endif]-->";
     $expected_2 = "<!--[if !IE]><!-->\n" . '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/no-ie.js') . '?' . $default_query_string . '"></script>' . "\n<!--<![endif]-->";
 
@@ -287,7 +287,7 @@ function testVersionQueryString() {
 
     $js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($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.8') > 0 , 'JavaScript version identifiers correctly appended to URLs');
   }
 
@@ -315,7 +315,7 @@ function testRenderOrder() {
     // Retrieve the rendered JavaScript and test against the regex.
     $js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $matches = array();
     if (preg_match_all('/weight_([-0-9]+_[0-9]+)/', $rendered_js, $matches)) {
       $result = $matches[1];
@@ -357,7 +357,7 @@ function testRenderOrder() {
     // Retrieve the rendered CSS and test against the regex.
     $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);
+    $rendered_css = $this->renderer->renderPlain($css_render_array);
     $matches = array();
     if (preg_match_all('/([a-z]+)_weight_([-0-9]+_[0-9]+)/', $rendered_css, $matches)) {
       $result = $matches[0];
@@ -380,7 +380,7 @@ function testRenderDifferentWeight() {
 
     $js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $this->assertTrue(strpos($rendered_js, 'lighter.css') < strpos($rendered_js, 'first.js'), 'Lighter CSS assets are rendered first.');
     $this->assertTrue(strpos($rendered_js, 'lighter.js') < strpos($rendered_js, 'first.js'), 'Lighter JavaScript assets are rendered first.');
     $this->assertTrue(strpos($rendered_js, 'before-jquery.js') < strpos($rendered_js, 'core/assets/vendor/jquery/jquery.min.js'), 'Rendering a JavaScript file above jQuery.');
@@ -402,7 +402,7 @@ function testAlter() {
     // takes place.
     $js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $this->assertTrue(strpos($rendered_js, 'simpletest.js') < strpos($rendered_js, 'core/misc/tableselect.js'), 'Altering JavaScript weight through the alter hook.');
   }
 
@@ -423,7 +423,7 @@ function testLibraryAlter() {
     $assets = AttachedAssets::createFromRenderArray($build);
     $js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $this->assertTrue(strpos($rendered_js, 'core/assets/vendor/jquery-form/jquery.form.min.js'), 'Altered library dependencies are added to the page.');
   }
 
@@ -474,9 +474,9 @@ function testAddJsFileWithQueryString() {
     $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.');
 
     $css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
-    $rendered_css = $this->renderer->render($css_render_array);
+    $rendered_css = $this->renderer->renderPlain($css_render_array);
     $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
-    $rendered_js = $this->renderer->render($js_render_array);
+    $rendered_js = $this->renderer->renderPlain($js_render_array);
     $query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
     $this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="' . str_replace('&', '&amp;', file_create_url('core/modules/system/tests/modules/common_test/querystring.css?arg1=value1&arg2=value2')) . '&amp;' . $query_string . '" media="all" />'), FALSE, 'CSS file with query string gets version query string correctly appended..');
     $this->assertNotIdentical(strpos($rendered_js, '<script src="' . str_replace('&', '&amp;', file_create_url('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2')) . '&amp;' . $query_string . '"></script>'), FALSE, 'JavaScript file with query string gets version query string correctly appended.');
diff --git a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php
index 3aa3cec..a18fd77 100644
--- a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php
+++ b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php
@@ -43,7 +43,7 @@ protected function setUp() {
    *   Assertion message.
    */
   protected function assertElements(array $elements, $expected_html, $message) {
-    $actual_html = drupal_render($elements);
+    $actual_html = \Drupal::service('renderer')->renderRoot($elements);
 
     $out = '<table><tr>';
     $out .= '<td valign="top"><pre>' . SafeMarkup::checkPlain($expected_html) . '</pre></td>';
@@ -198,7 +198,7 @@ function testMoreLink() {
     );
 
     foreach($elements as $element) {
-      $xml = new \SimpleXMLElement(drupal_render($element['value']));
+      $xml = new \SimpleXMLElement(\Drupal::service('renderer')->renderRoot($element['value']));
       $result = $xml->xpath($element['expected']);
       $this->assertTrue($result, '"' . $element['name'] . '" input rendered correctly by drupal_render().');
     }
@@ -229,7 +229,7 @@ function testSystemCompactLink() {
     );
 
     foreach ($elements as $element) {
-      $xml = new \SimpleXMLElement(drupal_render($element['value']));
+      $xml = new \SimpleXMLElement(\Drupal::service('renderer')->renderRoot($element['value']));
       $result = $xml->xpath($element['expected']);
       $this->assertTrue($result, '"' . $element['name'] . '" is rendered correctly by drupal_render().');
     }
@@ -245,7 +245,7 @@ function testSystemCompactLink() {
       'expected' => '//div[@class="compact-link"]/a[contains(@href, "admin/compact?") and text()="Show descriptions"]',
     );
 
-    $xml = new \SimpleXMLElement(drupal_render($element['value']));
+    $xml = new \SimpleXMLElement(\Drupal::service('renderer')->renderRoot($element['value']));
     $result = $xml->xpath($element['expected']);
     $this->assertTrue($result, '"' . $element['name'] . '" is rendered correctly by drupal_render().');
   }
diff --git a/core/modules/system/src/Tests/Common/RenderTest.php b/core/modules/system/src/Tests/Common/RenderTest.php
index 66354a5..7ae448a 100644
--- a/core/modules/system/src/Tests/Common/RenderTest.php
+++ b/core/modules/system/src/Tests/Common/RenderTest.php
@@ -39,7 +39,7 @@ function testDrupalRenderThemePreprocessAttached() {
         '#markup' => 'Kittens!',
       ],
     ];
-    drupal_render($test_element);
+    \Drupal::service('renderer')->renderRoot($test_element);
 
     $expected_attached = [
       'library' => [
diff --git a/core/modules/system/src/Tests/Common/UrlTest.php b/core/modules/system/src/Tests/Common/UrlTest.php
index c095676..cf34843 100644
--- a/core/modules/system/src/Tests/Common/UrlTest.php
+++ b/core/modules/system/src/Tests/Common/UrlTest.php
@@ -9,7 +9,9 @@
 
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Render\RenderContext;
 use Drupal\Core\Url;
 use Drupal\simpletest\WebTestBase;
 
@@ -57,13 +59,14 @@ function testLinkCacheability() {
 
     foreach ($cases as $case) {
       list($title, $uri, $options, $expected_cacheability) = $case;
+      $expected_cacheability['contexts'] = Cache::mergeContexts($expected_cacheability['contexts'], ['languages:language_interface', 'theme']);
       $link = [
         '#type' => 'link',
         '#title' => $title,
         '#options' => $options,
         '#url' => Url::fromUri($uri),
       ];
-      drupal_render($link);
+      \Drupal::service('renderer')->renderRoot($link);
       $this->pass($title);
       $this->assertEqual($expected_cacheability, $link['#cache']);
     }
@@ -73,6 +76,9 @@ function testLinkCacheability() {
    * Tests that default and custom attributes are handled correctly on links.
    */
   function testLinkAttributes() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     // Test that hreflang is added when a link has a known language.
     $language = new Language(array('id' => 'fr', 'name' => 'French'));
     $hreflang_link = array(
@@ -90,10 +96,10 @@ function testLinkAttributes() {
     $hreflang_override_link = $hreflang_link;
     $hreflang_override_link['#options']['attributes']['hreflang'] = 'foo';
 
-    $rendered = drupal_render($hreflang_link);
+    $rendered = $renderer->renderRoot($hreflang_link);
     $this->assertTrue($this->hasAttribute('hreflang', $rendered, $langcode), format_string('hreflang attribute with value @langcode is present on a rendered link when langcode is provided in the render array.', array('@langcode' => $langcode)));
 
-    $rendered = drupal_render($hreflang_override_link);
+    $rendered = $renderer->renderRoot($hreflang_override_link);
     $this->assertTrue($this->hasAttribute('hreflang', $rendered, 'foo'), format_string('hreflang attribute with value @hreflang is present on a rendered link when @hreflang is provided in the render array.', array('@hreflang' => 'foo')));
 
     // Test the active class in links produced by _l() and #type 'link'.
@@ -149,7 +155,7 @@ function testLinkAttributes() {
         ),
       ),
     );
-    $link_theme = drupal_render($type_link);
+    $link_theme = $renderer->renderRoot($type_link);
     $this->assertTrue($this->hasAttribute('class', $link_theme, $class_theme), format_string('Custom class @class is present on link when requested by #type', array('@class' => $class_theme)));
   }
 
@@ -157,13 +163,18 @@ function testLinkAttributes() {
    * Tests that link functions support render arrays as 'text'.
    */
   function testLinkRenderArrayText() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     // Build a link with _l() for reference.
     $l = \Drupal::l('foo', Url::fromUri('https://www.drupal.org'));
 
     // Test a renderable array passed to _l().
-    $renderable_text = array('#markup' => 'foo');
-    $l_renderable_text = \Drupal::l($renderable_text, Url::fromUri('https://www.drupal.org'));
-    $this->assertEqual($l_renderable_text, $l);
+    $renderer->executeInRenderContext(new RenderContext(), function() use ($renderer, $l) {
+      $renderable_text = array('#markup' => 'foo');
+      $l_renderable_text = \Drupal::l($renderable_text, Url::fromUri('https://www.drupal.org'));
+      $this->assertEqual($l_renderable_text, $l);
+    });
 
     // Test a themed link with plain text 'text'.
     $type_link_plain_array = array(
@@ -171,7 +182,7 @@ function testLinkRenderArrayText() {
       '#title' => 'foo',
       '#url' => Url::fromUri('https://www.drupal.org'),
     );
-    $type_link_plain = drupal_render($type_link_plain_array);
+    $type_link_plain = $renderer->renderRoot($type_link_plain_array);
     $this->assertEqual($type_link_plain, $l);
 
     // Build a themed link with renderable 'text'.
@@ -180,7 +191,7 @@ function testLinkRenderArrayText() {
       '#title' => array('#markup' => 'foo'),
       '#url' => Url::fromUri('https://www.drupal.org'),
     );
-    $type_link_nested = drupal_render($type_link_nested_array);
+    $type_link_nested = $renderer->renderRoot($type_link_nested_array);
     $this->assertEqual($type_link_nested, $l);
   }
 
diff --git a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php
index 7d513eb..138794f 100644
--- a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php
@@ -559,6 +559,9 @@ function testLanguageFallback() {
    *   The entity type to run the tests with.
    */
   protected function doTestLanguageFallback($entity_type) {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     $current_langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
     $this->langcodes[] = $current_langcode;
 
@@ -609,16 +612,16 @@ protected function doTestLanguageFallback($entity_type) {
     // Get an view builder.
     $controller = $this->entityManager->getViewBuilder($entity_type);
     $entity2_build = $controller->view($entity2);
-    $entity2_output = drupal_render($entity2_build);
+    $entity2_output = $renderer->renderRoot($entity2_build);
     $translation = $this->entityManager->getTranslationFromContext($entity2, $default_langcode);
     $translation_build = $controller->view($translation);
-    $translation_output = drupal_render($translation_build);
+    $translation_output = $renderer->renderRoot($translation_build);
     $this->assertIdentical($entity2_output, $translation_output, 'When the entity has no translation no fallback is applied.');
 
     // Checks that entity translations are rendered properly.
     $controller = $this->entityManager->getViewBuilder($entity_type);
     $build = $controller->view($entity);
-    drupal_render($build);
+    $renderer->renderRoot($build);
     $this->assertEqual($build['label']['#markup'], $values[$current_langcode]['name'], 'By default the entity is rendered in the current language.');
 
     $langcodes = array_combine($this->langcodes, $this->langcodes);
@@ -630,7 +633,7 @@ protected function doTestLanguageFallback($entity_type) {
       // Unset the #cache key so that a fresh render is produced with each pass,
       // making the renderable array keys available to compare.
       unset($build['#cache']);
-      drupal_render($build);
+      $renderer->renderRoot($build);
       $this->assertEqual($build['label']['#markup'], $values[$expected]['name'], 'The entity is rendered in the expected language.');
     }
   }
diff --git a/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php b/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php
index 333dcda..a7aa63d 100644
--- a/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php
@@ -46,6 +46,8 @@ protected function setUp() {
    * Tests entity render cache handling.
    */
   public function testEntityViewBuilderCache() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $cache_contexts_manager = \Drupal::service("cache_contexts_manager");
 
     // Force a request via GET so we can get drupal_render() cache working.
@@ -72,7 +74,7 @@ public function testEntityViewBuilderCache() {
     $build['#markup'] = 'entity_render_test';
 
     // Test that a cache entry is created.
-    drupal_render($build);
+    $renderer->renderRoot($build);
     $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.');
 
     // Re-save the entity and check that the cache entry has been deleted.
@@ -82,7 +84,7 @@ public function testEntityViewBuilderCache() {
     // Rebuild the render array (creating a new cache entry in the process) and
     // delete the entity to check the cache entry is deleted.
     unset($build['#printed']);
-    drupal_render($build);
+    $renderer->renderRoot($build);
     $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.');
     $entity_test->delete();
     $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was deleted.');
@@ -95,6 +97,8 @@ public function testEntityViewBuilderCache() {
    * Tests entity render cache with references.
    */
   public function testEntityViewBuilderCacheWithReferences() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $cache_contexts_manager = \Drupal::service("cache_contexts_manager");
 
     // Force a request via GET so we can get drupal_render() cache working.
@@ -120,7 +124,7 @@ public function testEntityViewBuilderCacheWithReferences() {
     // Mock the build array to not require the theme registry.
     unset($build['#theme']);
     $build['#markup'] = 'entity_render_test';
-    drupal_render($build);
+    $renderer->renderRoot($build);
 
     // Test that a cache entry was created for the referenced entity.
     $this->assertTrue($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render element for the referenced entity has been cached.');
@@ -139,7 +143,7 @@ public function testEntityViewBuilderCacheWithReferences() {
     // Mock the build array to not require the theme registry.
     unset($build['#theme']);
     $build['#markup'] = 'entity_render_test';
-    drupal_render($build);
+    $renderer->renderRoot($build);
 
     // Test that a cache entry is created.
     $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.');
@@ -180,6 +184,9 @@ public function testEntityViewBuilderCacheToggling() {
    * Tests weighting of display components.
    */
   public function testEntityViewBuilderWeight() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     // Set a weight for the label component.
     entity_get_display('entity_test', 'entity_test', 'full')
       ->setComponent('label', array('weight' => 20))
@@ -188,7 +195,7 @@ public function testEntityViewBuilderWeight() {
     // Create and build a test entity.
     $entity_test = $this->createTestEntity('entity_test');
     $view =  $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
-    drupal_render($view);
+    $renderer->renderRoot($view);
 
     // Check that the weight is respected.
     $this->assertEqual($view['label']['#weight'], 20, 'The weight of a display component is respected.');
diff --git a/core/modules/system/src/Tests/Form/CheckboxTest.php b/core/modules/system/src/Tests/Form/CheckboxTest.php
index da53a79..13b7d07 100644
--- a/core/modules/system/src/Tests/Form/CheckboxTest.php
+++ b/core/modules/system/src/Tests/Form/CheckboxTest.php
@@ -33,7 +33,7 @@ function testFormCheckbox() {
       // @see \Drupal\Core\Render\Element\Checkbox::processCheckbox().
       foreach (array('0', '', 1, '1', 'foobar', '1foobar') as $return_value) {
         $form_array = \Drupal::formBuilder()->getForm('\Drupal\form_test\Form\FormTestCheckboxTypeJugglingForm', $default_value, $return_value);
-        $form = drupal_render($form_array);
+        $form = \Drupal::service('renderer')->renderRoot($form_array);
         if ($default_value === TRUE) {
           $checked = TRUE;
         }
diff --git a/core/modules/system/src/Tests/Form/FormTest.php b/core/modules/system/src/Tests/Form/FormTest.php
index be1fc26..910000b 100644
--- a/core/modules/system/src/Tests/Form/FormTest.php
+++ b/core/modules/system/src/Tests/Form/FormTest.php
@@ -124,7 +124,7 @@ function testRequiredFields() {
           // when you try to render them like this, so we ignore those for
           // testing the required marker.
           // @todo Fix this work-around (https://www.drupal.org/node/588438).
-          $form_output = ($type == 'radios') ? '' : drupal_render($form);
+          $form_output = ($type == 'radios') ? '' : \Drupal::service('renderer')->renderRoot($form);
           if ($required) {
             // Make sure we have a form error for this element.
             $this->assertTrue(isset($errors[$element]), "Check empty($key) '$type' field '$element'");
diff --git a/core/modules/system/src/Tests/Render/RenderCacheTest.php b/core/modules/system/src/Tests/Render/RenderCacheTest.php
index b4317d1..975358c 100644
--- a/core/modules/system/src/Tests/Render/RenderCacheTest.php
+++ b/core/modules/system/src/Tests/Render/RenderCacheTest.php
@@ -77,14 +77,14 @@ protected function doTestUser1WithContexts($contexts) {
     ];
     $element = $test_element;
     $element['#markup'] = 'content for user 1';
-    $output = \Drupal::service('renderer')->render($element);
+    $output = \Drupal::service('renderer')->renderRoot($element);
     $this->assertEqual($output, 'content for user 1');
 
     // Verify the cache is working by rendering the same element but with
     // different markup passed in; the result should be the same.
     $element = $test_element;
     $element['#markup'] = 'should not be used';
-    $output = \Drupal::service('renderer')->render($element);
+    $output = \Drupal::service('renderer')->renderRoot($element);
     $this->assertEqual($output, 'content for user 1');
     \Drupal::service('account_switcher')->switchBack();
 
@@ -93,7 +93,7 @@ protected function doTestUser1WithContexts($contexts) {
     \Drupal::service('account_switcher')->switchTo($first_authenticated_user);
     $element = $test_element;
     $element['#markup'] = 'content for authenticated users';
-    $output = \Drupal::service('renderer')->render($element);
+    $output = \Drupal::service('renderer')->renderRoot($element);
     $this->assertEqual($output, 'content for authenticated users');
     \Drupal::service('account_switcher')->switchBack();
 
@@ -102,7 +102,7 @@ protected function doTestUser1WithContexts($contexts) {
     \Drupal::service('account_switcher')->switchTo($second_authenticated_user);
     $element = $test_element;
     $element['#markup'] = 'should not be used';
-    $output = \Drupal::service('renderer')->render($element);
+    $output = \Drupal::service('renderer')->renderRoot($element);
     $this->assertEqual($output, 'content for authenticated users');
     \Drupal::service('account_switcher')->switchBack();
 
@@ -111,7 +111,7 @@ protected function doTestUser1WithContexts($contexts) {
     \Drupal::service('account_switcher')->switchTo($admin_user);
     $element = $test_element;
     $element['#markup'] = 'content for admin user';
-    $output = \Drupal::service('renderer')->render($element);
+    $output = \Drupal::service('renderer')->renderRoot($element);
     $this->assertEqual($output, 'content for admin user');
     \Drupal::service('account_switcher')->switchBack();
   }
diff --git a/core/modules/system/src/Tests/Theme/FunctionsTest.php b/core/modules/system/src/Tests/Theme/FunctionsTest.php
index a9a3dae..0ed135d 100644
--- a/core/modules/system/src/Tests/Theme/FunctionsTest.php
+++ b/core/modules/system/src/Tests/Theme/FunctionsTest.php
@@ -347,7 +347,7 @@ function testDrupalPreRenderLinks() {
     // thing. We expect a single <ul> with appropriate links contained within
     // it.
     $render_array = $base_array;
-    $html = drupal_render($render_array);
+    $html = \Drupal::service('renderer')->renderRoot($render_array);
     $dom = new \DOMDocument();
     $dom->loadHTML($html);
     $this->assertEqual($dom->getElementsByTagName('ul')->length, 1, 'One "ul" tag found in the rendered HTML.');
@@ -363,8 +363,8 @@ function testDrupalPreRenderLinks() {
     // sure we get two separate <ul>'s with the appropriate links contained
     // within each.
     $render_array = $base_array;
-    $child_html = drupal_render($render_array['first_child']);
-    $parent_html = drupal_render($render_array);
+    $child_html = \Drupal::service('renderer')->renderRoot($render_array['first_child']);
+    $parent_html = \Drupal::service('renderer')->renderRoot($render_array);
     // First check the child HTML.
     $dom = new \DOMDocument();
     $dom->loadHTML($child_html);
diff --git a/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php b/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
index 4b6d528..1b330d0 100644
--- a/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
+++ b/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
@@ -27,6 +27,8 @@ class TwigDebugMarkupTest extends WebTestBase {
    * Tests debug markup added to Twig template output.
    */
   function testTwigDebugMarkup() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $extension = twig_extension();
     \Drupal::service('theme_handler')->install(array('test_theme'));
     $this->config('system.theme')->set('default', 'test_theme')->save();
@@ -46,7 +48,7 @@ function testTwigDebugMarkup() {
     // Create a node and test different features of the debug markup.
     $node = $this->drupalCreateNode();
     $build = node_view($node);
-    $output = drupal_render($build);
+    $output = $renderer->renderRoot($build);
     $this->assertTrue(strpos($output, '<!-- THEME DEBUG -->') !== FALSE, 'Twig debug markup found in theme output when debug is enabled.');
     $this->setRawContent($output);
     $this->assertTrue(strpos($output, "THEME HOOK: 'node'") !== FALSE, 'Theme call information found.');
@@ -59,7 +61,7 @@ function testTwigDebugMarkup() {
     // debug markup are correct.
     $node2 = $this->drupalCreateNode();
     $build = node_view($node2);
-    $output = drupal_render($build);
+    $output = $renderer->renderRoot($build);
     $this->assertTrue(strpos($output, '* node--2--full' . $extension  . PHP_EOL . '   * node--2' . $extension . PHP_EOL . '   * node--page--full' . $extension . PHP_EOL . '   * node--page' . $extension . PHP_EOL . '   * node--full' . $extension . PHP_EOL . '   x node' . $extension) !== FALSE, 'Suggested template files found in order and base template shown as current template.');
 
     // Create another node and make sure the template suggestions shown in the
@@ -67,7 +69,7 @@ function testTwigDebugMarkup() {
     $node3 = $this->drupalCreateNode();
     $build = array('#theme' => 'node__foo__bar');
     $build += node_view($node3);
-    $output = drupal_render($build);
+    $output = $renderer->renderRoot($build);
     $this->assertTrue(strpos($output, "THEME HOOK: 'node__foo__bar'") !== FALSE, 'Theme call information found.');
     $this->assertTrue(strpos($output, '* node--foo--bar' . $extension . PHP_EOL . '   * node--foo' . $extension . PHP_EOL . '   * node--&lt;script type=&quot;text/javascript&quot;&gt;alert(&#039;yo&#039;);&lt;/script&gt;' . $extension . PHP_EOL . '   * node--3--full' . $extension . PHP_EOL . '   * node--3' . $extension . PHP_EOL . '   * node--page--full' . $extension . PHP_EOL . '   * node--page' . $extension . PHP_EOL . '   * node--full' . $extension . PHP_EOL . '   x node' . $extension) !== FALSE, 'Suggested template files found in order and base template shown as current template.');
 
@@ -79,7 +81,7 @@ function testTwigDebugMarkup() {
     $this->resetAll();
 
     $build = node_view($node);
-    $output = drupal_render($build);
+    $output = $renderer->renderRoot($build);
     $this->assertFalse(strpos($output, '<!-- THEME DEBUG -->') !== FALSE, 'Twig debug markup not found in theme output when debug is disabled.');
   }
 
diff --git a/core/modules/system/src/Tests/Theme/TwigEnvironmentTest.php b/core/modules/system/src/Tests/Theme/TwigEnvironmentTest.php
index f113ead..b5450df 100644
--- a/core/modules/system/src/Tests/Theme/TwigEnvironmentTest.php
+++ b/core/modules/system/src/Tests/Theme/TwigEnvironmentTest.php
@@ -30,6 +30,8 @@ class TwigEnvironmentTest extends KernelTestBase {
    * Tests inline templates.
    */
   public function testInlineTemplate() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     /** @var \Drupal\Core\Template\TwigEnvironment $environment */
     $environment = \Drupal::service('twig');
     $this->assertEqual($environment->renderInline('test-no-context'), 'test-no-context');
@@ -42,7 +44,7 @@ public function testInlineTemplate() {
       '#template' => 'test-with-context {{ unsafe_content }}',
       '#context' => array('unsafe_content' => $unsafe_string),
     );
-    $this->assertEqual(drupal_render($element), 'test-with-context ' . SafeMarkup::checkPlain($unsafe_string));
+    $this->assertEqual($renderer->renderRoot($element), 'test-with-context ' . SafeMarkup::checkPlain($unsafe_string));
 
     // Enable twig_auto_reload and twig_debug.
     $settings = Settings::getAll();
@@ -61,8 +63,8 @@ public function testInlineTemplate() {
     );
     $element_copy = $element;
     // Render it twice so that twig caching is triggered.
-    $this->assertEqual(drupal_render($element), 'test-with-context muuh');
-    $this->assertEqual(drupal_render($element_copy), 'test-with-context muuh');
+    $this->assertEqual($renderer->renderRoot($element), 'test-with-context muuh');
+    $this->assertEqual($renderer->renderRoot($element_copy), 'test-with-context muuh');
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Theme/TwigFilterTest.php b/core/modules/system/src/Tests/Theme/TwigFilterTest.php
index 3aa2d54..2adc8c9 100644
--- a/core/modules/system/src/Tests/Theme/TwigFilterTest.php
+++ b/core/modules/system/src/Tests/Theme/TwigFilterTest.php
@@ -41,7 +41,7 @@ public function testTwigWithoutFilter() {
         'class' => array('red', 'green', 'blue'),
       ),
     );
-    $rendered = drupal_render($filter_test);
+    $rendered = \Drupal::service('renderer')->renderRoot($filter_test);
     $this->setRawContent($rendered);
 
     $elements = array(
diff --git a/core/modules/system/src/Tests/Theme/TwigNamespaceTest.php b/core/modules/system/src/Tests/Theme/TwigNamespaceTest.php
index 2b6f3c5..4065e84 100644
--- a/core/modules/system/src/Tests/Theme/TwigNamespaceTest.php
+++ b/core/modules/system/src/Tests/Theme/TwigNamespaceTest.php
@@ -58,7 +58,7 @@ public function testTemplateDiscovery() {
   public function testTwigNamespaces() {
     // Test twig @extends and @include in template files.
     $test = array('#theme' => 'twig_namespace_test');
-    $this->setRawContent(drupal_render($test));
+    $this->setRawContent(\Drupal::service('renderer')->renderRoot($test));
 
     $this->assertText('This line is from twig_namespace_a/templates/test.html.twig');
     $this->assertText('This line is from twig_namespace_b/templates/test.html.twig');
diff --git a/core/modules/system/src/Tests/Theme/TwigRawTest.php b/core/modules/system/src/Tests/Theme/TwigRawTest.php
index 5cec948..7ef605f 100644
--- a/core/modules/system/src/Tests/Theme/TwigRawTest.php
+++ b/core/modules/system/src/Tests/Theme/TwigRawTest.php
@@ -31,7 +31,7 @@ public function testAutoescapeRaw() {
       '#theme' => 'twig_raw_test',
       '#script' => '<script>alert("This alert is real because I will put it through the raw filter!");</script>',
     );
-    $rendered = drupal_render($test);
+    $rendered = \Drupal::service('renderer')->renderRoot($test);
     $this->setRawContent($rendered);
     $this->assertRaw('<script>alert("This alert is real because I will put it through the raw filter!");</script>');
   }
@@ -48,7 +48,7 @@ public function testAutoescape() {
       '#theme' => 'twig_autoescape_test',
       '#script' => $script,
     ];
-    $rendered = drupal_render($build);
+    $rendered = \Drupal::service('renderer')->renderRoot($build);
     $this->setRawContent($rendered);
     $this->assertEscaped($script);
   }
diff --git a/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php b/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php
index 8d89b6e..6b43aa8 100644
--- a/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php
+++ b/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php
@@ -77,8 +77,7 @@ public function jsAndCssQuerystring() {
         ),
       ),
     );
-    drupal_render($attached);
-    return '';
+    return \Drupal::service('renderer')->renderRoot($attached);
   }
 
   /**
diff --git a/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php b/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
index d8f609a..86a0128 100644
--- a/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
+++ b/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\theme_test\EventSubscriber;
 
+use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Url;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Symfony\Component\HttpKernel\KernelEvents;
@@ -33,12 +34,22 @@ class ThemeTestSubscriber implements EventSubscriberInterface {
   protected $currentRouteMatch;
 
   /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
    * Constructs a new ThemeTestSubscriber.
    *
    * @param \Drupal\Core\Routing\RouteMatchInterface $current_route_match
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
    */
-  public function __construct(RouteMatchInterface $current_route_match) {
+  public function __construct(RouteMatchInterface $current_route_match, RendererInterface $renderer) {
     $this->currentRouteMatch = $current_route_match;
+    $this->renderer = $renderer;
   }
 
   /**
@@ -62,7 +73,7 @@ public function onRequest(GetResponseEvent $event) {
         '#url' => Url::fromRoute('user.page'),
         '#attributes' => array('title' => 'Themed output generated in a KernelEvents::REQUEST listener'),
       );
-      $GLOBALS['theme_test_output'] = drupal_render($more_link);
+      $GLOBALS['theme_test_output'] = $this->renderer->renderPlain($more_link);
     }
   }
 
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.services.yml b/core/modules/system/tests/modules/theme_test/theme_test.services.yml
index add8b4c..482c219 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.services.yml
+++ b/core/modules/system/tests/modules/theme_test/theme_test.services.yml
@@ -1,7 +1,7 @@
 services:
   theme_test.subscriber:
     class: Drupal\theme_test\EventSubscriber\ThemeTestSubscriber
-    arguments: [@current_route_match]
+    arguments: [@current_route_match, @renderer]
     tags:
       - { name: event_subscriber }
 
diff --git a/core/modules/text/src/Tests/Formatter/TextFormatterTest.php b/core/modules/text/src/Tests/Formatter/TextFormatterTest.php
index cae892e..324f2cf 100644
--- a/core/modules/text/src/Tests/Formatter/TextFormatterTest.php
+++ b/core/modules/text/src/Tests/Formatter/TextFormatterTest.php
@@ -90,7 +90,7 @@ public function testFormatters() {
     foreach ($formatters as $formatter) {
       // Verify the text field formatter's render array.
       $build = $entity->get('formatted_text')->view(array('type' => $formatter));
-      drupal_render($build[0]);
+      \Drupal::service('renderer')->renderRoot($build[0]);
       $this->assertEqual($build[0]['#markup'], "<p>Hello, world!</p>\n");
       $this->assertEqual($build[0]['#cache']['tags'], FilterFormat::load('my_text_format')->getCacheTags(), format_string('The @formatter formatter has the expected cache tags when formatting a formatted text field.', array('@formatter' => $formatter)));
     }
diff --git a/core/modules/text/src/Tests/TextFieldTest.php b/core/modules/text/src/Tests/TextFieldTest.php
index 1507a02..37120c9 100644
--- a/core/modules/text/src/Tests/TextFieldTest.php
+++ b/core/modules/text/src/Tests/TextFieldTest.php
@@ -87,6 +87,9 @@ function testTextfieldWidgetsFormatted() {
    * Helper function for testTextfieldWidgetsFormatted().
    */
   function _testTextfieldWidgetsFormatted($field_type, $widget_type) {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     // Create a field.
     $field_name = Unicode::strtolower($this->randomMachineName());
     $field_storage = entity_create('field_storage_config', array(
@@ -138,7 +141,7 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) {
     $entity = entity_load('entity_test', $id);
     $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
     $content = $display->build($entity);
-    $this->setRawContent(drupal_render($content));
+    $this->setRawContent($renderer->renderRoot($content));
     $this->assertNoRaw($value, 'HTML tags are not displayed.');
     $this->assertEscaped($value, 'Escaped HTML is displayed correctly.');
 
@@ -177,7 +180,7 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) {
     $entity = entity_load('entity_test', $id);
     $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
     $content = $display->build($entity);
-    $this->setRawContent(drupal_render($content));
+    $this->setRawContent($renderer->renderRoot($content));
     $this->assertRaw($value, 'Value is displayed unfiltered');
   }
 
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index bca08e8..af992d1 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -287,7 +287,7 @@ function toolbar_get_rendered_subtrees() {
     $link = $element->link;
     if ($element->subtree) {
       $subtree = $menu_tree->build($element->subtree);
-      $output = drupal_render($subtree);
+      $output = \Drupal::service('renderer')->renderPlain($subtree);
     }
     else {
       $output = '';
diff --git a/core/modules/tour/tests/tour_test/src/Plugin/tour/tip/TipPluginImage.php b/core/modules/tour/tests/tour_test/src/Plugin/tour/tip/TipPluginImage.php
index 109c587..90a5a4f 100644
--- a/core/modules/tour/tests/tour_test/src/Plugin/tour/tip/TipPluginImage.php
+++ b/core/modules/tour/tests/tour_test/src/Plugin/tour/tip/TipPluginImage.php
@@ -40,14 +40,15 @@ class TipPluginImage extends TipPluginBase {
    * {@inheritdoc}
    */
   public function getOutput() {
-    $image = array(
+    $prefix = '<h2 class="tour-tip-label" id="tour-tip-' . $this->get('ariaId') . '-label">' . SafeMarkup::checkPlain($this->get('label')) . '</h2>';
+    $prefix .= '<p class="tour-tip-image" id="tour-tip-' . $this->get('ariaId') . '-contents">';
+    return [
+      '#prefix' => $prefix,
       '#theme' => 'image',
       '#uri' => $this->get('url'),
       '#alt' => $this->get('alt'),
-    );
-    $output = '<h2 class="tour-tip-label" id="tour-tip-' . $this->get('ariaId') . '-label">' . SafeMarkup::checkPlain($this->get('label')) . '</h2>';
-    $output .= '<p class="tour-tip-image" id="tour-tip-' . $this->get('ariaId') . '-contents">' . drupal_render($image) . '</p>';
-    return array('#markup' => $output);
+      '#suffix' => '</p>',
+    ];
   }
 
 }
diff --git a/core/modules/user/src/Tests/UserBlocksTest.php b/core/modules/user/src/Tests/UserBlocksTest.php
index 366f519..830b87a 100644
--- a/core/modules/user/src/Tests/UserBlocksTest.php
+++ b/core/modules/user/src/Tests/UserBlocksTest.php
@@ -105,7 +105,7 @@ function testWhosOnlineBlock() {
     // Test block output.
     \Drupal::currentUser()->setAccount($user1);
     $content = entity_view($block, 'block');
-    $this->setRawContent(render($content));
+    $this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
     $this->assertRaw(t('2 users'), 'Correct number of online users (2 users).');
     $this->assertText($user1->getUsername(), 'Active user 1 found in online list.');
     $this->assertText($user2->getUsername(), 'Active user 2 found in online list.');
diff --git a/core/modules/views/src/Controller/ViewAjaxController.php b/core/modules/views/src/Controller/ViewAjaxController.php
index 6b7e1d5..83a203b 100644
--- a/core/modules/views/src/Controller/ViewAjaxController.php
+++ b/core/modules/views/src/Controller/ViewAjaxController.php
@@ -175,8 +175,7 @@ public function ajaxView(Request $request) {
         $view->dom_id = $dom_id;
 
         if ($preview = $view->preview($display_id, $args)) {
-          $response->addCommand(new ReplaceCommand(".js-view-dom-id-$dom_id", $this->renderer->render($preview)));
-          $response->setAttachments($preview['#attached']);
+          $response->addCommand(new ReplaceCommand(".js-view-dom-id-$dom_id", $preview));
         }
         return $response;
       }
diff --git a/core/modules/views/src/Plugin/views/style/StylePluginBase.php b/core/modules/views/src/Plugin/views/style/StylePluginBase.php
index 8fa4617..af10867 100644
--- a/core/modules/views/src/Plugin/views/style/StylePluginBase.php
+++ b/core/modules/views/src/Plugin/views/style/StylePluginBase.php
@@ -678,7 +678,7 @@ protected function renderFields(array $result) {
             '#cache_properties' => $field_ids,
           ];
           $renderer->addCacheableDependency($data, $this->view->storage);
-          $renderer->render($data);
+          $renderer->renderPlain($data);
 
           // Extract field output from the render array and post process it.
           $fields = $this->view->field;
diff --git a/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php b/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php
index 6a8a12d..1037f45 100644
--- a/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php
+++ b/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php
@@ -220,7 +220,7 @@ protected function assertTranslations($display, $renderer_id, array $expected, $
     foreach ($expected as $index => $expected_output) {
       if (!empty($view->result[$index])) {
         $build = $view->rowPlugin->render($view->result[$index]);
-        $output = drupal_render($build);
+        $output = \Drupal::service('renderer')->renderRoot($build);
         $result = strpos($output, $expected_output) !== FALSE;
         if (!$result) {
           break;
diff --git a/core/modules/views/src/Tests/Handler/AreaEntityTest.php b/core/modules/views/src/Tests/Handler/AreaEntityTest.php
index 28d867f..4bc9af7 100644
--- a/core/modules/views/src/Tests/Handler/AreaEntityTest.php
+++ b/core/modules/views/src/Tests/Handler/AreaEntityTest.php
@@ -122,9 +122,11 @@ public function testEntityArea() {
    *   The entities.
    */
   public function doTestRender($entities) {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_entity_area');
     $preview = $view->preview('default', [$entities[1]->id()]);
-    $this->setRawContent(\Drupal::service('renderer')->render($preview));
+    $this->setRawContent(\Drupal::service('renderer')->renderRoot($preview));
     $view_class = 'js-view-dom-id-' . $view->dom_id;
     $header_xpath = '//div[@class = "' . $view_class . '"]/div[1]';
     $footer_xpath = '//div[@class = "' . $view_class . '"]/div[3]';
@@ -138,7 +140,7 @@ public function doTestRender($entities) {
     $this->assertTrue(strpos(trim((string) $result[0]), 'full') !== FALSE, 'The rendered entity appeared in the right view mode.');
 
     $preview = $view->preview('default', array($entities[1]->id()));
-    $this->setRawContent(drupal_render($preview));
+    $this->setRawContent($renderer->renderRoot($preview));
 
     $result = $this->xpath($header_xpath);
     $this->assertTrue(strpos(trim((string) $result[0]), $entities[0]->label()) !== FALSE, 'The rendered entity appears in the header of the view.');
@@ -160,7 +162,7 @@ public function doTestRender($entities) {
     $view->setHandler('default', 'header', 'entity_entity_test', $item);
 
     $preview = $view->preview('default', array($entities[1]->id()));
-    $this->setRawContent(drupal_render($preview));
+    $this->setRawContent($renderer->renderRoot($preview));
     $view_class = 'js-view-dom-id-' . $view->dom_id;
     $result = $this->xpath('//div[@class = "' . $view_class . '"]/div[1]');
     $this->assertTrue(strpos(trim((string) $result[0]), $entities[0]->label()) !== FALSE, 'The rendered entity appears in the header of the view.');
@@ -169,7 +171,7 @@ public function doTestRender($entities) {
     // Test entity access.
     $view = Views::getView('test_entity_area');
     $preview = $view->preview('default', array($entities[2]->id()));
-    $this->setRawContent(drupal_render($preview));
+    $this->setRawContent($renderer->renderRoot($preview));
     $view_class = 'js-view-dom-id-' . $view->dom_id;
     $result = $this->xpath('//div[@class = "' . $view_class . '"]/div[3]');
     $this->assertTrue(strpos($result[0], $entities[2]->label()) === FALSE, 'The rendered entity does not appear in the footer of the view.');
diff --git a/core/modules/views/src/Tests/Handler/AreaTest.php b/core/modules/views/src/Tests/Handler/AreaTest.php
index 385db71..bdc1e48 100644
--- a/core/modules/views/src/Tests/Handler/AreaTest.php
+++ b/core/modules/views/src/Tests/Handler/AreaTest.php
@@ -108,7 +108,7 @@ public function testRenderArea() {
 
     // Check whether the strings exist in the output and are sanitized.
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $this->container->get('renderer')->renderRoot($output);
     $this->assertTrue(strpos($output, Xss::filterAdmin($header_string)) !== FALSE, 'Views header exists in the output and is sanitized');
     $this->assertTrue(strpos($output, Xss::filterAdmin($footer_string)) !== FALSE, 'Views footer exists in the output and is sanitized');
     $this->assertTrue(strpos($output, Xss::filterAdmin($empty_string)) !== FALSE, 'Views empty exists in the output and is sanitized');
@@ -127,7 +127,7 @@ public function testAreaAccess() {
     $this->assertEqual(0, count($handlers));
 
     $output = $view->preview();
-    $output = \Drupal::service('renderer')->render($output);
+    $output = \Drupal::service('renderer')->renderRoot($output);
     // The area output should not be present since access was denied.
     $this->assertFalse(strpos($output, 'a custom string') !== FALSE);
     $view->destroy();
@@ -149,7 +149,7 @@ public function testAreaAccess() {
     $handlers = $view->display_handler->getHandlers('empty');
 
     $output = $view->preview();
-    $output = \Drupal::service('renderer')->render($output);
+    $output = \Drupal::service('renderer')->renderRoot($output);
     $this->assertTrue(strpos($output, 'a custom string') !== FALSE);
     $this->assertEqual(1, count($handlers));
   }
@@ -187,7 +187,7 @@ public function testRenderAreaToken() {
 
     // Test we have the site:name token in the output.
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $this->container->get('renderer')->renderRoot($output);
     $expected = \Drupal::token()->replace('[site:name]');
     $this->assertTrue(strpos($output, $expected) !== FALSE);
   }
diff --git a/core/modules/views/src/Tests/Handler/AreaTextTest.php b/core/modules/views/src/Tests/Handler/AreaTextTest.php
index 5387a6e..e37f2a2 100644
--- a/core/modules/views/src/Tests/Handler/AreaTextTest.php
+++ b/core/modules/views/src/Tests/Handler/AreaTextTest.php
@@ -35,6 +35,8 @@ protected function setUp() {
   }
 
   public function testAreaText() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_view');
     $view->setDisplay();
 
@@ -56,18 +58,18 @@ public function testAreaText() {
 
     $view->display_handler->handlers['header']['area']->options['content']['format'] = $this->randomString();
     $build = $view->display_handler->handlers['header']['area']->render();
-    $this->assertEqual('', drupal_render($build), 'Nonexistent format should return empty markup.');
+    $this->assertEqual('', $renderer->renderRoot($build), 'Nonexistent format should return empty markup.');
 
     $view->display_handler->handlers['header']['area']->options['content']['format'] = filter_default_format();
     $build = $view->display_handler->handlers['header']['area']->render();
-    $this->assertEqual(check_markup($string), drupal_render($build), 'Existent format should return something');
+    $this->assertEqual(check_markup($string), $renderer->renderRoot($build), 'Existent format should return something');
 
     // Empty results, and it shouldn't be displayed .
     $this->assertEqual(array(), $view->display_handler->handlers['header']['area']->render(TRUE), 'No result should lead to no header');
     // Empty results, and it should be displayed.
     $view->display_handler->handlers['header']['area']->options['empty'] = TRUE;
     $build = $view->display_handler->handlers['header']['area']->render(TRUE);
-    $this->assertEqual(check_markup($string), drupal_render($build), 'No result, but empty enabled lead to a full header');
+    $this->assertEqual(check_markup($string), $renderer->renderRoot($build), 'No result, but empty enabled lead to a full header');
   }
 
 }
diff --git a/core/modules/views/src/Tests/Handler/AreaViewTest.php b/core/modules/views/src/Tests/Handler/AreaViewTest.php
index dc03fa9..858e4fa 100644
--- a/core/modules/views/src/Tests/Handler/AreaViewTest.php
+++ b/core/modules/views/src/Tests/Handler/AreaViewTest.php
@@ -36,6 +36,8 @@ class AreaViewTest extends ViewUnitTestBase {
    * Tests the view area handler.
    */
   public function testViewArea() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_area_view');
 
     // Tests \Drupal\views\Plugin\views\area\View::calculateDependencies().
@@ -43,14 +45,14 @@ public function testViewArea() {
 
     $this->executeView($view);
     $output = $view->render();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     $this->assertTrue(strpos($output, 'js-view-dom-id-' . $view->dom_id) !== FALSE, 'The test view is correctly embedded.');
     $view->destroy();
 
     $view->setArguments(array(27));
     $this->executeView($view);
     $output = $view->render();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     $this->assertTrue(strpos($output, 'John') === FALSE, 'The test view is correctly embedded with inherited arguments.');
     $this->assertTrue(strpos($output, 'George') !== FALSE, 'The test view is correctly embedded with inherited arguments.');
     $view->destroy();
diff --git a/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php b/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php
index 5554a8a..09b397d 100644
--- a/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php
+++ b/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php
@@ -125,7 +125,7 @@ protected function assertFieldAccess($entity_type_id, $field_name, $field_conten
     $account_switcher->switchTo($this->userWithAccess);
     $executable = Views::getView($view_id);
     $build = $executable->preview();
-    $this->setRawContent($renderer->render($build));
+    $this->setRawContent($renderer->renderRoot($build));
 
     $this->assertText($field_content);
     $this->assertTrue(isset($executable->field[$field_name]));
@@ -133,7 +133,7 @@ protected function assertFieldAccess($entity_type_id, $field_name, $field_conten
     $account_switcher->switchTo($this->userWithoutAccess);
     $executable = Views::getView($view_id);
     $build = $executable->preview();
-    $this->setRawContent($renderer->render($build));
+    $this->setRawContent($renderer->renderRoot($build));
 
     $this->assertNoText($field_content);
     $this->assertFalse(isset($executable->field[$field_name]));
diff --git a/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php b/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php
index 2aa0e9c..96976db 100644
--- a/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php
+++ b/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\views\Tests\Handler;
 
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Render\RenderContext;
 use Drupal\views\Views;
 
 /**
@@ -67,6 +68,9 @@ protected function setUp() {
    * Testing the "Grouped rows" functionality.
    */
   public function testGroupRows() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
     $edit = array(
       'title' => $this->randomMachineName(),
       $this->fieldName => array('a', 'b', 'c'),
@@ -77,7 +81,10 @@ public function testGroupRows() {
 
     // Test grouped rows.
     $this->executeView($view);
-    $this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[0]), 'a, b, c');
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field[$this->fieldName]->advancedRender($view->result[0]);
+    });
+    $this->assertEqual($output, 'a, b, c');
 
     // Change the group_rows checkbox to false.
     $view = Views::getView('test_group_rows');
@@ -88,11 +95,20 @@ public function testGroupRows() {
     $view->render();
 
     $view->row_index = 0;
-    $this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[0]), 'a');
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field[$this->fieldName]->advancedRender($view->result[0]);
+    });
+    $this->assertEqual($output, 'a');
     $view->row_index = 1;
-    $this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[1]), 'b');
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field[$this->fieldName]->advancedRender($view->result[1]);
+    });
+    $this->assertEqual($output, 'b');
     $view->row_index = 2;
-    $this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[2]), 'c');
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field[$this->fieldName]->advancedRender($view->result[2]);
+    });
+    $this->assertEqual($output, 'c');
   }
 
 }
diff --git a/core/modules/views/src/Tests/Handler/FieldUnitTest.php b/core/modules/views/src/Tests/Handler/FieldUnitTest.php
index 7c319d4..5a5d73f 100644
--- a/core/modules/views/src/Tests/Handler/FieldUnitTest.php
+++ b/core/modules/views/src/Tests/Handler/FieldUnitTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Tests\Handler;
 
+use Drupal\Core\Render\RenderContext;
 use Drupal\views\Tests\ViewUnitTestBase;
 use Drupal\views\Plugin\views\field\FieldPluginBase;
 use Drupal\views\Views;
@@ -52,12 +53,18 @@ protected function viewsData() {
    * Tests that the render function is called.
    */
   public function testRender() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
     $view = Views::getView('test_field_tokens');
     $this->executeView($view);
 
     $random_text = $this->randomMachineName();
     $view->field['job']->setTestValue($random_text);
-    $this->assertEqual($view->field['job']->theme($view->result[0]), $random_text, 'Make sure the render method rendered the manual set value.');
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['job']->theme($view->result[0]);
+    });
+    $this->assertEqual($output, $random_text, 'Make sure the render method rendered the manual set value.');
   }
 
   /**
@@ -141,6 +148,9 @@ protected function assertNotSubString($haystack, $needle, $message = '', $group
    * Tests general rewriting of the output.
    */
   public function testRewrite() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
     $view = Views::getView('test_view');
     $view->initHandlers();
     $this->executeView($view);
@@ -149,11 +159,15 @@ public function testRewrite() {
 
     // Don't check the rewrite checkbox, so the text shouldn't appear.
     $id_field->options['alter']['text'] = $random_text = $this->randomMachineName();
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertNotSubString($output, $random_text);
 
     $id_field->options['alter']['alter_text'] = TRUE;
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertSubString($output, $random_text);
   }
 
@@ -161,6 +175,9 @@ public function testRewrite() {
    * Tests the field tokens, row level and field level.
    */
   public function testFieldTokens() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
     $view = Views::getView('test_field_tokens');
     $this->executeView($view);
     $name_field_0 = $view->field['name'];
@@ -182,19 +199,25 @@ public function testFieldTokens() {
       $expected_output_1 = "$row->views_test_data_name $row->views_test_data_name";
       $expected_output_2 = "$row->views_test_data_name $row->views_test_data_name $row->views_test_data_name";
 
-      $output = $name_field_0->advancedRender($row);
+      $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_0, $row) {
+        return $name_field_0->advancedRender($row);
+      });
       $this->assertEqual($output, $expected_output_0, format_string('Test token replacement: "!token" gave "!output"', [
         '!token' => $name_field_0->options['alter']['text'],
         '!output' => $output,
       ]));
 
-      $output = $name_field_1->advancedRender($row);
+      $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_1, $row) {
+        return $name_field_1->advancedRender($row);
+      });
       $this->assertEqual($output, $expected_output_1, format_string('Test token replacement: "!token" gave "!output"', [
         '!token' => $name_field_1->options['alter']['text'],
         '!output' => $output,
       ]));
 
-      $output = $name_field_2->advancedRender($row);
+      $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_2, $row) {
+        return $name_field_2->advancedRender($row);
+      });
       $this->assertEqual($output, $expected_output_2, format_string('Test token replacement: "!token" gave "!output"', [
         '!token' => $name_field_2->options['alter']['text'],
         '!output' => $output,
@@ -207,7 +230,9 @@ public function testFieldTokens() {
 
     $random_text = $this->randomMachineName();
     $job_field->setTestValue($random_text);
-    $output = $job_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) {
+      return $job_field->advancedRender($row);
+    });
     $this->assertSubString($output, $random_text, format_string('Make sure the self token (!token => !value) appears in the output (!output)', [
       '!value' => $random_text,
       '!output' => $output,
@@ -219,7 +244,9 @@ public function testFieldTokens() {
     $job_field->options['alter']['text'] = $old_token;
     $random_text = $this->randomMachineName();
     $job_field->setTestValue($random_text);
-    $output = $job_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) {
+      return $job_field->advancedRender($row);
+    });
     $this->assertSubString($output, $old_token, format_string('Make sure the old token style (!token => !value) is not changed in the output (!output)', [
       '!value' => $random_text,
       '!output' => $output,
@@ -231,13 +258,15 @@ public function testFieldTokens() {
    * Tests the exclude setting.
    */
   public function testExclude() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_field_output');
     $view->initHandlers();
     // Hide the field and see whether it's rendered.
     $view->field['name']->options['exclude'] = TRUE;
 
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     foreach ($this->dataSet() as $entry) {
       $this->assertNotSubString($output, $entry['name']);
     }
@@ -246,7 +275,7 @@ public function testExclude() {
     $view->field['name']->options['exclude'] = FALSE;
 
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     foreach ($this->dataSet() as $entry) {
       $this->assertSubString($output, $entry['name']);
     }
@@ -266,6 +295,9 @@ function testEmpty() {
    * This tests alters the result to get easier and less coupled results.
    */
   function _testHideIfEmpty() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
     $view = Views::getView('test_view');
     $view->initDisplay();
     $this->executeView($view);
@@ -282,22 +314,30 @@ function _testHideIfEmpty() {
 
     // Test a valid string.
     $view->result[0]->{$column_map_reversed['name']} = $random_name;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $random_name, 'By default, a string should not be treated as empty.');
 
     // Test an empty string.
     $view->result[0]->{$column_map_reversed['name']} = "";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "", 'By default, "" should not be treated as empty.');
 
     // Test zero as an integer.
     $view->result[0]->{$column_map_reversed['name']} = 0;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, '0', 'By default, 0 should not be treated as empty.');
 
     // Test zero as a string.
     $view->result[0]->{$column_map_reversed['name']} = "0";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "0", 'By default, "0" should not be treated as empty.');
 
     // Test when results are not rewritten and non-zero empty values are hidden.
@@ -307,22 +347,30 @@ function _testHideIfEmpty() {
 
     // Test a valid string.
     $view->result[0]->{$column_map_reversed['name']} = $random_name;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $random_name, 'If hide_empty is checked, a string should not be treated as empty.');
 
     // Test an empty string.
     $view->result[0]->{$column_map_reversed['name']} = "";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "", 'If hide_empty is checked, "" should be treated as empty.');
 
     // Test zero as an integer.
     $view->result[0]->{$column_map_reversed['name']} = 0;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, '0', 'If hide_empty is checked, but not empty_zero, 0 should not be treated as empty.');
 
     // Test zero as a string.
     $view->result[0]->{$column_map_reversed['name']} = "0";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "0", 'If hide_empty is checked, but not empty_zero, "0" should not be treated as empty.');
 
     // Test when results are not rewritten and all empty values are hidden.
@@ -332,12 +380,16 @@ function _testHideIfEmpty() {
 
     // Test zero as an integer.
     $view->result[0]->{$column_map_reversed['name']} = 0;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "", 'If hide_empty and empty_zero are checked, 0 should be treated as empty.');
 
     // Test zero as a string.
     $view->result[0]->{$column_map_reversed['name']} = "0";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "", 'If hide_empty and empty_zero are checked, "0" should be treated as empty.');
 
     // Test when results are rewritten to a valid string and non-zero empty
@@ -350,22 +402,30 @@ function _testHideIfEmpty() {
 
     // Test a valid string.
     $view->result[0]->{$column_map_reversed['name']} = $random_value;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $random_name, 'If the rewritten string is not empty, it should not be treated as empty.');
 
     // Test an empty string.
     $view->result[0]->{$column_map_reversed['name']} = "";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $random_name, 'If the rewritten string is not empty, "" should not be treated as empty.');
 
     // Test zero as an integer.
     $view->result[0]->{$column_map_reversed['name']} = 0;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $random_name, 'If the rewritten string is not empty, 0 should not be treated as empty.');
 
     // Test zero as a string.
     $view->result[0]->{$column_map_reversed['name']} = "0";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $random_name, 'If the rewritten string is not empty, "0" should not be treated as empty.');
 
     // Test when results are rewritten to an empty string and non-zero empty results are hidden.
@@ -377,22 +437,30 @@ function _testHideIfEmpty() {
 
     // Test a valid string.
     $view->result[0]->{$column_map_reversed['name']} = $random_name;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $random_name, 'If the rewritten string is empty, it should not be treated as empty.');
 
     // Test an empty string.
     $view->result[0]->{$column_map_reversed['name']} = "";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "", 'If the rewritten string is empty, "" should be treated as empty.');
 
     // Test zero as an integer.
     $view->result[0]->{$column_map_reversed['name']} = 0;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, '0', 'If the rewritten string is empty, 0 should not be treated as empty.');
 
     // Test zero as a string.
     $view->result[0]->{$column_map_reversed['name']} = "0";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "0", 'If the rewritten string is empty, "0" should not be treated as empty.');
 
     // Test when results are rewritten to zero as a string and non-zero empty
@@ -405,22 +473,30 @@ function _testHideIfEmpty() {
 
     // Test a valid string.
     $view->result[0]->{$column_map_reversed['name']} = $random_name;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "0", 'If the rewritten string is zero and empty_zero is not checked, the string rewritten as 0 should not be treated as empty.');
 
     // Test an empty string.
     $view->result[0]->{$column_map_reversed['name']} = "";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "0", 'If the rewritten string is zero and empty_zero is not checked, "" rewritten as 0 should not be treated as empty.');
 
     // Test zero as an integer.
     $view->result[0]->{$column_map_reversed['name']} = 0;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "0", 'If the rewritten string is zero and empty_zero is not checked, 0 should not be treated as empty.');
 
     // Test zero as a string.
     $view->result[0]->{$column_map_reversed['name']} = "0";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "0", 'If the rewritten string is zero and empty_zero is not checked, "0" should not be treated as empty.');
 
     // Test when results are rewritten to a valid string and non-zero empty
@@ -433,22 +509,30 @@ function _testHideIfEmpty() {
 
     // Test a valid string.
     $view->result[0]->{$column_map_reversed['name']} = $random_name;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $random_value, 'If the original and rewritten strings are valid, it should not be treated as empty.');
 
     // Test an empty string.
     $view->result[0]->{$column_map_reversed['name']} = "";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "", 'If either the original or rewritten string is invalid, "" should be treated as empty.');
 
     // Test zero as an integer.
     $view->result[0]->{$column_map_reversed['name']} = 0;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $random_value, 'If the original and rewritten strings are valid, 0 should not be treated as empty.');
 
     // Test zero as a string.
     $view->result[0]->{$column_map_reversed['name']} = "0";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $random_value, 'If the original and rewritten strings are valid, "0" should not be treated as empty.');
 
     // Test when results are rewritten to zero as a string and all empty
@@ -461,22 +545,30 @@ function _testHideIfEmpty() {
 
     // Test a valid string.
     $view->result[0]->{$column_map_reversed['name']} = $random_name;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "", 'If the rewritten string is zero, it should be treated as empty.');
 
     // Test an empty string.
     $view->result[0]->{$column_map_reversed['name']} = "";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "", 'If the rewritten string is zero, "" should be treated as empty.');
 
     // Test zero as an integer.
     $view->result[0]->{$column_map_reversed['name']} = 0;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "", 'If the rewritten string is zero, 0 should not be treated as empty.');
 
     // Test zero as a string.
     $view->result[0]->{$column_map_reversed['name']} = "0";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "", 'If the rewritten string is zero, "0" should not be treated as empty.');
   }
 
@@ -484,6 +576,9 @@ function _testHideIfEmpty() {
    * Tests the usage of the empty text.
    */
   function _testEmptyText() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
     $view = Views::getView('test_view');
     $view->initDisplay();
     $this->executeView($view);
@@ -493,27 +588,37 @@ function _testEmptyText() {
 
     $empty_text = $view->field['name']->options['empty'] = $this->randomMachineName();
     $view->result[0]->{$column_map_reversed['name']} = "";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $empty_text, 'If a field is empty, the empty text should be used for the output.');
 
     $view->result[0]->{$column_map_reversed['name']} = "0";
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, "0", 'If a field is 0 and empty_zero is not checked, the empty text should not be used for the output.');
 
     $view->result[0]->{$column_map_reversed['name']} = "0";
     $view->field['name']->options['empty_zero'] = TRUE;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $empty_text, 'If a field is 0 and empty_zero is checked, the empty text should be used for the output.');
 
     $view->result[0]->{$column_map_reversed['name']} = "";
     $view->field['name']->options['alter']['alter_text'] = TRUE;
     $alter_text = $view->field['name']->options['alter']['text'] = $this->randomMachineName();
     $view->field['name']->options['hide_alter_empty'] = FALSE;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $alter_text, 'If a field is empty, some rewrite text exists, but hide_alter_empty is not checked, render the rewrite text.');
 
     $view->field['name']->options['hide_alter_empty'] = TRUE;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+      return $view->field['name']->advancedRender($view->result[0]);
+    });
     $this->assertIdentical($render, $empty_text, 'If a field is empty, some rewrite text exists, and hide_alter_empty is checked, use the empty text.');
   }
 
diff --git a/core/modules/views/src/Tests/Handler/FieldWebTest.php b/core/modules/views/src/Tests/Handler/FieldWebTest.php
index 3fde585..39e1eb2 100644
--- a/core/modules/views/src/Tests/Handler/FieldWebTest.php
+++ b/core/modules/views/src/Tests/Handler/FieldWebTest.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Render\RenderContext;
 use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
 use Drupal\views\Views;
 
@@ -197,6 +198,9 @@ protected function xpathContent($content, $xpath, array $arguments = array()) {
    * Tests rewriting the output to a link.
    */
   public function testAlterUrl() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
     $view = Views::getView('test_view');
     $view->setDisplay();
     $view->initHandlers();
@@ -211,13 +215,17 @@ public function testAlterUrl() {
     // Tests that the suffix/prefix appears on the output.
     $id_field->options['alter']['prefix'] = $prefix = $this->randomMachineName();
     $id_field->options['alter']['suffix'] = $suffix = $this->randomMachineName();
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertSubString($output, $prefix);
     $this->assertSubString($output, $suffix);
     unset($id_field->options['alter']['prefix']);
     unset($id_field->options['alter']['suffix']);
 
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertSubString($output, $path, 'Make sure that the path is part of the output');
 
     // Some generic test code adapted from the UrlTest class, which tests
@@ -228,44 +236,60 @@ public function testAlterUrl() {
 
       $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['absolute' => $absolute]);
       $alter['absolute'] = $absolute;
-      $result = $id_field->theme($row);
+      $result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+        return $id_field->theme($row);
+      });
       $this->assertSubString($result, $expected_result);
 
       $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['fragment' => 'foo', 'absolute' => $absolute]);
       $alter['path'] = 'node/123#foo';
-      $result = $id_field->theme($row);
+      $result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+        return $id_field->theme($row);
+      });
       $this->assertSubString($result, $expected_result);
 
       $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['query' => ['foo' => NULL], 'absolute' => $absolute]);
       $alter['path'] = 'node/123?foo';
-      $result = $id_field->theme($row);
+      $result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+        return $id_field->theme($row);
+      });
       $this->assertSubString($result, $expected_result);
 
       $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['query' => ['foo' => 'bar', 'bar' => 'baz'], 'absolute' => $absolute]);
       $alter['path'] = 'node/123?foo=bar&bar=baz';
-      $result = $id_field->theme($row);
+      $result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+        return $id_field->theme($row);
+      });
       $this->assertSubString(Html::decodeEntities($result), Html::decodeEntities($expected_result));
 
       // @todo The route-based URL generator strips out NULL attributes.
       // $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['query' => ['foo' => NULL], 'fragment' => 'bar', 'absolute' => $absolute]);
       $expected_result = \Drupal::urlGenerator()->generateFromPath('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
       $alter['path'] = 'node/123?foo#bar';
-      $result = $id_field->theme($row);
+      $result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+        return $id_field->theme($row);
+      });
       $this->assertSubString(Html::decodeEntities($result), Html::decodeEntities($expected_result));
 
       $expected_result = \Drupal::url('<front>', [], ['absolute' => $absolute]);
       $alter['path'] = '<front>';
-      $result = $id_field->theme($row);
+      $result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+        return $id_field->theme($row);
+      });
       $this->assertSubString($result, $expected_result);
     }
 
     // Tests the replace spaces with dashes feature.
     $id_field->options['alter']['replace_spaces'] = TRUE;
     $id_field->options['alter']['path'] = $path = $this->randomMachineName() . ' ' . $this->randomMachineName();
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertSubString($output, str_replace(' ', '-', $path));
     $id_field->options['alter']['replace_spaces'] = FALSE;
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     // The url has a space in it, so to check we have to decode the url output.
     $this->assertSubString(urldecode($output), $path);
 
@@ -273,44 +297,60 @@ public function testAlterUrl() {
     // Switch on the external flag should output an external url as well.
     $id_field->options['alter']['external'] = TRUE;
     $id_field->options['alter']['path'] = $path = 'www.drupal.org';
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertSubString($output, 'http://www.drupal.org');
 
     // Setup a not external url, which shouldn't lead to an external url.
     $id_field->options['alter']['external'] = FALSE;
     $id_field->options['alter']['path'] = $path = 'www.drupal.org';
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertNotSubString($output, 'http://www.drupal.org');
 
     // Tests the transforming of the case setting.
     $id_field->options['alter']['path'] = $path = $this->randomMachineName();
     $id_field->options['alter']['path_case'] = 'none';
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertSubString($output, $path);
 
     // Switch to uppercase and lowercase.
     $id_field->options['alter']['path_case'] = 'upper';
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertSubString($output, strtoupper($path));
     $id_field->options['alter']['path_case'] = 'lower';
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertSubString($output, strtolower($path));
 
     // Switch to ucfirst and ucwords.
     $id_field->options['alter']['path_case'] = 'ucfirst';
     $id_field->options['alter']['path'] = 'drupal has a great community';
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertSubString($output, UrlHelper::encodePath('Drupal has a great community'));
 
     $id_field->options['alter']['path_case'] = 'ucwords';
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $this->assertSubString($output, UrlHelper::encodePath('Drupal Has A Great Community'));
     unset($id_field->options['alter']['path_case']);
 
     // Tests the linkclass setting and see whether it actually exists in the
     // output.
     $id_field->options['alter']['link_class'] = $class = $this->randomMachineName();
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $elements = $this->xpathContent($output, '//a[contains(@class, :class)]', array(':class' => $class));
     $this->assertTrue($elements);
     // @fixme link_class, alt, rel cannot be unset, which should be fixed.
@@ -318,21 +358,27 @@ public function testAlterUrl() {
 
     // Tests the alt setting.
     $id_field->options['alter']['alt'] = $rel = $this->randomMachineName();
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $elements = $this->xpathContent($output, '//a[contains(@title, :alt)]', array(':alt' => $rel));
     $this->assertTrue($elements);
     $id_field->options['alter']['alt'] = '';
 
     // Tests the rel setting.
     $id_field->options['alter']['rel'] = $rel = $this->randomMachineName();
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $elements = $this->xpathContent($output, '//a[contains(@rel, :rel)]', array(':rel' => $rel));
     $this->assertTrue($elements);
     $id_field->options['alter']['rel'] = '';
 
     // Tests the target setting.
     $id_field->options['alter']['target'] = $target = $this->randomMachineName();
-    $output = $id_field->theme($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
+      return $id_field->theme($row);
+    });
     $elements = $this->xpathContent($output, '//a[contains(@target, :target)]', array(':target' => $target));
     $this->assertTrue($elements);
     unset($id_field->options['alter']['target']);
@@ -342,6 +388,8 @@ public function testAlterUrl() {
    * Tests the field/label/wrapper classes.
    */
   public function testFieldClasses() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_field_classes');
     $view->initHandlers();
 
@@ -352,13 +400,13 @@ public function testFieldClasses() {
     // Setup some kind of label by default.
     $id_field->options['label'] = $this->randomMachineName();
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     $this->assertFalse($this->xpathContent($output, '//div[contains(@class, :class)]', array(':class' => 'field-content')));
     $this->assertFalse($this->xpathContent($output, '//div[contains(@class, :class)]', array(':class' => 'field-label')));
 
     $id_field->options['element_default_classes'] = TRUE;
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     // Per default the label and the element of the field are spans.
     $this->assertTrue($this->xpathContent($output, '//span[contains(@class, :class)]', array(':class' => 'field-content')));
     $this->assertTrue($this->xpathContent($output, '//span[contains(@class, :class)]', array(':class' => 'views-label')));
@@ -374,13 +422,13 @@ public function testFieldClasses() {
       // Set a custom wrapper element css class.
       $id_field->options['element_wrapper_class'] = $random_class;
       $output = $view->preview();
-      $output = drupal_render($output);
+      $output = $renderer->renderRoot($output);
       $this->assertTrue($this->xpathContent($output, "//{$element_type}[contains(@class, :class)]", array(':class' => $random_class)));
 
       // Set no custom css class.
       $id_field->options['element_wrapper_class'] = '';
       $output = $view->preview();
-      $output = drupal_render($output);
+      $output = $renderer->renderRoot($output);
       $this->assertFalse($this->xpathContent($output, "//{$element_type}[contains(@class, :class)]", array(':class' => $random_class)));
       $this->assertTrue($this->xpathContent($output, "//li[contains(@class, views-row)]/{$element_type}"));
     }
@@ -394,13 +442,13 @@ public function testFieldClasses() {
       // Set a custom label element css class.
       $id_field->options['element_label_class'] = $random_class;
       $output = $view->preview();
-      $output = drupal_render($output);
+      $output = $renderer->renderRoot($output);
       $this->assertTrue($this->xpathContent($output, "//li[contains(@class, views-row)]//{$element_type}[contains(@class, :class)]", array(':class' => $random_class)));
 
       // Set no custom css class.
       $id_field->options['element_label_class'] = '';
       $output = $view->preview();
-      $output = drupal_render($output);
+      $output = $renderer->renderRoot($output);
       $this->assertFalse($this->xpathContent($output, "//li[contains(@class, views-row)]//{$element_type}[contains(@class, :class)]", array(':class' => $random_class)));
       $this->assertTrue($this->xpathContent($output, "//li[contains(@class, views-row)]//{$element_type}"));
     }
@@ -414,13 +462,13 @@ public function testFieldClasses() {
       // Set a custom label element css class.
       $id_field->options['element_class'] = $random_class;
       $output = $view->preview();
-      $output = drupal_render($output);
+      $output = $renderer->renderRoot($output);
       $this->assertTrue($this->xpathContent($output, "//li[contains(@class, views-row)]//div[contains(@class, views-field)]//{$element_type}[contains(@class, :class)]", array(':class' => $random_class)));
 
       // Set no custom css class.
       $id_field->options['element_class'] = '';
       $output = $view->preview();
-      $output = drupal_render($output);
+      $output = $renderer->renderRoot($output);
       $this->assertFalse($this->xpathContent($output, "//li[contains(@class, views-row)]//div[contains(@class, views-field)]//{$element_type}[contains(@class, :class)]", array(':class' => $random_class)));
       $this->assertTrue($this->xpathContent($output, "//li[contains(@class, views-row)]//div[contains(@class, views-field)]//{$element_type}"));
     }
@@ -451,6 +499,9 @@ public function testFieldClasses() {
    * Tests trimming/read-more/ellipses.
    */
   public function testTextRendering() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
     $view = Views::getView('test_field_output');
     $view->initHandlers();
     $name_field = $view->field['name'];
@@ -463,18 +514,24 @@ public function testTextRendering() {
     $row = $view->result[0];
 
     $name_field->options['alter']['strip_tags'] = TRUE;
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
     $this->assertSubString($output, $random_text, 'Find text without html if stripping of views field output is enabled.');
     $this->assertNotSubString($output, $html_text, 'Find no text with the html if stripping of views field output is enabled.');
 
     // Tests preserving of html tags.
     $name_field->options['alter']['preserve_tags'] = '<div>';
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
     $this->assertSubString($output, $random_text, 'Find text without html if stripping of views field output is enabled but a div is allowed.');
     $this->assertSubString($output, $html_text, 'Find text with the html if stripping of views field output is enabled but a div is allowed.');
 
     $name_field->options['alter']['strip_tags'] = FALSE;
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
     $this->assertSubString($output, $random_text, 'Find text without html if stripping of views field output is disabled.');
     $this->assertSubString($output, $html_text, 'Find text with the html if stripping of views field output is disabled.');
 
@@ -483,13 +540,17 @@ public function testTextRendering() {
     $views_test_data_name = $row->views_test_data_name;
     $row->views_test_data_name = '  ' . $views_test_data_name . '     ';
     $name_field->options['alter']['trim_whitespace'] = TRUE;
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
 
     $this->assertSubString($output, $views_test_data_name, 'Make sure the trimmed text can be found if trimming is enabled.');
     $this->assertNotSubString($output, $row->views_test_data_name, 'Make sure the untrimmed text can be found if trimming is enabled.');
 
     $name_field->options['alter']['trim_whitespace'] = FALSE;
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
     $this->assertSubString($output, $views_test_data_name, 'Make sure the trimmed text can be found if trimming is disabled.');
     $this->assertSubString($output, $row->views_test_data_name, 'Make sure the untrimmed text can be found  if trimming is disabled.');
 
@@ -502,12 +563,16 @@ public function testTextRendering() {
     $name_field->options['alter']['max_length'] = 5;
     $trimmed_name = Unicode::substr($row->views_test_data_name, 0, 5);
 
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
     $this->assertSubString($output, $trimmed_name, format_string('Make sure the trimmed output (!trimmed) appears in the rendered output (!output).', array('!trimmed' => $trimmed_name, '!output' => $output)));
     $this->assertNotSubString($output, $row->views_test_data_name, format_string("Make sure the untrimmed value (!untrimmed) shouldn't appear in the rendered output (!output).", array('!untrimmed' => $row->views_test_data_name, '!output' => $output)));
 
     $name_field->options['alter']['max_length'] = 9;
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
     $this->assertSubString($output, $trimmed_name, format_string('Make sure the untrimmed (!untrimmed) output appears in the rendered output  (!output).', array('!trimmed' => $trimmed_name, '!output' => $output)));
 
     // Take word_boundary into account for the tests.
@@ -547,7 +612,9 @@ public function testTextRendering() {
 
     foreach ($tuples as $tuple) {
       $row->views_test_data_name = $tuple['value'];
-      $output = $name_field->advancedRender($row);
+      $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+        return $name_field->advancedRender($row);
+      });
 
       if ($tuple['trimmed']) {
         $this->assertNotSubString($output, $tuple['value'], format_string('The untrimmed value (!untrimmed) should not appear in the trimmed output (!output).', array('!untrimmed' => $tuple['value'], '!output' => $output)));
@@ -564,22 +631,30 @@ public function testTextRendering() {
     $name_field->options['alter']['more_link_text'] = $more_text = $this->randomMachineName();
     $name_field->options['alter']['more_link_path'] = $more_path = $this->randomMachineName();
 
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
     $this->assertSubString($output, $more_text, 'Make sure a read more text is displayed if the output got trimmed');
     $this->assertTrue($this->xpathContent($output, '//a[contains(@href, :path)]', array(':path' => $more_path)), 'Make sure the read more link points to the right destination.');
 
     $name_field->options['alter']['more_link'] = FALSE;
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
     $this->assertNotSubString($output, $more_text, 'Make sure no read more text appears.');
     $this->assertFalse($this->xpathContent($output, '//a[contains(@href, :path)]', array(':path' => $more_path)), 'Make sure no read more link appears.');
 
     // Check for the ellipses.
     $row->views_test_data_name = $this->randomMachineName(8);
     $name_field->options['alter']['max_length'] = 5;
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
     $this->assertSubString($output, '…', 'An ellipsis should appear if the output is trimmed');
     $name_field->options['alter']['max_length'] = 10;
-    $output = $name_field->advancedRender($row);
+    $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
+      return $name_field->advancedRender($row);
+    });
     $this->assertNotSubString($output, '…', 'No ellipsis should appear if the output is not trimmed');
   }
 
diff --git a/core/modules/views/src/Tests/Plugin/CacheTest.php b/core/modules/views/src/Tests/Plugin/CacheTest.php
index bae30f5..08e1565 100644
--- a/core/modules/views/src/Tests/Plugin/CacheTest.php
+++ b/core/modules/views/src/Tests/Plugin/CacheTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Tests\Plugin;
 
+use Drupal\Core\Render\RenderContext;
 use Drupal\node\Entity\Node;
 use Drupal\views\Tests\ViewUnitTestBase;
 use Drupal\views\Views;
@@ -282,14 +283,18 @@ function testHeaderStorage() {
     $output = $view->buildRenderable();
     /** @var \Drupal\Core\Render\RendererInterface $renderer */
     $renderer = \Drupal::service('renderer');
-    $renderer->render($output);
+    $renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) {
+      return $renderer->render($output);;
+    });
 
     unset($view->pre_render_called);
     $view->destroy();
 
     $view->setDisplay();
     $output = $view->buildRenderable();
-    $renderer->render($output);
+    $renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) {
+      return $renderer->render($output);;
+    });
 
     $this->assertTrue(in_array('views_test_data/test', $output['#attached']['library']), 'Make sure libraries are added for cached views.');
     $this->assertEqual(['foo' => 'bar'], $output['#attached']['drupalSettings'], 'Make sure drupalSettings are added for cached views.');
diff --git a/core/modules/views/src/Tests/Plugin/DisplayTest.php b/core/modules/views/src/Tests/Plugin/DisplayTest.php
index 81bb1c2..0517063 100644
--- a/core/modules/views/src/Tests/Plugin/DisplayTest.php
+++ b/core/modules/views/src/Tests/Plugin/DisplayTest.php
@@ -52,6 +52,8 @@ protected function setUp() {
    * @see \Drupal\views_test_data\Plugin\views\display\DisplayTest
    */
   public function testDisplayPlugin() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_view');
 
     // Add a new 'display_test' display and test it's there.
@@ -97,7 +99,7 @@ public function testDisplayPlugin() {
     $this->assertIdentical($view->display_handler->getOption('test_option'), '');
 
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
 
     $this->assertTrue(strpos($output, '<h1></h1>') !== FALSE, 'An empty value for test_option found in output.');
 
@@ -106,7 +108,7 @@ public function testDisplayPlugin() {
     $view->save();
 
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
 
     // Test we have our custom <h1> tag in the output of the view.
     $this->assertTrue(strpos($output, '<h1>Test option title</h1>') !== FALSE, 'The test_option value found in display output title.');
@@ -166,6 +168,9 @@ public function testGetAttachedDisplays() {
    * Tests the readmore functionality.
    */
   public function testReadMore() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     if (!isset($this->options['validate']['type'])) {
       return;
     }
@@ -175,7 +180,7 @@ public function testReadMore() {
     $this->executeView($view);
 
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
 
     $this->setRawContent($output);
     $result = $this->xpath('//a[@class=:class]', array(':class' => 'more-link'));
@@ -185,7 +190,7 @@ public function testReadMore() {
     // Test the renderMoreLink method directly. This could be directly unit
     // tested.
     $more_link = $view->display_handler->renderMoreLink();
-    $more_link = drupal_render($more_link);
+    $more_link = $renderer->renderRoot($more_link);
     $this->setRawContent($more_link);
     $result = $this->xpath('//a[@class=:class]', array(':class' => 'more-link'));
     $this->assertEqual($result[0]->attributes()->href, \Drupal::url('view.test_display_more.page_1'), 'The right more link is shown.');
@@ -201,7 +206,7 @@ public function testReadMore() {
     $view->display_handler->setOption('use_more', 0);
     $this->executeView($view);
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     $this->setRawContent($output);
     $result = $this->xpath('//a[@class=:class]', array(':class' => 'more-link'));
     $this->assertTrue(empty($result), 'The more link is not shown.');
@@ -219,7 +224,7 @@ public function testReadMore() {
     ));
     $this->executeView($view);
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     $this->setRawContent($output);
     $result = $this->xpath('//a[@class=:class]', array(':class' => 'more-link'));
     $this->assertTrue(empty($result), 'The more link is not shown when view has more records.');
diff --git a/core/modules/views/src/Tests/Plugin/ExposedFormTest.php b/core/modules/views/src/Tests/Plugin/ExposedFormTest.php
index 7e58113..ffc9bbe 100644
--- a/core/modules/views/src/Tests/Plugin/ExposedFormTest.php
+++ b/core/modules/views/src/Tests/Plugin/ExposedFormTest.php
@@ -135,7 +135,7 @@ public function testExposedFormRender() {
     $this->executeView($view);
     $exposed_form = $view->display_handler->getPlugin('exposed_form');
     $output = $exposed_form->renderExposedForm();
-    $this->setRawContent(drupal_render($output));
+    $this->setRawContent(\Drupal::service('renderer')->renderRoot($output));
 
     $this->assertFieldByXpath('//form/@id', $this->getExpectedExposedFormId($view), 'Expected form ID found.');
 
diff --git a/core/modules/views/src/Tests/Plugin/PagerTest.php b/core/modules/views/src/Tests/Plugin/PagerTest.php
index 15384c1..d9c1eec 100644
--- a/core/modules/views/src/Tests/Plugin/PagerTest.php
+++ b/core/modules/views/src/Tests/Plugin/PagerTest.php
@@ -280,7 +280,7 @@ public function testRenderNullPager() {
     $view->setAjaxEnabled(TRUE);
     $view->pager = NULL;
     $output = $view->render();
-    $output = drupal_render($output);
+    $output = \Drupal::service('renderer')->renderRoot($output);
     $this->assertEqual(preg_match('/<ul class="pager">/', $output), 0, 'The pager is not rendered.');
   }
 
diff --git a/core/modules/views/src/Tests/Plugin/StyleGridTest.php b/core/modules/views/src/Tests/Plugin/StyleGridTest.php
index 1f4e9b4..d5d7e60 100644
--- a/core/modules/views/src/Tests/Plugin/StyleGridTest.php
+++ b/core/modules/views/src/Tests/Plugin/StyleGridTest.php
@@ -72,7 +72,7 @@ protected function assertGrid(ViewExecutable $view, $alignment, $columns) {
     $view->style_plugin->options['columns'] = $columns;
     $this->executeView($view);
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = \Drupal::service('renderer')->renderRoot($output);
     $this->setRawContent($output);
     if (!in_array($alignment, $this->alignmentsTested)) {
       $result = $this->xpath('//div[contains(@class, "views-view-grid") and contains(@class, :alignment) and contains(@class, :columns)]', array(':alignment' => $alignment, ':columns' => 'cols-' . $columns));
diff --git a/core/modules/views/src/Tests/Plugin/StyleHtmlListTest.php b/core/modules/views/src/Tests/Plugin/StyleHtmlListTest.php
index 0642eff..80243d1 100644
--- a/core/modules/views/src/Tests/Plugin/StyleHtmlListTest.php
+++ b/core/modules/views/src/Tests/Plugin/StyleHtmlListTest.php
@@ -31,7 +31,7 @@ class StyleHtmlListTest extends ViewUnitTestBase {
   function testDefaultRowClasses() {
     $view = Views::getView('test_style_html_list');
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = \Drupal::service('renderer')->renderRoot($output);
 
     // Check that an empty class attribute is not added if the wrapper class is
     // not set.
@@ -46,7 +46,7 @@ function testDefaultRowClasses() {
     $view->style_plugin->options['wrapper_class'] = 'wrapper-class';
 
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = \Drupal::service('renderer')->renderRoot($output);
 
     // Check that class attribute is present if the wrapper class is set.
     $this->assertTrue(strpos($output, '<div class="wrapper-class">') !== FALSE, 'Class is added to DIV');
diff --git a/core/modules/views/src/Tests/Plugin/StyleMappingTest.php b/core/modules/views/src/Tests/Plugin/StyleMappingTest.php
index 24b06d2..2712958 100644
--- a/core/modules/views/src/Tests/Plugin/StyleMappingTest.php
+++ b/core/modules/views/src/Tests/Plugin/StyleMappingTest.php
@@ -51,7 +51,7 @@ public function testMappedOutput() {
    */
   protected function mappedOutputHelper($view) {
     $output = $view->preview();
-    $rendered_output = drupal_render($output);
+    $rendered_output = \Drupal::service('renderer')->renderRoot($output);
     $this->storeViewPreview($rendered_output);
     $rows = $this->elements->body->div->div->div;
     $data_set = $this->dataSet();
diff --git a/core/modules/views/src/Tests/Plugin/StyleTableUnitTest.php b/core/modules/views/src/Tests/Plugin/StyleTableUnitTest.php
index 3d132df..28eff6b 100644
--- a/core/modules/views/src/Tests/Plugin/StyleTableUnitTest.php
+++ b/core/modules/views/src/Tests/Plugin/StyleTableUnitTest.php
@@ -123,7 +123,7 @@ public function testTable() {
     $this->prepareView($view);
     $view->field['name']->options['exclude'] = TRUE;
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = \Drupal::service('renderer')->renderRoot($output);
     $this->assertFalse(strpos($output, 'views-field-name') !== FALSE, "Excluded field's wrapper was not rendered.");
     $view->destroy();
 
@@ -131,7 +131,7 @@ public function testTable() {
     // rendered.
     $this->executeView($view);
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = \Drupal::service('renderer')->renderRoot($output);
 
     $this->assertFalse(strpos($output, 'custom text') !== FALSE, 'Empty handler was not rendered on a non empty table.');
 
@@ -140,7 +140,7 @@ public function testTable() {
     $view->executed = TRUE;
     $view->result = array();
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = \Drupal::service('renderer')->renderRoot($output);
 
     $this->assertTrue(strpos($output, 'custom text') !== FALSE, 'Empty handler got rendered on an empty table.');
   }
diff --git a/core/modules/views/src/Tests/Plugin/StyleTest.php b/core/modules/views/src/Tests/Plugin/StyleTest.php
index 86c2f91..a0675c2 100644
--- a/core/modules/views/src/Tests/Plugin/StyleTest.php
+++ b/core/modules/views/src/Tests/Plugin/StyleTest.php
@@ -46,6 +46,9 @@ protected function setUp() {
    * Tests the general rendering of styles.
    */
   public function testStyle() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
+
     // This run use the test row plugin and render with it.
     $view = Views::getView('test_view');
     $view->setDisplay();
@@ -65,7 +68,7 @@ public function testStyle() {
     $view->rowPlugin->setOutput($random_text);
 
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     $this->assertTrue(strpos($output, $random_text) !== FALSE, 'Make sure that the rendering of the row plugin appears in the output of the view.');
 
     // Test without row plugin support.
@@ -85,7 +88,7 @@ public function testStyle() {
     // rendered.
     $view->style_plugin->setOutput($random_text);
     $output = $view->preview();
-    $output = drupal_render($output);
+    $output = $renderer->renderRoot($output);
     $this->assertTrue(strpos($output, $random_text) !== FALSE, 'Make sure that the rendering of the style plugin appears in the output of the view.');
   }
 
@@ -229,7 +232,7 @@ function testCustomRowClasses() {
     $view->style_plugin->options['row_class'] = $random_name . " test-token-{{ name }}";
 
     $output = $view->preview();
-    $this->storeViewPreview(drupal_render($output));
+    $this->storeViewPreview(\Drupal::service('renderer')->renderRoot($output));
 
     $rows = $this->elements->body->div->div->div;
     $count = 0;
diff --git a/core/modules/views/src/Tests/Plugin/StyleUnformattedTest.php b/core/modules/views/src/Tests/Plugin/StyleUnformattedTest.php
index 0835d98..083a975 100644
--- a/core/modules/views/src/Tests/Plugin/StyleUnformattedTest.php
+++ b/core/modules/views/src/Tests/Plugin/StyleUnformattedTest.php
@@ -30,7 +30,7 @@ function testDefaultRowClasses() {
     $view = Views::getView('test_view');
     $view->setDisplay();
     $output = $view->preview();
-    $this->storeViewPreview(drupal_render($output));
+    $this->storeViewPreview(\Drupal::service('renderer')->renderRoot($output));
 
     $rows = $this->elements->body->div->div->div;
     $count = 0;
diff --git a/core/modules/views/src/Tests/ViewElementTest.php b/core/modules/views/src/Tests/ViewElementTest.php
index bd17747..014f2ea 100644
--- a/core/modules/views/src/Tests/ViewElementTest.php
+++ b/core/modules/views/src/Tests/ViewElementTest.php
@@ -52,12 +52,14 @@ protected function setUp() {
    * Tests the rendered output and form output of a view element.
    */
   public function testViewElement() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_view_embed');
     $view->setDisplay();
 
     // Set the content as our rendered array.
     $render = $this->render;
-    $this->setRawContent(drupal_render($render));
+    $this->setRawContent($renderer->renderRoot($render));
 
     $xpath = $this->xpath('//div[@class="views-element-container"]');
     $this->assertTrue($xpath, 'The view container has been found in the rendered output.');
@@ -102,7 +104,7 @@ public function testViewElement() {
 
     // Test the render array again.
     $render = $this->render;
-    $this->setRawContent(drupal_render($render));
+    $this->setRawContent($renderer->renderRoot($render));
     // There should be 1 row in the results, 'John' arg 25.
     $xpath = $this->xpath('//div[@class="view-content"]/div');
     $this->assertEqual(count($xpath), 1);
@@ -118,13 +120,15 @@ public function testViewElement() {
    * embed display plugin.
    */
   public function testViewElementEmbed() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_view_embed');
     $view->setDisplay('embed_1');
 
     // Set the content as our rendered array.
     $render = $this->render;
     $render['#embed'] = TRUE;
-    $this->setRawContent(drupal_render($render));
+    $this->setRawContent($renderer->renderRoot($render));
 
     $xpath = $this->xpath('//div[@class="views-element-container"]');
     $this->assertTrue($xpath, 'The view container has been found in the rendered output.');
@@ -170,7 +174,7 @@ public function testViewElementEmbed() {
     // Test the render array again.
     $render = $this->render;
     $render['#embed'] = TRUE;
-    $this->setRawContent(drupal_render($render));
+    $this->setRawContent($renderer->renderRoot($render));
     // There should be 1 row in the results, 'John' arg 25.
     $xpath = $this->xpath('//div[@class="view-content"]/div');
     $this->assertEqual(count($xpath), 1);
diff --git a/core/modules/views/src/Tests/ViewRenderTest.php b/core/modules/views/src/Tests/ViewRenderTest.php
index ac60e6a..bbf3fe1 100644
--- a/core/modules/views/src/Tests/ViewRenderTest.php
+++ b/core/modules/views/src/Tests/ViewRenderTest.php
@@ -40,7 +40,7 @@ public function testRender() {
     // Make sure that the rendering just calls the preprocess function once.
     $view = Views::getView('test_view_render');
     $output = $view->preview();
-    drupal_render($output);
+    $this->container->get('renderer')->renderRoot($output);
 
     $this->assertEqual(\Drupal::state()->get('views_render.test'), 1);
   }
diff --git a/core/modules/views/src/Tests/ViewsTemplateTest.php b/core/modules/views/src/Tests/ViewsTemplateTest.php
index 966029c..c67befe 100644
--- a/core/modules/views/src/Tests/ViewsTemplateTest.php
+++ b/core/modules/views/src/Tests/ViewsTemplateTest.php
@@ -45,7 +45,7 @@ public function testTemplate() {
     $output = $view->preview();
 
     // Check if we got the rendered output of our template file.
-    $this->assertTrue(strpos(drupal_render($output), 'This module defines its own display template.') !== FALSE, 'Display plugin DisplayTemplateTest defines its own template.');
+    $this->assertTrue(strpos(\Drupal::service('renderer')->renderRoot($output), 'This module defines its own display template.') !== FALSE, 'Display plugin DisplayTemplateTest defines its own template.');
 
   }
 
diff --git a/core/modules/views_ui/src/Controller/ViewsUIController.php b/core/modules/views_ui/src/Controller/ViewsUIController.php
index f03328a..474f7bc 100644
--- a/core/modules/views_ui/src/Controller/ViewsUIController.php
+++ b/core/modules/views_ui/src/Controller/ViewsUIController.php
@@ -157,7 +157,7 @@ public function ajaxOperation(ViewEntityInterface $view, $op, Request $request)
     if ($request->request->get('js')) {
       $list = $this->entityManager()->getListBuilder('view')->render();
       $response = new AjaxResponse();
-      $response->addCommand(new ReplaceCommand('#views-entity-list', drupal_render($list)));
+      $response->addCommand(new ReplaceCommand('#views-entity-list', $list));
       return $response;
     }
 
diff --git a/core/modules/views_ui/src/Tests/CustomBooleanTest.php b/core/modules/views_ui/src/Tests/CustomBooleanTest.php
index f9a990b..08fbda8 100644
--- a/core/modules/views_ui/src/Tests/CustomBooleanTest.php
+++ b/core/modules/views_ui/src/Tests/CustomBooleanTest.php
@@ -100,7 +100,7 @@ public function testCustomOption() {
 
       $view = Views::getView('test_view');
       $output = $view->preview();
-      $output = drupal_render($output);
+      $output = \Drupal::service('renderer')->renderRoot($output);
 
       $replacements = array('%type' => $type);
       $this->{$values['test']}(strpos($output, $values['true']), format_string('Expected custom boolean TRUE value in output for %type.', $replacements));
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index fe33da1..6cfaf5d 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -685,12 +685,12 @@ public function rebuildCurrentTab(ViewUI $view, AjaxResponse $response, $display
     // Regenerate the main display area.
     $build = $this->getDisplayTab($view);
     static::addMicroweights($build);
-    $response->addCommand(new HtmlCommand('#views-tab-' . $display_id, drupal_render($build)));
+    $response->addCommand(new HtmlCommand('#views-tab-' . $display_id, $build));
 
     // Regenerate the top area so changes to display names and order will appear.
     $build = $this->renderDisplayTop($view);
     static::addMicroweights($build);
-    $response->addCommand(new ReplaceCommand('#views-display-top', drupal_render($build)));
+    $response->addCommand(new ReplaceCommand('#views-display-top', $build));
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
index e0c7ff4..2037588 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
@@ -66,14 +66,14 @@ public function testBubblingWithoutPreRender() {
     ];
 
     // Render the element and verify the presence of #attached JavaScript.
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $expected_libraries = ['test/parent', 'test/child', 'test/subchild'];
     $this->assertEquals($element['#attached']['library'], $expected_libraries, 'The element, child and subchild #attached libraries are included.');
 
     // Load the element from cache and verify the presence of the #attached
     // JavaScript.
     $element = ['#cache' => ['keys' => ['simpletest', 'drupal_render', 'children_attached']]];
-    $this->assertTrue(strlen($this->renderer->render($element)) > 0, 'The element was retrieved from cache.');
+    $this->assertTrue(strlen($this->renderer->renderRoot($element)) > 0, 'The element was retrieved from cache.');
     $this->assertEquals($element['#attached']['library'], $expected_libraries, 'The element, child and subchild #attached libraries are included.');
   }
 
@@ -116,7 +116,7 @@ public function testContextBubblingCustomCacheBin() {
         ],
       ],
     ];
-    $this->renderer->render($build);
+    $this->renderer->renderRoot($build);
 
     $this->assertRenderCacheItem('parent:foo', [
       '#cache_redirect' => TRUE,
@@ -143,7 +143,7 @@ public function testContextBubblingEdgeCases(array $element, array $expected_top
       ->method('convertTokensToKeys')
       ->willReturnArgument(0);
 
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
 
     $this->assertEquals($expected_top_level_contexts, $element['#cache']['contexts'], 'Expected cache contexts found.');
     foreach ($expected_cache_items as $cid => $expected_cache_item) {
@@ -345,7 +345,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // contexts: user.roles.
     $element = $test_element;
     $current_user_role = 'A';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertRenderCacheItem('parent', [
       '#cache_redirect' => TRUE,
       '#cache' => [
@@ -369,7 +369,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // contexts: foo, user.roles.
     $element = $test_element;
     $current_user_role = 'B';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertRenderCacheItem('parent', [
       '#cache_redirect' => TRUE,
       '#cache' => [
@@ -401,7 +401,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // and 'user.roles' cache contexts, resulting in a cache miss every time.)
     $element = $test_element;
     $current_user_role = 'A';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertRenderCacheItem('parent', [
       '#cache_redirect' => TRUE,
       '#cache' => [
@@ -425,7 +425,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // accessible => bubbled cache contexts: foo, bar, user.roles.
     $element = $test_element;
     $current_user_role = 'C';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $final_parent_cache_item = [
       '#cache_redirect' => TRUE,
       '#cache' => [
@@ -449,7 +449,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // Request 5: role A again, verifying the merging like we did for request 3.
     $element = $test_element;
     $current_user_role = 'A';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertRenderCacheItem('parent', $final_parent_cache_item);
     $this->assertRenderCacheItem('parent:bar:foo:r.A', [
       '#attached' => [],
@@ -464,7 +464,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // Request 6: role B again, verifying the merging like we did for request 3.
     $element = $test_element;
     $current_user_role = 'B';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertRenderCacheItem('parent', $final_parent_cache_item);
     $this->assertRenderCacheItem('parent:bar:foo:r.B', [
       '#attached' => [],
@@ -570,7 +570,7 @@ public function testOverWriteCacheKeys() {
        ],
       '#pre_render' => [__NAMESPACE__ . '\\BubblingTest::bubblingCacheOverwritePrerender'],
     ];
-    $this->renderer->render($data);
+    $this->renderer->renderRoot($data);
   }
 }
 
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
index a2f91ea..3e3e3bd 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
@@ -43,7 +43,7 @@ public function testRenderBasic($build, $expected, callable $setup_code = NULL)
       $setup_code();
     }
 
-    $this->assertSame($expected, $this->renderer->render($build));
+    $this->assertSame($expected, $this->renderer->renderRoot($build));
   }
 
   /**
@@ -322,7 +322,7 @@ public function testRenderSorting() {
         '#markup' => $first,
       ],
     ];
-    $output = $this->renderer->render($elements);
+    $output = $this->renderer->renderRoot($elements);
 
     // The lowest weight element should appear last in $output.
     $this->assertTrue(strpos($output, $second) > strpos($output, $first), 'Elements were sorted correctly by weight.');
@@ -357,7 +357,7 @@ public function testRenderSortingWithSetHashSorted() {
       ),
       '#sorted' => TRUE,
     );
-    $output = $this->renderer->render($elements);
+    $output = $this->renderer->renderRoot($elements);
 
     // The elements should appear in output in the same order as the array.
     $this->assertTrue(strpos($output, $second) < strpos($output, $first), 'Elements were not sorted.');
@@ -439,11 +439,11 @@ public function testRenderTwice() {
       '#markup' => 'test',
     ];
 
-    $this->assertEquals('test', $this->renderer->render($build));
+    $this->assertEquals('test', $this->renderer->renderRoot($build));
     $this->assertTrue($build['#printed']);
 
     // We don't want to reprint already printed render arrays.
-    $this->assertEquals('', $this->renderer->render($build));
+    $this->assertEquals('', $this->renderer->renderRoot($build));
   }
 
   /**
@@ -470,10 +470,10 @@ protected function assertAccess($build, $access) {
     $sensitive_content = $this->randomContextValue();
     $build['#markup'] = $sensitive_content;
     if ($access) {
-      $this->assertSame($sensitive_content, $this->renderer->render($build));
+      $this->assertSame($sensitive_content, $this->renderer->renderRoot($build));
     }
     else {
-      $this->assertSame('', $this->renderer->render($build));
+      $this->assertSame('', $this->renderer->renderRoot($build));
     }
   }
 
@@ -565,13 +565,13 @@ public function testRenderCache() {
     // Render the element and confirm that it goes through the rendering
     // process (which will set $element['#printed']).
     $element = $test_element;
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertTrue(isset($element['#printed']), 'No cache hit');
 
     // Render the element again and confirm that it is retrieved from the cache
     // instead (so $element['#printed'] will not be set).
     $element = $test_element;
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertFalse(isset($element['#printed']), 'Cache hit');
 
     // Test that cache tags are correctly collected from the render element,
@@ -608,7 +608,7 @@ public function testRenderCacheMaxAge($max_age, $is_render_cached, $render_cache
       ],
       '#markup' => '',
     ];
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
 
     $cache_item = $this->cacheFactory->get('render')->get('render_cache_test:en:stark');
     if (!$is_render_cached) {
@@ -657,7 +657,7 @@ public function testRenderCacheProperties(array $expected_results) {
       'child2' => ['#markup' => 2],
       '#custom_property' => ['custom_value'],
     ];
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
 
     $cache = $this->cacheFactory->get('render');
     $data = $cache->get('render_cache_test:en:stark')->data;
