diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 7400017..4d441f2 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1350,7 +1350,7 @@ function template_preprocess_page(&$variables) {
   $variables['language']          = $language_interface;
   $variables['logo']              = theme_get_setting('logo.url');
   $variables['site_name']         = (theme_get_setting('features.name') ? SafeMarkup::checkPlain($site_config->get('name')) : '');
-  $variables['site_slogan']       = (theme_get_setting('features.slogan') ? Xss::filterAdmin($site_config->get('slogan')) : '');
+  $variables['site_slogan']['#markup'] = (theme_get_setting('features.slogan') ? $site_config->get('slogan') : '');
 
   // An exception might be thrown.
   try {
diff --git a/core/lib/Drupal/Component/Utility/SafeMarkup.php b/core/lib/Drupal/Component/Utility/SafeMarkup.php
index d97dc2f..675a6b2 100644
--- a/core/lib/Drupal/Component/Utility/SafeMarkup.php
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -15,9 +15,9 @@
  * provides a store for known safe strings and methods to manage them
  * throughout the page request.
  *
- * Strings sanitized by self::checkPlain() or Xss::filter() are automatically
- * marked safe, as are markup strings created from render arrays via
- * drupal_render().
+ * Strings sanitized by self::checkPlain() and self::escape() or
+ * self::xssFilter() are automatically marked safe, as are markup strings
+ * created from @link theme_render render arrays @endlink via drupal_render().
  *
  * This class should be limited to internal use only. Module developers should
  * instead use the appropriate
@@ -141,17 +141,79 @@ public static function escape($string) {
   /**
    * Applies a very permissive XSS/HTML filter for admin-only use.
    *
-   * @param string $string
-   *   A string.
+   * Note: This method only filters if $string is not marked safe already.
    *
-   * @return string
-   *   The escaped string. If $string was already set as safe with
-   *   self::set(), it won't be escaped again.
+   * @deprecated as of Drupal 8.0.x, will be removed before Drupal 8.0.0. If the
+   *   string used as part of a @link theme_render render array @endlink use
+   *   #markup to allow the render system to filter automatically. If the result
+   *   is not being used directly in the rendering system (for example, when its
+   *   result is being combined with other strings before rendering), use
+   *   Xss::filterAdmin(). Otherwise, use SafeMarkup::xssFilter() and the tag
+   *   list provided by Xss::getAdminTagList() instead. In the rare instance
+   *   that the caller does not want to filter strings that are marked safe
+   *   already, it needs to check SafeMarkup::isSafe() itself.
    *
+   * @see \Drupal\Component\Utility\SafeMarkup::xssFilter()
+   * @see \Drupal\Component\Utility\SafeMarkup::isSafe()
    * @see \Drupal\Component\Utility\Xss::filterAdmin()
+   * @see \Drupal\Component\Utility\Xss::getAdminTagList()
    */
   public static function checkAdminXss($string) {
-    return static::isSafe($string) ? $string : Xss::filterAdmin($string);
+    return static::isSafe($string) ? $string : static::xssFilter($string, Xss::getAdminTagList());
+  }
+
+  /**
+   * Filters HTML for XSS vulnerabilities and marks the result as safe.
+   *
+   * Calling this method unnecessarily will result in bloating the safe string
+   * list and increases the chance of unintended side effects.
+   *
+   * If Twig receives a value that is not marked as safe then it will
+   * automatically encode special characters in a plain-text string for display
+   * as HTML. Therefore, SafeMarkup::filterXss() should only be used when the
+   * string might contain HTML that needs to be rendered properly by the
+   * browser.
+   *
+   * If you need to filter for admin use, like Xss::filterAdmin(), then:
+   * - If the string is used as part of a @link theme_render render array @endlink,
+   *   use #markup to allow the render system to filter by the admin tag list
+   *   automatically.
+   * - Otherwise, use the SafeMarkup::xssFilter() with tag list provided by
+   *   Xss::getAdminTagList() instead.
+   *
+   * This method should only be used instead of Xss::filter() when the result is
+   * being added to a render array that is constructed before rendering begins.
+   *
+   * In the rare instance that the caller does not want to filter strings that
+   * are marked safe already, it needs to check SafeMarkup::isSafe() itself.
+   *
+   * @param $string
+   *   The string with raw HTML in it. It will be stripped of everything that
+   *   can cause an XSS attack. The string provided will always be escaped
+   *   regardless of whether the string is already marked as safe.
+   * @param array $html_tags
+   *   (optional) An array of HTML tags. If omitted, it uses the default tag
+   *   list defined by \Drupal\Component\Utility\Xss::filter().
+   *
+   * @return string
+   *   An XSS-safe version of $string, or an empty string if $string is not
+   *   valid UTF-8. The string is marked as safe.
+   *
+   * @ingroup sanitization
+   *
+   * @see \Drupal\Component\Utility\Xss::filter()
+   * @see \Drupal\Component\Utility\Xss::filterAdmin()
+   * @see \Drupal\Component\Utility\Xss::getAdminTagList()
+   * @see \Drupal\Component\Utility\SafeMarkup::isSafe()
+   */
+  public static function xssFilter($string, $html_tags = NULL) {
+    if (is_null($html_tags)) {
+      $string = Xss::filter($string);
+    }
+    else {
+      $string = Xss::filter($string, $html_tags);
+    }
+    return static::set($string);
   }
 
   /**
diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php
index f967ca6..a68ca2b 100644
--- a/core/lib/Drupal/Component/Utility/Xss.php
+++ b/core/lib/Drupal/Component/Utility/Xss.php
@@ -29,14 +29,19 @@ class Xss {
    * Based on kses by Ulf Harnhammar, see http://sourceforge.net/projects/kses.
    * For examples of various XSS attacks, see: http://ha.ckers.org/xss.html.
    *
-   * This code does five things:
+   * This method is preferred to
+   * \Drupal\Component\Utility\SafeMarkup::xssFilter() when the result is not
+   * being used directly in the rendering system (for example, when its result
+   * is being combined with other strings before rendering). This avoids
+   * bloating the safe string list with partial strings if the whole result will
+   * be marked safe.
+   *
+   * This code does four things:
    * - Removes characters and constructs that can trick browsers.
    * - Makes sure all HTML entities are well-formed.
    * - Makes sure all HTML tags and attributes are well-formed.
    * - Makes sure no HTML tags contain URLs with a disallowed protocol (e.g.
    *   javascript:).
-   * - Marks the sanitized, XSS-safe version of $string as safe markup for
-   *   rendering.
    *
    * @param $string
    *   The string with raw HTML in it. It will be stripped of everything that
@@ -49,7 +54,7 @@ class Xss {
    *   valid UTF-8.
    *
    * @see \Drupal\Component\Utility\Unicode::validateUtf8()
-   * @see \Drupal\Component\Utility\SafeMarkup
+   * @see \Drupal\Component\Utility\SafeMarkup::xssFilter()
    *
    * @ingroup sanitization
    */
@@ -83,7 +88,7 @@ public static function filter($string, $html_tags = array('a', 'em', 'strong', '
     // for output. All other known XSS vectors have been filtered out by this
     // point and any HTML tags remaining will have been deliberately allowed, so
     // it is acceptable to call SafeMarkup::set() on the resultant string.
-    return SafeMarkup::set(preg_replace_callback('%
+    return preg_replace_callback('%
       (
       <(?=[^a-zA-Z!/])  # a lone <
       |                 # or
@@ -92,7 +97,7 @@ public static function filter($string, $html_tags = array('a', 'em', 'strong', '
       <[^>]*(>|$)       # a string that starts with a <, up until the > or the end of the string
       |                 # or
       >                 # just a >
-      )%x', $splitter, $string));
+      )%x', $splitter, $string);
   }
 
   /**
@@ -103,6 +108,13 @@ public static function filter($string, $html_tags = array('a', 'em', 'strong', '
    * is desired (so \Drupal\Component\Utility\SafeMarkup::checkPlain() is
    * not acceptable).
    *
+   * This method is preferred to
+   * \Drupal\Component\Utility\SafeMarkup::xssFilter() when the result is
+   * not being used directly in the rendering system (for example, when its
+   * result is being combined with other strings before rendering). This avoids
+   * bloating the safe string list with partial strings if the whole result will
+   * be marked safe.
+   *
    * Allows all tags that can be used inside an HTML body, save
    * for scripts and styles.
    *
@@ -111,6 +123,12 @@ public static function filter($string, $html_tags = array('a', 'em', 'strong', '
    *
    * @return string
    *   The filtered string.
+   *
+   * @ingroup sanitization
+   *
+   * @see \Drupal\Component\Utility\SafeMarkup::xssFilter()
+   * @see \Drupal\Component\Utility\Xss::getAdminTagList()
+   *
    */
   public static function filterAdmin($string) {
     return static::filter($string, static::$adminTags);
@@ -319,4 +337,14 @@ protected static function needsRemoval($html_tags, $elem) {
     return !isset($html_tags[strtolower($elem)]);
   }
 
+  /**
+   * Gets the list of html tags allowed by Xss::filterAdmin().
+   *
+   * @return array
+   *   The list of html tags allowed by filterAdmin().
+   */
+  public static function getAdminTagList() {
+    return static::$adminTags;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Render/Element/HtmlTag.php b/core/lib/Drupal/Core/Render/Element/HtmlTag.php
index 5dc3afe..f0c78bb 100644
--- a/core/lib/Drupal/Core/Render/Element/HtmlTag.php
+++ b/core/lib/Drupal/Core/Render/Element/HtmlTag.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Render\Element;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Utility\Xss;
 use Drupal\Core\Template\Attribute;
 
 /**
@@ -50,7 +51,7 @@ public function getInfo() {
    * pre-render callback being a #markup element, it is not passed through
    * \Drupal\Component\Utility\Xss::filterAdmin(). This is because it is marked
    * safe here, which causes
-   * \Drupal\Component\Utility\SafeMarkup::checkAdminXss() to regard it as safe
+   * \Drupal\Core\Render\Renderer::xssFilterAdminIfUnsafe() to regard it as safe
    * and bypass the call to \Drupal\Component\Utility\Xss::filterAdmin().
    *
    * @param array $element
@@ -161,7 +162,7 @@ public static function preRenderConditionalComments($element) {
     }
     else {
       // The IE expression might contain some user input data.
-      $expression = SafeMarkup::checkAdminXss($browsers['IE']);
+      $expression = Xss::filterAdmin($browsers['IE']);
     }
 
     // If the #prefix and #suffix properties are used, wrap them with
@@ -173,8 +174,8 @@ public static function preRenderConditionalComments($element) {
 
     // Ensure what we are dealing with is safe.
     // This would be done later anyway in drupal_render().
-    $prefix = isset($elements['#prefix']) ? SafeMarkup::checkAdminXss($elements['#prefix']) : '';
-    $suffix = isset($elements['#suffix']) ? SafeMarkup::checkAdminXss($elements['#suffix']) : '';
+    $prefix = isset($elements['#prefix']) ? Xss::filterAdmin($elements['#prefix']) : '';
+    $suffix = isset($elements['#suffix']) ? Xss::filterAdmin($elements['#suffix']) : '';
 
     // Now calling SafeMarkup::set is safe, because we ensured the
     // data coming in was at least admin escaped.
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index d98955d..503a26d 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -393,7 +393,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     if (isset($elements['#markup'])) {
       // @todo Decide how to support non-HTML in the render API in
       //   https://www.drupal.org/node/2501313.
-      $elements['#markup'] = SafeMarkup::checkAdminXss($elements['#markup']);
+      $elements['#markup'] = $this->xssFilterAdminIfUnsafe($elements['#markup']);
     }
 
     // Assume that if #theme is set it represents an implemented hook.
@@ -407,7 +407,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
       );
       foreach ($markup_keys as $key) {
         if (!empty($elements[$key]) && is_scalar($elements[$key])) {
-          $elements[$key] = SafeMarkup::checkAdminXss($elements[$key]);
+          $elements[$key] = $this->xssFilterAdminIfUnsafe($elements[$key]);
         }
       }
     }
@@ -493,8 +493,8 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     // with how render cached output gets stored. This ensures that placeholder
     // replacement logic gets the same data to work with, no matter if #cache is
     // disabled, #cache is enabled, there is a cache hit or miss.
-    $prefix = isset($elements['#prefix']) ? SafeMarkup::checkAdminXss($elements['#prefix']) : '';
-    $suffix = isset($elements['#suffix']) ? SafeMarkup::checkAdminXss($elements['#suffix']) : '';
+    $prefix = isset($elements['#prefix']) ? $this->xssFilterAdminIfUnsafe($elements['#prefix']) : '';
+    $suffix = isset($elements['#suffix']) ? $this->xssFilterAdminIfUnsafe($elements['#suffix']) : '';
 
     $elements['#markup'] = $prefix . $elements['#children'] . $suffix;
 
@@ -651,4 +651,23 @@ public function addCacheableDependency(array &$elements, $dependency) {
     $meta_a->merge($meta_b)->applyTo($elements);
   }
 
+  /**
+   * Applies a very permissive XSS/HTML filter for admin-only use.
+   *
+   * Note: This method only filters if $string is not marked safe already. This
+   * ensures that HTML intended for display is not filtered.
+   *
+   * @param string $string
+   *   A string.
+   *
+   * @return string
+   *   The escaped string. If SafeMarkup::isSafe($string) returns TRUE, it won't
+   *   be escaped again.
+   */
+  protected function xssFilterAdminIfUnsafe($string) {
+    // @todo https://www.drupal.org/node/2506581 replace with
+    //   SafeMarkup::isSafe() and Xss::filterAdmin().
+    return SafeMarkup::checkAdminXss($string);
+  }
+
 }
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 29ba9fc..744861c 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -6,7 +6,7 @@
  */
 
 use Drupal\aggregator\Entity\Feed;
-use Drupal\Component\Utility\Xss;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Routing\RouteMatchInterface;
 
 /**
@@ -166,7 +166,7 @@ function aggregator_cron() {
  *   The filtered content.
  */
 function aggregator_filter_xss($value) {
-  return Xss::filter($value, preg_split('/\s+|<|>/', \Drupal::config('aggregator.settings')->get('items.allowed_html'), -1, PREG_SPLIT_NO_EMPTY));
+  return SafeMarkup::xssFilter($value, preg_split('/\s+|<|>/', \Drupal::config('aggregator.settings')->get('items.allowed_html'), -1, PREG_SPLIT_NO_EMPTY));
 }
 
 /**
diff --git a/core/modules/aggregator/src/Controller/AggregatorController.php b/core/modules/aggregator/src/Controller/AggregatorController.php
index eaa82e5..13ff3d9 100644
--- a/core/modules/aggregator/src/Controller/AggregatorController.php
+++ b/core/modules/aggregator/src/Controller/AggregatorController.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\aggregator\Controller;
 
-use Drupal\Component\Utility\Xss;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Datetime\DateFormatter;
 use Drupal\aggregator\FeedInterface;
@@ -187,7 +187,7 @@ public function pageLast() {
    *   The feed label.
    */
   public function feedTitle(FeedInterface $aggregator_feed) {
-    return Xss::filter($aggregator_feed->label());
+    return SafeMarkup::xssFilter($aggregator_feed->label());
   }
 
 }
diff --git a/core/modules/block_content/block_content.pages.inc b/core/modules/block_content/block_content.pages.inc
index 23a7dea..9e019cd 100644
--- a/core/modules/block_content/block_content.pages.inc
+++ b/core/modules/block_content/block_content.pages.inc
@@ -5,7 +5,6 @@
  * Provides page callbacks for custom blocks.
  */
 
-use Drupal\Component\Utility\Xss;
 use Drupal\Core\Url;
 use Drupal\block_content\Entity\BlockContentType;
 use Drupal\block_content\Entity\BlockContent;
@@ -28,7 +27,9 @@ function template_preprocess_block_content_add_list(&$variables) {
   foreach ($variables['content'] as $type) {
     $variables['types'][$type->id()] = array(
       'link' => \Drupal::l($type->label(), new Url('block_content.add_form', array('block_content_type' => $type->id()), array('query' => $query))),
-      'description' => Xss::filterAdmin($type->getDescription()),
+      'description' => array(
+        '#markup' => $type->getDescription(),
+      ),
       'title' => $type->label(),
       'localized_options' => array(
         'query' => $query,
diff --git a/core/modules/block_content/src/BlockContentTypeListBuilder.php b/core/modules/block_content/src/BlockContentTypeListBuilder.php
index 2a51159..c625021 100644
--- a/core/modules/block_content/src/BlockContentTypeListBuilder.php
+++ b/core/modules/block_content/src/BlockContentTypeListBuilder.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\block_content;
 
-use Drupal\Component\Utility\Xss;
 use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
 use Drupal\Core\Entity\EntityInterface;
 
@@ -45,7 +44,7 @@ public function buildHeader() {
    */
   public function buildRow(EntityInterface $entity) {
     $row['type'] = $entity->link();
-    $row['description'] = Xss::filterAdmin($entity->getDescription());
+    $row['description']['data']['#markup'] = $entity->getDescription();
     return $row + parent::buildRow($entity);
   }
 
diff --git a/core/modules/comment/src/CommentTypeListBuilder.php b/core/modules/comment/src/CommentTypeListBuilder.php
index 6f42f84..4f99871 100644
--- a/core/modules/comment/src/CommentTypeListBuilder.php
+++ b/core/modules/comment/src/CommentTypeListBuilder.php
@@ -46,7 +46,7 @@ public function buildHeader() {
    */
   public function buildRow(EntityInterface $entity) {
     $row['type'] = SafeMarkup::checkPlain($entity->label());
-    $row['description'] = Xss::filterAdmin($entity->getDescription());
+    $row['description']['data'] = ['#markup' => $entity->getDescription()];
     return $row + parent::buildRow($entity);
   }
 
diff --git a/core/modules/dblog/src/Controller/DbLogController.php b/core/modules/dblog/src/Controller/DbLogController.php
index 2e2eccf..3982da6 100644
--- a/core/modules/dblog/src/Controller/DbLogController.php
+++ b/core/modules/dblog/src/Controller/DbLogController.php
@@ -207,7 +207,7 @@ public function overview() {
           $this->dateFormatter->format($dblog->timestamp, 'short'),
           $message,
           array('data' => $username),
-          Xss::filter($dblog->link),
+          SafeMarkup::xssFilter($dblog->link),
         ),
         // Attributes for table row.
         'class' => array(Html::getClass('dblog-' . $dblog->type), $classes[$dblog->severity]),
@@ -285,7 +285,7 @@ public function eventDetails($event_id) {
         ),
         array(
           array('data' => $this->t('Operations'), 'header' => TRUE),
-          SafeMarkup::checkAdminXss($dblog->link),
+          array('data' => array('#markup' => $dblog->link)),
         ),
       );
       $build['dblog_table'] = array(
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 400d6cc..77b2794 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -353,7 +353,10 @@ function _filter_tips($format_id, $long = FALSE) {
       if ($filter->status) {
         $tip = $filter->tips($long);
         if (isset($tip)) {
-          $tips[$format->label()][$name] = array('tip' => $tip, 'id' => $name);
+          $tips[$format->label()][$name] = array(
+            'tip' => array('#markup' => $tip),
+            'id' => $name,
+          );
         }
       }
     }
@@ -430,7 +433,6 @@ function template_preprocess_filter_tips(&$variables) {
   foreach ($variables['tips'] as $name => $tiplist) {
     foreach ($tiplist as $tip_key => $tip) {
       $tiplist[$tip_key]['attributes'] = new Attribute();
-      $tiplist[$tip_key]['tip'] = Xss::filterAdmin($tiplist[$tip_key]['tip']);
     }
 
     $variables['tips'][$name] = array(
diff --git a/core/modules/filter/src/Plugin/Filter/FilterCaption.php b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
index 6f90565..ed241d8 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterCaption.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
@@ -45,7 +45,7 @@ public function process($text, $langcode) {
         // Sanitize caption: decode HTML encoding, limit allowed HTML tags; only
         // allow inline tags that are allowed by default, plus <br>.
         $caption = Html::decodeEntities($caption);
-        $caption = Xss::filter($caption, array('a', 'em', 'strong', 'cite', 'code', 'br'));
+        $caption = SafeMarkup::xssFilter($caption, array('a', 'em', 'strong', 'cite', 'code', 'br'));
 
         // The caption must be non-empty.
         if (Unicode::strlen($caption) === 0) {
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 8ce12ba..90086a6 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -540,7 +540,7 @@ function template_preprocess_forum_list(&$variables) {
   $row = 0;
   // Sanitize each forum so that the template can safely print the data.
   foreach ($variables['forums'] as $id => $forum) {
-    $variables['forums'][$id]->description = Xss::filterAdmin($forum->description->value);
+    $variables['forums'][$id]->description = array('#markup' => $forum->description->value);
     $variables['forums'][$id]->link = forum_uri($forum);
     $variables['forums'][$id]->name = SafeMarkup::checkPlain($forum->label());
     $variables['forums'][$id]->is_container = !empty($forum->forum_container->value);
diff --git a/core/modules/language/src/Form/NegotiationConfigureForm.php b/core/modules/language/src/Form/NegotiationConfigureForm.php
index d33138b..938564e 100644
--- a/core/modules/language/src/Form/NegotiationConfigureForm.php
+++ b/core/modules/language/src/Form/NegotiationConfigureForm.php
@@ -301,7 +301,7 @@ protected function configureFormTable(array &$form, $type)  {
           $table_form['enabled'][$method_id]['#attributes'] = array('disabled' => 'disabled');
         }
 
-        $table_form['description'][$method_id] = array('#markup' => Xss::filterAdmin($method['description']));
+        $table_form['description'][$method_id] = array('#markup' => $method['description']);
 
         $config_op = array();
         if (isset($method['config_route_name'])) {
diff --git a/core/modules/menu_ui/src/Controller/MenuController.php b/core/modules/menu_ui/src/Controller/MenuController.php
index 7287054..560bb18 100644
--- a/core/modules/menu_ui/src/Controller/MenuController.php
+++ b/core/modules/menu_ui/src/Controller/MenuController.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\menu_ui\Controller;
 
-use Drupal\Component\Utility\Xss;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Menu\MenuParentFormSelectorInterface;
 use Drupal\system\MenuInterface;
@@ -77,7 +77,7 @@ public function getParentOptions(Request $request) {
    *   The menu label.
    */
   public function menuTitle(MenuInterface $menu) {
-    return Xss::filter($menu->label());
+    return SafeMarkup::xssFilter($menu->label());
   }
 
 }
diff --git a/core/modules/menu_ui/src/MenuListBuilder.php b/core/modules/menu_ui/src/MenuListBuilder.php
index d93f945..95d029c 100644
--- a/core/modules/menu_ui/src/MenuListBuilder.php
+++ b/core/modules/menu_ui/src/MenuListBuilder.php
@@ -39,7 +39,7 @@ public function buildRow(EntityInterface $entity) {
       'data' => $this->getLabel($entity),
       'class' => array('menu-label'),
     );
-    $row['description'] = Xss::filterAdmin($entity->getDescription());
+    $row['description']['data'] = ['#markup' => $entity->getDescription()];
     return $row + parent::buildRow($entity);
   }
 
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index f7374da..062686a 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -508,7 +508,9 @@ function template_preprocess_node_add_list(&$variables) {
       $variables['types'][$type->id()] = array(
         'type' => $type->id(),
         'add_link' => \Drupal::l($type->label(), new Url('node.add', array('node_type' => $type->id()))),
-        'description' => Xss::filterAdmin($type->getDescription()),
+        'description' => array(
+          '#markup' => $type->getDescription(),
+        ),
       );
     }
   }
diff --git a/core/modules/node/src/Controller/NodeController.php b/core/modules/node/src/Controller/NodeController.php
index b8e4918..3291f33 100644
--- a/core/modules/node/src/Controller/NodeController.php
+++ b/core/modules/node/src/Controller/NodeController.php
@@ -8,7 +8,6 @@
 namespace Drupal\node\Controller;
 
 use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Component\Utility\Xss;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Datetime\DateFormatter;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
@@ -195,7 +194,7 @@ public function revisionOverview(NodeInterface $node) {
           '#context' => [
             'date' => $link,
             'username' => $this->renderer->renderPlain($username),
-            'message' => Xss::filter($revision->revision_log->value),
+            'message' => SafeMarkup::xssFilter($revision->revision_log->value),
           ],
         ],
       ];
diff --git a/core/modules/node/src/NodeTypeListBuilder.php b/core/modules/node/src/NodeTypeListBuilder.php
index 46bec74..89ea825 100644
--- a/core/modules/node/src/NodeTypeListBuilder.php
+++ b/core/modules/node/src/NodeTypeListBuilder.php
@@ -10,7 +10,6 @@
 use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
 use Drupal\Core\Url;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Component\Utility\Xss;
 
 /**
  * Defines a class to build a listing of node type entities.
@@ -39,7 +38,7 @@ public function buildRow(EntityInterface $entity) {
       'data' => $this->getLabel($entity),
       'class' => array('menu-label'),
     );
-    $row['description'] = Xss::filterAdmin($entity->getDescription());
+    $row['description']['data'] = ['#markup' => $entity->getDescription()];
     return $row + parent::buildRow($entity);
   }
 
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index b4c4126..25d52b5 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -8,7 +8,6 @@
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\Unicode;
-use Drupal\Component\Utility\Xss;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -768,7 +767,7 @@ function search_excerpt($keys, $text, $langcode = NULL) {
   // Highlight keywords. Must be done at once to prevent conflicts ('strong'
   // and '<strong>').
   $text = trim(preg_replace('/' . $boundary . '(?:' . implode('|', $keys) . ')' . $boundary . '/iu', '<strong>\0</strong>', ' ' . $text . ' '));
-  return Xss::filter($text, ['strong']);
+  return SafeMarkup::xssFilter($text, ['strong']);
 }
 
 /**
diff --git a/core/modules/simpletest/src/Form/SimpletestResultsForm.php b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
index ea3447e..49c086c 100644
--- a/core/modules/simpletest/src/Form/SimpletestResultsForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
@@ -313,7 +313,7 @@ public static function addResultForm(array &$form, array $results) {
       $rows = array();
       foreach ($assertions as $assertion) {
         $row = array();
-        $row[] = SafeMarkup::checkAdminXss($assertion->message);
+        $row[] = ['data' => ['#markup' => $assertion->message]];
         $row[] = $assertion->message_group;
         $row[] = \Drupal::service('file_system')->basename(($assertion->file));
         $row[] = $assertion->line;
diff --git a/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php b/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php
index c5ebaa5..16b0368 100644
--- a/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php
+++ b/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Plugin\Block;
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Block\BlockBase;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Config\ConfigFactoryInterface;
@@ -173,7 +174,7 @@ public function build() {
     );
 
     $build['site_slogan'] = array(
-      '#markup' => Xss::filterAdmin($site_config->get('slogan')),
+      '#markup' => $site_config->get('slogan'),
       '#access' => $this->configuration['use_site_slogan'],
     );
 
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 276e393..1141a35 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -58,7 +58,7 @@ function template_preprocess_admin_block_content(&$variables) {
     foreach ($variables['content'] as $key => $item) {
       $variables['content'][$key]['link'] = \Drupal::l($item['title'], $item['url']);
       if (!$variables['compact'] && isset($item['description'])) {
-        $variables['content'][$key]['description'] = Xss::filterAdmin($item['description']);
+        $variables['content'][$key]['description'] = ['#markup' => $item['description']];
       }
       else {
         $variables['content'][$key]['description'] = FALSE;
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 732e27b..24673ee 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -802,7 +802,7 @@ function system_preprocess_block(&$variables) {
       }
       $variables['site_slogan'] = '';
       if ($variables['content']['site_slogan']['#access'] && $variables['content']['site_slogan']['#markup']) {
-        $variables['site_slogan'] = $variables['content']['site_slogan']['#markup'];
+        $variables['site_slogan']['#markup'] = $variables['content']['site_slogan']['#markup'];
       }
       break;
 
diff --git a/core/modules/taxonomy/src/Controller/TaxonomyController.php b/core/modules/taxonomy/src/Controller/TaxonomyController.php
index e24eb6f..2e35678 100644
--- a/core/modules/taxonomy/src/Controller/TaxonomyController.php
+++ b/core/modules/taxonomy/src/Controller/TaxonomyController.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\taxonomy\Controller;
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\taxonomy\TermInterface;
@@ -54,7 +55,7 @@ public function addForm(VocabularyInterface $taxonomy_vocabulary) {
    *   The term label.
    */
   public function vocabularyTitle(VocabularyInterface $taxonomy_vocabulary) {
-    return Xss::filter($taxonomy_vocabulary->label());
+    return SafeMarkup::xssFilter($taxonomy_vocabulary->label());
   }
 
   /**
@@ -67,7 +68,7 @@ public function vocabularyTitle(VocabularyInterface $taxonomy_vocabulary) {
    *   The term label.
    */
   public function termTitle(TermInterface $taxonomy_term) {
-    return Xss::filter($taxonomy_term->getName());
+    return SafeMarkup::xssFilter($taxonomy_term->getName());
   }
 
 }
diff --git a/core/modules/user/src/Controller/UserController.php b/core/modules/user/src/Controller/UserController.php
index 03dcb10..a52372d 100644
--- a/core/modules/user/src/Controller/UserController.php
+++ b/core/modules/user/src/Controller/UserController.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\user\Controller;
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Datetime\DateFormatter;
@@ -161,7 +162,7 @@ public function userPage() {
    *   The user account name.
    */
   public function userTitle(UserInterface $user = NULL) {
-    return $user ? Xss::filter($user->getUsername()) : '';
+    return $user ? SafeMarkup::xssFilter($user->getUsername()) : '';
   }
 
   /**
diff --git a/core/modules/views/src/Plugin/Block/ViewsBlock.php b/core/modules/views/src/Plugin/Block/ViewsBlock.php
index 8762c32..97db7c9 100644
--- a/core/modules/views/src/Plugin/Block/ViewsBlock.php
+++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\views\Plugin\Block;
 
-use Drupal\Core\Config\Entity\Query\Query;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Xss;
+use Drupal\Core\Config\Entity\Query\Query;
 use Drupal\Core\Form\FormStateInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -32,7 +33,8 @@ public function build() {
     if ($output = $this->view->buildRenderable($this->displayID, [], FALSE)) {
       // Override the label to the dynamic title configured in the view.
       if (empty($this->configuration['views_label']) && $this->view->getTitle()) {
-        $output['#title'] = Xss::filterAdmin($this->view->getTitle());
+        // @todo https://www.drupal.org/node/2527360 remove call to SafeMarkup.
+        $output['#title'] = SafeMarkup::xssFilter($this->view->getTitle(), Xss::getAdminTagList());
       }
 
       // Before returning the block output, convert it to a renderable array
diff --git a/core/modules/views/src/Plugin/views/display/Page.php b/core/modules/views/src/Plugin/views/display/Page.php
index 193979e..7ec3560 100644
--- a/core/modules/views/src/Plugin/views/display/Page.php
+++ b/core/modules/views/src/Plugin/views/display/Page.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Plugin\views\display;
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Form\FormStateInterface;
@@ -181,7 +182,8 @@ public function execute() {
     //   it should be dropped.
     if (is_array($render)) {
       $render += array(
-        '#title' => Xss::filterAdmin($this->view->getTitle()),
+        // @todo https://www.drupal.org/node/2527360 remove call to SafeMarkup.
+        '#title' => SafeMarkup::xssFilter($this->view->getTitle(), Xss::getAdminTagList()),
       );
     }
     return $render;
diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php
index 386456c..251d025 100644
--- a/core/modules/views/src/Plugin/views/field/Field.php
+++ b/core/modules/views/src/Plugin/views/field/Field.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\views\Plugin\views\field;
 
-use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Xss as CoreXss;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
@@ -671,7 +670,7 @@ public function renderItems($items) {
     if (!empty($items)) {
       $items = $this->prepareItemsByDelta($items);
       if ($this->options['multi_type'] == 'separator' || !$this->options['group_rows']) {
-        $separator = $this->options['multi_type'] == 'separator' ? SafeMarkup::checkAdminXss($this->options['separator']) : '';
+        $separator = $this->options['multi_type'] == 'separator' ? CoreXss::filterAdmin($this->options['separator']) : '';
         $build = [
           '#type' => 'inline_template',
           '#template' => '{{ items | safe_join(separator) }}',
diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module
index 45cf248..86add67 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -129,7 +129,7 @@ function views_ui_preprocess_views_view(&$variables) {
 
   // Render title for the admin preview.
   if (!empty($view->live_preview)) {
-    $variables['title'] = Xss::filterAdmin($view->getTitle());
+    $variables['title']['#markup'] = $view->getTitle();
   }
 
   if (!empty($view->live_preview) && \Drupal::moduleHandler()->moduleExists('contextual')) {
diff --git a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
index 1776e75..90a475f 100644
--- a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\Component\Utility;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Utility\Xss;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -213,6 +214,41 @@ public function testReplace($search, $replace, $subject, $expected, $is_safe) {
   }
 
   /**
+   * Tests the interaction between the safe list and XSS filtering.
+   *
+   * @covers ::xssFilter
+   * @covers ::escape
+   */
+  public function testAdminXss() {
+    // Use the predefined XSS admin tag list. This strips the <marquee> tags.
+    $this->assertEquals('text', SafeMarkup::xssFilter('<marquee>text</marquee>', Xss::getAdminTagList()));
+    $this->assertTrue(SafeMarkup::isSafe('text'), 'The string \'text\' is marked as safe.');
+
+    // This won't strip the <marquee> tags and the string with HTML will be
+    // marked as safe.
+    $filtered = SafeMarkup::xssFilter('<marquee>text</marquee>', array('marquee'));
+    $this->assertEquals('<marquee>text</marquee>', $filtered);
+    $this->assertTrue(SafeMarkup::isSafe('<marquee>text</marquee>'), 'The string \'<marquee>text</marquee>\' is marked as safe.');
+
+    // SafeMarkup::xssFilter() with the default tag list will strip the
+    // <marquee> tag even though the string was marked safe above.
+    $this->assertEquals('text', SafeMarkup::xssFilter('<marquee>text</marquee>'));
+
+    // SafeMarkup::escape() will not escape the markup tag since the string was
+    // marked safe above.
+    $this->assertEquals('<marquee>text</marquee>', SafeMarkup::escape($filtered));
+
+    // SafeMarkup::checkPlain() will escape the markup tag even though the
+    // string was marked safe above.
+    $this->assertEquals('&lt;marquee&gt;text&lt;/marquee&gt;', SafeMarkup::checkPlain($filtered));
+
+    // Ensure that SafeMarkup::xssFilter strips all tags when passed an empty
+    // array and uses the default tag list when not passed a tag list.
+    $this->assertEquals('text', SafeMarkup::xssFilter('<em>text</em>', []));
+    $this->assertEquals('<em>text</em>', SafeMarkup::xssFilter('<em>text</em>'));
+  }
+
+  /**
    * Data provider for testReplace().
    *
    * @see testReplace()
diff --git a/core/themes/bartik/bartik.theme b/core/themes/bartik/bartik.theme
index 1f69448..4f83a9e 100644
--- a/core/themes/bartik/bartik.theme
+++ b/core/themes/bartik/bartik.theme
@@ -125,6 +125,6 @@ function _bartik_process_page(&$variables) {
   if ($variables['hide_site_slogan']) {
     // If toggle_site_slogan is FALSE, the site_slogan will be empty, so we
     // rebuild it.
-    $variables['site_slogan'] = Xss::filterAdmin($site_config->get('slogan'));
+    $variables['site_slogan']['#markup'] = $site_config->get('slogan');
   }
 }
diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme
index b71e22c..b508d94 100644
--- a/core/themes/seven/seven.theme
+++ b/core/themes/seven/seven.theme
@@ -75,7 +75,6 @@ function seven_preprocess_node_add_list(&$variables) {
     /** @var \Drupal\node\NodeTypeInterface $type */
     foreach ($variables['content'] as $type) {
       $variables['types'][$type->id()]['label'] = SafeMarkup::checkPlain($type->label());
-      $variables['types'][$type->id()]['description'] = Xss::filterAdmin($type->getDescription());
       $variables['types'][$type->id()]['url'] = \Drupal::url('node.add', array('node_type' => $type->id()));
     }
   }
@@ -85,13 +84,12 @@ function seven_preprocess_node_add_list(&$variables) {
  * Implements hook_preprocess_HOOK() for block content add list templates.
  *
  * Displays the list of available custom block types for creation, adding
- * separate variables for the label, description, and url.
+ * separate variables for the label and url.
  */
 function seven_preprocess_block_content_add_list(&$variables) {
   if (!empty($variables['content'])) {
     foreach ($variables['content'] as $type) {
       $variables['types'][$type->id()]['label'] = SafeMarkup::checkPlain($type->label());
-      $variables['types'][$type->id()]['description'] = Xss::filterAdmin($type->getDescription());
       $options = array('query' => \Drupal::request()->query->all());
       $variables['types'][$type->id()]['url'] = \Drupal::url('block_content.add_form', array('block_content_type' => $type->id()), $options);
     }
